├── .env.example ├── .eslintrc ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── README.md ├── docs ├── .nojekyll ├── assets │ ├── hierarchy.js │ ├── highlight.css │ ├── icons.js │ ├── icons.svg │ ├── main.js │ ├── navigation.js │ ├── search.js │ └── style.css ├── classes │ └── EvmAgentKit.html ├── functions │ ├── createEvmTools.html │ ├── createMcpServer.html │ ├── createVercelAITools.html │ ├── executeAction.html │ ├── findAction.html │ ├── getActionExamples.html │ ├── startMcpServer.html │ └── zodToMCPShape.html ├── index.html ├── interfaces │ ├── Action.html │ ├── ActionExample.html │ ├── Config.html │ ├── Creator.html │ ├── FluxbeamServerResponse.html │ ├── Quote.html │ ├── TokenCheck.html │ └── TransformedResponse.html ├── modules.html ├── types │ ├── GetDebridgeTokensInfoParams.html │ ├── Handler.html │ └── MCPSchemaShape.html └── variables │ ├── ACTIONS.html │ ├── EVM_ADDRESS_REGEX.html │ ├── chainIdSchema.html │ └── getDebridgeTokensInfoSchema.html ├── guides ├── add_your_own_tool.md ├── setup_locally.md └── test_it_out.md ├── ls.vitest.config.ts ├── package.json ├── src ├── actions │ ├── agent │ │ ├── createImage.ts │ │ ├── getWalletAddress.ts │ │ └── get_info.ts │ ├── coingecko │ │ ├── getCoingeckoLatestPools.ts │ │ ├── getCoingeckoTokenInfo.ts │ │ ├── getCoingeckoTokenPriceData.ts │ │ ├── getCoingeckoTopGainers.ts │ │ ├── getCoingeckoTrendingPools.ts │ │ └── getCoingeckoTrendingTokens.ts │ ├── compound │ │ └── supply.ts │ ├── defillama │ │ ├── fetch_price_action.ts │ │ └── get_protocol_tvl_action.ts │ ├── elfa_ai │ │ └── elfa_ai_actions.ts │ ├── fourmeme │ │ ├── create_token_action.ts │ │ ├── get_token_holdings_action.ts │ │ ├── get_trending_tokens_action.ts │ │ ├── purchase_token_action.ts │ │ └── sell_token_action.ts │ └── index.ts ├── agent │ └── index.ts ├── constants │ └── index.ts ├── index.ts ├── langchain │ ├── agent │ │ ├── create_image.ts │ │ ├── get_info.ts │ │ ├── index.ts │ │ └── wallet_address.ts │ ├── compound │ │ ├── index.ts │ │ └── supply.ts │ ├── defillama │ │ ├── fetch_price.ts │ │ ├── get_protocol_tvl.ts │ │ └── index.ts │ ├── elfa_ai │ │ ├── elfa_ai_api.ts │ │ └── index.ts │ ├── fourmeme │ │ ├── create_token.ts │ │ ├── get_token_holdings.ts │ │ ├── get_trending_tokens.ts │ │ ├── index.ts │ │ ├── purchase_token.ts │ │ └── sell_token.ts │ └── index.ts ├── mcp │ └── index.ts ├── network │ ├── index.ts │ ├── network.ts │ └── types.ts ├── tools │ ├── agent │ │ ├── create_image.ts │ │ ├── get_info.ts │ │ ├── get_wallet_address.ts │ │ └── index.ts │ ├── coingecko │ │ ├── get_latest_pools.ts │ │ ├── get_token_info.ts │ │ ├── get_token_price_data.ts │ │ ├── get_top_gainers.ts │ │ ├── get_trending_pools.ts │ │ ├── get_trending_tokens.ts │ │ └── index.ts │ ├── compound │ │ ├── constants.ts │ │ ├── index.ts │ │ ├── supply.ts │ │ └── utils.ts │ ├── defillama │ │ ├── constants.ts │ │ ├── fetch_price.ts │ │ ├── get_protocol_tvl.ts │ │ └── index.ts │ ├── elfa_ai │ │ ├── elfa_ai_api.ts │ │ └── index.ts │ ├── erc20 │ │ ├── get_erc20_balance.ts │ │ ├── index.ts │ │ └── transfer.ts │ ├── fourmeme │ │ ├── index.ts │ │ ├── token_manager.ts │ │ └── token_manager_abi.ts │ └── index.ts ├── types │ ├── action.ts │ └── index.ts ├── utils │ ├── actionExecutor.ts │ ├── index.ts │ └── zodToMCPSchema.ts ├── vercel-ai │ └── index.ts └── wallet-providers │ ├── evmWalletProvider.ts │ ├── index.ts │ ├── viemWalletProvider.ts │ └── walletProvider.ts ├── test ├── index.ts └── mcp.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= 2 | RPC_URL= 3 | EVM_PRIVATE_KEY= 4 | ELFA_AI_API_KEY= 5 | COINGECKO_PRO_API_KEY= -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint", "prettier"], 5 | "extends": [ 6 | "eslint:recommended", 7 | "plugin:@typescript-eslint/recommended", 8 | "prettier" 9 | ], 10 | "ignorePatterns": ["examples/**/*", "src/utils/keypair.ts", "test/**/*", "dist/**/*", "src/langchain/evals/**/*"], 11 | "rules": { 12 | "prettier/prettier": [ 13 | "error", 14 | { 15 | "endOfLine": "auto" 16 | } 17 | ], 18 | "no-constant-condition": "off", 19 | "@typescript-eslint/explicit-function-return-type": "off", 20 | "@typescript-eslint/no-explicit-any": "off", 21 | "@typescript-eslint/no-empty-object-type": "off", 22 | "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }], 23 | "no-console": ["warn", { "allow": ["warn", "error"] }], 24 | "curly": ["error", "all"], 25 | "eqeqeq": ["error", "always"], 26 | "no-floating-decimal": "error", 27 | "no-var": "error", 28 | "prefer-const": "error", 29 | }, 30 | "env": { 31 | "browser": true, 32 | "node": true, 33 | "es6": true 34 | }, 35 | "parserOptions": { 36 | "ecmaVersion": 2020, 37 | "sourceType": "module" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .env 4 | 5 | # VSCode 6 | .vscode 7 | 8 | .DS_Store 9 | .turbo 10 | .turbo/ 11 | 12 | CLAUDE.md -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | tsc && lint-staged -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "**/*.+(ts|tsx)": [ 3 | "eslint . --ext .ts --fix" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | docs 4 | *.md -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": false, 5 | "printWidth": 80, 6 | "tabWidth": 2, 7 | "useTabs": false, 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "endOfLine": "auto", 11 | "bracketSameLine": false 12 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to EVM Agent Kit 2 | 3 | Thank you for your interest in contributing to EVM Agent Kit! This document provides guidelines and instructions for contributing to this project. 4 | 5 | ## Table of Contents 6 | - [Code of Conduct](#code-of-conduct) 7 | - [Getting Started](#getting-started) 8 | - [Development Workflow](#development-workflow) 9 | - [Code Style Guidelines](#code-style-guidelines) 10 | - [Testing](#testing) 11 | - [Documentation](#documentation) 12 | - [Pull Request Process](#pull-request-process) 13 | - [Release Process](#release-process) 14 | - [Creating New Tools and Actions](#creating-new-tools-and-actions) 15 | 16 | ## Code of Conduct 17 | 18 | By participating in this project, you agree to abide by our Code of Conduct: 19 | - Be respectful and inclusive 20 | - Be patient and welcoming 21 | - Be thoughtful 22 | - Be collaborative 23 | - Ask for help when unsure 24 | - Stay on topic 25 | - Be open to constructive feedback 26 | 27 | ## Getting Started 28 | 29 | 1. Fork the repository 30 | 2. Clone your fork: 31 | ```bash 32 | git clone https://github.com/your-username/evm-agent-kit.git 33 | ``` 34 | 3. Add the upstream repository: 35 | ```bash 36 | git remote add upstream https://github.com/original-owner/evm-agent-kit.git 37 | ``` 38 | 4. Install dependencies: 39 | ```bash 40 | pnpm install 41 | ``` 42 | 5. Create a new branch for your feature/fix: 43 | ```bash 44 | git checkout -b feature/your-feature-name 45 | ``` 46 | 47 | ### Environment Setup 48 | 49 | 1. Copy the example environment file: 50 | ```bash 51 | cp .env.example .env 52 | ``` 53 | 2. Fill in the required environment variables in `.env` 54 | 55 | ### Requirements 56 | - Node.js >= 20.0.0 57 | - pnpm >= 8.0.0 58 | 59 | ## Development Workflow 60 | 61 | 1. Make sure you have the latest changes from the main branch: 62 | ```bash 63 | git fetch upstream 64 | git checkout main 65 | git merge upstream/main 66 | git checkout your-feature-branch 67 | git rebase main 68 | ``` 69 | 70 | 2. Make your changes following the code style guidelines 71 | 3. Write or update tests as needed 72 | 4. Update documentation if necessary 73 | 5. Commit your changes using conventional commits: 74 | ``` 75 | feat: add new feature 76 | fix: resolve bug 77 | docs: update documentation 78 | test: add test cases 79 | chore: update dependencies 80 | ``` 81 | 6. Push your changes to your fork: 82 | ```bash 83 | git push origin your-feature-branch 84 | ``` 85 | 86 | ## Code Style Guidelines 87 | 88 | We use ESLint and Prettier to maintain consistent code style. Our configuration includes: 89 | 90 | ### TypeScript Standards 91 | - Use TypeScript for all new code 92 | - Maintain strict type checking 93 | - Avoid using `any` type when possible 94 | - Use explicit return types for functions 95 | - Follow the existing code structure and patterns 96 | 97 | ### Formatting Rules 98 | - Use double quotes for strings 99 | - Use 2 spaces for indentation 100 | - Maximum line length of 100 characters 101 | - Always use semicolons 102 | - Use trailing commas in multiline constructs 103 | - Always use curly braces for control statements 104 | 105 | ### Best Practices 106 | - Write self-documenting code with clear variable and function names 107 | - Keep functions small and focused 108 | - Use async/await for asynchronous operations 109 | - Handle errors appropriately 110 | - Add comments for complex logic 111 | - Follow the DRY (Don't Repeat Yourself) principle 112 | 113 | ### Git Hooks 114 | We use Husky and lint-staged to enforce code quality: 115 | - Pre-commit hooks run linting and formatting 116 | - Commits are blocked if code style checks fail 117 | 118 | ## Testing 119 | 120 | 1. Write tests for all new features and bug fixes 121 | 2. Run tests using the appropriate command: 122 | ```bash 123 | # Run all tests 124 | pnpm test 125 | 126 | # Run Vercel AI specific tests 127 | pnpm test:vercel-ai 128 | 129 | # Run MCP tests 130 | pnpm test:mcp 131 | 132 | # Run tests with Vitest 133 | pnpm eval 134 | ``` 135 | 3. Maintain or improve test coverage 136 | 4. Include both unit tests and integration tests where appropriate 137 | 138 | ## Documentation 139 | 140 | 1. Update the README.md if needed 141 | 2. Document new features and APIs 142 | 3. Include examples for new functionality 143 | 4. Update any relevant guides in the `guides/` directory 144 | 5. Add JSDoc comments for public APIs 145 | 6. Generate documentation: 146 | ```bash 147 | pnpm docs 148 | ``` 149 | 7. Generate tool summaries: 150 | ```bash 151 | pnpm tool-summary 152 | pnpm tool-summary:langchain 153 | ``` 154 | 155 | ## Project Structure 156 | 157 | The project is organized into the following main directories: 158 | 159 | - `src/` 160 | - `actions/` - Action definitions for AI agents 161 | - `agent/` - Core agent implementation 162 | - `langchain/` - LangChain tool implementations 163 | - `tools/` - Core tool implementations 164 | - `utils/` - Utility functions 165 | - `wallet-providers/` - Wallet provider implementations 166 | - `constants/` - Project constants 167 | - `types/` - TypeScript type definitions 168 | - `mcp/` - Model Context Protocol implementation 169 | - `vercel-ai/` - Vercel AI SDK integration 170 | - `network/` - Network-related functionality 171 | 172 | ## Pull Request Process 173 | 174 | 1. **Important**: Direct pushes to the main branch are not allowed. All changes must go through pull requests. 175 | 2. Create a pull request from your feature branch to the main branch 176 | 3. Ensure your PR addresses a single concern 177 | 4. Update the README.md with details of changes if needed 178 | 5. Update the documentation if you're changing functionality 179 | 6. The PR will be merged once you have the sign-off of at least one maintainer 180 | 7. Make sure all CI checks pass 181 | 8. Keep your PR up to date with the main branch: 182 | ```bash 183 | git fetch upstream 184 | git rebase upstream/main 185 | git push -f origin your-feature-branch 186 | ``` 187 | 188 | ### PR Description Template 189 | ```markdown 190 | ## Description 191 | [Description of the changes] 192 | 193 | ## Type of Change 194 | - [ ] Bug fix 195 | - [ ] New feature 196 | - [ ] Breaking change 197 | - [ ] Documentation update 198 | 199 | ## Testing 200 | [Description of how you tested the changes] 201 | 202 | ## Checklist 203 | - [ ] My code follows the style guidelines 204 | - [ ] I have performed a self-review 205 | - [ ] I have commented my code where needed 206 | - [ ] I have updated the documentation 207 | - [ ] My changes generate no new warnings 208 | - [ ] I have added tests that prove my fix/feature works 209 | - [ ] All tests pass locally 210 | - [ ] I have rebased my branch with the latest main 211 | ``` 212 | 213 | ## Release Process 214 | 215 | 1. Version bumping is handled by maintainers 216 | 2. Releases are made from the main branch 217 | 3. Release notes are generated from PR descriptions 218 | 4. Documentation is updated for each release 219 | 220 | ## Creating New Tools and Actions 221 | 222 | When adding new functionality to EVM Agent Kit, you'll need to create three components: 223 | 1. A core tool that interacts with external services or blockchain operations 224 | 2. A LangChain tool that makes your functionality accessible to AI agents 225 | 3. An action that provides a structured interface for your tool 226 | 227 | For a complete example of creating a new tool, please refer to the [Creating a New Tool](README.md#creating-a-new-tool) section in the README.md file. 228 | 229 | ## Questions? 230 | 231 | If you have any questions, please open an issue or reach out to the maintainers. 232 | 233 | Thank you for contributing to EVM Agent Kit! -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /docs/assets/hierarchy.js: -------------------------------------------------------------------------------- 1 | window.hierarchyData = "eJyrVirKzy8pVrKKjtVRKkpNy0lNLsnMzwMKVNfWAgCbHgqm" -------------------------------------------------------------------------------- /docs/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #795E26; 3 | --dark-hl-0: #DCDCAA; 4 | --light-hl-1: #000000; 5 | --dark-hl-1: #D4D4D4; 6 | --light-hl-2: #A31515; 7 | --dark-hl-2: #CE9178; 8 | --light-hl-3: #AF00DB; 9 | --dark-hl-3: #C586C0; 10 | --light-hl-4: #001080; 11 | --dark-hl-4: #9CDCFE; 12 | --light-hl-5: #008000; 13 | --dark-hl-5: #6A9955; 14 | --light-hl-6: #0000FF; 15 | --dark-hl-6: #569CD6; 16 | --light-hl-7: #0070C1; 17 | --dark-hl-7: #4FC1FF; 18 | --light-hl-8: #098658; 19 | --dark-hl-8: #B5CEA8; 20 | --light-hl-9: #000000; 21 | --dark-hl-9: #C8C8C8; 22 | --light-code-background: #FFFFFF; 23 | --dark-code-background: #1E1E1E; 24 | } 25 | 26 | @media (prefers-color-scheme: light) { :root { 27 | --hl-0: var(--light-hl-0); 28 | --hl-1: var(--light-hl-1); 29 | --hl-2: var(--light-hl-2); 30 | --hl-3: var(--light-hl-3); 31 | --hl-4: var(--light-hl-4); 32 | --hl-5: var(--light-hl-5); 33 | --hl-6: var(--light-hl-6); 34 | --hl-7: var(--light-hl-7); 35 | --hl-8: var(--light-hl-8); 36 | --hl-9: var(--light-hl-9); 37 | --code-background: var(--light-code-background); 38 | } } 39 | 40 | @media (prefers-color-scheme: dark) { :root { 41 | --hl-0: var(--dark-hl-0); 42 | --hl-1: var(--dark-hl-1); 43 | --hl-2: var(--dark-hl-2); 44 | --hl-3: var(--dark-hl-3); 45 | --hl-4: var(--dark-hl-4); 46 | --hl-5: var(--dark-hl-5); 47 | --hl-6: var(--dark-hl-6); 48 | --hl-7: var(--dark-hl-7); 49 | --hl-8: var(--dark-hl-8); 50 | --hl-9: var(--dark-hl-9); 51 | --code-background: var(--dark-code-background); 52 | } } 53 | 54 | :root[data-theme='light'] { 55 | --hl-0: var(--light-hl-0); 56 | --hl-1: var(--light-hl-1); 57 | --hl-2: var(--light-hl-2); 58 | --hl-3: var(--light-hl-3); 59 | --hl-4: var(--light-hl-4); 60 | --hl-5: var(--light-hl-5); 61 | --hl-6: var(--light-hl-6); 62 | --hl-7: var(--light-hl-7); 63 | --hl-8: var(--light-hl-8); 64 | --hl-9: var(--light-hl-9); 65 | --code-background: var(--light-code-background); 66 | } 67 | 68 | :root[data-theme='dark'] { 69 | --hl-0: var(--dark-hl-0); 70 | --hl-1: var(--dark-hl-1); 71 | --hl-2: var(--dark-hl-2); 72 | --hl-3: var(--dark-hl-3); 73 | --hl-4: var(--dark-hl-4); 74 | --hl-5: var(--dark-hl-5); 75 | --hl-6: var(--dark-hl-6); 76 | --hl-7: var(--dark-hl-7); 77 | --hl-8: var(--dark-hl-8); 78 | --hl-9: var(--dark-hl-9); 79 | --code-background: var(--dark-code-background); 80 | } 81 | 82 | .hl-0 { color: var(--hl-0); } 83 | .hl-1 { color: var(--hl-1); } 84 | .hl-2 { color: var(--hl-2); } 85 | .hl-3 { color: var(--hl-3); } 86 | .hl-4 { color: var(--hl-4); } 87 | .hl-5 { color: var(--hl-5); } 88 | .hl-6 { color: var(--hl-6); } 89 | .hl-7 { color: var(--hl-7); } 90 | .hl-8 { color: var(--hl-8); } 91 | .hl-9 { color: var(--hl-9); } 92 | pre, code { background: var(--code-background); } 93 | -------------------------------------------------------------------------------- /docs/assets/navigation.js: -------------------------------------------------------------------------------- 1 | window.navigationData = "eJyFlFFPwjAUhf/LnokKCipvy5hIDIpsISbGkNLdsYWtXdqOTI3/3cEIo1u5vPac87W37b2fv5aCQllDy92m9hqYeomV1bEyoqJykSZESpDXJ+JVpNKkdGxiFljDbu/hr3Nk2FTFnNXxmCkQIaEloZL0cK8/aIXdgqRZAgjj4MBQDmdhvDYyKgkNCyCKC3O60rD4U5IXKyCpB2ILYg4y40ya6zFbMfh7zpWZtVewqM83wJwI6MaYr2UUIgiTIRcpBGhlBh+GHYMawUrEwRr255ATFvIZESSVNV59ZyUZsTZ2uHm87/Z7J7s8ExYkIJrEw/Kl9NSZeTSClHgRyaAJ0dVLLNvxJ2+vXg3ZEhGTVbL745WkE25PwzQiMZsE1XYmhGZAQO5iurRHo7nrecu5O3Y/TLCWCQGuTW9z/pyIHSt/14JQDiSf8+Tke4Q524+Hsn7NoaMGdy3UlGZVA55nHS0XYQsQFBJ7cuFwmg2BQgE0V9CcqzVOMyCgsFw6T6lVBFE+lzaAjeW1TAhQKiIUevu6A0H98MDnux7Ue7MmaYYW6OsfosplYw==" -------------------------------------------------------------------------------- /docs/assets/search.js: -------------------------------------------------------------------------------- 1 | window.searchData = "eJy1nW1z2roSgP8L+Zppa/k932hCe5kmIZfQnnOm0/G44CSeAObaJm1Op//9SrKNd9ECy0u/3FNi7YukR7uSbOn+6uTZj6Jz8fVX5zmdTzoXlgjOO/N4lnQuOr2XWfcxmZef0rJz3lnmU/m38TQuiqR4C569eSpnU1mgfiQLdX6fN/pcS6z0jbN5UebLcZnlO/Wd4cJA93lnEeey5JqDrUnrnXCgzXkyLtNszjLZlj3Y4o94Ok12N9jZqtyRlqJ4MsmTouBaBOWPadWH9JHVolW5gy3li/HnfLrbkiy3zKfHWFrk6UtcJp+S193W6rLPuuw+FsU7px1fj0n5Pp7G83Gy26Is+31V9mCLZR7Pi4eEMfxAyYOtjfNENtKHbJnfJLNklD0nnEGohR6k0EwKlbXQwT48JOX4SZu+y9Nx8v718ilO5/3Jbke0pLa/UJLfX8dKMp2c3pv76ZIxlmh/ikr2VB4xosiaH/vGEdP6XZ6V2Tibjl4Y41xLLGqJ8mXfEb8+/nTN+/OH7HORzh8vM/k/yfg5Y41H3QaplF0q2TGQPcajlQ/XchgU5V2WTRmdIgVXDky14KIWPLp1NBdXcRkf2kQak4lUcNJ2GmWLj5L/JC8G832dWjxWktn85L02kmIT+e8D+q2sRU/Sc7Uu3YP7t1AtrbvvhK2EvTrAlZPa1zz3pg9xNz3AFQ1zIqXj9BivFisfuguGG4uV0XhxlF1Zn5VZOeW5L+NyyeuRlXk5+ykasWP8uJ/FeXkji8mJN8+FQknMWomjrY9+pGWZ5N3xOFvOS9UYezhSVsJxJVzUwkfGtqY93r+O0vEzZ8ZWRbamVb6/lo3cwZ4USZyPn1pPJCc/snzCaJpKsvXluZXcxxvPWZtLymIjFFgflnO9YCve4gJb16SG2i9JPk6m3f523agU14DEQcI9Xtwn+QvoxVY3LrCf39v0rpXYqthu1+jdy1F/cHu/UvgS52n8fSo7uX6yVRFaUH3sjaL+7YdBVEnuUHkWReXrIpEUR2pOFcXbF+KNaCW1yYW/utfX0ovu1dWwd3+/lyN4sXwady6Hve6oF/Vvuh97ezlTdWaUzuLH5DSu9K4/dKO7/u3HvfxQsT9SOeiETnTv+tGn3j/R/ag7+rxfH2l3ZCaKZHyJqlx0QscUv/c33eEouundaqn9nVM46yQRNcHwxA6OBnete+//iUb9y0+94WGOyvTRuvn9NaoyyAkdvu91h5f/Qf7Knv9rMLw6oGmrHIMcbtLMCV0eDXu3V2qYjAafeocQ0Mwao2raeMrW1GyO/uqPRrrHLwefb0d6GB3SmhrSeioT1XMZPaZO5LKC9XIgW7J3+WkQXcs4eD+K7gaD6/2cVZyuliNRtdSN9IrpT7ipO/2wJNY6qTv+hEmNcvFu2L/sRVfdUfdoR/VyPVLr9T/j7l30sdu/7Q2P6XcVqeo1/B9xshn0x/K5Gvx/jtAj4tMGX08ZqD4MPg9veje9qJ76VLTu42SzExzVc6AK0kOdE67XzsbwS4x0LiPfQzyWblRP+DPdwV3vtttvZjI7NJ5li2Qep828ZWMdavc2mLzrDe+ue3/3R/9wzS6SfDFNfqbl67Gmh/3BUBm+7n3pXe80m6dZroxOk5dk83btdpPVRJHdwtW88NgmbgfZ3XDANd2Op0WencyBq97NAR5MktkBLqAhosYceFsLTVaP+INk/TXlBl1nu15PNj5teo2nVunzUi6UdlpCRbnGYPN0dRTq/YxncmRR5lABflOl88Wy5Ok7a8puCITIww3msmXJt7cqfITB5OdiGs9j9FZ+u1UssZ9p8S70Lbfd4vhPPJ9MwZ6JShHF2/qvWzvJ7PvN7vN7W/9nu56zusy2im9SX6SzdJqQ4w5aaIsdYmSSFOM8XTCa5AwXPcRYUnXxziqBcgc13PgpmcU7260pdYiJpzUUN9hoizGNQFD1y47LJ5kRKDPtUz6wZfVaMHvM4xlD51n9FrApT9cBuLnN7EjN57g2y6rwoQbztHgmIVs31hQ81FAxznJWrZqCexgCe7u9LzerrdBh72Pvb2IGbpThbhyPq+8q7vGgaRWj51ylcoVylXzP08lj9flIoV7VbzSxpfT2sL6WID5Seu5iSW9LQ5U0tpRkJ5IP0+XP70k8q/bph0mxyOYFSQNdkj9ui/RRptAljdoW5WdQkEZvQyXoGv93mZWkD/rBHjPKmdqk6pMZp1V1VhVLN2ebyp9tc7EbqXmHFV1uVpU7wIyUTGfL2YCehwE7dcFsyxxsqyEp2NUNssOOLBc35Q4zw2u3quDhDad2VnaYqIsconxzkoP6d6S2dRMoO+vv7LJ8lky2DX2iGH+c/G/TeNuk9ayR2JBnCJ/Jt5QP8g9r8+T2BWX7kPvSM/mZjJdlslEjes5VKpMGWjhQ73+NMlzl/2aTUXZzeXf/FINpS6sYPd8rRykhndmw6iot4YeU3m/nkoNJ8rNz8asjA3ahGvSiI97Yb0JZ8iFNphP1VXizNhlnM/WeRf5rko2X+p/f6mJfEvWJtipclX77rnP+9d25Y70JQ//bt/OvjbB+oP/Q6Gj/ogUt+cuiBC1D0EKCQv4SlKAwBAUStOUvmxK0DUEbCTryl0MJOoaggwRd+cs9t703QRAiQdcQdJGgJ395lEXPEPSQoC9/+ZSgbwj6SDCQvwJKMDAEAyQoCfoaUoKhIRhiABQPFsmOZcJjrdGj8aH5IQDCBFmKC4tkyDIhsjBFlmLDIjmyTJAsTJKl+LBIliwTJgvTZClGLJcUNoGyMFGW4sQimbJMqCxMlaVYsUiuLBMsC5NlKV4ski3LhMvCdFmKGYvkyzIBszBhQjEjSMKESZjAhAnFjCAJEyZhYi1G6SBFRykiTGHChGJGkIQJkzCBCROKGUESJkzCBCZMKGYESZgwCROYMKGYESRhwiRMYMKEYkaQhAmTMIEJE4oZQRImTMIEJkwoZgRJmDAJE5gwWzFjk4TZJmE2JsxWzNgkYbZJmI0JsxUzNkmYbRJmr2VCnQrpXEgkQ0yYrZixScJskzAbE2YrZmySMNskzMaE2YoZmyTMNgmzMWG2YsYmCbNNwmxMmK2YsUnCbJMwGxNmK2ZskjDbJMzGhDmKGYckzDEJczBhjmLGIQlzTMIcTJijmHFIwhyTMAcT5ihmHJIwxyTMWZtv6QkXPeMiplyYMEcx45CEOSZhDibMUcw4JGGOSZiDCXMUMw5JmGMS5mDCHMWMQxLmmIQ5mDBHMeOQhDkmYQ4mzFXMuCRhrkmYiwlzFTP0FNcEzMWAuQoZl6TTNQFzMWCuQsYl6XRNwFwMmKuQcUk6XRMwd21Sr2f1JJ0uMa/HgLkKGZek0zUBczFgrkLGJel0TcBcDJirkHFJOl0TMBcD5ipkXJJO1wTMxYB5GjCSTs8EzMOAeYoZj6TTMwnzMGGeYsYjCfNMwjxMmKeY8UjCPJMwDxPmKWY8kjDPJMzDhHmKGc+hRpVnEuatLR312pEkzCNWj5gwTzHj0StPkzAPE+YpZjySMM8kzMOEeYoZjyTMMwnzMGG+YsYjCfNNwnxMmK+Y8UnCfJMwHxPmi41d5ZuE+ZgwXzHjk3j6JmE+JsxXzPgknr5JmI8J8xUzPomnbxLmY8J8xYxPBkDfJMxf26DQOxQknj6xR4EJ8xUzPomnbxLmY8J8xYxPb46YhPmYsEAx49MbJCZhASYs0ISReAYmYQEmLFDMBCSegUlYgAkLFDMBSVhgEhZgwgLFTCAotgOTsAATFihmApKwwCQswIQFipmAJCwwCQswYYFiJiAJC0zCgrVtML0PRhIWEDthmLBAMROQhAUmYQEmLFTMBCRhoUlYiAkLFTMBvQ1nEhZiwkLFTEgSFpqEhZiw0N4ISWgSFmLCQsVMSOIZmoSFmLBQMROSATA0CQsxYaFiJiTxDE3CQkxYqJgJSTxDk7AQExYqZkISz9AkrP6T3sB/SfIymfSrjfyvXzvNV7G/OlG9uy/Tfv3G4FdHZvaLX79/t7v58hfY0FfPlKVKSdJ81AZ0vQO6rD10FVCLbbdabJunpfkisNXiBq0WOblkaVm9Bm7VBMAZGXE4aupbIJrPflpdMpGsdPk8lzZ9qgnq6YF6+nsqRV+gAp2ACZfHRHOPC1ACdFRCLg8JeMlOq0606gRXTXs9UKvHavUw3aFO1gFaQfvbzLbSGpOXWVmdJgWVDEEtmYRQt6GA6oIhab3bQ+NsvCjqk6OgsqAXbGY3aG0v+mRsnBo1toF/9h7+4U51wciSy1COFvSRIQhhIGx4vB4wviUHfjnAL1702HZiETQbcNPmVXjXaUOgHFBo79EGjBOCrREHdLzD63jzbCnwGRBg88Ig+4wgcBpED4cXPthn54AVkHMcXgLcepAQaAbj1+GNXxmmVkec8+RRaoEZDQQ/n4nhiwqk8/JZXRnXagI0MGFYfYwBBi8A1+eq0V+EmE0VgqYKmUMXfoIOvALd6TksTeYdRiCig5hi7aGOvKgKqIWJkYf2tvumgGLQkBaPOfMCKaAOtKbFGxzqOyKig0GFQ2Y71t8wVokxX32NBaaLgOOAWdmtx9dALgGqXR7cjKOwIDYArhxee+w8xQq0g+mgwxtOzKOnwAaIRg5vKrb70ChQD1KMw0sxrLOewALI6g4vnPJOaAITIEQ6vNxuXLgBsi5gxmYzg9aPaHCHYHCH7B5cXXsIxiEYhlw19PVoIPIAvCy2c5su7wJ6QZdY7C6Z1F+VVz2t+mdRf38O0jPQHPBiOqmZWMrCBmYHI+piKLD4ATAJNkxrNz0BdSDiCHbE2XxfE9AMQBBsEDZeBghIAC5bbJe3X6EHtIP4ZbHj14a78IBagILFHmzkRVSgiYGvgu/r2lVwQB9I9oI9ErbccgdUg7ws2ENhy1VxQDXc9ODNJlYHwcD8E4wrr96K8Xna6qOjQBdoR28PHdXX+yA+A58C3hBSGxI6DhXV98wga4BhE/IIhGckgFdAUcCjTheHDQRig8dTsX7aH0z5QGu7vNYGhzJAvYBTAa+BmkO8oGYARo838YYnN4A3YGwHvHwHz2GD5gHpzeONPOqKA6AQVNHlVXHtukUwdsFURjB16VMooKFAZA2Z9Vu7SwHUDfjj8rIrvMMapH6wxmaqqQ+/gJqBngt54bg+cAJ0gM4KnEoq5DV0fRoT1AmEI58XjprrxMEAATSyVBDTKpBLfF671Ac+gQ4QxHxeD22+jBHwDGAUvBgCDgWCbgMxLeB11+rgOWhrQKHH9EZdo0jvagOXbF6r67Q9ro5Mg6YHinxe3fDJZ6AK0O3zhis40Az0gGHv86aU7cXuIMWC8bqHEn0CjNonCQHoIS+zNf9vCKDfQLftoSIi3hbCvY8DVFFLYzAMbV7D/5tNykwRakxwgHshY0h/O5d5aZFM5bS9c/H12+/f/wdsKKL/"; -------------------------------------------------------------------------------- /docs/functions/createMcpServer.html: -------------------------------------------------------------------------------- 1 | createMcpServer | evm-agent-kit

Function createMcpServer

  • Creates an MCP server from a set of actions

    2 |

    Parameters

    • actions: Record<string, Action>
    • EvmAgentKit: EvmAgentKit
    • options: { name: string; version: string }

    Returns McpServer

3 | -------------------------------------------------------------------------------- /docs/functions/createVercelAITools.html: -------------------------------------------------------------------------------- 1 | createVercelAITools | evm-agent-kit

Function createVercelAITools

2 | -------------------------------------------------------------------------------- /docs/functions/findAction.html: -------------------------------------------------------------------------------- 1 | findAction | evm-agent-kit

Function findAction

  • Find an action by its name or one of its similes

    2 |

    Parameters

    • query: string

    Returns undefined | Action | Action

3 | -------------------------------------------------------------------------------- /docs/functions/getActionExamples.html: -------------------------------------------------------------------------------- 1 | getActionExamples | evm-agent-kit

Function getActionExamples

3 | -------------------------------------------------------------------------------- /docs/functions/zodToMCPShape.html: -------------------------------------------------------------------------------- 1 | zodToMCPShape | evm-agent-kit

Function zodToMCPShape

  • Converts a Zod object schema to a flat shape for MCP tools

    2 |

    Parameters

    • schema: ZodTypeAny

      The Zod schema to convert

      3 |

    Returns { keys: string[]; result: MCPSchemaShape }

    A flattened schema shape compatible with MCP tools

    4 |

    Error if the schema is not an object type

    5 |
6 | -------------------------------------------------------------------------------- /docs/types/GetDebridgeTokensInfoParams.html: -------------------------------------------------------------------------------- 1 | GetDebridgeTokensInfoParams | evm-agent-kit

Type Alias GetDebridgeTokensInfoParams

GetDebridgeTokensInfoParams: z.infer<typeof getDebridgeTokensInfoSchema>
2 | -------------------------------------------------------------------------------- /docs/types/MCPSchemaShape.html: -------------------------------------------------------------------------------- 1 | MCPSchemaShape | evm-agent-kit

Type Alias MCPSchemaShape

MCPSchemaShape: { [key: string]: ZodType<any, ZodTypeDef, any> }

Type declaration

  • [key: string]: ZodType<any, ZodTypeDef, any>
2 | -------------------------------------------------------------------------------- /docs/variables/EVM_ADDRESS_REGEX.html: -------------------------------------------------------------------------------- 1 | EVM_ADDRESS_REGEX | evm-agent-kit

Variable EVM_ADDRESS_REGEXConst

EVM_ADDRESS_REGEX: RegExp = ...
2 | -------------------------------------------------------------------------------- /docs/variables/chainIdSchema.html: -------------------------------------------------------------------------------- 1 | chainIdSchema | evm-agent-kit

Variable chainIdSchemaConst

chainIdSchema: ZodEffects<ZodString, string, string> = ...
2 | -------------------------------------------------------------------------------- /guides/setup_locally.md: -------------------------------------------------------------------------------- 1 | # How to Setup Locally 2 | 3 | Setting up the **EVM Agent Kit** on your local machine involves cloning the repository, installing dependencies, configuring environment variables, and building the project. Follow the steps below to get started. 4 | 5 | ## Prerequisites 6 | 7 | - **Node**: Ensure you have Node version 20.x or higher installed. You can download it from [Node Official Website](https://nodejs.org/). 8 | - **Package Manager**: The project uses `pnpm` for package management. You can install it with `npm install -g pnpm`. 9 | - **Git**: Ensure Git is installed and configured. Download from [Git Official Website](https://git-scm.com/). 10 | 11 | ## Step-by-Step Guide 12 | 13 | 1. **Clone the Repository** 14 | ```bash 15 | git clone https://github.com/hologramlabs/evm-agent-kit.git 16 | ``` 17 | 18 | 2. **Navigate to the Project Directory** 19 | ```bash 20 | cd evm-agent-kit 21 | ``` 22 | 23 | 3. **Install Dependencies** 24 | 25 | Install all necessary dependencies by running: 26 | ```bash 27 | pnpm install 28 | ``` 29 | 30 | 4. **Configure Environment Variables** 31 | 32 | Create a `.env` file in the root directory of the project to store your environment variables securely. This file should include the following variables: 33 | ```env 34 | OPENAI_API_KEY=your_openai_api_key_here 35 | RPC_URL=your_evm_rpc_url_here 36 | EVM_PRIVATE_KEY=your_wallet_private_key 37 | ``` 38 | 39 | - **OPENAI_API_KEY**: Your OpenAI API key for generating images and interacting with OpenAI services. 40 | - **RPC_URL**: Your EVM RPC URL for blockchain interactions (e.g., BSC, Ethereum). 41 | - **EVM_PRIVATE_KEY**: Your wallet's private key. 42 | 43 | **Note:** Ensure that the `.env` file is added to `.gitignore` to prevent exposing sensitive information. 44 | 45 | 5. **Build the Project** 46 | 47 | Compile the TypeScript code to JavaScript using the build script: 48 | ```bash 49 | pnpm run build 50 | ``` 51 | 52 | This will generate the compiled files in the `dist/` directory. 53 | 54 | 6. **Generate Documentation (Optional)** 55 | 56 | If you wish to generate the project documentation, use the following command: 57 | ```bash 58 | pnpm run docs 59 | ``` 60 | 61 | The documentation will be available in the `docs/` directory. 62 | 63 | --- 64 | 65 | **Additional Information:** 66 | 67 | - **Git Configuration:** Ensure that Git is properly configured with your user name and email. You can set them using: 68 | ```bash 69 | git config --global user.name "Your Name" 70 | git config --global user.email "your.email@example.com" 71 | ``` 72 | 73 | - **Verifying Installation:** 74 | 75 | After installing dependencies and building the project, you can verify the installation by running: 76 | ```bash 77 | pnpm run build 78 | pnpm run test 79 | ``` 80 | 81 | Ensure that all tests pass successfully. 82 | 83 | --- -------------------------------------------------------------------------------- /guides/test_it_out.md: -------------------------------------------------------------------------------- 1 | # How to Test It Out 2 | 3 | Testing the **EVM Agent Kit** ensures that all functionalities are working as expected. You can run automated tests or interact with the agent in different ways to verify its operations. 4 | 5 | ## Running Automated Tests 6 | 7 | The project includes test scripts in the `test/` directory. To execute the tests: 8 | 9 | 1. **Ensure Dependencies are Installed** 10 | - If you haven't installed the dependencies yet, refer to the [Setup Locally](./setup_locally.md) guide. 11 | 12 | 2. **Run the Test Script** 13 | ```bash 14 | pnpm run test 15 | ``` 16 | This will run the main test script. Ensure that your environment variables are correctly set in the `.env` file before running the tests. 17 | 18 | 3. **Run MCP Tests** 19 | ```bash 20 | pnpm run test:mcp 21 | ``` 22 | This will run the Model Context Protocol tests. 23 | 24 | 4. **Run Vercel AI Tests** 25 | ```bash 26 | pnpm run test:vercel-ai 27 | ``` 28 | This will run tests for the Vercel AI integration. 29 | 30 | ## Code Examples 31 | 32 | ### Check ERC20 Token Balance 33 | 34 | ```typescript 35 | import { EvmAgentKit } from "evm-agent-kit"; 36 | 37 | const agent = new EvmAgentKit( 38 | "your-wallet-private-key", 39 | "https://rpc-url.example.com", 40 | { OPENAI_API_KEY: "your-openai-api-key" } // optional config 41 | ); 42 | 43 | const balance = await agent.getBalance("0x1234567890123456789012345678901234567890"); 44 | console.log("Token Balance:", balance); 45 | ``` 46 | 47 | ### Transfer ERC20 Tokens 48 | 49 | ```typescript 50 | import { EvmAgentKit } from "evm-agent-kit"; 51 | 52 | const agent = new EvmAgentKit( 53 | "your-wallet-private-key", 54 | "https://rpc-url.example.com", 55 | { OPENAI_API_KEY: "your-openai-api-key" } // optional config 56 | ); 57 | 58 | const txHash = await agent.transfer( 59 | "0x1234567890123456789012345678901234567890", // to address 60 | 1.5, // amount 61 | "0xabcdef1234567890abcdef1234567890abcdef12" // token address (optional for native token) 62 | ); 63 | console.log("Transfer Transaction:", txHash); 64 | ``` 65 | 66 | ### Launch a Token on Four.meme (BSC only) 67 | 68 | ```typescript 69 | import { EvmAgentKit, FOUR_MEME_ADDRESSES } from "evm-agent-kit"; 70 | 71 | const agent = new EvmAgentKit( 72 | "your-wallet-private-key", 73 | "https://bsc-dataseed.binance.org/", // BSC RPC URL 74 | { OPENAI_API_KEY: "your-openai-api-key" } // optional config 75 | ); 76 | 77 | const result = await agent.createFourMemeToken( 78 | FOUR_MEME_ADDRESSES.BSC, // Token Manager Address 79 | 0.1, // Fee in BNB 80 | "My EVM Token", // name 81 | "MET", // short name/symbol 82 | "An awesome EVM token for testing", // description 83 | "https://example.com/token-image.png", // image URL 84 | Date.now() + 3600 * 1000, // launch time (1 hour from now) 85 | "Meme", // category 86 | "https://mytoken.com", // website (optional) 87 | "https://twitter.com/mytoken", // Twitter URL (optional) 88 | "https://t.me/mytoken" // Telegram URL (optional) 89 | ); 90 | 91 | console.log("Token Creation Result:", result); 92 | ``` 93 | 94 | ### Get CoinGecko Token Data 95 | 96 | ```typescript 97 | import { EvmAgentKit } from "evm-agent-kit"; 98 | 99 | const agent = new EvmAgentKit( 100 | "your-wallet-private-key", 101 | "https://rpc-url.example.com", 102 | { OPENAI_API_KEY: "your-openai-api-key" } // optional config 103 | ); 104 | 105 | // Get trending tokens 106 | const trending = await agent.getTrendingTokens(); 107 | console.log("Trending tokens:", trending); 108 | 109 | // Get token price data 110 | const priceData = await agent.getTokenPriceDataUsingCoingecko( 111 | "0x1234567890123456789012345678901234567890" 112 | ); 113 | console.log("Token price data:", priceData); 114 | ``` 115 | 116 | ### Get DeFiLlama Protocol TVL 117 | 118 | ```typescript 119 | import { EvmAgentKit } from "evm-agent-kit"; 120 | 121 | const agent = new EvmAgentKit( 122 | "your-wallet-private-key", 123 | "https://rpc-url.example.com", 124 | { OPENAI_API_KEY: "your-openai-api-key" } // optional config 125 | ); 126 | 127 | const tvl = await agent.fetchProtocolTvl("uniswap"); 128 | console.log("Uniswap TVL:", tvl); 129 | ``` 130 | 131 | ## Using LangChain Tools 132 | 133 | ```typescript 134 | import { EvmAgentKit, createEvmTools } from "evm-agent-kit"; 135 | 136 | const agent = new EvmAgentKit( 137 | "your-wallet-private-key", 138 | "https://rpc-url.example.com", 139 | { OPENAI_API_KEY: "your-openai-api-key" } // optional config 140 | ); 141 | 142 | // Get all available tools 143 | const tools = createEvmTools(agent); 144 | 145 | // Find a specific tool 146 | const getBalanceTool = tools.find(tool => tool.name === "get_erc20_balance"); 147 | 148 | // Use the tool 149 | if (getBalanceTool) { 150 | const result = await getBalanceTool.call("0x1234567890123456789012345678901234567890"); 151 | console.log(JSON.parse(result)); 152 | } 153 | ``` 154 | 155 | ## Best Practices 156 | 157 | ### Environment Setup 158 | - Verify `.env` file contains correct and secure values 159 | - Ensure all required environment variables are set 160 | - Use a secure wallet that you know the private key for testing purposes 161 | 162 | ### Testing 163 | - Maintain comprehensive test coverage 164 | - Monitor console logs during testing 165 | - Start with testnet before moving to mainnet 166 | 167 | ## Troubleshooting 168 | 169 | ### Test Failures 170 | 171 | #### Missing Environment Variables 172 | - **Issue:** Tests fail due to missing environment variables 173 | - **Solution:** Check `.env` file for all required variables 174 | 175 | #### Network Problems 176 | - **Issue:** Network-related errors 177 | - **Solution:** Verify internet connection and EVM RPC endpoint accessibility 178 | - **Solution:** For chain-specific features like Four.meme, ensure you're using the correct chain (BSC) 179 | 180 | ### Agent Issues 181 | 182 | #### Chain Compatibility 183 | - **Issue:** Feature doesn't work on specific chain 184 | - **Solution:** Check if the feature is chain-specific (like Four.meme token creation being BSC-only) -------------------------------------------------------------------------------- /ls.vitest.config.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { defineConfig } from "vitest/config"; 3 | import tsconfigPaths from "vite-tsconfig-paths"; 4 | import { nodePolyfills } from "vite-plugin-node-polyfills"; 5 | 6 | export default defineConfig({ 7 | test: { 8 | include: ["**/*.eval.?(c|m)[jt]s"], 9 | reporters: ["langsmith/vitest/reporter"], 10 | setupFiles: ["dotenv/config"], 11 | }, 12 | plugins: [ 13 | tsconfigPaths(), 14 | nodePolyfills({ 15 | include: ["crypto", "stream", "util"], 16 | }), 17 | ], 18 | // ... existing config ... 19 | resolve: { 20 | alias: { 21 | "@cks-systems/manifest-sdk": path.resolve( 22 | __dirname, 23 | "node_modules/@cks-systems/manifest-sdk/dist/cjs/index.js", 24 | ), 25 | }, 26 | mainFields: ["browser", "module", "main"], 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "evm-agent-kit", 3 | "version": "0.0.0", 4 | "description": "Connect AI Agents to EVM protocols", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "docs": "typedoc src --out docs", 10 | "test": "tsx test/index.ts", 11 | "test:vercel-ai": "tsx test/agent_sdks/vercel_ai.ts", 12 | "test:mcp": "tsx test/mcp.ts", 13 | "generate": "tsx src/utils/keypair.ts", 14 | "lint": "eslint . --ext .ts", 15 | "lint:fix": "eslint . --ext .ts --fix", 16 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 17 | "tool-summary": "tsx src/utils/analyzeTools.ts", 18 | "tool-summary:langchain": "tsx src/utils/analyzeTools.ts --langchain", 19 | "check-tool-names:langchain": "tsx scripts/check-langchain-tool-duplicates.ts", 20 | "eval": "vitest run --config ls.vitest.config.ts" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/evm-kit/evm-agent-kit.git" 25 | }, 26 | "engines": { 27 | "node": ">=20.0.0", 28 | "pnpm": ">=8.0.0" 29 | }, 30 | "keywords": [ 31 | "Ethereum", 32 | "agent", 33 | "ai", 34 | "Evm agent kit" 35 | ], 36 | "author": "evm-kit", 37 | "license": "Apache-2.0", 38 | "dependencies": { 39 | "@ai-sdk/openai": "^1.0.11", 40 | "@langchain/core": "0.3.26", 41 | "@langchain/groq": "^0.1.2", 42 | "@langchain/langgraph": "^0.2.36", 43 | "@langchain/openai": "^0.3.16", 44 | "@modelcontextprotocol/sdk": "^1.5.0", 45 | "@openzeppelin/contracts": "^5.2.0", 46 | "ai": "4.0.22", 47 | "axios": "^1.7.9", 48 | "bn.js": "^5.2.1", 49 | "bs58": "^6.0.0", 50 | "chai": "^5.1.2", 51 | "decimal.js": "^10.4.3", 52 | "dotenv": "^16.4.7", 53 | "ethers": "^6.13.5", 54 | "form-data": "^4.0.1", 55 | "fuse.js": "^7.1.0", 56 | "langchain": "^0.3.8", 57 | "langsmith": "^0.3.7", 58 | "openai": "^4.77.0", 59 | "tiktoken": "^1.0.18", 60 | "typedoc": "^0.27.6", 61 | "viem": "^2.23.14", 62 | "web3": "^4.16.0", 63 | "zod": "^3.24.1" 64 | }, 65 | "devDependencies": { 66 | "@types/bn.js": "^5.1.6", 67 | "@types/chai": "^5.0.1", 68 | "@types/node": "^22.10.2", 69 | "@typescript-eslint/eslint-plugin": "^8.18.2", 70 | "@typescript-eslint/parser": "^8.18.2", 71 | "eslint": "^8.56.0", 72 | "eslint-config-prettier": "^9.1.0", 73 | "eslint-plugin-prettier": "^5.2.1", 74 | "husky": "^9.1.7", 75 | "lint-staged": "^15.3.0", 76 | "prettier": "^3.4.2", 77 | "tsx": "^4.19.2", 78 | "typescript": "^5.7.2", 79 | "vite-plugin-node-polyfills": "^0.23.0", 80 | "vite-tsconfig-paths": "^5.1.4", 81 | "vitest": "^3.0.5" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/actions/agent/createImage.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "../../types/action"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { z } from "zod"; 4 | import { create_image } from "../../tools/agent"; 5 | 6 | const createImageAction: Action = { 7 | name: "CREATE_IMAGE", 8 | similes: [ 9 | "generate image", 10 | "create artwork", 11 | "make image", 12 | "generate artwork", 13 | "create picture", 14 | "generate picture", 15 | ], 16 | description: 17 | "Create an AI-generated image based on a text prompt using OpenAI's DALL-E models", 18 | examples: [ 19 | [ 20 | { 21 | input: { 22 | prompt: "A beautiful sunset over a mountain landscape", 23 | model: "dall-e-3", 24 | size: "1024x1024", 25 | quality: "standard", 26 | style: "natural", 27 | }, 28 | output: { 29 | status: "success", 30 | imageUrl: "https://example.com/image.png", 31 | message: "Successfully generated image", 32 | }, 33 | explanation: "Generate an image of a sunset landscape using DALL-E 3", 34 | }, 35 | ], 36 | ], 37 | schema: z.object({ 38 | prompt: z 39 | .string() 40 | .min(1) 41 | .max(1000) 42 | .describe("The text description of the image to generate"), 43 | model: z 44 | .enum(["dall-e-3"]) 45 | .default("dall-e-3") 46 | .describe("The AI model to use for generation"), 47 | size: z 48 | .enum(["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]) 49 | .default("1024x1024") 50 | .describe("The size of the generated image"), 51 | quality: z 52 | .enum(["standard", "hd"]) 53 | .default("standard") 54 | .describe("The quality level of the generated image"), 55 | style: z 56 | .enum(["natural", "vivid"]) 57 | .default("natural") 58 | .describe("The style of the generated image"), 59 | }), 60 | handler: async (agent: EvmAgentKit, input: Record) => { 61 | try { 62 | if (!agent.config?.OPENAI_API_KEY) { 63 | return { 64 | status: "error", 65 | message: "OpenAI API key not found in agent configuration", 66 | }; 67 | } 68 | 69 | const { prompt, model, size } = input; 70 | const response = await create_image(agent, prompt, model, size); 71 | 72 | return { 73 | status: "success", 74 | imageUrl: response.images[0].url, 75 | message: "Successfully generated image", 76 | }; 77 | } catch (error: any) { 78 | // Handle specific OpenAI error types 79 | if (error.response) { 80 | const { status, data } = error.response; 81 | if (status === 429) { 82 | return { 83 | status: "error", 84 | message: "Rate limit exceeded. Please try again later.", 85 | }; 86 | } 87 | return { 88 | status: "error", 89 | message: `OpenAI API error: ${data.error?.message || error.message}`, 90 | }; 91 | } 92 | 93 | return { 94 | status: "error", 95 | message: `Failed to generate image: ${error.message}`, 96 | }; 97 | } 98 | }, 99 | }; 100 | 101 | export default createImageAction; 102 | -------------------------------------------------------------------------------- /src/actions/agent/getWalletAddress.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "../../types/action"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { z } from "zod"; 4 | import { get_wallet_address } from "../../tools/agent"; 5 | 6 | const getWalletAddressAction: Action = { 7 | name: "GET_WALLET_ADDRESS", 8 | similes: ["wallet address", "address", "wallet"], 9 | description: "Get wallet address of the agent", 10 | examples: [ 11 | [ 12 | { 13 | input: {}, 14 | output: { 15 | status: "success", 16 | address: "0x1234567890abcdef", 17 | }, 18 | explanation: "The agent's wallet address is 0x1234567890abcdef", 19 | }, 20 | ], 21 | ], 22 | schema: z.object({}), 23 | handler: async (agent: EvmAgentKit) => ({ 24 | status: "success", 25 | address: get_wallet_address(agent), 26 | }), 27 | }; 28 | 29 | export default getWalletAddressAction; 30 | -------------------------------------------------------------------------------- /src/actions/agent/get_info.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "../../types/action"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { z } from "zod"; 4 | import { get_info } from "../../tools/agent"; 5 | 6 | const getInfoAction: Action = { 7 | name: "GET_INFO", 8 | similes: [ 9 | "get information", 10 | "find information", 11 | "search for", 12 | "tell me about", 13 | "what is", 14 | "explain", 15 | ], 16 | description: 17 | "Get detailed information about any topic using Perplexity's AI models", 18 | examples: [ 19 | [ 20 | { 21 | input: { 22 | prompt: "What is blockchain technology and how does it work?", 23 | }, 24 | output: { 25 | status: "success", 26 | message: "Blockchain is a distributed ledger technology...", 27 | }, 28 | explanation: "Get detailed information about blockchain technology", 29 | }, 30 | ], 31 | ], 32 | schema: z.object({ 33 | prompt: z 34 | .string() 35 | .min(1) 36 | .max(2000) 37 | .describe("The question or topic to get information about"), 38 | }), 39 | handler: async (agent: EvmAgentKit, input: Record) => { 40 | try { 41 | if (!agent.config?.PERPLEXITY_API_KEY) { 42 | return { 43 | status: "error", 44 | message: "Perplexity API key not found in agent configuration", 45 | }; 46 | } 47 | 48 | const { prompt } = input; 49 | const response = await get_info(agent, prompt); 50 | 51 | return { 52 | status: "success", 53 | message: response, 54 | }; 55 | } catch (error: any) { 56 | // Handle specific Perplexity API error types 57 | if (error.response) { 58 | const { status, data } = error.response; 59 | if (status === 429) { 60 | return { 61 | status: "error", 62 | message: "Rate limit exceeded. Please try again later.", 63 | }; 64 | } 65 | return { 66 | status: "error", 67 | message: `Perplexity API error: ${data.error?.message || error.message}`, 68 | }; 69 | } 70 | 71 | return { 72 | status: "error", 73 | message: `Failed to get information: ${error.message}`, 74 | }; 75 | } 76 | }, 77 | }; 78 | 79 | export default getInfoAction; 80 | -------------------------------------------------------------------------------- /src/actions/coingecko/getCoingeckoLatestPools.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Action } from "../../types"; 3 | 4 | const getCoingeckoLatestPoolsActions: Action = { 5 | name: "GET_COINGECKO_LATEST_POOLS", 6 | description: "Get the latest pools on Coingecko", 7 | similes: [ 8 | "Get the latest pools on Coingecko", 9 | "get me a list of the latest pools on coingecko", 10 | "what are the latest pools on coingecko", 11 | ], 12 | examples: [ 13 | [ 14 | { 15 | input: {}, 16 | output: { 17 | data: [ 18 | { 19 | id: "eth_0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", 20 | type: "pool", 21 | attributes: { 22 | base_token_price_usd: "3653.12491645176", 23 | base_token_price_native_currency: "1.0", 24 | quote_token_price_usd: "0.998343707926245", 25 | quote_token_price_native_currency: "0.000273040545093221", 26 | base_token_price_quote_token: "3662.46", 27 | quote_token_price_base_token: "0.00027304", 28 | address: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", 29 | name: "WETH / USDC 0.05%", 30 | pool_created_at: "2021-12-29T12:35:14Z", 31 | fdv_usd: "11007041041", 32 | market_cap_usd: null, 33 | price_change_percentage: { 34 | m5: "0", 35 | h1: "0.51", 36 | h6: "0.86", 37 | h24: "7.71", 38 | }, 39 | transactions: { 40 | m5: { 41 | buys: 7, 42 | sells: 2, 43 | buyers: 7, 44 | sellers: 2, 45 | }, 46 | m15: { 47 | buys: 19, 48 | sells: 27, 49 | buyers: 19, 50 | sellers: 27, 51 | }, 52 | m30: { 53 | buys: 49, 54 | sells: 61, 55 | buyers: 45, 56 | sellers: 57, 57 | }, 58 | h1: { 59 | buys: 97, 60 | sells: 144, 61 | buyers: 83, 62 | sellers: 124, 63 | }, 64 | h24: { 65 | buys: 2966, 66 | sells: 3847, 67 | buyers: 1625, 68 | sellers: 2399, 69 | }, 70 | }, 71 | volume_usd: { 72 | m5: "868581.7348314", 73 | h1: "16798158.0138526", 74 | h6: "164054610.850188", 75 | h24: "536545444.904535", 76 | }, 77 | reserve_in_usd: "163988541.3812", 78 | }, 79 | relationships: { 80 | base_token: { 81 | data: { 82 | id: "eth_0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", 83 | type: "token", 84 | }, 85 | }, 86 | quote_token: { 87 | data: { 88 | id: "eth_0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", 89 | type: "token", 90 | }, 91 | }, 92 | dex: { 93 | data: { 94 | id: "uniswap_v3", 95 | type: "dex", 96 | }, 97 | }, 98 | }, 99 | }, 100 | ], 101 | included: [ 102 | { 103 | id: "eth_0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", 104 | type: "token", 105 | attributes: { 106 | address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", 107 | name: "Wrapped Ether", 108 | symbol: "WETH", 109 | image_url: 110 | "https://assets.coingecko.com/coins/images/2518/small/weth.png?1696503332", 111 | coingecko_coin_id: "weth", 112 | }, 113 | }, 114 | ], 115 | }, 116 | explanation: "Get the latest pools on Coingecko", 117 | }, 118 | ], 119 | ], 120 | schema: z.object({}), 121 | handler: async (agent, _input) => { 122 | try { 123 | return { 124 | status: "success", 125 | result: await agent.getCoingeckoLatestPools(), 126 | }; 127 | } catch (e) { 128 | return { 129 | status: "error", 130 | // @ts-expect-error - error is not a property of unknown 131 | message: e.message, 132 | }; 133 | } 134 | }, 135 | }; 136 | 137 | export default getCoingeckoLatestPoolsActions; 138 | -------------------------------------------------------------------------------- /src/actions/coingecko/getCoingeckoTokenInfo.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Action } from "../../types"; 3 | 4 | const getCoingeckoTokenInfoAction: Action = { 5 | name: "GET_COINGECKO_TOKEN_INFO_ACTION", 6 | description: "Get token information from Coingecko", 7 | similes: [ 8 | "get token information from coingecko", 9 | "get coingecko token information", 10 | "get token info", 11 | ], 12 | examples: [ 13 | [ 14 | { 15 | input: { 16 | tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum 17 | }, 18 | output: { 19 | data: { 20 | id: "ethereum_0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", 21 | type: "token", 22 | attributes: { 23 | address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", 24 | name: "USD Coin", 25 | symbol: "USDC", 26 | decimals: 6, 27 | image_url: 28 | "https://coin-images.coingecko.com/coins/images/6319/large/usdc.png?1696506694", 29 | coingecko_coin_id: "usd-coin", 30 | websites: ["https://www.circle.com/en/usdc"], 31 | discord_url: "https://discord.com/invite/buildoncircle", 32 | telegram_handle: null, 33 | twitter_handle: "circle", 34 | description: 35 | "USDC is a fully collateralized US dollar stablecoin. USDC is the bridge between dollars and trading on cryptocurrency exchanges. The technology behind CENTRE makes it possible to exchange value between people, businesses and financial institutions just like email between mail services and texts between SMS providers. We believe by removing artificial economic borders, we can create a more inclusive global economy.", 36 | gt_score: 54.12844036697248, 37 | categories: [], 38 | gt_category_ids: [], 39 | }, 40 | }, 41 | }, 42 | explanation: "Get USDC token info from Coingecko", 43 | }, 44 | ], 45 | ], 46 | schema: z.object({ 47 | tokenAddress: z.string().nonempty(), 48 | }), 49 | handler: async (agent, input) => { 50 | try { 51 | const tokenInfo = await agent.getTokenInfoUsingCoingecko( 52 | input.tokenAddress, 53 | ); 54 | return { 55 | status: "success", 56 | result: tokenInfo, 57 | }; 58 | } catch (e) { 59 | return { 60 | status: "error", 61 | // @ts-expect-error - error is not a property of unknown 62 | message: e.message, 63 | }; 64 | } 65 | }, 66 | }; 67 | 68 | export default getCoingeckoTokenInfoAction; 69 | -------------------------------------------------------------------------------- /src/actions/coingecko/getCoingeckoTokenPriceData.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Action } from "../../types"; 3 | 4 | const getCoingeckoTokenPriceDataAction: Action = { 5 | name: "GET_COINGECKO_TOKEN_PRICE_DATA_ACTION", 6 | description: "Get the price data of a token on Coingecko", 7 | similes: [ 8 | "Get the price data of a token on Coingecko", 9 | "get me the price data of a token on coingecko", 10 | "what's the price of this token on coingecko", 11 | ], 12 | examples: [ 13 | [ 14 | { 15 | input: { 16 | tokenAddresses: ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"], 17 | }, 18 | explanation: 19 | "Get the price data of the token with the address EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", 20 | output: { 21 | status: "success", 22 | result: { 23 | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v: { 24 | usd: 0.999899, 25 | }, 26 | }, 27 | }, 28 | }, 29 | ], 30 | ], 31 | schema: z.object({ 32 | tokenAddresses: z.array(z.string().nonempty()), 33 | }), 34 | handler: async (agent, input) => { 35 | try { 36 | return { 37 | status: "success", 38 | result: await agent.getTokenPriceDataUsingCoingecko( 39 | ...input.tokenAddresses, 40 | ), 41 | }; 42 | } catch (e) { 43 | return { 44 | status: "error", 45 | // @ts-expect-error - e is an object 46 | message: e.message, 47 | }; 48 | } 49 | }, 50 | }; 51 | 52 | export default getCoingeckoTokenPriceDataAction; 53 | -------------------------------------------------------------------------------- /src/actions/coingecko/getCoingeckoTopGainers.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Action } from "../../types"; 3 | 4 | const getCoingeckoTopGainersAction: Action = { 5 | name: "GET_COINGECKO_TOP_GAINERS", 6 | description: "Get the top gainers on Coingecko", 7 | similes: [ 8 | "Get the top gainers on Coingecko", 9 | "get me a list of the top gainers on coingecko", 10 | "what are the top gainers on coingecko", 11 | ], 12 | examples: [ 13 | [ 14 | { 15 | input: { 16 | duration: "24h", 17 | topCoins: 300, 18 | }, 19 | output: { 20 | status: "success", 21 | result: [ 22 | { 23 | top_gainers: [ 24 | { 25 | id: "bonk", 26 | symbol: "bonk", 27 | name: "Bonk", 28 | image: 29 | "https://assets.coingecko.com/coins/images/28600/original/bonk.jpg?1696527587", 30 | market_cap_rank: 75, 31 | usd: 0.000024645873833743, 32 | usd_24h_vol: 105344205.633894, 33 | usd_1y_change: 4244.15510979623, 34 | }, 35 | { 36 | id: "0x0-ai-ai-smart-contract", 37 | symbol: "0x0", 38 | name: "0x0.ai: AI Smart Contract", 39 | image: 40 | "https://assets.coingecko.com/coins/images/28880/original/0x0.png?1696527857", 41 | market_cap_rank: 235, 42 | usd: 0.388236182838391, 43 | usd_24h_vol: 1608196.56989005, 44 | usd_1y_change: 3688.24996780839, 45 | }, 46 | ], 47 | }, 48 | ], 49 | }, 50 | explanation: "Get the top gainers on Coingecko for the last 24 hours", 51 | }, 52 | ], 53 | ], 54 | schema: z.object({ 55 | duration: z.enum(["1h", "24h", "7d", "14d", "30d", "60d", "1y"]).optional(), 56 | }), 57 | handler: async (agent, input) => { 58 | try { 59 | return { 60 | status: "success", 61 | result: await agent.getTopGainersOnCoingecko(input.duration), 62 | }; 63 | } catch (e) { 64 | return { 65 | status: "error", 66 | // @ts-expect-error - error is an object 67 | message: e.message, 68 | }; 69 | } 70 | }, 71 | }; 72 | 73 | export default getCoingeckoTopGainersAction; 74 | -------------------------------------------------------------------------------- /src/actions/coingecko/getCoingeckoTrendingPools.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Action } from "../../types"; 3 | 4 | const getCoingeckoTrendingPoolsAction: Action = { 5 | name: "GET_COINGECKO_TRENDING_POOLS_ACTION", 6 | description: "Get the trending pools on Coingecko", 7 | similes: [ 8 | "Get the trending pools on Coingecko", 9 | "get me a list of the trending pools on coingecko", 10 | "what are the trending pools on coingecko", 11 | ], 12 | examples: [ 13 | [ 14 | { 15 | input: { duration: "24h" }, 16 | output: { 17 | status: "success", 18 | result: { 19 | data: [ 20 | { 21 | id: "eth_0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", 22 | type: "pool", 23 | attributes: { 24 | base_token_price_usd: "3653.12491645176", 25 | base_token_price_native_currency: "1.0", 26 | quote_token_price_usd: "0.998343707926245", 27 | quote_token_price_native_currency: "0.000273040545093221", 28 | base_token_price_quote_token: "3662.46", 29 | quote_token_price_base_token: "0.00027304", 30 | address: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", 31 | name: "WETH / USDC 0.05%", 32 | pool_created_at: "2021-12-29T12:35:14Z", 33 | fdv_usd: "11007041041", 34 | market_cap_usd: null, 35 | price_change_percentage: { 36 | m5: "0", 37 | h1: "0.51", 38 | h6: "0.86", 39 | h24: "7.71", 40 | }, 41 | transactions: { 42 | m5: { 43 | buys: 7, 44 | sells: 2, 45 | buyers: 7, 46 | sellers: 2, 47 | }, 48 | m15: { 49 | buys: 19, 50 | sells: 27, 51 | buyers: 19, 52 | sellers: 27, 53 | }, 54 | m30: { 55 | buys: 49, 56 | sells: 61, 57 | buyers: 45, 58 | sellers: 57, 59 | }, 60 | h1: { 61 | buys: 97, 62 | sells: 144, 63 | buyers: 83, 64 | sellers: 124, 65 | }, 66 | h24: { 67 | buys: 2966, 68 | sells: 3847, 69 | buyers: 1625, 70 | sellers: 2399, 71 | }, 72 | }, 73 | volume_usd: { 74 | m5: "868581.7348314", 75 | h1: "16798158.0138526", 76 | h6: "164054610.850188", 77 | h24: "536545444.904535", 78 | }, 79 | reserve_in_usd: "163988541.3812", 80 | }, 81 | relationships: { 82 | base_token: { 83 | data: { 84 | id: "eth_0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", 85 | type: "token", 86 | }, 87 | }, 88 | quote_token: { 89 | data: { 90 | id: "eth_0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", 91 | type: "token", 92 | }, 93 | }, 94 | dex: { 95 | data: { 96 | id: "uniswap_v3", 97 | type: "dex", 98 | }, 99 | }, 100 | }, 101 | }, 102 | ], 103 | included: [ 104 | { 105 | id: "eth_0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", 106 | type: "token", 107 | attributes: { 108 | address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", 109 | name: "Wrapped Ether", 110 | symbol: "WETH", 111 | image_url: 112 | "https://assets.coingecko.com/coins/images/2518/small/weth.png?1696503332", 113 | coingecko_coin_id: "weth", 114 | }, 115 | }, 116 | ], 117 | }, 118 | }, 119 | explanation: 120 | "Get the trending pools on Coingecko for the last 24 hours", 121 | }, 122 | ], 123 | ], 124 | schema: z.object({ 125 | duration: z.enum(["5m", "1h", "24h", "6h"]).optional(), 126 | }), 127 | handler: async (agent, input) => { 128 | try { 129 | const data = await agent.getCoingeckoTrendingPools(input.duration); 130 | 131 | return { 132 | status: "success", 133 | result: data, 134 | }; 135 | } catch (e) { 136 | return { 137 | status: "error", 138 | // @ts-expect-error - error is an object 139 | message: e.message, 140 | }; 141 | } 142 | }, 143 | }; 144 | 145 | export default getCoingeckoTrendingPoolsAction; 146 | -------------------------------------------------------------------------------- /src/actions/compound/supply.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "../../types/action"; 2 | import { z } from "zod"; 3 | import { EvmAgentKit } from "../../agent"; 4 | 5 | const supplyCompoundAction: Action = { 6 | name: "COMPOUND_SUPPLY", 7 | similes: [ 8 | "supply compound", 9 | "supply compound token", 10 | "supply compound token to compound", 11 | "supply compound token to compound protocol", 12 | "supply compound token to compound protocol for a specific token", 13 | "supply compound token to compound protocol for a specific token for a specific user", 14 | ], 15 | description: 16 | "Supply a token to Compound protocol for a specific token. The token must be an approved collateral asset for the Compound market.", 17 | examples: [ 18 | [ 19 | { 20 | input: { 21 | assetId: "usdc", 22 | amount: "1", 23 | }, 24 | output: { 25 | status: "success", 26 | summary: { 27 | supplyBalance: "1000.50", 28 | tokenSymbol: "USDC", 29 | tokenDecimals: 6, 30 | }, 31 | }, 32 | explanation: "Supply 1 USDC to Compound protocol.", 33 | }, 34 | ], 35 | ], 36 | schema: z.object({ 37 | assetId: z 38 | .string() 39 | .describe( 40 | "The asset to supply, one of 'weth', 'cbbtc', 'wsteth', or 'usdc'", 41 | ), 42 | amount: z.string().describe("The amount of tokens to supply"), 43 | }), 44 | handler: async (agent: EvmAgentKit, input: Record) => { 45 | const { assetId, amount } = input; 46 | try { 47 | const supplyData = await agent.getSupply(assetId, amount); 48 | 49 | return { 50 | status: "success", 51 | summary: JSON.stringify(supplyData), 52 | }; 53 | } catch (error) { 54 | return { 55 | status: "error", 56 | summary: { 57 | message: `Failed to supplyCompound: ${(error as Error).message}`, 58 | }, 59 | }; 60 | } 61 | }, 62 | }; 63 | 64 | export default supplyCompoundAction; 65 | -------------------------------------------------------------------------------- /src/actions/defillama/fetch_price_action.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { Action } from "../../types/action"; 4 | 5 | const fetchPriceAction: Action = { 6 | name: "DEFILLAMA_FETCH_PRICE", 7 | similes: [ 8 | "get token price", 9 | "fetch price", 10 | "check token price", 11 | "get price from defillama", 12 | ], 13 | description: 14 | "Fetches the price of one or more tokens using the DeFiLlama price API. Tokens are specified using chain:address format.", 15 | examples: [ 16 | [ 17 | { 18 | input: { 19 | chainTokenAddrStrings: [ 20 | "ethereum:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 21 | ], 22 | // searchWidth: "6h" 23 | }, 24 | output: { 25 | status: "success", 26 | summary: { 27 | prices: { 28 | "ethereum:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": 2000.5, 29 | }, 30 | }, 31 | }, 32 | explanation: 33 | "The user wants to fetch the price of WETH on Ethereum. The action returns the current price in USD.", 34 | }, 35 | ], 36 | ], 37 | schema: z.object({ 38 | chainTokenAddrStrings: z 39 | .array(z.string()) 40 | .describe( 41 | 'Array of strings in format of "chain:token_address" (e.g. "ethereum:0x0000000000000000000000000000000000000000")', 42 | ), 43 | // searchWidth: z.string().optional().describe("The width of the search window for the price data. Default is '6h'"), 44 | }), 45 | handler: async (agent: EvmAgentKit, input: any) => { 46 | try { 47 | const prices = await agent.fetchTokenPrices(input.chainTokenAddrStrings); 48 | 49 | return { 50 | status: "success", 51 | summary: { 52 | prices, 53 | }, 54 | }; 55 | } catch (error: any) { 56 | return { 57 | status: "error", 58 | error: error.message, 59 | }; 60 | } 61 | }, 62 | }; 63 | 64 | export default fetchPriceAction; 65 | -------------------------------------------------------------------------------- /src/actions/defillama/get_protocol_tvl_action.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { Action } from "../../types/action"; 4 | 5 | const getProtocolTvlAction: Action = { 6 | name: "DEFILLAMA_GET_PROTOCOL_TVL", 7 | similes: [ 8 | "get protocol tvl", 9 | "fetch protocol tvl", 10 | "check protocol tvl", 11 | "get total value locked", 12 | "fetch total value locked", 13 | ], 14 | description: 15 | "Fetches the Total Value Locked (TVL) for a protocol using the DeFiLlama API. The protocol name should match the DeFiLlama protocol identifier.", 16 | examples: [ 17 | [ 18 | { 19 | input: { 20 | protocolName: "aave-v3", 21 | }, 22 | output: { 23 | status: "success", 24 | summary: { 25 | tvl: 1234567890.5, 26 | protocolName: "aave-v3", 27 | }, 28 | }, 29 | explanation: 30 | "The user wants to fetch the TVL for Aave V3 protocol. The action returns the current TVL in USD.", 31 | }, 32 | ], 33 | ], 34 | schema: z.object({ 35 | protocolName: z 36 | .string() 37 | .describe( 38 | "The DeFiLlama protocol identifier (e.g. 'aave-v3', 'uniswap-v3')", 39 | ), 40 | }), 41 | handler: async (agent: EvmAgentKit, input: any) => { 42 | try { 43 | const tvl = await agent.fetchProtocolTvl(input.protocolName); 44 | 45 | return { 46 | status: "success", 47 | summary: { 48 | tvl, 49 | protocolName: input.protocolName, 50 | }, 51 | }; 52 | } catch (error: any) { 53 | return { 54 | status: "error", 55 | error: error.message, 56 | }; 57 | } 58 | }, 59 | }; 60 | 61 | export default getProtocolTvlAction; 62 | -------------------------------------------------------------------------------- /src/actions/fourmeme/create_token_action.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "../../types/action"; 2 | import { z } from "zod"; 3 | import { EvmAgentKit } from "../../agent"; 4 | import { bsc } from "viem/chains"; 5 | import { FOUR_MEME_ADDRESSES } from "../../constants"; 6 | 7 | const fourmemeCreateTokenAction: Action = { 8 | name: "FOURMEME_CREATE_TOKEN", 9 | similes: ["create token", "fourmeme", "make token", "fourmeme create token"], 10 | description: 11 | "Creates a new token on FourMeme. Requires BNB amount, token name, short name, description, image URL, launch time, and category label. Optional fields include website URL, Twitter URL, and Telegram URL. If the required fields are not provided, you should ask the user for them before proceeding. When the user inputs the launchTime, convert it to seconds from now. BEFORE YOU PROCEED, DOUBLE CHECK EVERYTHING WITH THE USER AND MAKE THEM CONFIRM EVERYTHING.", 12 | examples: [ 13 | [ 14 | { 15 | input: { 16 | bnbAmount: 0.1, 17 | name: "My Token", 18 | shortName: "MTK", 19 | desc: "My Token is a token on the Binance Smart Chain.", 20 | imgUrl: "https://example.com/mytoken.png", 21 | launchTimeFromNow: 120, 22 | label: "Meme", 23 | }, 24 | output: { 25 | status: "success", 26 | summary: { 27 | txHash: "0x1234567890123456789012345678901234567890", 28 | }, 29 | }, 30 | explanation: 31 | "Create a token on FourMeme with 0.1 BNB. The token with name 'My Token' and symbol 'MTK' will be launched 2 minutes from now. It's image is https://example.com/mytoken.png. The token is a meme token.", 32 | }, 33 | ], 34 | ], 35 | schema: z.object({ 36 | bnbAmount: z 37 | .number() 38 | .describe("Amount of BNB used for initial purchase of the token"), 39 | name: z.string().describe("Full name of the token"), 40 | shortName: z.string().describe("Short name/symbol of the token"), 41 | desc: z.string().describe("Description of the token"), 42 | imgUrl: z.string().describe("URL to the token's image"), 43 | launchTimeFromNow: z 44 | .number() 45 | .describe("When do you want the token to be launched?"), 46 | label: z 47 | .enum([ 48 | "Meme", 49 | "AI", 50 | "Defi", 51 | "Games", 52 | "Infra", 53 | "De-Sci", 54 | "Social", 55 | "Depin", 56 | "Charity", 57 | "Others", 58 | ]) 59 | .describe("Category label for the token"), 60 | webUrl: z.string().optional().describe("Website URL (optional)"), 61 | twitterUrl: z 62 | .string() 63 | .optional() 64 | .describe("Twitter profile URL (optional)"), 65 | telegramUrl: z 66 | .string() 67 | .optional() 68 | .describe("Telegram group URL (optional)"), 69 | }), 70 | handler: async (agent: EvmAgentKit, input: Record) => { 71 | const { 72 | bnbAmount, 73 | name, 74 | shortName, 75 | desc, 76 | imgUrl, 77 | launchTimeFromNow, 78 | label, 79 | webUrl, 80 | twitterUrl, 81 | telegramUrl, 82 | } = input; 83 | // Get current chain from wallet provider 84 | const network = agent.wallet.getNetwork(); 85 | if (!network.chainId) { 86 | throw new Error("Failed to get network chain ID"); 87 | } 88 | const chainId = parseInt(network.chainId); 89 | 90 | // Check if we're on BSC mainnet 91 | if (chainId !== bsc.id) { 92 | return { 93 | status: "error", 94 | summary: { 95 | message: `FourMeme token creation is only supported on BSC mainnet (chainId ${bsc.id}). Current chain: ${network.chainId}`, 96 | }, 97 | }; 98 | } 99 | 100 | // Get the BSC contract address 101 | const tokenManagerAddress = FOUR_MEME_ADDRESSES.BSC as `0x${string}`; 102 | const txHash = await agent.createFourMemeToken( 103 | tokenManagerAddress, 104 | bnbAmount, 105 | name, 106 | shortName, 107 | desc, 108 | imgUrl, 109 | launchTimeFromNow, 110 | label, 111 | webUrl, 112 | twitterUrl, 113 | telegramUrl, 114 | ); 115 | return { 116 | status: "success", 117 | summary: { 118 | txHash: txHash, 119 | }, 120 | }; 121 | }, 122 | }; 123 | 124 | export default fourmemeCreateTokenAction; 125 | -------------------------------------------------------------------------------- /src/actions/fourmeme/get_token_holdings_action.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { Action } from "../../types/action"; 4 | 5 | const fourmemeGetTokenHoldingsAction: Action = { 6 | name: "FOURMEME_GET_TOKEN_HOLDINGS", 7 | similes: [ 8 | "get my token holdings", 9 | "fourmeme get my token holdings", 10 | "get my tokens", 11 | "fourmeme get my tokens", 12 | ], 13 | description: 14 | "Retrieves all token listings held by the current user on FourMeme platform. When returning ALWAYS show the full token address, token name, token symbol, description, my token amount, total increase percent, day increase percent, and progress to bonding curve.", 15 | examples: [], 16 | schema: z.object({}), 17 | handler: async (agent: EvmAgentKit, _input: any) => { 18 | const tokens = await agent.getFourMemeTokens(); 19 | return { 20 | status: "success", 21 | summary: { 22 | tokens: tokens, 23 | }, 24 | }; 25 | }, 26 | }; 27 | 28 | export default fourmemeGetTokenHoldingsAction; 29 | -------------------------------------------------------------------------------- /src/actions/fourmeme/get_trending_tokens_action.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { Action } from "../../types/action"; 4 | 5 | const fourmemeGetTrendingTokensAction: Action = { 6 | name: "FOURMEME_GET_TRENDING_TOKENS", 7 | similes: ["get trending tokens", "fourmeme get trending tokens"], 8 | description: 9 | "Retrieves the latest trending tokens on FourMeme platform. When returning ALWAYS show the full token address, token name, token symbol, description, current price and market cap.", 10 | examples: [], 11 | schema: z.object({}), 12 | handler: async (agent: EvmAgentKit, _input: any) => { 13 | const trendingTokens = await agent.getFourMemeTrendingTokens(); 14 | return { 15 | status: "success", 16 | summary: { 17 | trendingTokens: JSON.stringify(trendingTokens), 18 | }, 19 | }; 20 | }, 21 | }; 22 | 23 | export default fourmemeGetTrendingTokensAction; 24 | -------------------------------------------------------------------------------- /src/actions/fourmeme/purchase_token_action.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { Action } from "../../types/action"; 4 | import { FOUR_MEME_ADDRESSES } from "../../constants"; 5 | import { Address } from "viem"; 6 | const fourmemePurchaseTokenAction: Action = { 7 | name: "FOURMEME_PURCHASE_TOKEN", 8 | similes: [ 9 | "purchase token", 10 | "fourmeme purchase token", 11 | "buy token", 12 | "fourmeme buy token", 13 | ], 14 | description: 15 | "Purchases a token on the FourMeme platform. Always confirm with the user the token address and amount before purchasing the token. The token address must be a valid FourMeme token address.", 16 | examples: [ 17 | [ 18 | { 19 | input: { 20 | tokenAddress: "0x1234567890123456789012345678901234567890", 21 | tokenAmount: "1000000000000000000", 22 | }, 23 | output: { 24 | status: "success", 25 | summary: { 26 | txHash: "0x1234567890123456789012345678901234567890", 27 | }, 28 | }, 29 | explanation: 30 | "The user wants to purchase 1000000000000000000 tokens of the token with address 0x1234567890123456789012345678901234567890 on the FourMeme platform. The token address is provided and is valid, so the action is successful.", 31 | }, 32 | ], 33 | ], 34 | schema: z.object({ 35 | tokenAddress: z.string().describe("The address of the token to purchase"), 36 | tokenAmount: z.string().describe("The amount of tokens to purchase"), 37 | }), 38 | handler: async (agent: EvmAgentKit, input: any) => { 39 | try { 40 | const tokenManagerAddress = FOUR_MEME_ADDRESSES.BSC as Address; 41 | const txHash = await agent.purchaseFourMemeToken( 42 | tokenManagerAddress, 43 | input.tokenAddress, 44 | input.tokenAmount, 45 | ); 46 | return { 47 | status: "success", 48 | summary: { 49 | txHash: txHash, 50 | }, 51 | }; 52 | } catch (error: any) { 53 | return { 54 | status: "error", 55 | error: error.message, 56 | }; 57 | } 58 | }, 59 | }; 60 | 61 | export default fourmemePurchaseTokenAction; 62 | -------------------------------------------------------------------------------- /src/actions/fourmeme/sell_token_action.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { Action } from "../../types/action"; 4 | import { Address } from "viem"; 5 | import { FOUR_MEME_ADDRESSES } from "../../constants"; 6 | 7 | const fourmemeSellTokenAction: Action = { 8 | name: "FOURMEME_SELL_TOKEN", 9 | similes: [ 10 | "sell token", 11 | "fourmeme sell token", 12 | "sell my token", 13 | "fourmeme sell my token", 14 | ], 15 | description: 16 | "Sells a token on the FourMeme platform. Always confirm with the user, the token address and amount before selling the token. The token address must be a valid FourMeme token address. If no token address is provided, prompt the user to call FOURMEME_GET_TOKENS first.", 17 | examples: [ 18 | [ 19 | { 20 | input: { 21 | tokenAddress: "0x1234567890123456789012345678901234567890", 22 | tokenAmount: "1000000000000000000", 23 | }, 24 | output: { 25 | status: "success", 26 | summary: { 27 | txHash: "0x1234567890123456789012345678901234567890", 28 | }, 29 | }, 30 | explanation: 31 | "The user wants to sell 1000000000000000000 tokens of the token with address 0x1234567890123456789012345678901234567890 on the FourMeme platform. The token address is provided, so the action is successful.", 32 | }, 33 | ], 34 | ], 35 | schema: z.object({ 36 | tokenAddress: z.string().describe("The address of the token to sell"), 37 | tokenAmount: z.string().describe("The amount of tokens to sell"), 38 | }), 39 | handler: async (agent: EvmAgentKit, input: any) => { 40 | const tokenManagerAddress = FOUR_MEME_ADDRESSES.BSC as Address; 41 | const txHash = await agent.sellFourMemeToken( 42 | tokenManagerAddress, 43 | input.tokenAddress, 44 | input.tokenAmount, 45 | ); 46 | return { 47 | status: "success", 48 | summary: { 49 | txHash: txHash, 50 | }, 51 | }; 52 | }, 53 | }; 54 | 55 | export default fourmemeSellTokenAction; 56 | -------------------------------------------------------------------------------- /src/actions/index.ts: -------------------------------------------------------------------------------- 1 | import createImageAction from "./agent/createImage"; 2 | import getWalletAddressAction from "./agent/getWalletAddress"; 3 | import getInfoAction from "./agent/get_info"; 4 | import { elfaPingAction } from "./elfa_ai/elfa_ai_actions"; 5 | import { elfaApiKeyStatusAction } from "./elfa_ai/elfa_ai_actions"; 6 | import { elfaGetSmartMentionsAction } from "./elfa_ai/elfa_ai_actions"; 7 | import { elfaGetTopMentionsByTickerAction } from "./elfa_ai/elfa_ai_actions"; 8 | import { elfaSearchMentionsByKeywordsAction } from "./elfa_ai/elfa_ai_actions"; 9 | import { elfaTrendingTokensAction } from "./elfa_ai/elfa_ai_actions"; 10 | import { elfaSmartTwitterAccountStats } from "./elfa_ai/elfa_ai_actions"; 11 | import getCoingeckoLatestPoolsActions from "./coingecko/getCoingeckoLatestPools"; 12 | import getCoingeckoTokenInfoAction from "./coingecko/getCoingeckoTokenInfo"; 13 | import getCoingeckoTokenPriceDataAction from "./coingecko/getCoingeckoTokenPriceData"; 14 | import getCoingeckoTopGainersAction from "./coingecko/getCoingeckoTopGainers"; 15 | import getCoingeckoTrendingPoolsAction from "./coingecko/getCoingeckoTrendingPools"; 16 | import getCoingeckoTrendingTokensAction from "./coingecko/getCoingeckoTrendingTokens"; 17 | import fourmemeCreateTokenAction from "./fourmeme/create_token_action"; 18 | import fourmemeGetTokenHoldingsAction from "./fourmeme/get_token_holdings_action"; 19 | import fourmemeGetTrendingTokensAction from "./fourmeme/get_trending_tokens_action"; 20 | import fourmemeSellTokenAction from "./fourmeme/sell_token_action"; 21 | import supplyCompoundAction from "./compound/supply"; 22 | import fourmemePurchaseTokenAction from "./fourmeme/purchase_token_action"; 23 | import fetchPriceAction from "./defillama/fetch_price_action"; 24 | import getProtocolTvlAction from "./defillama/get_protocol_tvl_action"; 25 | 26 | export const ACTIONS = { 27 | GET_INFO_ACTION: getInfoAction, 28 | WALLET_ADDRESS_ACTION: getWalletAddressAction, 29 | CREATE_IMAGE_ACTION: createImageAction, 30 | ELFA_PING_ACTION: elfaPingAction, 31 | ELFA_API_KEY_STATUS_ACTION: elfaApiKeyStatusAction, 32 | ELFA_GET_SMART_MENTIONS_ACTION: elfaGetSmartMentionsAction, 33 | ELFA_GET_TOP_MENTIONS_BY_TICKER_ACTION: elfaGetTopMentionsByTickerAction, 34 | ELFA_SEARCH_MENTIONS_BY_KEYWORDS_ACTION: elfaSearchMentionsByKeywordsAction, 35 | ELFA_TRENDING_TOKENS_ACTION: elfaTrendingTokensAction, 36 | ELFA_SMART_TWITTER_ACCOUNT_STATS_ACTION: elfaSmartTwitterAccountStats, 37 | GET_COINGECKO_LATEST_POOLS_ACTION: getCoingeckoLatestPoolsActions, 38 | GET_COINGECKO_TOKEN_INFO_ACTION: getCoingeckoTokenInfoAction, 39 | GET_COINGECKO_TOKEN_PRICE_DATA_ACTION: getCoingeckoTokenPriceDataAction, 40 | GET_COINGECKO_TOP_GAINERS_ACTION: getCoingeckoTopGainersAction, 41 | GET_COINGECKO_TRENDING_POOLS_ACTION: getCoingeckoTrendingPoolsAction, 42 | GET_COINGECKO_TRENDING_TOKENS_ACTION: getCoingeckoTrendingTokensAction, 43 | FOURMEME_CREATE_TOKEN_ACTION: fourmemeCreateTokenAction, 44 | FOURMEME_GET_TOKEN_HOLDINGS_ACTION: fourmemeGetTokenHoldingsAction, 45 | FOURMEME_SELL_TOKEN_ACTION: fourmemeSellTokenAction, 46 | FOURMEME_GET_TRENDING_TOKENS_ACTION: fourmemeGetTrendingTokensAction, 47 | FOURMEME_PURCHASE_TOKEN_ACTION: fourmemePurchaseTokenAction, 48 | COMPOUND_SUPPLY_ACTION: supplyCompoundAction, 49 | DEFILLAMA_FETCH_PRICE: fetchPriceAction, 50 | DEFILLAMA_GET_PROTOCOL_TVL: getProtocolTvlAction, 51 | }; 52 | 53 | export type { Action, ActionExample, Handler } from "../types/action"; 54 | -------------------------------------------------------------------------------- /src/constants/index.ts: -------------------------------------------------------------------------------- 1 | import { bsc } from "viem/chains"; 2 | 3 | // FourMeme contract addresses by chain 4 | export const FOUR_MEME_ADDRESSES: Record = { 5 | // BSC Mainnet 6 | [bsc.id.toString()]: "0x5c952063c7fc8610FFDB798152D69F0B9550762b", 7 | // For backwards compatibility 8 | BSC: "0x5c952063c7fc8610FFDB798152D69F0B9550762b", 9 | // Default to BSC Mainnet for now 10 | DEFAULT: "0x5c952063c7fc8610FFDB798152D69F0B9550762b", 11 | }; 12 | 13 | // For backwards compatibility 14 | export const FOUR_MEME_BSC_ADDRESS = FOUR_MEME_ADDRESSES.BSC; 15 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "./agent"; 2 | import { createEvmTools } from "./langchain"; 3 | import { createEvmTools as createVercelAITools } from "./vercel-ai"; 4 | import { startMcpServer, createMcpServer } from "./mcp"; 5 | 6 | export { 7 | EvmAgentKit, 8 | createEvmTools, 9 | createVercelAITools, 10 | startMcpServer, 11 | createMcpServer, 12 | }; 13 | 14 | // Optional: Export types that users might need 15 | export * from "./types"; 16 | 17 | // Export action system 18 | export { ACTIONS } from "./actions"; 19 | export * from "./utils/actionExecutor"; 20 | 21 | // Export MCP server 22 | export * from "./utils/zodToMCPSchema"; 23 | -------------------------------------------------------------------------------- /src/langchain/agent/create_image.ts: -------------------------------------------------------------------------------- 1 | import { Tool } from "langchain/tools"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { create_image } from "../../tools/agent"; 4 | 5 | export class EvmCreateImageTool extends Tool { 6 | name = "evm_create_image"; 7 | description = 8 | "Create an image using OpenAI's DALL-E. Input should be a string prompt for the image."; 9 | 10 | constructor(private evmKit: EvmAgentKit) { 11 | super(); 12 | } 13 | 14 | private validateInput(input: string): void { 15 | if (typeof input !== "string" || input.trim().length === 0) { 16 | throw new Error("Input must be a non-empty string prompt"); 17 | } 18 | } 19 | 20 | protected async _call(input: string): Promise { 21 | try { 22 | this.validateInput(input); 23 | const result = await create_image(this.evmKit, input.trim()); 24 | 25 | return JSON.stringify({ 26 | status: "success", 27 | message: "Image created successfully", 28 | ...result, 29 | }); 30 | } catch (error: any) { 31 | return JSON.stringify({ 32 | status: "error", 33 | message: error.message, 34 | code: error.code || "UNKNOWN_ERROR", 35 | }); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/langchain/agent/get_info.ts: -------------------------------------------------------------------------------- 1 | import { Tool } from "langchain/tools"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { get_info } from "../../tools/agent"; 4 | 5 | export class EvmGetInfoTool extends Tool { 6 | name = "evm_get_info"; 7 | description = 8 | "Get detailed and latest information about any topic using Perplexity AI. Input should be a question or topic to get information about."; 9 | 10 | constructor(private evmKit: EvmAgentKit) { 11 | super(); 12 | } 13 | 14 | private validateInput(input: string): void { 15 | if (typeof input !== "string" || input.trim().length === 0) { 16 | throw new Error("Input must be a non-empty string question"); 17 | } 18 | } 19 | 20 | protected async _call(input: string): Promise { 21 | try { 22 | this.validateInput(input); 23 | const result = await get_info(this.evmKit, input.trim()); 24 | 25 | return JSON.stringify({ 26 | status: "success", 27 | message: "Information retrieved successfully", 28 | content: result, 29 | }); 30 | } catch (error: any) { 31 | return JSON.stringify({ 32 | status: "error", 33 | message: error.message, 34 | code: error.code || "UNKNOWN_ERROR", 35 | }); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/langchain/agent/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./create_image"; 2 | export * from "./wallet_address"; 3 | export * from "./get_info"; 4 | -------------------------------------------------------------------------------- /src/langchain/agent/wallet_address.ts: -------------------------------------------------------------------------------- 1 | import { Tool } from "langchain/tools"; 2 | import { EvmAgentKit } from "../../agent"; 3 | 4 | export class EvmGetWalletAddressTool extends Tool { 5 | name = "evm_get_wallet_address"; 6 | description = `Get the wallet address of the agent`; 7 | 8 | constructor(private evmKit: EvmAgentKit) { 9 | super(); 10 | } 11 | 12 | async _call(_input: string): Promise { 13 | return this.evmKit.wallet_address.toString(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/langchain/compound/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./supply"; 2 | -------------------------------------------------------------------------------- /src/langchain/compound/supply.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Tool } from "@langchain/core/tools"; 3 | import { EvmAgentKit } from "../../agent"; 4 | import { supply } from "../../tools/compound/supply"; 5 | 6 | export const CompoundSupplySchema = z.object({ 7 | assetId: z.string().describe("The asset ID to supply to Compound"), 8 | amount: z.string().describe("The amount to supply"), 9 | }); 10 | 11 | /** 12 | * Tool for supplying assets to Compound 13 | */ 14 | export class CompoundSupplyTool extends Tool { 15 | name = "COMPOUND_SUPPLY"; 16 | description = ` 17 | This tool allows supplying collateral assets to Compound. 18 | It takes: 19 | - assetId: The asset to supply, one of 'weth', 'cbbtc', 'wsteth', or 'usdc' 20 | - amount: The amount of tokens to supply in human-readable format 21 | Examples: 22 | - 1 WETH 23 | - 0.1 WETH 24 | - 0.01 WETH 25 | Important notes: 26 | - Use the exact amount provided 27 | - The token must be an approved collateral asset for the Compound market 28 | 29 | Inputs (JSON string): 30 | - assetId: string, eg "weth" (required) 31 | - amount: string, eg "1" (required) 32 | `; 33 | 34 | constructor(private agent: EvmAgentKit) { 35 | super(); 36 | } 37 | 38 | /** @ignore */ 39 | async _call(input: string): Promise { 40 | const { assetId, amount } = JSON.parse(input); 41 | const result = await supply({ 42 | agent: this.agent, 43 | assetId, 44 | amount, 45 | }); 46 | if (typeof result === "string") { 47 | return result; 48 | } 49 | return JSON.stringify(result); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/langchain/defillama/fetch_price.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | import { DynamicStructuredTool } from "@langchain/core/tools"; 3 | import { z } from "zod"; 4 | import { fetchPrices } from "../../tools"; 5 | 6 | export function fetchPriceTool(_agent: EvmAgentKit) { 7 | return new DynamicStructuredTool({ 8 | name: "fetch_price", 9 | description: "Fetch the price of a token", 10 | schema: z.object({ 11 | chainTokenAddrStrings: z 12 | .array(z.string()) 13 | .describe( 14 | 'String array of strings in format of "chain:token_address" (e.g. "ethereum:0x0000000000000000000000000000000000000000")', 15 | ), 16 | searchWidth: z 17 | .string() 18 | .optional() 19 | .describe( 20 | "The width of the search window for the price data. Default is '6h'", 21 | ), 22 | }), 23 | func: async (input: any) => { 24 | try { 25 | const { chainTokenAddrStrings, searchWidth } = input; 26 | const prices = await fetchPrices({ 27 | chainTokenAddrStrings, 28 | searchWidth, 29 | }); 30 | return { 31 | prices, 32 | status: "success", 33 | }; 34 | } catch (error: any) { 35 | return { 36 | status: "error", 37 | summary: { 38 | error: error.message, 39 | }, 40 | }; 41 | } 42 | }, 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /src/langchain/defillama/get_protocol_tvl.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | import { DynamicStructuredTool } from "langchain/tools"; 3 | import { z } from "zod"; 4 | import { getProtocolTvl } from "../../tools"; 5 | 6 | export function getProtocolTvlTool(_agent: EvmAgentKit) { 7 | return new DynamicStructuredTool({ 8 | name: "get_protocol_tvl", 9 | description: "Get Total Value Locked (TVL) for a specific protocol", 10 | schema: z.object({ 11 | slug: z.string().describe("The protocol slug identifier"), 12 | }), 13 | func: async (input: any) => { 14 | try { 15 | const { slug } = input; 16 | const prices = await getProtocolTvl(slug); 17 | return { 18 | prices, 19 | status: "success", 20 | }; 21 | } catch (error: any) { 22 | return { 23 | status: "error", 24 | summary: { 25 | error: error.message, 26 | }, 27 | }; 28 | } 29 | }, 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /src/langchain/defillama/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./get_protocol_tvl"; 2 | export * from "./fetch_price"; 3 | -------------------------------------------------------------------------------- /src/langchain/elfa_ai/elfa_ai_api.ts: -------------------------------------------------------------------------------- 1 | import { Tool } from "langchain/tools"; 2 | import { EvmAgentKit } from "../../agent"; 3 | 4 | export class ElfaPingTool extends Tool { 5 | name = "elfa_ping"; 6 | description = "Checks the health of the Elfa AI API by pinging it."; 7 | 8 | constructor(private agent: EvmAgentKit) { 9 | super(); 10 | } 11 | 12 | protected async _call(): Promise { 13 | try { 14 | const data = await this.agent.pingElfaAiApi(); 15 | return JSON.stringify({ status: "success", data }); 16 | } catch (error: any) { 17 | return JSON.stringify({ 18 | status: "error", 19 | message: error.message, 20 | }); 21 | } 22 | } 23 | } 24 | 25 | export class ElfaApiKeyStatusTool extends Tool { 26 | name = "elfa_api_key_status"; 27 | description = 28 | "Retrieves the status and usage details of the Elfa AI API key."; 29 | 30 | constructor(private agent: EvmAgentKit) { 31 | super(); 32 | } 33 | 34 | protected async _call(): Promise { 35 | try { 36 | const data = await this.agent.getElfaAiApiKeyStatus(); 37 | return JSON.stringify({ status: "success", data }); 38 | } catch (error: any) { 39 | return JSON.stringify({ 40 | status: "error", 41 | message: error.message, 42 | }); 43 | } 44 | } 45 | } 46 | 47 | export class ElfaGetMentionsTool extends Tool { 48 | name = "elfa_get_smart_mentions"; 49 | description = `Retrieves tweets by smart accounts with smart engagement from the Elfa AI API. 50 | 51 | Inputs (JSON string): 52 | - limit: number, eg 100 (optional) 53 | - offset: number, eg 0 (optional)`; 54 | 55 | constructor(private agent: EvmAgentKit) { 56 | super(); 57 | } 58 | 59 | protected async _call(input: string): Promise { 60 | try { 61 | const parsedInput = JSON.parse(input); 62 | const { limit = 100, offset = 0 } = parsedInput; 63 | const data = await this.agent.getSmartMentions(limit, offset); 64 | return JSON.stringify({ status: "success", data }); 65 | } catch (error: any) { 66 | return JSON.stringify({ 67 | status: "error", 68 | message: error.message, 69 | }); 70 | } 71 | } 72 | } 73 | 74 | export class ElfaGetTopMentionsTool extends Tool { 75 | name = "elfa_get_top_mentions"; 76 | description = `Retrieves top tweets for a given ticker symbol from the Elfa AI API. 77 | 78 | Inputs (JSON string): 79 | - ticker: string, eg "SOL" (required) 80 | - timeWindow: string, eg "1h" (optional) 81 | - page: number, eg 1 (optional) 82 | - pageSize: number, eg 10 (optional) 83 | - includeAccountDetails: boolean, eg false (optional)`; 84 | 85 | constructor(private agent: EvmAgentKit) { 86 | super(); 87 | } 88 | 89 | protected async _call(input: string): Promise { 90 | try { 91 | const parsedInput = JSON.parse(input); 92 | const { 93 | ticker, 94 | timeWindow = "1h", 95 | page = 1, 96 | pageSize = 10, 97 | includeAccountDetails = false, 98 | } = parsedInput; 99 | if (!ticker) { 100 | throw new Error("Ticker is required."); 101 | } 102 | const data = await this.agent.getTopMentionsByTicker( 103 | ticker, 104 | timeWindow, 105 | page, 106 | pageSize, 107 | includeAccountDetails, 108 | ); 109 | return JSON.stringify({ status: "success", data }); 110 | } catch (error: any) { 111 | return JSON.stringify({ 112 | status: "error", 113 | message: error.message, 114 | }); 115 | } 116 | } 117 | } 118 | 119 | export class ElfaSearchMentionsTool extends Tool { 120 | name = "elfa_search_mentions_by_keywords"; 121 | description = `Searches for tweets by keywords within a specified date range using the Elfa AI API. 122 | 123 | Inputs (JSON string): 124 | - keywords : string, eg "ai, agents" (required) 125 | - from: number, eg 1738675001 (required) 126 | - to: number, eg 1738775001 (required) 127 | - limit: number, eg 20 (optional)`; 128 | 129 | constructor(private agent: EvmAgentKit) { 130 | super(); 131 | } 132 | 133 | protected async _call(input: string): Promise { 134 | try { 135 | const parsedInput = JSON.parse(input); 136 | const { keywords, from, to, limit = 20 } = parsedInput; 137 | if (!keywords || !from || !to) { 138 | throw new Error("Keywords, from, and to fields are required."); 139 | } 140 | const data = await this.agent.searchMentionsByKeywords( 141 | keywords, 142 | from, 143 | to, 144 | limit, 145 | ); 146 | return JSON.stringify({ status: "success", data }); 147 | } catch (error: any) { 148 | return JSON.stringify({ 149 | status: "error", 150 | message: error.message, 151 | }); 152 | } 153 | } 154 | } 155 | 156 | export class ElfaTrendingTokensTool extends Tool { 157 | name = "elfa_trending_tokens"; 158 | description = `Retrieves trending tokens based on mentions from the Elfa AI API.`; 159 | 160 | constructor(private agent: EvmAgentKit) { 161 | super(); 162 | } 163 | 164 | protected async _call(_input: string): Promise { 165 | try { 166 | const data = await this.agent.getTrendingTokens(); 167 | return JSON.stringify({ status: "success", data }); 168 | } catch (error: any) { 169 | return JSON.stringify({ 170 | status: "error", 171 | message: error.message, 172 | }); 173 | } 174 | } 175 | } 176 | 177 | export class ElfaAccountSmartStatsTool extends Tool { 178 | name = "elfa_account_smart_stats"; 179 | description = `Retrieves smart stats and social metrics for a specified twitter account from the Elfa AI API. 180 | 181 | Inputs: 182 | username: string, eg "elonmusk" (required)`; 183 | 184 | constructor(private agent: EvmAgentKit) { 185 | super(); 186 | } 187 | 188 | protected async _call(input: string): Promise { 189 | try { 190 | const username = input.trim(); 191 | const data = await this.agent.getSmartTwitterAccountStats(username); 192 | return JSON.stringify({ status: "success", data }); 193 | } catch (error: any) { 194 | return JSON.stringify({ 195 | status: "error", 196 | message: error.message, 197 | }); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/langchain/elfa_ai/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./elfa_ai_api"; 2 | -------------------------------------------------------------------------------- /src/langchain/fourmeme/create_token.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | import { DynamicStructuredTool } from "@langchain/core/tools"; 3 | import { z } from "zod"; 4 | import { Address } from "viem"; 5 | import { bsc } from "viem/chains"; 6 | import { FOUR_MEME_ADDRESSES } from "../../constants"; 7 | 8 | export function createFourMemeTokenTool(agent: EvmAgentKit) { 9 | return new DynamicStructuredTool({ 10 | name: "create_fourmeme_token", 11 | description: 12 | "Creates a new token on FourMeme. Requires BNB amount, token name, short name, description, image URL, launch time, and category label. Optional fields include website URL, Twitter URL, and Telegram URL. If the required fields are not provided, you should ask the user for them before proceeding. When the user inputs the launchTime, convert it to seconds from now. BEFORE YOU PROCEED, DOUBLE CHECK EVERYTHING WITH THE USER AND MAKE THEM CONFIRM EVERYTHING.", 13 | schema: z.object({ 14 | bnbAmount: z.number().describe("Amount of BNB to create the token"), 15 | name: z.string().describe("Full name of the token"), 16 | shortName: z.string().describe("Short name/symbol of the token"), 17 | desc: z.string().describe("Description of the token"), 18 | imgUrl: z.string().describe("URL to the token's image"), 19 | launchTimeFromNow: z 20 | .number() 21 | .describe("When do you want the token to be launched?"), 22 | label: z 23 | .enum([ 24 | "Meme", 25 | "AI", 26 | "Defi", 27 | "Games", 28 | "Infra", 29 | "De-Sci", 30 | "Social", 31 | "Depin", 32 | "Charity", 33 | "Others", 34 | ]) 35 | .describe("Category label for the token"), 36 | webUrl: z.string().optional().describe("Website URL (optional)"), 37 | twitterUrl: z 38 | .string() 39 | .optional() 40 | .describe("Twitter profile URL (optional)"), 41 | telegramUrl: z 42 | .string() 43 | .optional() 44 | .describe("Telegram group URL (optional)"), 45 | }), 46 | func: async ({ 47 | bnbAmount, 48 | name, 49 | shortName, 50 | desc, 51 | imgUrl, 52 | launchTimeFromNow, 53 | label, 54 | webUrl, 55 | twitterUrl, 56 | telegramUrl, 57 | }) => { 58 | try { 59 | // Get current chain from wallet provider 60 | const network = agent.wallet.getNetwork(); 61 | if (!network.chainId) { 62 | throw new Error("Failed to get network chain ID"); 63 | } 64 | const chainId = parseInt(network.chainId); 65 | 66 | // Check if we're on BSC mainnet 67 | if (chainId !== bsc.id) { 68 | return { 69 | txHash: null, 70 | status: "error", 71 | message: `FourMeme token creation is only supported on BSC mainnet (chainId ${bsc.id}). Current chain: ${network.chainId}`, 72 | }; 73 | } 74 | 75 | // Get the BSC contract address 76 | const tokenManagerAddress = FOUR_MEME_ADDRESSES.BSC; 77 | 78 | const txHash = await agent.createFourMemeToken( 79 | tokenManagerAddress as Address, 80 | bnbAmount, 81 | name, 82 | shortName, 83 | desc, 84 | imgUrl, 85 | launchTimeFromNow, 86 | label, 87 | webUrl, 88 | twitterUrl, 89 | telegramUrl, 90 | ); 91 | 92 | return { 93 | txHash, 94 | status: "success", 95 | chain: "BSC", 96 | }; 97 | } catch (error: any) { 98 | return { 99 | txHash: null, 100 | status: "error", 101 | message: error.message || "Failed to create token", 102 | }; 103 | } 104 | }, 105 | }); 106 | } 107 | -------------------------------------------------------------------------------- /src/langchain/fourmeme/get_token_holdings.ts: -------------------------------------------------------------------------------- 1 | import { DynamicStructuredTool } from "@langchain/core/tools"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { getTokenHoldings } from "../../tools/fourmeme/token_manager"; 4 | 5 | export function getTokenHoldingsTool(agent: EvmAgentKit) { 6 | return new DynamicStructuredTool({ 7 | name: "get_fourmeme_token_holdings", 8 | description: 9 | "Retrieves all token holdings held by the current user on FourMeme platform. When returning ALWAYS show the full token address, token name, token symbol, description, my token amount, total increase percent, day increase percent, and progress to bonding curve.", 10 | schema: {}, 11 | func: async (_input: any) => { 12 | try { 13 | const tokens = await getTokenHoldings(agent); 14 | return { 15 | status: "success", 16 | summary: { 17 | tokens: JSON.stringify(tokens), 18 | }, 19 | }; 20 | } catch (error: any) { 21 | return { 22 | error: "Failed to get token holdings", 23 | errorMsg: error.message || "Failed to get token holdings", 24 | }; 25 | } 26 | }, 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /src/langchain/fourmeme/get_trending_tokens.ts: -------------------------------------------------------------------------------- 1 | import { DynamicStructuredTool } from "@langchain/core/tools"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { getFourMemeTrendingTokens } from "../../tools/fourmeme/token_manager"; 4 | 5 | export function getTrendingTokensTool(_agent: EvmAgentKit) { 6 | return new DynamicStructuredTool({ 7 | name: "get_fourmeme_trending_tokens", 8 | description: 9 | "Retrieves the latest trending tokens on FourMeme platform. When returning ALWAYS show the full token address, token name, token symbol, description, my token amount, total increase percent, day increase percent, and progress to bonding curve.", 10 | schema: {}, 11 | func: async (_input: any) => { 12 | try { 13 | const trendingTokens = await getFourMemeTrendingTokens(_agent); 14 | return { 15 | status: "success", 16 | summary: { 17 | trendingTokens: JSON.stringify(trendingTokens), 18 | }, 19 | }; 20 | } catch (error: any) { 21 | return { 22 | error: "Failed to get trending tokens", 23 | errorMsg: error.message || "Failed to get trending tokens", 24 | }; 25 | } 26 | }, 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /src/langchain/fourmeme/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./create_token"; 2 | export * from "./get_token_holdings"; 3 | export * from "./get_trending_tokens"; 4 | export * from "./sell_token"; 5 | export * from "./purchase_token"; 6 | -------------------------------------------------------------------------------- /src/langchain/fourmeme/purchase_token.ts: -------------------------------------------------------------------------------- 1 | import { DynamicStructuredTool } from "@langchain/core/tools"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { purchaseFourMemeToken } from "../../tools/fourmeme/token_manager"; 4 | import { z } from "zod"; 5 | import { FOUR_MEME_ADDRESSES } from "../../constants"; 6 | import { Address } from "viem"; 7 | 8 | export function purchaseFourMemeTokenTool(agent: EvmAgentKit) { 9 | return new DynamicStructuredTool({ 10 | name: "purchase_fourmeme_token", 11 | description: 12 | "Purchases a token on the FourMeme platform. Always confirm with the user the token address and amount before purchasing the token. The token address must be a valid FourMeme token address.", 13 | schema: z.object({ 14 | tokenAddress: z.string().describe("The address of the token to purchase"), 15 | tokenAmount: z.string().describe("The amount of tokens to purchase"), 16 | }), 17 | func: async (input: any) => { 18 | try { 19 | const tokenManagerAddress = FOUR_MEME_ADDRESSES.BSC as Address; 20 | return await purchaseFourMemeToken({ 21 | agent, 22 | tokenManagerAddress, 23 | tokenAddress: input.tokenAddress as Address, 24 | amount: input.tokenAmount, 25 | }); 26 | } catch (error: any) { 27 | return { 28 | error: "Failed to purchase token", 29 | errorMsg: error.message || "Failed to purchase token", 30 | }; 31 | } 32 | }, 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/langchain/fourmeme/sell_token.ts: -------------------------------------------------------------------------------- 1 | import { DynamicStructuredTool } from "@langchain/core/tools"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { sellToken } from "../../tools/fourmeme/token_manager"; 4 | import { z } from "zod"; 5 | import { FOUR_MEME_ADDRESSES } from "../../constants"; 6 | import { Address } from "viem"; 7 | 8 | export function sellTokenTool(agent: EvmAgentKit) { 9 | return new DynamicStructuredTool({ 10 | name: "sell_fourmeme_token", 11 | description: 12 | "Sells a token on the FourMeme platform. Always confirm with the user, the token address and amount before selling the token. The token address must be a valid FourMeme token address. If no token address is provided, prompt the user to call FOURMEME_GET_TOKENS first.", 13 | schema: z.object({ 14 | tokenAddress: z.string().describe("The address of the token to sell"), 15 | tokenAmount: z.string().describe("The amount of tokens to sell"), 16 | }), 17 | func: async (input: any) => { 18 | try { 19 | const tokenManagerAddress = FOUR_MEME_ADDRESSES.BSC as Address; 20 | return await sellToken({ 21 | agent, 22 | tokenManagerAddress, 23 | tokenAddress: input.tokenAddress as Address, 24 | tokenAmount: input.tokenAmount, 25 | }); 26 | } catch (error: any) { 27 | return { 28 | error: "Failed to sell token", 29 | errorMsg: error.message || "Failed to sell token", 30 | }; 31 | } 32 | }, 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/langchain/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./agent"; 2 | export * from "./elfa_ai"; 3 | export * from "./fourmeme"; 4 | export * from "./compound"; 5 | export * from "./defillama"; 6 | 7 | import type { EvmAgentKit } from "../agent"; 8 | 9 | import { 10 | ElfaPingTool, 11 | ElfaApiKeyStatusTool, 12 | ElfaGetMentionsTool, 13 | ElfaTrendingTokensTool, 14 | ElfaSearchMentionsTool, 15 | ElfaGetTopMentionsTool, 16 | ElfaAccountSmartStatsTool, 17 | createFourMemeTokenTool, 18 | getTokenHoldingsTool, 19 | sellTokenTool, 20 | CompoundSupplyTool, 21 | getTrendingTokensTool, 22 | purchaseFourMemeTokenTool, 23 | getProtocolTvlTool, 24 | } from "./index"; 25 | 26 | export function createEvmTools(evmKit: EvmAgentKit) { 27 | return [ 28 | new ElfaPingTool(evmKit), 29 | new ElfaApiKeyStatusTool(evmKit), 30 | new ElfaGetMentionsTool(evmKit), 31 | new ElfaTrendingTokensTool(evmKit), 32 | new ElfaSearchMentionsTool(evmKit), 33 | new ElfaGetTopMentionsTool(evmKit), 34 | new ElfaAccountSmartStatsTool(evmKit), 35 | 36 | // FourMeme 37 | createFourMemeTokenTool(evmKit), 38 | getTokenHoldingsTool(evmKit), 39 | getTrendingTokensTool(evmKit), 40 | sellTokenTool(evmKit), 41 | purchaseFourMemeTokenTool(evmKit), 42 | // Compound 43 | new CompoundSupplyTool(evmKit), 44 | 45 | // DefiLlama 46 | getProtocolTvlTool(evmKit), 47 | ]; 48 | } 49 | -------------------------------------------------------------------------------- /src/mcp/index.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 3 | import { z } from "zod"; 4 | import type { Action } from "../types/action"; 5 | import { EvmAgentKit } from "../agent"; 6 | import { zodToMCPShape } from "../utils/zodToMCPSchema"; 7 | 8 | /** 9 | * Creates an MCP server from a set of actions 10 | */ 11 | export function createMcpServer( 12 | actions: Record, 13 | EvmAgentKit: EvmAgentKit, 14 | options: { 15 | name: string; 16 | version: string; 17 | }, 18 | ) { 19 | // Create MCP server instance 20 | const server = new McpServer({ 21 | name: options.name, 22 | version: options.version, 23 | }); 24 | 25 | const registeredActions: string[] = []; 26 | 27 | // Convert each action to an MCP tool 28 | for (const [_, action] of Object.entries(actions)) { 29 | const { result } = zodToMCPShape(action.schema); 30 | 31 | server.tool(action.name, action.description, result, async (params) => { 32 | try { 33 | // Execute the action handler with the params directly 34 | const result = await action.handler(EvmAgentKit, params); 35 | 36 | // Format the result as MCP tool response 37 | return { 38 | content: [ 39 | { 40 | type: "text", 41 | text: JSON.stringify(result, null, 2), 42 | }, 43 | ], 44 | }; 45 | } catch (error) { 46 | console.error("error", error); 47 | // Handle errors in MCP format 48 | return { 49 | isError: true, 50 | content: [ 51 | { 52 | type: "text", 53 | text: 54 | error instanceof Error 55 | ? error.message 56 | : "Unknown error occurred", 57 | }, 58 | ], 59 | }; 60 | } 61 | }); 62 | 63 | registeredActions.push(action.name); 64 | 65 | // Add examples as prompts if they exist 66 | if (action.examples && action.examples.length > 0) { 67 | server.prompt( 68 | `${action.name}-examples`, 69 | { 70 | showIndex: z 71 | .string() 72 | .optional() 73 | .describe("Example index to show (number)"), 74 | }, 75 | (args) => { 76 | const showIndex = args.showIndex 77 | ? parseInt(args.showIndex) 78 | : undefined; 79 | const examples = action.examples.flat(); 80 | const selectedExamples = 81 | typeof showIndex === "number" ? [examples[showIndex]] : examples; 82 | 83 | const exampleText = selectedExamples 84 | .map( 85 | (ex, idx) => ` 86 | Example ${idx + 1}: 87 | Input: ${JSON.stringify(ex.input, null, 2)} 88 | Output: ${JSON.stringify(ex.output, null, 2)} 89 | Explanation: ${ex.explanation} 90 | `, 91 | ) 92 | .join("\n"); 93 | 94 | return { 95 | messages: [ 96 | { 97 | role: "user", 98 | content: { 99 | type: "text", 100 | text: `Examples for ${action.name}:\n${exampleText}`, 101 | }, 102 | }, 103 | ], 104 | }; 105 | }, 106 | ); 107 | } 108 | } 109 | 110 | return server; 111 | } 112 | /** 113 | * Helper to start the MCP server with stdio transport 114 | * 115 | * @param actions - The actions to expose to the MCP server 116 | * @param EvmAgentKit - The EVM agent kit 117 | * @param options - The options for the MCP server 118 | * @returns The MCP server 119 | * @throws Error if the MCP server fails to start 120 | * @example 121 | * import { ACTIONS } from "./actions"; 122 | * import { startMcpServer } from "./mcpWrapper"; 123 | * 124 | * const EvmAgentKit = new EvmAgentKit(); 125 | * 126 | * startMcpServer(ACTIONS, EvmAgentKit, { 127 | * name: "actions", 128 | * version: "1.0.0" 129 | * }); 130 | */ 131 | export async function startMcpServer( 132 | actions: Record, 133 | EvmAgentKit: EvmAgentKit, 134 | options: { 135 | name: string; 136 | version: string; 137 | }, 138 | ) { 139 | try { 140 | console.error("MCP server starting..."); 141 | const server = createMcpServer(actions, EvmAgentKit, options); 142 | const transport = new StdioServerTransport(); 143 | await server.connect(transport); 144 | console.error("MCP server started"); 145 | return server; 146 | } catch (error) { 147 | console.error("Error starting MCP server", error); 148 | throw error; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/network/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./network"; 2 | export * from "./types"; 3 | -------------------------------------------------------------------------------- /src/network/network.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Chain, 3 | mainnet, 4 | sepolia, 5 | baseSepolia, 6 | arbitrumSepolia, 7 | optimismSepolia, 8 | base, 9 | arbitrum, 10 | optimism, 11 | polygonMumbai, 12 | polygon, 13 | } from "viem/chains"; 14 | import * as chains from "viem/chains"; 15 | 16 | /** 17 | * Maps EVM chain IDs to network IDs 18 | */ 19 | export const CHAIN_ID_TO_NETWORK_ID: Record = { 20 | 1: "ethereum-mainnet", 21 | 11155111: "ethereum-sepolia", 22 | 137: "polygon-mainnet", 23 | 80001: "polygon-mumbai", 24 | 8453: "base-mainnet", 25 | 84532: "base-sepolia", 26 | 42161: "arbitrum-mainnet", 27 | 421614: "arbitrum-sepolia", 28 | 10: "optimism-mainnet", 29 | 11155420: "optimism-sepolia", 30 | 56: "bnb-mainnet", 31 | 97: "bnb-testnet", 32 | }; 33 | 34 | /** 35 | * Maps Coinbase network IDs to EVM chain IDs 36 | */ 37 | export const NETWORK_ID_TO_CHAIN_ID: Record = Object.entries( 38 | CHAIN_ID_TO_NETWORK_ID, 39 | ).reduce( 40 | (acc, [chainId, networkId]) => { 41 | acc[networkId] = String(chainId); 42 | return acc; 43 | }, 44 | {} as Record, 45 | ); 46 | 47 | /** 48 | * Maps Coinbase network IDs to Viem chain objects 49 | */ 50 | export const NETWORK_ID_TO_VIEM_CHAIN: Record = { 51 | "ethereum-mainnet": mainnet, 52 | "ethereum-sepolia": sepolia, 53 | "polygon-mainnet": polygon, 54 | "polygon-mumbai": polygonMumbai, 55 | "base-mainnet": base, 56 | "base-sepolia": baseSepolia, 57 | "arbitrum-mainnet": arbitrum, 58 | "arbitrum-sepolia": arbitrumSepolia, 59 | "optimism-mainnet": optimism, 60 | "optimism-sepolia": optimismSepolia, 61 | "bnb-mainnet": chains.bsc, 62 | "bnb-testnet": chains.bscTestnet, 63 | }; 64 | 65 | /** 66 | * Get a chain from the viem chains object 67 | * 68 | * @param id - The chain ID 69 | * @returns The chain 70 | */ 71 | export const getChain = (id: string): Chain => { 72 | const chainList = Object.values(chains); 73 | return chainList.find((chain) => chain.id === parseInt(id)) as Chain; 74 | }; 75 | -------------------------------------------------------------------------------- /src/network/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Network is the network that the wallet provider is connected to. 3 | */ 4 | export interface Network { 5 | /** 6 | * The protocol family of the network. 7 | */ 8 | protocolFamily: string; 9 | 10 | /** 11 | * The network ID of the network. 12 | */ 13 | networkId?: string; 14 | 15 | /** 16 | * The chain ID of the network. 17 | */ 18 | chainId?: string; 19 | } 20 | -------------------------------------------------------------------------------- /src/tools/agent/create_image.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../index"; 2 | import OpenAI from "openai"; 3 | 4 | /** 5 | * Generate an image using OpenAI's DALL-E 6 | * @param agent BnbAgentKit instance 7 | * @param prompt Text description of the image to generate 8 | * @param size Image size ('256x256', '512x512', or '1024x1024') (default: '1024x1024') 9 | * @param n Number of images to generate (default: 1) 10 | * @returns Object containing the generated image URLs 11 | */ 12 | export async function create_image( 13 | agent: EvmAgentKit, 14 | prompt: string, 15 | size: "256x256" | "512x512" | "1024x1024" = "1024x1024", 16 | n: number = 1, 17 | ) { 18 | try { 19 | if (!agent.config?.OPENAI_API_KEY) { 20 | throw new Error("OpenAI API key not found in agent configuration"); 21 | } 22 | 23 | const openai = new OpenAI({ 24 | apiKey: agent.config?.OPENAI_API_KEY, 25 | }); 26 | 27 | const response = await openai.images.generate({ 28 | prompt, 29 | n, 30 | size, 31 | }); 32 | 33 | return { 34 | images: response.data.map((img: any) => img.url), 35 | }; 36 | } catch (error: any) { 37 | throw new Error(`Image generation failed: ${error.message}`); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/tools/agent/get_info.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../index"; 2 | import OpenAI from "openai"; 3 | import { ChatCompletionMessageParam } from "openai/resources"; 4 | 5 | /** 6 | * Get detailed and latest information about any topic using Perplexity AI. 7 | * @param agent BnbAgentKit instance 8 | * @param prompt Text description of the topic to get information about 9 | * @returns Object containing the generated information 10 | */ 11 | export async function get_info(agent: EvmAgentKit, prompt: string) { 12 | try { 13 | if (!agent.config?.PERPLEXITY_API_KEY) { 14 | throw new Error("Perplexity API key not found in agent configuration"); 15 | } 16 | 17 | const perplexity = new OpenAI({ 18 | apiKey: agent.config?.PERPLEXITY_API_KEY, 19 | baseURL: "https://api.perplexity.ai", 20 | }); 21 | 22 | const messages: ChatCompletionMessageParam[] = [ 23 | { 24 | role: "system", 25 | content: 26 | "You are an artificial intelligence assistant and you need to " + 27 | "engage in a helpful, detailed, polite conversation with a user.", 28 | }, 29 | { 30 | role: "user", 31 | content: prompt, 32 | }, 33 | ]; 34 | 35 | const response = await perplexity.chat.completions.create({ 36 | model: "llama-3.1-sonar-large-128k-online", 37 | messages, 38 | }); 39 | 40 | return response.choices[0].message.content; 41 | } catch (error: any) { 42 | console.error(error); 43 | throw new Error(`Perplexity failed: ${error.message}`); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/tools/agent/get_wallet_address.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | 3 | /** 4 | * Get the agents wallet address 5 | * @param agent - EvmAgentKit instance 6 | * @returns string 7 | */ 8 | export function get_wallet_address(agent: EvmAgentKit) { 9 | // return agent.wallet_address.toBase58(); 10 | return agent.wallet_address; 11 | } 12 | -------------------------------------------------------------------------------- /src/tools/agent/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./create_image"; 2 | export * from "./get_wallet_address"; 3 | export * from "./get_info"; 4 | -------------------------------------------------------------------------------- /src/tools/coingecko/get_latest_pools.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | 3 | export async function getLatestPools(agent: EvmAgentKit) { 4 | try { 5 | if (!agent.config?.COINGECKO_PRO_API_KEY) { 6 | throw new Error("No CoinGecko Pro API key provided"); 7 | } 8 | 9 | const url = `https://pro-api.coingecko.com/api/v3/onchain/networks/bsc/new_pools?include=base_token,network&x_cg_pro_api_key=${agent.config?.COINGECKO_PRO_API_KEY}`; 10 | const res = await fetch(url); 11 | const data = await res.json(); 12 | 13 | return data; 14 | } catch (e) { 15 | throw new Error( 16 | // @ts-expect-error - error is an object 17 | `Error fetching latest pools from CoinGecko: ${e.message}`, 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/tools/coingecko/get_token_info.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | 3 | export async function getTokenInfo(agent: EvmAgentKit, tokenAddress: string) { 4 | try { 5 | if (!agent.config?.COINGECKO_PRO_API_KEY) { 6 | throw new Error("No CoinGecko Pro API key provided"); 7 | } 8 | 9 | const url = `https://pro-api.coingecko.com/api/v3/onchain/networks/bsc/tokens/${tokenAddress}/info?x_cg_pro_api_key=${agent.config?.COINGECKO_PRO_API_KEY}`; 10 | const res = await fetch(url); 11 | const data = await res.json(); 12 | 13 | return data; 14 | } catch (e) { 15 | throw new Error( 16 | // @ts-expect-error - error is an object 17 | `Error fetching token info from CoinGecko: ${e.message}`, 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/tools/coingecko/get_token_price_data.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | 3 | export async function getTokenPriceData( 4 | agent: EvmAgentKit, 5 | tokenAddresses: string[], 6 | ) { 7 | try { 8 | const url = agent.config?.COINGECKO_PRO_API_KEY 9 | ? `https://pro-api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=${tokenAddresses.join(",")}&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true&x_cg_pro_api_key=${agent.config?.COINGECKO_PRO_API_KEY}` 10 | : `https://api.coingecko.com/api/v3/simple/token_price/ethereum?contract_addresses=${tokenAddresses.join(",")}&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true${agent.config?.COINGECKO_DEMO_API_KEY && `&x_cg_demo_api_key=${agent.config?.COINGECKO_DEMO_API_KEY}`}`; 11 | 12 | const res = await fetch(url); 13 | const data = await res.json(); 14 | 15 | return data; 16 | } catch (e) { 17 | throw new Error( 18 | // @ts-expect-error - error is an object 19 | `Error fetching token price data from CoinGecko: ${e.message}`, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/tools/coingecko/get_top_gainers.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | 3 | export async function getTopGainers( 4 | agent: EvmAgentKit, 5 | duration: "1h" | "24h" | "7d" | "14d" | "30d" | "60d" | "1y" = "24h", 6 | topCoins: 300 | 500 | 1000 | "all" = "all", 7 | ) { 8 | try { 9 | if (!agent.config?.COINGECKO_PRO_API_KEY) { 10 | throw new Error("No CoinGecko Pro API key provided"); 11 | } 12 | 13 | const url = `https://pro-api.coingecko.com/api/v3/coins/top_gainers_losers?vs_currency=usd&duration=${duration}&top_coins=${topCoins}&x_cg_pro_api_key=${agent.config?.COINGECKO_PRO_API_KEY}`; 14 | const res = await fetch(url); 15 | const data = await res.json(); 16 | 17 | return data; 18 | } catch (e) { 19 | throw new Error( 20 | // @ts-expect-error - error is an object 21 | `Error fetching top gainers from CoinGecko: ${e.message}`, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/tools/coingecko/get_trending_pools.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | 3 | export async function getTrendingPools( 4 | agent: EvmAgentKit, 5 | duration: "5m" | "1h" | "24h" | "6h" = "24h", 6 | ) { 7 | try { 8 | if (!agent.config?.COINGECKO_PRO_API_KEY) { 9 | throw new Error("No CoinGecko Pro API key provided"); 10 | } 11 | 12 | const url = `https://pro-api.coingecko.com/api/v3/onchain/networks/bsc/trending_pools?include=base_token,network&duration=${duration}&x_cg_pro_api_key=${agent.config?.COINGECKO_PRO_API_KEY}`; 13 | const res = await fetch(url); 14 | const data = await res.json(); 15 | 16 | return data; 17 | } catch (e) { 18 | throw new Error( 19 | // @ts-expect-error - error is an object 20 | `Error fetching trending pools from CoinGecko: ${e.message}`, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/tools/coingecko/get_trending_tokens.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | 3 | export async function getTrendingTokens(agent: EvmAgentKit) { 4 | try { 5 | const url = agent.config?.COINGECKO_PRO_API_KEY 6 | ? `https://pro-api.coingecko.com/api/v3/search/trending?x_cg_pro_api_key=${agent.config?.COINGECKO_PRO_API_KEY}` 7 | : `https://api.coingecko.com/api/v3/search/trending${agent.config?.COINGECKO_DEMO_API_KEY && `?x_cg_demo_api_key=${agent.config?.COINGECKO_DEMO_API_KEY}`}`; 8 | const res = await fetch(url); 9 | const data = await res.json(); 10 | 11 | return data; 12 | } catch (e) { 13 | // @ts-expect-error - e is an Error object 14 | throw new Error(`Couldn't get trending tokens: ${e.message}`); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/tools/coingecko/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./get_latest_pools"; 2 | export * from "./get_token_info"; 3 | export * from "./get_token_price_data"; 4 | export * from "./get_top_gainers"; 5 | export * from "./get_trending_pools"; 6 | export * from "./get_trending_tokens"; 7 | -------------------------------------------------------------------------------- /src/tools/compound/constants.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "viem"; 2 | 3 | export const SUPPORTED_NETWORKS = ["ethereum-mainnet", "ethereum-sepolia"]; 4 | 5 | export const COMET_ADDRESSES: Record = { 6 | "ethereum-mainnet": "0xc3d688B66703497DAA19211EEdff47f25384cdc3", 7 | }; 8 | 9 | export const ASSET_ADDRESSES: Record> = { 10 | "ethereum-mainnet": { 11 | weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 12 | cbbtc: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", 13 | wsteth: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", 14 | usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", 15 | }, 16 | }; 17 | 18 | // Re-export the ERC20 ABI from the erc20 action provider constants 19 | 20 | export const COMET_ABI = [ 21 | { 22 | inputs: [ 23 | { internalType: "address", name: "asset", type: "address" }, 24 | { internalType: "uint256", name: "amount", type: "uint256" }, 25 | ], 26 | name: "supply", 27 | outputs: [], 28 | stateMutability: "nonpayable", 29 | type: "function", 30 | }, 31 | { 32 | inputs: [ 33 | { internalType: "address", name: "asset", type: "address" }, 34 | { internalType: "uint256", name: "amount", type: "uint256" }, 35 | ], 36 | name: "withdraw", 37 | outputs: [], 38 | stateMutability: "nonpayable", 39 | type: "function", 40 | }, 41 | { 42 | inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], 43 | name: "getPrice", 44 | outputs: [{ internalType: "uint256", name: "", type: "uint256" }], 45 | stateMutability: "view", 46 | type: "function", 47 | }, 48 | { 49 | inputs: [{ internalType: "address", name: "account", type: "address" }], 50 | name: "borrowBalanceOf", 51 | outputs: [{ internalType: "uint256", name: "", type: "uint256" }], 52 | stateMutability: "view", 53 | type: "function", 54 | }, 55 | { 56 | inputs: [], 57 | name: "numAssets", 58 | outputs: [{ internalType: "uint8", name: "", type: "uint8" }], 59 | stateMutability: "view", 60 | type: "function", 61 | }, 62 | { 63 | inputs: [{ internalType: "uint8", name: "i", type: "uint8" }], 64 | name: "getAssetInfo", 65 | outputs: [ 66 | { 67 | components: [ 68 | { internalType: "uint8", name: "offset", type: "uint8" }, 69 | { internalType: "address", name: "asset", type: "address" }, 70 | { internalType: "address", name: "priceFeed", type: "address" }, 71 | { internalType: "uint64", name: "scale", type: "uint64" }, 72 | { 73 | internalType: "uint64", 74 | name: "borrowCollateralFactor", 75 | type: "uint64", 76 | }, 77 | { 78 | internalType: "uint64", 79 | name: "liquidateCollateralFactor", 80 | type: "uint64", 81 | }, 82 | { internalType: "uint64", name: "liquidationFactor", type: "uint64" }, 83 | { internalType: "uint128", name: "supplyCap", type: "uint128" }, 84 | ], 85 | internalType: "struct CometCore.AssetInfo", 86 | name: "", 87 | type: "tuple", 88 | }, 89 | ], 90 | stateMutability: "view", 91 | type: "function", 92 | }, 93 | { 94 | inputs: [], 95 | name: "baseToken", 96 | outputs: [{ internalType: "address", name: "", type: "address" }], 97 | stateMutability: "view", 98 | type: "function", 99 | }, 100 | { 101 | inputs: [], 102 | name: "baseTokenPriceFeed", 103 | outputs: [{ internalType: "address", name: "", type: "address" }], 104 | stateMutability: "view", 105 | type: "function", 106 | }, 107 | { 108 | inputs: [ 109 | { internalType: "address", name: "account", type: "address" }, 110 | { internalType: "address", name: "asset", type: "address" }, 111 | ], 112 | name: "collateralBalanceOf", 113 | outputs: [{ internalType: "uint128", name: "balance", type: "uint128" }], 114 | stateMutability: "view", 115 | type: "function", 116 | }, 117 | ] as const; 118 | 119 | export const PRICE_FEED_ABI = [ 120 | { 121 | inputs: [], 122 | name: "latestRoundData", 123 | outputs: [ 124 | { name: "roundId", type: "uint80" }, 125 | { name: "answer", type: "int256" }, 126 | { name: "startedAt", type: "uint256" }, 127 | { name: "updatedAt", type: "uint256" }, 128 | { name: "answeredInRound", type: "uint80" }, 129 | ], 130 | stateMutability: "view", 131 | type: "function", 132 | }, 133 | ] as const; 134 | -------------------------------------------------------------------------------- /src/tools/compound/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./supply"; 2 | -------------------------------------------------------------------------------- /src/tools/compound/supply.ts: -------------------------------------------------------------------------------- 1 | import { parseUnits, formatUnits, encodeFunctionData } from "viem"; 2 | import { COMET_ABI } from "./constants"; 3 | import { 4 | getCometAddress, 5 | getAssetAddress, 6 | getHealthRatio, 7 | getTokenDecimals, 8 | getTokenBalance, 9 | getTokenSymbol, 10 | } from "./utils"; 11 | import { approve } from "../../utils"; 12 | import { EvmAgentKit } from "../../agent"; 13 | 14 | /** 15 | * Supply assets to Compound 16 | * @param agent - The agent instance 17 | * @param args - The supply arguments containing assetId and amount 18 | * @returns A string with the result of the supply operation 19 | */ 20 | export async function supply(props: { 21 | agent: EvmAgentKit; 22 | assetId: string; 23 | amount: string; 24 | }): Promise { 25 | const { agent, assetId, amount } = props; 26 | try { 27 | const network = agent.wallet.getNetwork(); 28 | const cometAddress = getCometAddress(network); 29 | const tokenAddress = getAssetAddress(network, assetId); 30 | 31 | if (!tokenAddress) { 32 | throw new Error(`Token address undefined for assetId ${assetId}`); 33 | } 34 | 35 | const decimals = await getTokenDecimals(agent.wallet, tokenAddress); 36 | const amountAtomic = parseUnits(amount, decimals); 37 | 38 | // Check wallet balance before proceeding 39 | const walletBalance = await getTokenBalance(agent.wallet, tokenAddress); 40 | if (walletBalance < amountAtomic) { 41 | const humanBalance = formatUnits(walletBalance, decimals); 42 | return `Error: Insufficient balance. You have ${humanBalance}, but trying to supply ${amount}`; 43 | } 44 | 45 | // Get current health ratio for reference 46 | const currentHealth = await getHealthRatio(agent.wallet, cometAddress); 47 | 48 | // Approve Compound to spend tokens 49 | const approvalResult = await approve( 50 | agent.wallet, 51 | tokenAddress, 52 | cometAddress, 53 | amountAtomic, 54 | ); 55 | if (approvalResult.startsWith("Error")) { 56 | return `Error approving token: ${approvalResult}`; 57 | } 58 | 59 | // Supply tokens to Compound 60 | const data = encodeFunctionData({ 61 | abi: COMET_ABI, 62 | functionName: "supply", 63 | args: [tokenAddress, amountAtomic], 64 | }); 65 | 66 | const txHash = await agent.wallet.sendTransaction({ 67 | to: cometAddress, 68 | data, 69 | }); 70 | await agent.wallet.waitForTransactionReceipt(txHash); 71 | 72 | // Get new health ratio and token symbol 73 | const newHealth = await getHealthRatio(agent.wallet, cometAddress); 74 | const tokenSymbol = await getTokenSymbol(agent.wallet, tokenAddress); 75 | 76 | // Format the amount for display 77 | const formattedAmount = formatUnits(amountAtomic, decimals); 78 | 79 | return { 80 | status: "success", 81 | summary: { 82 | supplyBalance: formattedAmount, 83 | tokenSymbol: tokenSymbol, 84 | tokenDecimals: decimals, 85 | txHash: txHash, 86 | healthRatio: { 87 | before: currentHealth.toFixed(2), 88 | after: newHealth.toFixed(2), 89 | }, 90 | }, 91 | }; 92 | // Construct a detailed success message 93 | // return `Successfully supplied ${formattedAmount} ${tokenSymbol} to Compound. Transaction hash: ${txHash}. Health ratio changed from ${currentHealth.toFixed(2)} to ${newHealth.toFixed(2)}.`; 94 | } catch (error) { 95 | return `Error supplying to Compound: ${ 96 | error instanceof Error 97 | ? error.message 98 | : error && typeof error === "object" && "message" in error 99 | ? `Error: ${error.message}` 100 | : error 101 | }`; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/tools/defillama/fetch_price.ts: -------------------------------------------------------------------------------- 1 | import { DEFILLAMA_PRICES_URL } from "./constants"; 2 | 3 | /** 4 | * @param chainTokenAddrStrings String array of strings in format of "chain:token_address" (e.g. "ethereum:0x0000000000000000000000000000000000000000") 5 | * @param searchWidth The width of the search window for the price data. Default is "6h" 6 | * @returns JSON string of the price data 7 | */ 8 | export async function fetchPrices(args: { 9 | chainTokenAddrStrings: string[]; 10 | searchWidth?: string; 11 | }): Promise { 12 | try { 13 | const { chainTokenAddrStrings, searchWidth } = args; 14 | const params = new URLSearchParams({}); 15 | const tokens = chainTokenAddrStrings.join(","); 16 | 17 | if (searchWidth) { 18 | params.set("searchWidth", searchWidth); 19 | } 20 | 21 | const url = `${DEFILLAMA_PRICES_URL}/prices/current/${tokens}?${params.toString()}`; 22 | const response = await fetch(url); 23 | 24 | if (!response.ok) { 25 | throw new Error(`HTTP error! status: ${response.status}`); 26 | } 27 | 28 | const data = await response.json(); 29 | return JSON.stringify(data, null, 2); 30 | } catch (error: unknown) { 31 | return `Error fetching token prices: ${error instanceof Error ? error.message : String(error)}`; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/tools/defillama/get_protocol_tvl.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DEFILLAMA_NETWORK_SLUGS, 3 | DEFILLAMA_BASE_URL, 4 | DEFILLAMA_PROTOCOL_SLUGS, 5 | } from "./constants"; 6 | import Fuse from "fuse.js"; 7 | import axios from "axios"; 8 | 9 | /** 10 | * Find the closest matching slug for a given ticker or name 11 | * @param ticker - The ticker or name to match against available slugs 12 | * @param category - The category to search in ("protocol" or "network") 13 | * @returns The best matching slug or empty string if no match found 14 | */ 15 | export function getSlugMatch( 16 | ticker: string, 17 | category: "protocol" | "network", 18 | ): string[] { 19 | const slugList = 20 | category === "protocol" 21 | ? DEFILLAMA_PROTOCOL_SLUGS 22 | : DEFILLAMA_NETWORK_SLUGS; 23 | 24 | // Using fuse.js for fuzzy search 25 | const fuse = new Fuse(slugList, { 26 | includeScore: true, 27 | threshold: 0.4, // Adjust this value to control the matching sensitivity 28 | }); 29 | const fuseResults = fuse.search(ticker); 30 | 31 | // Return top 3 matches or fewer if less are available 32 | return fuseResults.slice(0, 3).map((result: any) => result.item); 33 | } 34 | 35 | /** 36 | * Get Total Value Locked (TVL) for a specific protocol 37 | * @param slug - The protocol slug identifier 38 | * @returns Promise with TVL information as a formatted string 39 | */ 40 | export async function getProtocolTvl(slug: string): Promise { 41 | const slugMatches = getSlugMatch(slug, "protocol"); 42 | 43 | if (slugMatches.length === 0) { 44 | throw new Error( 45 | `No matching protocol slugs found. Here is the list of available protocol slugs: ${DEFILLAMA_PROTOCOL_SLUGS}`, 46 | ); 47 | } 48 | 49 | // Try each potential match until one works 50 | let result: number | null = null; 51 | let usedSlug: string = ""; 52 | let lastError: any = null; 53 | 54 | // Try each match in order 55 | for (const match of slugMatches) { 56 | if (DEFILLAMA_PROTOCOL_SLUGS.includes(match)) { 57 | console.error("match", match); 58 | try { 59 | const apiUrl = `${DEFILLAMA_BASE_URL}/tvl/${match}`; 60 | const response = await axios.get(apiUrl); 61 | if (response.data) { 62 | result = response.data; 63 | usedSlug = match; 64 | break; 65 | } 66 | } catch (e) { 67 | lastError = e; 68 | // Continue to the next match 69 | } 70 | } 71 | } 72 | 73 | if (result === null) { 74 | console.error("Error fetching protocol TVL:", lastError); 75 | throw new Error("Failed to fetch TVL data for any matching protocol slug"); 76 | } 77 | 78 | return `The TVL for ${usedSlug} is ${result} usd dollars`; 79 | } 80 | -------------------------------------------------------------------------------- /src/tools/defillama/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fetch_price"; 2 | export * from "./get_protocol_tvl"; 3 | -------------------------------------------------------------------------------- /src/tools/elfa_ai/elfa_ai_api.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | import axios, { AxiosInstance } from "axios"; 3 | 4 | function createAxiosInstance(apiKey: string | undefined): AxiosInstance { 5 | if (!apiKey) { 6 | throw new Error("ELFA_AI_API_KEY is not configured in EvmAgentKit config."); 7 | } 8 | return axios.create({ 9 | baseURL: "https://api.elfa.ai", 10 | headers: { 11 | "x-elfa-api-key": apiKey, 12 | "Content-Type": "application/json", 13 | }, 14 | }); 15 | } 16 | 17 | export async function pingElfaAiApi(agent: EvmAgentKit): Promise { 18 | const apiKey = agent.config?.ELFA_AI_API_KEY; 19 | const axiosInstance = createAxiosInstance(apiKey); 20 | const response = await axiosInstance.get("/v1/ping"); 21 | return response.data; 22 | } 23 | 24 | export async function getElfaAiApiKeyStatus(agent: EvmAgentKit): Promise { 25 | const apiKey = agent.config?.ELFA_AI_API_KEY; 26 | const axiosInstance = createAxiosInstance(apiKey); 27 | const response = await axiosInstance.get("/v1/key-status"); 28 | return response.data; 29 | } 30 | 31 | export async function getSmartMentions( 32 | agent: EvmAgentKit, 33 | limit: number = 100, 34 | offset: number = 0, 35 | ): Promise { 36 | const apiKey = agent.config?.ELFA_AI_API_KEY; 37 | const axiosInstance = createAxiosInstance(apiKey); 38 | const response = await axiosInstance.get("/v1/mentions", { 39 | params: { limit, offset }, 40 | }); 41 | return response.data; 42 | } 43 | 44 | export async function getTopMentionsByTicker( 45 | agent: EvmAgentKit, 46 | ticker: string, 47 | timeWindow: string = "1h", 48 | page: number = 1, 49 | pageSize: number = 10, 50 | includeAccountDetails: boolean = false, 51 | ): Promise { 52 | const apiKey = agent.config?.ELFA_AI_API_KEY; 53 | const axiosInstance = createAxiosInstance(apiKey); 54 | const response = await axiosInstance.get("/v1/top-mentions", { 55 | params: { ticker, timeWindow, page, pageSize, includeAccountDetails }, 56 | }); 57 | return response.data; 58 | } 59 | 60 | export async function searchMentionsByKeywords( 61 | agent: EvmAgentKit, 62 | keywords: string, 63 | from: number, 64 | to: number, 65 | limit: number = 20, 66 | cursor?: string, 67 | ): Promise { 68 | const apiKey = agent.config?.ELFA_AI_API_KEY; 69 | const axiosInstance = createAxiosInstance(apiKey); 70 | const response = await axiosInstance.get("/v1/mentions/search", { 71 | params: { keywords, from, to, limit, cursor }, 72 | }); 73 | return response.data; 74 | } 75 | 76 | export async function getTrendingTokensUsingElfaAi( 77 | agent: EvmAgentKit, 78 | timeWindow: string = "24h", 79 | page: number = 1, 80 | pageSize: number = 50, 81 | minMentions: number = 5, 82 | ): Promise { 83 | const apiKey = agent.config?.ELFA_AI_API_KEY; 84 | const axiosInstance = createAxiosInstance(apiKey); 85 | const response = await axiosInstance.get("/v1/trending-tokens", { 86 | params: { timeWindow, page, pageSize, minMentions }, 87 | }); 88 | return response.data; 89 | } 90 | 91 | export async function getSmartTwitterAccountStats( 92 | agent: EvmAgentKit, 93 | username: string, 94 | ): Promise { 95 | const apiKey = agent.config?.ELFA_AI_API_KEY; 96 | const axiosInstance = createAxiosInstance(apiKey); 97 | const response = await axiosInstance.get("/v1/account/smart-stats", { 98 | params: { username }, 99 | }); 100 | return response.data; 101 | } 102 | -------------------------------------------------------------------------------- /src/tools/elfa_ai/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./elfa_ai_api"; 2 | -------------------------------------------------------------------------------- /src/tools/erc20/get_erc20_balance.ts: -------------------------------------------------------------------------------- 1 | import { formatEther, formatUnits } from "viem"; 2 | import { EvmAgentKit } from "../../agent"; 3 | import { Address } from "viem"; 4 | import { getTokenBalance, getTokenDecimals } from "../compound/utils"; 5 | 6 | /** 7 | * Get the balance of ETH or an ERC20 token for the agent's wallet 8 | * @param agent - EvmAgentKit instance 9 | * @param token_address - Optional ERC20 token address. If not provided, returns ETH balance 10 | * @returns Promise resolving to the balance as a number (in UI units) 11 | */ 12 | export async function get_erc20_balance( 13 | agent: EvmAgentKit, 14 | token_address?: Address, 15 | ): Promise { 16 | if (!token_address) { 17 | const balance = await agent.connection.getBalance({ 18 | address: agent.wallet_address as Address, 19 | }); 20 | return Number(formatEther(balance)); 21 | } 22 | 23 | const tokenBalance = await getTokenBalance(agent.wallet, token_address); 24 | 25 | const decimals = await getTokenDecimals(agent.wallet, token_address); 26 | 27 | return Number(formatUnits(tokenBalance, decimals)); 28 | } 29 | -------------------------------------------------------------------------------- /src/tools/erc20/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./get_erc20_balance"; 2 | export * from "./transfer"; 3 | -------------------------------------------------------------------------------- /src/tools/erc20/transfer.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../../agent"; 2 | import { Address, erc20Abi, encodeFunctionData } from "viem"; 3 | import { parseUnits } from "viem"; 4 | 5 | /** 6 | * Transfer ETH or ERC20 tokens to a recipient 7 | * @param agent EvmAgentKit instance 8 | * @param destination Recipient's address 9 | * @param amount Amount to transfer in token units 10 | * @param tokenAddress Optional ERC20 token address. If not provided, transfers ETH 11 | * @returns Transaction hash 12 | */ 13 | export async function transfer( 14 | agent: EvmAgentKit, 15 | destination: Address, 16 | amount: number, 17 | tokenAddress?: Address, 18 | ): Promise
{ 19 | try { 20 | if (!tokenAddress) { 21 | // Transfer native ETH 22 | const hash = await agent.wallet.sendTransaction({ 23 | to: destination, 24 | value: parseUnits(amount.toString(), 18), 25 | }); 26 | return hash; 27 | } else { 28 | // Transfer ERC20 token 29 | // const decimals = await agent.connection.readContract({ 30 | // address: tokenAddress, 31 | // abi: erc20Abi, 32 | // functionName: "decimals", 33 | // }); 34 | 35 | // const { request } = await agent.connection.simulateContract({ 36 | // address: tokenAddress, 37 | // abi: erc20Abi, 38 | // functionName: "transfer", 39 | // args: [destination, parseUnits(amount.toString(), decimals)], 40 | // account: agent.wallet, 41 | // }); 42 | 43 | // Fallback to regular transfer 44 | // Get token decimals first to ensure correct amount parsing 45 | const decimals = await agent.connection.readContract({ 46 | address: tokenAddress, 47 | abi: erc20Abi, 48 | functionName: "decimals", 49 | }); 50 | 51 | const hash = await agent.wallet.sendTransaction({ 52 | to: tokenAddress, 53 | data: encodeFunctionData({ 54 | abi: erc20Abi, 55 | functionName: "transfer", 56 | args: [destination, parseUnits(amount.toString(), Number(decimals))], 57 | }), 58 | }); 59 | return hash; 60 | } 61 | } catch (error) { 62 | console.error("Transfer failed:", error); 63 | throw error; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/tools/fourmeme/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./token_manager"; 2 | -------------------------------------------------------------------------------- /src/tools/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./defillama"; 2 | export * from "./agent"; 3 | export * from "./elfa_ai"; 4 | export * from "./coingecko"; 5 | export * from "./erc20"; 6 | export * from "./fourmeme"; 7 | export * from "./compound"; 8 | -------------------------------------------------------------------------------- /src/types/action.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../agent"; 2 | import { z } from "zod"; 3 | 4 | /** 5 | * Example of an action with input and output 6 | */ 7 | export interface ActionExample { 8 | input: Record; 9 | output: Record; 10 | explanation: string; 11 | } 12 | 13 | /** 14 | * Handler function type for executing the action 15 | */ 16 | export type Handler = ( 17 | agent: EvmAgentKit, 18 | input: Record, 19 | ) => Promise>; 20 | 21 | /** 22 | * Main Action interface inspired by ELIZA 23 | * This interface makes it easier to implement actions across different frameworks 24 | */ 25 | export interface Action { 26 | /** 27 | * Unique name of the action 28 | */ 29 | name: string; 30 | 31 | /** 32 | * Alternative names/phrases that can trigger this action 33 | */ 34 | similes: string[]; 35 | 36 | /** 37 | * Detailed description of what the action does 38 | */ 39 | description: string; 40 | 41 | /** 42 | * Array of example inputs and outputs for the action 43 | * Each inner array represents a group of related examples 44 | */ 45 | examples: ActionExample[][]; 46 | 47 | /** 48 | * Zod schema for input validation 49 | */ 50 | schema: z.ZodObject; 51 | 52 | /** 53 | * Function that executes the action 54 | */ 55 | handler: Handler; 56 | } 57 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | import { EvmAgentKit } from "../agent"; 2 | import { z } from "zod"; 3 | 4 | export interface Config { 5 | OPENAI_API_KEY?: string; 6 | PERPLEXITY_API_KEY?: string; 7 | PRIORITY_LEVEL?: "medium" | "high" | "veryHigh"; // medium, high, or veryHigh 8 | ELFA_AI_API_KEY?: string; 9 | COINGECKO_PRO_API_KEY?: string; 10 | COINGECKO_DEMO_API_KEY?: string; 11 | } 12 | 13 | export interface Creator { 14 | address: string; 15 | percentage: number; 16 | } 17 | 18 | /** 19 | * Example of an action with input and output 20 | */ 21 | export interface ActionExample { 22 | input: Record; 23 | output: Record; 24 | explanation: string; 25 | } 26 | 27 | /** 28 | * Handler function type for executing the action 29 | */ 30 | export type Handler = ( 31 | agent: EvmAgentKit, 32 | input: Record, 33 | ) => Promise>; 34 | 35 | /** 36 | * Main Action interface inspired by ELIZA 37 | * This interface makes it easier to implement actions across different frameworks 38 | */ 39 | export interface Action { 40 | /** 41 | * Unique name of the action 42 | */ 43 | name: string; 44 | 45 | /** 46 | * Alternative names/phrases that can trigger this action 47 | */ 48 | similes: string[]; 49 | 50 | /** 51 | * Detailed description of what the action does 52 | */ 53 | description: string; 54 | 55 | /** 56 | * Array of example inputs and outputs for the action 57 | * Each inner array represents a group of related examples 58 | */ 59 | examples: ActionExample[][]; 60 | 61 | /** 62 | * Zod schema for input validation 63 | */ 64 | schema: z.ZodObject; 65 | 66 | /** 67 | * Function that executes the action 68 | */ 69 | handler: Handler; 70 | } 71 | 72 | export interface TokenCheck { 73 | tokenProgram: string; 74 | tokenType: string; 75 | risks: Array<{ 76 | name: string; 77 | level: string; 78 | description: string; 79 | score: number; 80 | }>; 81 | score: number; 82 | } 83 | 84 | // Regular expressions for validating addresses 85 | export const EVM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/; 86 | 87 | // Chain ID validation schema 88 | export const chainIdSchema = z.string().refine( 89 | (val) => { 90 | const num = Number.parseInt(val, 10); 91 | // Regular chain IDs (1-99999) 92 | if (num > 0 && num < 100000) { 93 | return true; 94 | } 95 | // Special chain IDs (100000000+) 96 | if (num >= 100000000) { 97 | return true; 98 | } 99 | return false; 100 | }, 101 | { 102 | message: "Chain ID must be either 1-99999, or 100000000+", 103 | }, 104 | ); 105 | 106 | // Token info parameters schema 107 | export const getDebridgeTokensInfoSchema = z.object({ 108 | /** Chain ID to query tokens for */ 109 | chainId: chainIdSchema.describe( 110 | "Chain ID to get token information for. Examples: '1' (Ethereum), '56' (BNB Chain)", 111 | ), 112 | 113 | /** Optional token address to filter results */ 114 | tokenAddress: z 115 | .string() 116 | .optional() 117 | .describe( 118 | "Token address to query information for. For EVM chains: use 0x-prefixed address", 119 | ), 120 | 121 | /** Optional search term to filter tokens by name or symbol */ 122 | search: z 123 | .string() 124 | .optional() 125 | .describe( 126 | "Search term to filter tokens by name or symbol (e.g., 'USDC', 'Ethereum')", 127 | ), 128 | }); 129 | 130 | export type GetDebridgeTokensInfoParams = z.infer< 131 | typeof getDebridgeTokensInfoSchema 132 | >; 133 | 134 | export interface FluxbeamServerResponse { 135 | signature: string; 136 | } 137 | 138 | export interface Quote { 139 | amountIn: number; 140 | inputMint: string; 141 | minimumOut: number; 142 | outAmount: number; 143 | outputMint: string; 144 | pool: string; 145 | program: string; 146 | } 147 | 148 | export interface TransformedResponse { 149 | quote: Quote; 150 | } 151 | -------------------------------------------------------------------------------- /src/utils/actionExecutor.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "../types"; 2 | import { EvmAgentKit } from "../agent"; 3 | import { ACTIONS } from "../actions"; 4 | 5 | /** 6 | * Find an action by its name or one of its similes 7 | */ 8 | export function findAction(query: string) { 9 | const normalizedQuery = query.toLowerCase().trim(); 10 | return Object.values(ACTIONS).find( 11 | (action) => 12 | action.name.toLowerCase() === normalizedQuery || 13 | action.similes.some( 14 | (simile: string) => simile.toLowerCase() === normalizedQuery, 15 | ), 16 | ); 17 | } 18 | 19 | /** 20 | * Execute an action with the given input 21 | */ 22 | export async function executeAction( 23 | action: Action, 24 | agent: EvmAgentKit, 25 | input: Record, 26 | ): Promise> { 27 | try { 28 | // Validate input using Zod schema 29 | const validatedInput = action.schema.parse(input); 30 | 31 | // Execute the action with validated input 32 | const result = await action.handler(agent, validatedInput); 33 | 34 | return { 35 | status: "success", 36 | ...result, 37 | }; 38 | } catch (error: any) { 39 | // Handle Zod validation errors specially 40 | if (error.errors) { 41 | return { 42 | status: "error", 43 | message: "Validation error", 44 | details: error.errors, 45 | code: "VALIDATION_ERROR", 46 | }; 47 | } 48 | 49 | return { 50 | status: "error", 51 | message: error.message, 52 | code: error.code || "EXECUTION_ERROR", 53 | }; 54 | } 55 | } 56 | 57 | /** 58 | * Get examples for an action 59 | */ 60 | export function getActionExamples(action: Action): string { 61 | return action.examples 62 | .flat() 63 | .map((example) => { 64 | return `Input: ${JSON.stringify(example.input, null, 2)} 65 | Output: ${JSON.stringify(example.output, null, 2)} 66 | Explanation: ${example.explanation} 67 | ---`; 68 | }) 69 | .join("\n"); 70 | } 71 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { encodeFunctionData } from "viem"; 2 | import { EvmWalletProvider } from "../wallet-providers"; 3 | import { erc20Abi } from "viem"; 4 | 5 | /** 6 | * Approves a spender to spend tokens on behalf of the owner 7 | * 8 | * @param wallet - The wallet provider 9 | * @param tokenAddress - The address of the token contract 10 | * @param spenderAddress - The address of the spender 11 | * @param amount - The amount to approve in atomic units (wei) 12 | * @returns A success message or error message 13 | */ 14 | export async function approve( 15 | wallet: EvmWalletProvider, 16 | tokenAddress: string, 17 | spenderAddress: string, 18 | amount: bigint, 19 | ): Promise { 20 | try { 21 | const data = encodeFunctionData({ 22 | abi: erc20Abi, 23 | functionName: "approve", 24 | args: [spenderAddress as `0x${string}`, amount], 25 | }); 26 | 27 | const txHash = await wallet.sendTransaction({ 28 | to: tokenAddress as `0x${string}`, 29 | data, 30 | }); 31 | 32 | await wallet.waitForTransactionReceipt(txHash); 33 | 34 | return `Successfully approved ${spenderAddress} to spend ${amount} tokens`; 35 | } catch (error) { 36 | return `Error approving tokens: ${error}`; 37 | } 38 | } 39 | 40 | /** 41 | * Scales a gas estimate by a given multiplier. 42 | * 43 | * This function converts the gas estimate to a number, applies the multiplier, 44 | * rounds the result to the nearest integer, and returns it as a bigint. 45 | * 46 | * @param gas - The original gas estimate (bigint). 47 | * @param multiplier - The factor by which to scale the estimate. 48 | * @returns The adjusted gas estimate as a bigint. 49 | */ 50 | export function applyGasMultiplier(gas: bigint, multiplier: number): bigint { 51 | return BigInt(Math.round(Number(gas) * multiplier)); 52 | } 53 | -------------------------------------------------------------------------------- /src/utils/zodToMCPSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | // Define the raw shape type that MCP tools expect 4 | export type MCPSchemaShape = { 5 | [key: string]: z.ZodType; 6 | }; 7 | 8 | // // Type guards for Zod schema types 9 | // function isZodOptional(schema: z.ZodTypeAny): schema is z.ZodOptional { 10 | // return schema instanceof z.ZodOptional; 11 | // } 12 | 13 | function isZodObject(schema: z.ZodTypeAny): schema is z.ZodObject { 14 | // Check both instanceof and the typeName property 15 | return ( 16 | schema instanceof z.ZodObject || schema?._def?.typeName === "ZodObject" 17 | ); 18 | } 19 | 20 | /** 21 | * Converts a Zod object schema to a flat shape for MCP tools 22 | * @param schema The Zod schema to convert 23 | * @returns A flattened schema shape compatible with MCP tools 24 | * @throws Error if the schema is not an object type 25 | */ 26 | export function zodToMCPShape(schema: z.ZodTypeAny): { 27 | result: MCPSchemaShape; 28 | keys: string[]; 29 | } { 30 | if (!isZodObject(schema)) { 31 | throw new Error("MCP tools require an object schema at the top level"); 32 | } 33 | 34 | const shape = schema.shape; 35 | const result: MCPSchemaShape = {}; 36 | 37 | for (const [key, value] of Object.entries(shape)) { 38 | result[key] = value as any; 39 | } 40 | 41 | return { 42 | result, 43 | keys: Object.keys(result), 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/vercel-ai/index.ts: -------------------------------------------------------------------------------- 1 | import { tool, type CoreTool } from "ai"; 2 | import { EvmAgentKit } from "../agent"; 3 | import { executeAction } from "../utils/actionExecutor"; 4 | import { ACTIONS } from "../actions"; 5 | import { Action } from "../types"; 6 | 7 | export function createEvmTools( 8 | EvmAgentKit: EvmAgentKit, 9 | ): Record { 10 | const tools: Record = {}; 11 | const actionKeys = Object.keys(ACTIONS); 12 | 13 | for (const key of actionKeys) { 14 | const action = ACTIONS[key as keyof typeof ACTIONS] as Action; 15 | tools[key] = tool({ 16 | // @ts-expect-error Value matches type however TS still shows error 17 | id: action.name, 18 | description: ` 19 | ${action.description} 20 | 21 | Similes: ${action.similes.map( 22 | (simile) => ` 23 | ${simile} 24 | `, 25 | )} 26 | `.slice(0, 1023), 27 | parameters: action.schema, 28 | execute: async (params) => 29 | await executeAction(action, EvmAgentKit, params), 30 | }); 31 | } 32 | 33 | return tools; 34 | } 35 | -------------------------------------------------------------------------------- /src/wallet-providers/evmWalletProvider.ts: -------------------------------------------------------------------------------- 1 | // TODO: Improve type safety 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | import { WalletProvider } from "./walletProvider"; 4 | import { 5 | TransactionRequest, 6 | ReadContractParameters, 7 | ReadContractReturnType, 8 | ContractFunctionName, 9 | Abi, 10 | ContractFunctionArgs, 11 | } from "viem"; 12 | 13 | /** 14 | * EvmWalletProvider is the abstract base class for all EVM wallet providers. 15 | * 16 | * @abstract 17 | */ 18 | export abstract class EvmWalletProvider extends WalletProvider { 19 | /** 20 | * Sign a message. 21 | * 22 | * @param message - The message to sign. 23 | * @returns The signed message. 24 | */ 25 | abstract signMessage(message: string | Uint8Array): Promise<`0x${string}`>; 26 | 27 | /** 28 | * Sign a typed data. 29 | * 30 | * @param typedData - The typed data to sign. 31 | * @returns The signed typed data. 32 | */ 33 | abstract signTypedData(typedData: any): Promise<`0x${string}`>; 34 | 35 | /** 36 | * Sign a transaction. 37 | * 38 | * @param transaction - The transaction to sign. 39 | * @returns The signed transaction. 40 | */ 41 | abstract signTransaction( 42 | transaction: TransactionRequest, 43 | ): Promise<`0x${string}`>; 44 | 45 | /** 46 | * Send a transaction. 47 | * 48 | * @param transaction - The transaction to send. 49 | * @returns The transaction hash. 50 | */ 51 | abstract sendTransaction( 52 | transaction: TransactionRequest, 53 | ): Promise<`0x${string}`>; 54 | 55 | /** 56 | * Wait for a transaction receipt. 57 | * 58 | * @param txHash - The transaction hash. 59 | * @returns The transaction receipt. 60 | */ 61 | abstract waitForTransactionReceipt(txHash: `0x${string}`): Promise; 62 | 63 | /** 64 | * Read a contract. 65 | * 66 | * @param params - The parameters to read the contract. 67 | * @returns The response from the contract. 68 | */ 69 | abstract readContract< 70 | const abi extends Abi | readonly unknown[], 71 | functionName extends ContractFunctionName, 72 | const args extends ContractFunctionArgs, 73 | >( 74 | params: ReadContractParameters, 75 | ): Promise>; 76 | } 77 | -------------------------------------------------------------------------------- /src/wallet-providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./evmWalletProvider"; 2 | export * from "./viemWalletProvider"; 3 | export * from "./walletProvider"; 4 | -------------------------------------------------------------------------------- /src/wallet-providers/walletProvider.ts: -------------------------------------------------------------------------------- 1 | import { Network } from "../network"; 2 | // import { sendAnalyticsEvent } from "../analytics"; 3 | 4 | /** 5 | * WalletProvider is the abstract base class for all wallet providers. 6 | * 7 | * @abstract 8 | */ 9 | export abstract class WalletProvider { 10 | /** 11 | * Initializes the wallet provider. 12 | */ 13 | constructor() { 14 | // Wait for the next tick to ensure child class is initialized 15 | Promise.resolve().then(() => { 16 | this.trackInitialization(); 17 | }); 18 | } 19 | 20 | /** 21 | * Tracks the initialization of the wallet provider. 22 | */ 23 | private trackInitialization() { 24 | try { 25 | // sendAnalyticsEvent({ 26 | // name: "agent_initialization", 27 | // action: "initialize_wallet_provider", 28 | // component: "wallet_provider", 29 | // wallet_provider: this.getName(), 30 | // wallet_address: this.getAddress(), 31 | // network_id: this.getNetwork().networkId, 32 | // chain_id: this.getNetwork().chainId, 33 | // protocol_family: this.getNetwork().protocolFamily, 34 | // }); 35 | } catch (error) { 36 | console.warn("Failed to track wallet provider initialization:", error); 37 | } 38 | } 39 | 40 | /** 41 | * Get the address of the wallet provider. 42 | * 43 | * @returns The address of the wallet provider. 44 | */ 45 | abstract getAddress(): string; 46 | 47 | /** 48 | * Get the network of the wallet provider. 49 | * 50 | * @returns The network of the wallet provider. 51 | */ 52 | abstract getNetwork(): Network; 53 | 54 | /** 55 | * Get the name of the wallet provider. 56 | * 57 | * @returns The name of the wallet provider. 58 | */ 59 | abstract getName(): string; 60 | 61 | /** 62 | * Get the balance of the native asset of the network. 63 | * 64 | * @returns The balance of the native asset of the network. 65 | */ 66 | abstract getBalance(): Promise; 67 | 68 | /** 69 | * Transfer the native asset of the network. 70 | * 71 | * @param to - The destination address. 72 | * @param value - The amount to transfer in whole units (e.g. ETH) 73 | * @returns The transaction hash. 74 | */ 75 | abstract nativeTransfer(to: string, value: string): Promise; 76 | } 77 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import { HumanMessage } from "@langchain/core/messages"; 2 | import { MemorySaver } from "@langchain/langgraph"; 3 | import { createReactAgent } from "@langchain/langgraph/prebuilt"; 4 | import { ChatOpenAI } from "@langchain/openai"; 5 | import * as dotenv from "dotenv"; 6 | import * as fs from "fs"; 7 | import * as readline from "readline"; 8 | import { EvmAgentKit } from "../src"; 9 | import { createEvmTools } from "../src/langchain"; 10 | 11 | dotenv.config(); 12 | 13 | function validateEnvironment(): void { 14 | const missingVars: string[] = []; 15 | const requiredVars = ["OPENAI_API_KEY", "RPC_URL", "EVM_PRIVATE_KEY"]; 16 | 17 | requiredVars.forEach((varName) => { 18 | if (!process.env[varName]) { 19 | missingVars.push(varName); 20 | } 21 | }); 22 | 23 | if (missingVars.length > 0) { 24 | console.error("Error: Required environment variables are not set"); 25 | missingVars.forEach((varName) => { 26 | console.error(`${varName}=your_${varName.toLowerCase()}_here`); 27 | }); 28 | process.exit(1); 29 | } 30 | } 31 | 32 | validateEnvironment(); 33 | 34 | const WALLET_DATA_FILE = "wallet_data.txt"; 35 | 36 | async function initializeAgent() { 37 | try { 38 | const llm = new ChatOpenAI({ 39 | modelName: "gpt-4o-mini", 40 | temperature: 0.3, 41 | }); 42 | 43 | let walletDataStr: string | null = null; 44 | 45 | if (fs.existsSync(WALLET_DATA_FILE)) { 46 | try { 47 | walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8"); 48 | } catch (error) { 49 | console.error("Error reading wallet data:", error); 50 | } 51 | } 52 | 53 | const evmAgent = new EvmAgentKit( 54 | process.env.EVM_PRIVATE_KEY!, 55 | process.env.RPC_URL!, 56 | { 57 | OPENAI_API_KEY: process.env.OPENAI_API_KEY!, 58 | PERPLEXITY_API_KEY: process.env.PERPLEXITY_API_KEY!, 59 | }, 60 | ); 61 | 62 | const tools = createEvmTools(evmAgent); 63 | 64 | const memory = new MemorySaver(); 65 | const config = { configurable: { thread_id: "Evm Agent Kit!" } }; 66 | 67 | const agent = createReactAgent({ 68 | llm, 69 | tools, 70 | checkpointSaver: memory, 71 | messageModifier: ` 72 | You are a helpful agent that can interact onchain using the Evm Agent Kit. You are 73 | empowered to interact onchain using your tools. If you ever need funds, you can request them from the 74 | faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX 75 | (internal) HTTP error code, ask the user to try again later. If someone asks you to do something you 76 | can't do with your currently available tools, you must say so, and encourage them to implement it 77 | themselves using the Evm Agent Kit, recommend they go to https://www.EvmAgentKit.xyz for more information. Be 78 | concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested. 79 | `, 80 | }); 81 | 82 | if (walletDataStr) { 83 | fs.writeFileSync(WALLET_DATA_FILE, walletDataStr); 84 | } 85 | 86 | return { agent, config }; 87 | } catch (error) { 88 | console.error("Failed to initialize agent:", error); 89 | throw error; 90 | } 91 | } 92 | 93 | async function runAutonomousMode(agent: any, config: any, interval = 10) { 94 | console.log("Starting autonomous mode..."); 95 | 96 | while (true) { 97 | try { 98 | const thought = 99 | "Be creative and do something interesting on the blockchain. " + 100 | "Choose an action or set of actions and execute it that highlights your abilities."; 101 | 102 | const stream = await agent.stream( 103 | { messages: [new HumanMessage(thought)] }, 104 | config, 105 | ); 106 | 107 | for await (const chunk of stream) { 108 | if ("agent" in chunk) { 109 | console.log(chunk.agent.messages[0].content); 110 | } else if ("tools" in chunk) { 111 | console.log(chunk.tools.messages[0].content); 112 | } 113 | console.log("-------------------"); 114 | } 115 | 116 | await new Promise((resolve) => setTimeout(resolve, interval * 1000)); 117 | } catch (error) { 118 | if (error instanceof Error) { 119 | console.error("Error:", error.message); 120 | } 121 | process.exit(1); 122 | } 123 | } 124 | } 125 | 126 | async function runChatMode(agent: any, config: any) { 127 | console.log("Starting chat mode... Type 'exit' to end."); 128 | 129 | const rl = readline.createInterface({ 130 | input: process.stdin, 131 | output: process.stdout, 132 | }); 133 | 134 | const question = (prompt: string): Promise => 135 | new Promise((resolve) => rl.question(prompt, resolve)); 136 | 137 | try { 138 | while (true) { 139 | const userInput = await question("\nPrompt: "); 140 | 141 | if (userInput.toLowerCase() === "exit") { 142 | break; 143 | } 144 | 145 | const stream = await agent.stream( 146 | { messages: [new HumanMessage(userInput)] }, 147 | config, 148 | ); 149 | 150 | for await (const chunk of stream) { 151 | if ("agent" in chunk) { 152 | console.log(chunk.agent.messages[0].content); 153 | } else if ("tools" in chunk) { 154 | console.log(chunk.tools.messages[0].content); 155 | } 156 | console.log("-------------------"); 157 | } 158 | } 159 | } catch (error) { 160 | if (error instanceof Error) { 161 | console.error("Error:", error.message); 162 | } 163 | process.exit(1); 164 | } finally { 165 | rl.close(); 166 | } 167 | } 168 | 169 | async function chooseMode(): Promise<"chat" | "auto"> { 170 | const rl = readline.createInterface({ 171 | input: process.stdin, 172 | output: process.stdout, 173 | }); 174 | 175 | const question = (prompt: string): Promise => 176 | new Promise((resolve) => rl.question(prompt, resolve)); 177 | 178 | while (true) { 179 | console.log("\nAvailable modes:"); 180 | console.log("1. chat - Interactive chat mode"); 181 | console.log("2. auto - Autonomous action mode"); 182 | 183 | const choice = (await question("\nChoose a mode (enter number or name): ")) 184 | .toLowerCase() 185 | .trim(); 186 | 187 | rl.close(); 188 | 189 | if (choice === "1" || choice === "chat") { 190 | return "chat"; 191 | } else if (choice === "2" || choice === "auto") { 192 | return "auto"; 193 | } 194 | console.log("Invalid choice. Please try again."); 195 | } 196 | } 197 | 198 | async function main() { 199 | try { 200 | console.log("Starting Agent..."); 201 | const { agent, config } = await initializeAgent(); 202 | const mode = await chooseMode(); 203 | 204 | if (mode === "chat") { 205 | console.log("Starting chat mode..."); 206 | await runChatMode(agent, config); 207 | } else { 208 | console.log("Starting autonomous mode..."); 209 | await runAutonomousMode(agent, config); 210 | } 211 | } catch (error) { 212 | if (error instanceof Error) { 213 | console.error("Error:", error.message); 214 | } 215 | process.exit(1); 216 | } 217 | } 218 | 219 | if (require.main === module) { 220 | main().catch((error) => { 221 | console.error("Fatal error:", error); 222 | process.exit(1); 223 | }); 224 | } 225 | -------------------------------------------------------------------------------- /test/mcp.ts: -------------------------------------------------------------------------------- 1 | import { startMcpServer } from "../src/mcp"; 2 | import { ACTIONS } from "../src/actions"; 3 | import { EvmAgentKit } from "../src/agent"; 4 | import * as dotenv from "dotenv"; 5 | 6 | dotenv.config(); 7 | 8 | const agent = new EvmAgentKit( 9 | process.env.EVM_PRIVATE_KEY!, 10 | process.env.RPC_URL!, 11 | { 12 | OPENAI_API_KEY: process.env.OPENAI_API_KEY!, 13 | }, 14 | ); 15 | 16 | const finalActions = { 17 | ...ACTIONS, 18 | }; 19 | 20 | startMcpServer(finalActions, agent, { name: "evm-agent", version: "0.0.1" }); 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "lib": ["es2020", "dom"], 6 | "declaration": true, 7 | "declarationMap": true, 8 | "sourceMap": true, 9 | "outDir": "./dist", 10 | "rootDir": "./src", 11 | "strict": true, 12 | "noImplicitAny": true, 13 | "strictBindCallApply": true, 14 | "strictPropertyInitialization": true, 15 | "noImplicitThis": true, 16 | "useUnknownInCatchVariables": true, 17 | "alwaysStrict": true, 18 | "exactOptionalPropertyTypes": true, 19 | "noImplicitReturns": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noImplicitOverride": true, 22 | "esModuleInterop": true, 23 | "forceConsistentCasingInFileNames": true, 24 | "skipLibCheck": true, 25 | "resolveJsonModule": true, 26 | "paths": { 27 | "@cks-systems/manifest-sdk": ["./node_modules/@cks-systems/manifest-sdk/dist/browser.js"] 28 | } 29 | }, 30 | "include": ["src/**/*"], 31 | "exclude": ["node_modules", "dist", "**/*.test.ts"] 32 | } 33 | --------------------------------------------------------------------------------