├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── agentkit-core ├── README.md ├── package.json ├── src │ ├── BaseActions │ │ ├── EncodeFunctionData.ts │ │ ├── FomatHelpers.ts │ │ ├── GetBalance.ts │ │ ├── GetStatusFromUserop.ts │ │ ├── ReadContract.ts │ │ ├── SendTransaction.ts │ │ ├── SignMessage.ts │ │ └── index.ts │ ├── actions │ │ ├── checkTransactionAction.ts │ │ ├── getAddressAction.ts │ │ ├── getBalanceAction.ts │ │ ├── getTokenDetailsAction.ts │ │ ├── index.ts │ │ ├── smartSwapAction │ │ │ ├── dln.debridge.finance.json │ │ │ └── index.ts │ │ └── smartTransferAction.ts │ ├── agentkit.ts │ ├── constants.ts │ ├── index.ts │ ├── langchain.ts │ ├── services.ts │ └── types.ts └── tsconfig.json ├── agentkit-demo ├── .env.sample ├── .gitignore ├── README.md ├── bun.lockb ├── index.ts ├── package.json └── tsconfig.json ├── biome.json ├── bun.lockb ├── jest.config.base.cjs ├── package.json ├── repo-banner.png ├── tsconfig.base.json └── turbo.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | 3 | **/.python-version 4 | 5 | # IDE 6 | .idea/* 7 | .vscode/* 8 | 9 | ## Emacs 10 | *~ 11 | \#*\# 12 | .\#* 13 | **/.projectile 14 | 15 | # Python cache files 16 | **/__pycache__/ 17 | 18 | # Wallet data 19 | **/wallet_data.txt 20 | 21 | # Tools 22 | **/.pytest_cache 23 | 24 | **/.ruff_cache 25 | 26 | **/.mypy_cache 27 | 28 | # Build 29 | **/_build/ 30 | 31 | **/build/ 32 | 33 | **/dist/ 34 | 35 | # Virtual Environments 36 | **/venv/ 37 | **/.venv/ 38 | 39 | # Environment Configurations 40 | **/env/ 41 | **/.env/ 42 | **/.env 43 | **/.env.local/ 44 | **/.env.test/ 45 | 46 | # Logs 47 | logs 48 | *.log 49 | npm-debug.log* 50 | yarn-debug.log* 51 | yarn-error.log* 52 | lerna-debug.log* 53 | .pnpm-debug.log* 54 | 55 | # IDE 56 | .idea/* 57 | .vscode/* 58 | 59 | ## Emacs 60 | *~ 61 | \#*\# 62 | .\#* 63 | **/.projectile 64 | 65 | # Build outputs 66 | dist 67 | out 68 | .next 69 | .nuxt 70 | build/Release 71 | .turbo/ 72 | 73 | # Coverage 74 | coverage 75 | **/.coverage 76 | *.lcov 77 | .nyc_output 78 | lib-cov 79 | 80 | # Dependencies 81 | node_modules/ 82 | jspm_packages/ 83 | bower_components 84 | web_modules/ 85 | .pnp.* 86 | 87 | # Cache 88 | .npm 89 | .eslintcache 90 | .stylelintcache 91 | .parcel-cache 92 | .cache 93 | .temp 94 | .rpt2_cache/ 95 | .rts2_cache_cjs/ 96 | .rts2_cache_es/ 97 | .rts2_cache_umd/ 98 | 99 | # IDE 100 | .vscode 101 | .vscode-test 102 | .idea 103 | .tern-port 104 | 105 | # Environment Configurations 106 | **/env/ 107 | **/.env/ 108 | **/.env 109 | **/.env.local/ 110 | **/.env.test/ 111 | 112 | # Runtime data 113 | pids 114 | *.pid 115 | *.seed 116 | *.pid.lock 117 | *.tsbuildinfo 118 | .node_repl_history 119 | 120 | # Yarn v2 121 | .yarn/cache 122 | .yarn/unplugged 123 | .yarn/build-state.yml 124 | .yarn/install-state.gz 125 | 126 | # Misc 127 | .DS_Store 128 | **/*_local* 129 | api.json 130 | 131 | # JSDoc 132 | docs/ 133 | plugin-*/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 0xGasless Agentkit Contributing Guide 2 | Thank you for your interest in contributing to 0xGasless Agentkit! We welcome all contributions, no matter how big or small. Some of the ways you can contribute include: 3 | - Adding new actions to the core package 4 | - Updating existing Langchain Toolkits or adding new Langchain Toolkits to support new tools 5 | - Creating new AI frameworks extensions 6 | - Adding tests and improving documentation 7 | 8 | ### Set-up 9 | 10 | Clone the repo by running: 11 | 12 | ```bash 13 | git clone git@github.com:0xgasless/0xgasless-agentkit.git 14 | ``` 15 | 16 | ## Python Development 17 | ### Prerequisites 18 | - Python 3.10 or higher 19 | - Rust/Cargo installed ([Rust Installation Instructions](https://doc.rust-lang.org/cargo/getting-started/installation.html)) 20 | - Poetry for package management and tooling 21 | - [Poetry Installation Instructions](https://python-poetry.org/docs/#installation) 22 | 23 | `agentkit-langchain` also requires a [0xGasless API Key](https://portal.0xgasless.com/access/api). 24 | 25 | ### Development Tools 26 | #### Formatting 27 | `make format` 28 | 29 | #### Linting 30 | - Check linter 31 | `make lint` 32 | 33 | - Fix linter errors 34 | `make lint-fix` 35 | 36 | #### Unit Testing 37 | - Run unit tests 38 | `make test` 39 | 40 | ## Typescript Development 41 | ### Prerequisites 42 | - Node.js 18 or higher 43 | - npm for package management 44 | 45 | Install dependencies: 46 | 47 | ```bash 48 | npm install 49 | ``` 50 | 51 | ### Development Tools 52 | #### Building 53 | 54 | To build all packages: 55 | 56 | ```bash 57 | npm run build 58 | ``` 59 | 60 | #### Linting & Formatting 61 | 62 | To check for lint errors: 63 | 64 | ```bash 65 | npm run lint 66 | ``` 67 | 68 | To automatically fix lint errors: 69 | 70 | ```bash 71 | npm run lint-fix 72 | ``` 73 | 74 | To format code: 75 | 76 | ```bash 77 | npm run format 78 | ``` 79 | 80 | #### Testing 81 | 82 | To run all tests: 83 | 84 | ```bash 85 | npm test 86 | ``` 87 | 88 | #### Documentation 89 | 90 | To generate documentation: 91 | 92 | ```bash 93 | npm run docs 94 | ``` 95 | 96 | #### Typescript Code Style 97 | 98 | All code must follow the project's ESLint and Prettier configurations. The key rules are: 99 | - Use TypeScript 100 | - Follow JSDoc documentation standards 101 | - Use 2 spaces for indentation 102 | - Maximum line length of 100 characters 103 | - Double quotes for strings 104 | - Semicolons required 105 | 106 | 107 | ## Adding an Action to Agentkit Core 108 | ### Python 109 | - Actions are defined in `./cdp-agentkit-core/python/cdp_agentkit_core/actions` module. See `./cdp-agentkit-core/python/cdp_agentkit_core/actions/mint_nft.py` for an example. 110 | - Actions are created by subclassing `CdpAction` 111 | E.g. 112 | ```python 113 | class DeployNftAction(CdpAction): 114 | """Deploy NFT action.""" 115 | 116 | name: str = "mint_nft" 117 | description: str = MINT_NFT_PROMPT 118 | args_schema: type[BaseModel] | None = MintNftInput 119 | func: Callable[..., str] = mint_nft 120 | ``` 121 | 122 | #### Components of an Agentic Action 123 | - `name` - Name of the action. 124 | - `description` - A string that will provide the AI Agent with context on what the function does and a natural language description of the input. 125 | - E.g. 126 | ```python 127 | MINT_NFT_PROMPT = """ 128 | This tool will mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation. It takes the contract address of the NFT onchain and the destination address onchain that will receive the NFT as inputs.""" 129 | ``` 130 | - `arg_schema` - A Pydantic Model that defines the input argument schema for the action. 131 | - E.g. 132 | ```python 133 | class MintNftInput(BaseModel): 134 | """Input argument schema for mint NFT action.""" 135 | 136 | contract_address: str = Field( 137 | ..., 138 | description="The contract address of the NFT (ERC-721) to mint, e.g. `0x036CbD53842c5426634e7929541eC2318f3dCF7e`", 139 | ) 140 | destination: str = Field( 141 | ..., 142 | description="The destination address that will receive the NFT onchain, e.g. `0x036CbD53842c5426634e7929541eC2318f3dCF7e`", 143 | ) 144 | ``` 145 | - `func` - A function (or Callable class) that executes the action. 146 | - E.g. 147 | ```python 148 | def mint_nft(wallet: Wallet, contract_address: str, destination: str) -> str: 149 | """Mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation. 150 | 151 | Args: 152 | wallet (Wallet): The wallet to trade the asset from. 153 | contract_address (str): The contract address of the NFT (ERC-721) to mint, e.g. `0x036CbD53842c5426634e7929541eC2318f3dCF7e`. 154 | destination (str): The destination address that will receive the NFT onchain, e.g. `0x036CbD53842c5426634e7929541eC2318f3dCF7e`. 155 | 156 | Returns: 157 | str: A message containing the NFT mint details. 158 | 159 | """ 160 | mint_args = {"to": destination, "quantity": "1"} 161 | 162 | mint_invocation = wallet.invoke_contract( 163 | contract_address=contract_address, method="mint", args=mint_args 164 | ).wait() 165 | 166 | return f"Minted NFT from contract {contract_address} to address {destination} on network {wallet.network_id}.\nTransaction hash for the mint: {mint_invocation.transaction.transaction_hash}\nTransaction link for the mint: {mint_invocation.transaction.transaction_link}" 167 | ``` 168 | 169 | ### Typescript 170 | Actions are defined in `cdp-agentkit-core/typescript/src/actions` module. See `cdp-agentkit-core/typescript/src/actions/cdp/mint_nft.ts` for an example. 171 | 172 | Actions are created by implementing the `CdpAction` interface: 173 | 174 | ```typescript 175 | import { CdpAction } from "./cdp_action"; 176 | import { Wallet } from "@coinbase/coinbase-sdk"; 177 | import { z } from "zod"; 178 | 179 | const MINT_NFT_PROMPT = ` 180 | This tool will mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation. It takes the contract address of the NFT onchain and the destination address onchain that will receive the NFT as inputs. Do not use the contract address as the destination address. If you are unsure of the destination address, please ask the user before proceeding.`; 181 | 182 | /** 183 | * Input schema for mint NFT action. 184 | */ 185 | const MintNftInput = z 186 | .object({ 187 | contractAddress: z.string().describe("The contract address of the NFT to mint"), 188 | destination: z.string().describe("The destination address that will receive the NFT"), 189 | }) 190 | .strip() 191 | .describe("Instructions for minting an NFT"); 192 | 193 | /** 194 | * Mints an NFT (ERC-721) to a specified destination address onchain. 195 | * 196 | * @param wallet - The wallet to mint the NFT from. 197 | * @param args - The input arguments for the action. 198 | * @returns A message containing the NFT mint details. 199 | */ 200 | async function mintNft(wallet: Wallet, args: z.infer): Promise { 201 | const mintArgs = { 202 | to: args.destination, 203 | quantity: "1", 204 | }; 205 | 206 | try { 207 | const mintInvocation = await wallet.invokeContract({ 208 | contractAddress: args.contractAddress, 209 | method: "mint", 210 | args: mintArgs, 211 | }); 212 | 213 | const result = await mintInvocation.wait(); 214 | 215 | return `Minted NFT from contract ${args.contractAddress} to address ${args.destination} on network ${wallet.getNetworkId()}.\nTransaction hash for the mint: ${result.getTransaction().getTransactionHash()}\nTransaction link for the mint: ${result.getTransaction().getTransactionLink()}`; 216 | } catch (error) { 217 | return `Error minting NFT: ${error}`; 218 | } 219 | } 220 | 221 | /** 222 | * Mint NFT action. 223 | */ 224 | export class MintNftAction implements CdpAction { 225 | public name = "mint_nft"; 226 | public description = MINT_NFT_PROMPT; 227 | public argsSchema = MintNftInput; 228 | public func = mintNft; 229 | } 230 | ``` 231 | 232 | #### Components of an Agentic Action 233 | 234 | 1. **Input Schema**: Define the input parameters using Zod schemas 235 | 2. **Prompt**: A description that helps the AI understand when and how to use the action 236 | 3. **Action Class**: Implements the `CdpAction` interface with: 237 | - `name`: Unique identifier for the action 238 | - `description`: The prompt text 239 | - `argsSchema`: The Zod schema for validating inputs 240 | - `func`: The implementation function 241 | 4. **Implementation Function**: The actual logic that executes the action 242 | 243 | ## Adding an Agentic Action to Langchain Toolkit 244 | For both Python and Typescript, follow these steps: 245 | 1. Ensure the action is implemented in `cdp-agentkit-core` and in a released version. 246 | 2. Update the `cdp-agentkit-core` dependency to the latest version. 247 | 3. Add the action to the list of tools in the `CdpToolkit` class documentation. 248 | 249 | ## Adding an Agentic Action to the Twitter Toolkit 250 | ### Python 251 | 1. Ensure the action is implemented in `cdp-agentkit-core/actions/social/twitter`. 252 | 2. Add a wrapper method to `TwitterApiWrapper` in `./twitter_langchain/twitter_api_wrapper.py` 253 | - E.g. 254 | ```python 255 | def post_tweet_wrapper(self, tweet: str) -> str: 256 | """Post tweet to Twitter. 257 | 258 | Args: 259 | client (tweepy.Client): The tweepy client to use. 260 | tweet (str): The text of the tweet to post to twitter. Tweets can be maximum 280 characters. 261 | 262 | Returns: 263 | str: A message containing the result of the post action and the tweet. 264 | 265 | """ 266 | 267 | return post_tweet(client=self.client, tweet=tweet) 268 | ``` 269 | 3. Add call to the wrapper in `TwitterApiWrapper.run` in `./twitter_langchain/twitter_api_wrapper.py` 270 | - E.g. 271 | ```python 272 | if mode == "post_tweet": 273 | return self.post_tweet_wrapper(**kwargs) 274 | 275 | ``` 276 | 4. Add the action to the list of available tools in the `TwitterToolkit` in `./twitter_langchain/twitter_toolkit.py` 277 | - E.g. 278 | ```python 279 | actions: List[Dict] = [ 280 | { 281 | "mode": "post_tweet", 282 | "name": "post_tweet", 283 | "description": POST_TWEET_PROMPT, 284 | "args_schema": PostTweetInput, 285 | }, 286 | ] 287 | ``` 288 | 5. Update `TwitterToolkit` documentation 289 | - Add the action to the list of tools 290 | - Add any additional ENV requirements 291 | 292 | 293 | 294 | ## Changelog 295 | - For new features and bug fixes, please add a new changelog entry to the `CHANGELOG.md` file in the appropriate packages and include that in your Pull Request. 296 | 297 | ## Pull Request Process 298 | 299 | 1. Create a new branch for your changes 300 | 2. Make your changes following the coding standards 301 | 3. Add tests for any new functionality 302 | 4. Update documentation as needed 303 | 5. Update the CHANGELOG.md 304 | 6. Submit a pull request 305 | 306 | ## Getting Help 307 | 308 | If you have questions or need help, please: 309 | 1. Check the existing documentation 310 | 2. Search through existing issues 311 | 3. Create a new issue with your question 312 | 313 | Thank you for contributing to CDP AgentKit! 314 | 315 | 316 | 317 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache-2.0 License 2 | 3 | Copyright 2024 0xGasless 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | 0xGasless AgentKit 5 | 6 |

7 |

8 | 0xGasless AgentKit 9 |

10 |

11 | 0xGasless AgentKit lets you build AI agents that can trade, manage funds, and interact with DeFi protocols in natural language. 12 |

13 |
14 | 15 |
16 | NPM Downloads 17 | GitHub License 18 |
19 | 20 |
21 | Typescript Version 22 | PyPI - Python Version 23 |
24 | 25 | ## Table of Contents 26 | - [🚀 Overview](#-overview) 27 | - [✨ Key Features](#-key-features) 28 | - [📚 Examples](#-examples) 29 | - [🏗️ Repository Structure](#️-repository-structure) 30 | - [💻 Contributing](#-contributing) 31 | - [📖 Documentation](#-documentation) 32 | - [🔒 Security](#-security) 33 | - [📄 License](#-license) 34 | 35 | # 🚀 Overview 36 | 0xGasless AgentKit is a powerful toolkit for building AI agents that can interact with blockchain networks and DeFi protocols. It enables gasless transactions and account abstraction on EVM chains, making it easier to create sophisticated blockchain applications. 37 | 38 | **Create agents that can:** 39 | - Execute gasless transactions without holding native tokens 40 | - Transfer and trade tokens 41 | - Deploy smart contracts 42 | - Interact with DeFi protocols 43 | - Get wallet details and balances 44 | - Create and manage smart accounts 45 | 46 | **How it works** 47 | 48 | 0xGasless AgentKit leverages ERC-4337 account abstraction to enable gasless transactions: 49 | 1. Configure your agent with a wallet 50 | 2. Use the built-in tools for blockchain interactions 51 | 3. Execute transactions without requiring native tokens for gas 52 | 4. Integrate with any AI framework of your choice 53 | 54 | # ✨ Key Features 55 | 56 | - **Framework-agnostic**: Common AI Agent primitives that can be used with any AI framework 57 | - **Python and Node.js Support**: Full support for both Python and TypeScript 58 | - **Gasless Transactions**: Execute transactions without holding native tokens 59 | - **Account Abstraction**: Built on ERC-4337 standard 60 | - **Multi-Chain Support**: Works across major EVM chains: 61 | - BSC (56) 62 | - Avalanche (43114) 63 | - Base (8453) 64 | - Sonic (146) 65 | - Moonbeam (1284) 66 | 67 | **Supported Onchain Actions:** 68 | - Getting wallet details and balances 69 | - Transferring and trading tokens 70 | - Coming Soon: 71 | - Deploying ERC-20 tokens 72 | - Deploying ERC-721 tokens and minting NFTs 73 | - Buying and selling Dex Swap ERC-20 coins 74 | - Wrapping ETH to WETH on Base 75 | 76 | # 📚 Examples 77 | 78 | Check out [agentkit-langchain/examples](./agentkit-demo/index.ts) for inspiration and help getting started! 79 | - [Chatbot Typescript](./agentkit-demo/index.ts): Simple example of a Node.js Chatbot that can perform complex onchain interactions, using OpenAI. 80 | 81 | # 🏗️ Repository Structure 82 | 83 | AgentKit is organized as a [monorepo](https://en.wikipedia.org/wiki/Monorepo) that contains multiple packages: 84 | 85 | ``` 86 | ./ 87 | ├── agentkit-core/ 88 | │ └── typescript/ 89 | │ ├── BaseActions/ 90 | │ │ ├── SendTransaction.ts 91 | │ │ ├── SignMessage.ts 92 | │ │ ├── EncodeFunctionData.ts 93 | │ │ ├── FormatHelpers.ts 94 | │ │ ├── GetBalance.ts 95 | │ │ ├── GetStatusFromUserop.ts 96 | │ │ └── ReadContract.ts 97 | │ └── Actions/ 98 | ├── agentkit-demo/ 99 | │ ├── typescript/ 100 | │ └── examples/ 101 | ``` 102 | 103 | ## Base Actions 104 | 105 | | Action | Description | File | 106 | |--------|-------------|------| 107 | | SendTransaction | Execute blockchain transactions | [SendTransaction.ts](./agentkit-core/src/BaseActions/SendTransaction.ts) | 108 | | SignMessage | Sign messages for authentication | [SignMessage.ts](./agentkit-core/src/BaseActions/SignMessage.ts) | 109 | | EncodeFunctionData | Encode function calls for smart contracts | [EncodeFunctionData.ts](./agentkit-core/src/BaseActions/EncodeFunctionData.ts) | 110 | | FormatHelpers | Utility functions for data formatting | [FormatHelpers.ts](./agentkit-core/src/BaseActions/FormatHelpers.ts) | 111 | | GetBalance | Retrieve token balances | [GetBalance.ts](./agentkit-core/src/BaseActions/GetBalance.ts) | 112 | | GetStatusFromUserop | Check transaction status | [GetStatusFromUserop.ts](./agentkit-core/src/BaseActions/GetStatusFromUserop.ts) | 113 | | ReadContract | Read data from smart contracts | [ReadContract.ts](./agentkit-core/src/BaseActions/ReadContract.ts) | 114 | 115 | ## High-Level Actions 116 | 117 | | Action | Description | File | Base Actions Used | 118 | |--------|-------------|------|------------------| 119 | | SmartTransfer | Execute gasless token transfers | [smartTransferAction.ts](./agentkit-core/src/Actions/smartTransferAction.ts) | SendTransactions | 120 | | CheckTransaction | Monitor transaction status | [checkTransactionAction.ts](./agentkit-core/src/Actions/checkTransactionAction.ts) | CheckTransactions | 121 | | GetAddress | Retrieve wallet addresses | [getAddressAction.ts](./agentkit-core/src/Actions/getAddressAction.ts) | GetAddressActions| 122 | | GetBalance | Check token balances | [getBalanceAction.ts](./agentkit-core/src/Actions/getBalanceAction.ts) | GetBalance | 123 | | GetTokenDetails | Fetch token information | [getTokenDetailsAction.ts](./agentkit-core/src/Actions/getTokenDetailsAction.ts) | GetTokenDetails | 124 | | SmartSwap | Perform token swaps | [smartSwapAction/](./agentkit-core/src/Actions/smartSwapAction/) | SwapTransactions | 125 | 126 | # 🚀 Quickstarts 127 | 128 | ## 📘 Typescript 129 | 130 | ### By use case 131 | - **Money transmission** 132 | - Send and receive payments [[SmartTransfer](./agentkit-core/src/Actions/smartTransferAction.ts)] 133 | - Check transaction status [[CheckTransaction](./agentkit-core/src/Actions/checkTransactionAction.ts)] 134 | - Get wallet address [[GetAddress](./agentkit-core/src/Actions/getAddressAction.ts)] 135 | - **Token Operations** 136 | - Check token balances [[GetBalance](./agentkit-core/src/Actions/getBalanceAction.ts)] 137 | - Get token details [[GetTokenDetails](./agentkit-core/src/Actions/getTokenDetailsAction.ts)] 138 | - Create new tokens [[CreateFourmemeToken](./agentkit-core/src/Actions/createFourmemeTokenAction.ts)] 139 | - **DeFi Operations** 140 | - Swap tokens without gas fees 141 | - Wrap ETH to WETH 142 | - Interact with DEX protocols 143 | 144 | # 🛠️ Supported Tools and Frameworks 145 | 146 | ## Tools 147 | 148 | | Plugin | Tools | 149 | | --- | --- | 150 | | Smart Transfer | Execute gasless token transfers | 151 | | Smart Swap | Perform token swaps without gas | 152 | | Token Creation | Deploy new tokens | 153 | | Balance Check | Check token balances | 154 | | Transaction Monitor | Monitor transaction status | 155 | | Contract Reader | Read smart contract data | 156 | 157 | ## Common Use Cases 158 | 159 | | Use Case | Required Actions | Description | 160 | |----------|-----------------|-------------| 161 | | Token Transfer | GetAddress, GetBalance, SmartTransfer, CheckTransaction | Complete flow for transferring tokens | 162 | | Token Creation | CreateFourmemeToken, GetTokenDetails | Deploy and verify new tokens | 163 | | Token Swap | GetBalance, SmartSwap, CheckTransaction | Swap tokens with gasless execution | 164 | | Balance Check | GetAddress, GetBalance | Check balances for any token | 165 | | Transaction Monitoring | CheckTransaction | Monitor transaction status | 166 | 167 | # 💻 Contributing 168 | 169 | AgentKit welcomes community contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for more information. 170 | 171 | Some ways you can contribute: 172 | - Adding new actions to the core package 173 | - Updating existing Langchain Toolkits or adding new ones 174 | - Creating new AI frameworks extensions 175 | - Adding tests and improving documentation 176 | 177 | # 📖 Documentation 178 | 179 | - [AgentKit Documentation](https://docs.0xgasless.com/docs) 180 | 181 | # 🔒 Security 182 | 183 | The AgentKit team takes security seriously. 184 | See [SECURITY.md](SECURITY.md) for more information. 185 | 186 | # 📄 License 187 | 188 | Apache-2.0 189 | 190 | # 🤝 Community 191 | - Follow us on [X](https://x.com/0xGasless) 192 | - Follow us on [LinkedIn](https://www.linkedin.com/company/0xgasless/) 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /agentkit-core/README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | 0xGasless AgentKit 5 | 6 |

7 |

0xGasless AgentKit

8 |

A powerful toolkit for gasless transactions and account abstraction on EVM chains.

9 |
10 | 11 | ## Features 12 | 13 | - 🌟 **Gasless Transactions**: Execute transactions without holding native tokens 14 | - 🔐 **Account Abstraction**: Built on ERC-4337 standard 15 | - 💱 **Token Operations**: Transfers, swaps, and deployments without gas fees 16 | - 🔄 **Multi-Chain Support**: Works across major EVM chains 17 | - 🤖 **AI Integration**: Built-in LangChain compatibility 18 | 19 | ## Supported Networks 20 | 21 | - Base (8453) 22 | - Fantom (250) 23 | - Moonbeam (1284) 24 | - Metis (1088) 25 | - Avalanche (43114) 26 | - BSC (56) 27 | 28 | ## Installation 29 | 30 | Using bun: 31 | 32 | ```bash 33 | bun add @0xgasless/agentkit 34 | ``` 35 | 36 | ```typescript 37 | import { Agentkit, LangchainAgentkitToolkit } from "@0xgasless/agentkit"; 38 | import { HumanMessage } from "@langchain/core/messages"; 39 | import { MemorySaver } from "@langchain/langgraph"; 40 | import { createReactAgent } from "@langchain/langgraph/prebuilt"; 41 | import { ChatOpenAI } from "@langchain/openai"; 42 | import * as dotenv from "dotenv"; 43 | import * as readline from "readline"; 44 | 45 | dotenv.config(); 46 | 47 | function validateEnvironment(): void { 48 | const missingVars: string[] = []; 49 | 50 | const requiredVars = ["OPENROUTER_API_KEY", "PRIVATE_KEY", "RPC_URL", "API_KEY", "CHAIN_ID"]; 51 | 52 | requiredVars.forEach(varName => { 53 | if (!process.env[varName]) { 54 | missingVars.push(varName); 55 | } 56 | }); 57 | 58 | if (missingVars.length > 0) { 59 | console.error("Error: Required environment variables are not set"); 60 | missingVars.forEach(varName => { 61 | console.error(`${varName}=your_${varName.toLowerCase()}_here`); 62 | }); 63 | process.exit(1); 64 | } 65 | 66 | if (!process.env.CHAIN_ID) { 67 | console.warn("Warning: CHAIN_ID not set, defaulting to base-sepolia"); 68 | } 69 | } 70 | 71 | validateEnvironment(); 72 | 73 | async function initializeAgent() { 74 | try { 75 | const llm = new ChatOpenAI({ 76 | model: "gpt-4o", 77 | openAIApiKey: process.env.OPENROUTER_API_KEY, 78 | configuration: { 79 | baseURL: "https://openrouter.ai/api/v1", 80 | }, 81 | }); 82 | 83 | // Initialize 0xGasless AgentKit 84 | const agentkit = await Agentkit.configureWithWallet({ 85 | privateKey: process.env.PRIVATE_KEY as `0x${string}`, 86 | rpcUrl: process.env.RPC_URL, 87 | apiKey: process.env.API_KEY as string, 88 | chainID: Number(process.env.CHAIN_ID) || 8453, // Base Sepolia 89 | }); 90 | 91 | // Initialize AgentKit Toolkit and get tools 92 | const agentkitToolkit = new LangchainAgentkitToolkit(agentkit); 93 | const tools = agentkitToolkit.getTools(); 94 | 95 | const memory = new MemorySaver(); 96 | const agentConfig = { configurable: { thread_id: "0xGasless AgentKit Chatbot Example!" } }; 97 | 98 | const agent = createReactAgent({ 99 | llm, 100 | tools, 101 | checkpointSaver: memory, 102 | messageModifier: ` 103 | You are a helpful agent that can interact with EVM chains using 0xGasless smart accounts. You can perform 104 | gasless transactions using the account abstraction wallet. You can check balances of ETH and any ERC20 token 105 | by providing their contract address. If someone asks you to do something you can't do with your currently 106 | available tools, you must say so. Be concise and helpful with your responses. 107 | `, 108 | }); 109 | 110 | return { agent, config: agentConfig }; 111 | } catch (error) { 112 | console.error("Failed to initialize agent:", error); 113 | throw error; 114 | } 115 | } 116 | 117 | // For runAutonomousMode, runChatMode, chooseMode and main functions, reference: 118 | 119 | /** 120 | * Run the agent autonomously with specified intervals 121 | * 122 | * @param agent - The agent executor 123 | * @param config - Agent configuration 124 | * @param interval - Time interval between actions in seconds 125 | */ 126 | 127 | //biome-ignore lint/suspicious/noExplicitAny: 128 | async function runAutonomousMode(agent: any, config: any, interval = 10) { 129 | console.log("Starting autonomous mode..."); 130 | 131 | // eslint-disable-next-line no-constant-condition 132 | while (true) { 133 | try { 134 | const thought = 135 | "Be creative and do something interesting on the blockchain. " + 136 | "Choose an action or set of actions and execute it that highlights your abilities."; 137 | 138 | const stream = await agent.stream({ messages: [new HumanMessage(thought)] }, config); 139 | 140 | for await (const chunk of stream) { 141 | if ("agent" in chunk) { 142 | console.log(chunk.agent.messages[0].content); 143 | } else if ("tools" in chunk) { 144 | console.log(chunk.tools.messages[0].content); 145 | } 146 | console.log("-------------------"); 147 | } 148 | 149 | await new Promise(resolve => setTimeout(resolve, interval * 1000)); 150 | } catch (error) { 151 | if (error instanceof Error) { 152 | console.error("Error:", error.message); 153 | } 154 | process.exit(1); 155 | } 156 | } 157 | } 158 | 159 | /** 160 | * Run the agent interactively based on user input 161 | * 162 | * @param agent - The agent executor 163 | * @param config - Agent configuration 164 | */ 165 | //biome-ignore lint/suspicious/noExplicitAny: 166 | async function runChatMode(agent: any, config: any) { 167 | console.log("Starting chat mode... Type 'exit' to end."); 168 | 169 | const rl = readline.createInterface({ 170 | input: process.stdin, 171 | output: process.stdout, 172 | }); 173 | 174 | const question = (prompt: string): Promise => 175 | new Promise(resolve => rl.question(prompt, resolve)); 176 | 177 | try { 178 | while (true) { 179 | const userInput = await question("\nPrompt: "); 180 | 181 | if (userInput.toLowerCase() === "exit") { 182 | break; 183 | } 184 | 185 | const stream = await agent.stream({ messages: [new HumanMessage(userInput)] }, config); 186 | 187 | for await (const chunk of stream) { 188 | if ("agent" in chunk) { 189 | console.log(chunk.agent.messages[0].content); 190 | } else if ("tools" in chunk) { 191 | console.log(chunk.tools.messages[0].content); 192 | } 193 | console.log("-------------------"); 194 | } 195 | } 196 | } catch (error) { 197 | if (error instanceof Error) { 198 | console.error("Error:", error.message); 199 | } 200 | process.exit(1); 201 | } finally { 202 | rl.close(); 203 | } 204 | } 205 | 206 | /** 207 | * Choose whether to run in autonomous or chat mode based on user input 208 | * 209 | * @returns Selected mode 210 | */ 211 | async function chooseMode(): Promise<"chat" | "auto"> { 212 | const rl = readline.createInterface({ 213 | input: process.stdin, 214 | output: process.stdout, 215 | }); 216 | 217 | const question = (prompt: string): Promise => 218 | new Promise(resolve => rl.question(prompt, resolve)); 219 | 220 | // eslint-disable-next-line no-constant-condition 221 | while (true) { 222 | console.log("\nAvailable modes:"); 223 | console.log("1. chat - Interactive chat mode"); 224 | console.log("2. auto - Autonomous action mode"); 225 | 226 | const choice = (await question("\nChoose a mode (enter number or name): ")) 227 | .toLowerCase() 228 | .trim(); 229 | 230 | if (choice === "1" || choice === "chat") { 231 | rl.close(); 232 | return "chat"; 233 | } else if (choice === "2" || choice === "auto") { 234 | rl.close(); 235 | return "auto"; 236 | } 237 | console.log("Invalid choice. Please try again."); 238 | } 239 | } 240 | 241 | /** 242 | * Start the chatbot agent 243 | */ 244 | async function main() { 245 | try { 246 | const { agent, config } = await initializeAgent(); 247 | const mode = await chooseMode(); 248 | 249 | if (mode === "chat") { 250 | await runChatMode(agent, config); 251 | } else { 252 | await runAutonomousMode(agent, config); 253 | } 254 | } catch (error) { 255 | if (error instanceof Error) { 256 | console.error("Error:", error.message); 257 | } 258 | process.exit(1); 259 | } 260 | } 261 | 262 | if (require.main === module) { 263 | console.log("Starting Agent..."); 264 | main().catch(error => { 265 | console.error("Fatal error:", error); 266 | process.exit(1); 267 | }); 268 | } 269 | 270 | 271 | ``` 272 | 273 | ## Available Actions 274 | 275 | - `GetBalanceAction`: Check ETH and token balances 276 | - `SmartTransferAction`: Transfer tokens gaslessly 277 | - `SwapAction`: Perform token swaps without gas 278 | - `DeploySmartTokenAction`: Deploy new ERC20 tokens 279 | 280 | ## Documentation 281 | 282 | - [AgentKit Documentation](https://docs.0xgasless.com/docs) 283 | 284 | ## License 285 | 286 | Apache-2.0 287 | -------------------------------------------------------------------------------- /agentkit-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@0xgasless/agentkit", 3 | "description": "0xGasless Agentkit - Gasless transactions and account abstraction toolkit", 4 | "repository": "https://github.com/0xgasless/agentkit", 5 | "version": "0.0.9", 6 | "author": { 7 | "name": "Permissionless Puter", 8 | "email": "puter@prmsnls.xyz", 9 | "url": "https://bento.me/puter" 10 | }, 11 | "license": "Apache-2.0", 12 | "main": "dist/index.js", 13 | "types": "dist/types/index.d.ts", 14 | "files": [ 15 | "dist" 16 | ], 17 | "scripts": { 18 | "lint": "biome lint --write src", 19 | "format": "biome format --write src", 20 | "check": "tsc --noEmit", 21 | "test": "bunx jest --no-cache --testMatch='**/*_test.ts'", 22 | "test:dry-run": "bun install && bun ci && bun publish --dry-run", 23 | "test:e2e": "bunx jest --no-cache --testMatch=**/e2e.ts --coverageThreshold '{}'", 24 | "test:types": "tsd --files src/tests/types.test-d.ts", 25 | "clean": "rm -rf dist && rm -rf docs", 26 | "docs": "bunx --yes typedoc --entryPoints ./src --entryPointStrategy expand --exclude ./src/tests/**/*.ts", 27 | "docs:serve": "bunx serve ./docs", 28 | "dev": "bun link && concurrently \"tsc --watch\" \"tsc-alias -w\"", 29 | "build": "tsc --project ./tsconfig.json && tsc-alias -p ./tsconfig.json", 30 | "prepare": "bun run format && bun run lint && bun run clean && bun run build && bun run docs" 31 | }, 32 | "dependencies": { 33 | "@0xgasless/smart-account": "latest", 34 | "@langchain/core": "^0.3.40", 35 | "axios": "^1.7.9", 36 | "merkletreejs": "^0.4.1", 37 | "viem": "2", 38 | "zod": "^3.23.8" 39 | }, 40 | "devDependencies": { 41 | "@biomejs/biome": "1.9.4", 42 | "@types/jest": "^29.5.14", 43 | "@types/secp256k1": "^4.0.6", 44 | "concurrently": "^8.2.2", 45 | "jest": "^29.7.0", 46 | "mock-fs": "^5.2.0", 47 | "ts-jest": "^29.2.5", 48 | "tsc-alias": "^1.8.10", 49 | "tsd": "^0.31.2", 50 | "typescript": "^5.7.2" 51 | }, 52 | "exports": { 53 | ".": { 54 | "types": "./dist/types/index.d.ts", 55 | "default": "./dist/index.js" 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /agentkit-core/src/BaseActions/EncodeFunctionData.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { encodeFunctionData, Abi, AbiFunctionNotFoundError } from "viem"; 3 | import { AgentkitAction } from "../agentkit"; // Adjust path if necessary 4 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 5 | 6 | // Helper to validate JSON strings 7 | const jsonString = z.string().refine( 8 | data => { 9 | try { 10 | JSON.parse(data); 11 | return true; 12 | } catch { 13 | return false; 14 | } 15 | }, 16 | { message: "Must be a valid JSON string" }, 17 | ); 18 | 19 | // 1. Define the schema for the input parameters 20 | export const EncodeFunctionDataSchema = z.object({ 21 | abiString: jsonString.describe("The contract ABI as a JSON string array."), 22 | functionName: z.string().describe("The name of the function to encode."), 23 | argsString: jsonString 24 | .describe("The arguments for the function as a JSON string array.") 25 | .optional(), 26 | }); 27 | 28 | // Infer the type from the schema 29 | type EncodeFunctionDataInput = z.infer; 30 | 31 | // 2. Define the prompt detailing the tool's use 32 | export const EncodeFunctionDataPrompt = ` 33 | Name: encode_function_data 34 | Description: Encodes a contract function call into hexadecimal data (calldata) using the provided ABI, function name, and arguments. This is necessary for sending transactions that interact with smart contracts. 35 | Usage: Use this tool to prepare the 'data' field for the 'send_transaction' action when you need to call a specific function on a smart contract. 36 | Input Parameters: 37 | - abiString (string, required): The contract Application Binary Interface (ABI) as a JSON formatted string. Example: '[{"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]' 38 | - functionName (string, required): The exact name of the function you want to call, as defined in the ABI. Example: "transfer" 39 | - argsString (string, optional): The arguments required by the function, provided as a JSON formatted string array. The order and types must match the function signature in the ABI. Example: '["0xRecipientAddress", "1000000000000000000"]' (for address and uint256). If the function takes no arguments, omit this parameter or provide an empty array '[]'. 40 | Output: 41 | - On success: Returns the encoded calldata as a hexadecimal string (0x...). Example: "Encoded Data: 0xa9059cbb000000000000000000000000recipientaddress000000000000000000000000000000000000000000000de0b6b3a7640000" 42 | - On failure: Returns an error message detailing the issue. Example: "Error: Failed to encode function data: Function 'transferr' not found on ABI." 43 | `; 44 | 45 | // 3. Define the core function logic - NO wallet needed 46 | export async function encodeFunctionDataFunc( 47 | _wallet: ZeroXgaslessSmartAccount, 48 | { abiString, functionName, argsString }: EncodeFunctionDataInput, 49 | ): Promise { 50 | try { 51 | // Parse the ABI string into an Abi object 52 | const abi = JSON.parse(abiString) as Abi; 53 | 54 | // Parse the args string into an array, or use an empty array if not provided 55 | // biome-ignore lint/suspicious/noExplicitAny: 56 | const args = argsString ? (JSON.parse(argsString) as any[]) : []; 57 | 58 | // Encode the function data using viem 59 | const data = encodeFunctionData({ 60 | abi: abi, 61 | functionName: functionName, 62 | args: args, 63 | }); 64 | 65 | // Return the encoded data 66 | return `Encoded Data: ${data}`; 67 | } catch (error: unknown) { 68 | console.error("Error in encodeFunctionDataFunc:", error); 69 | let errorMessage = "Failed to encode function data"; 70 | if (error instanceof AbiFunctionNotFoundError) { 71 | errorMessage = `Error: ${errorMessage}: Function '${error.message}' not found on ABI. Check spelling and ABI correctness.`; 72 | } else if (error instanceof Error) { 73 | errorMessage = `Error: ${errorMessage}: ${error.message}`; 74 | } else { 75 | errorMessage = `Error: ${errorMessage}: ${String(error)}`; 76 | } 77 | // Handle potential JSON parsing errors or encoding errors from viem 78 | return errorMessage; 79 | } 80 | } 81 | 82 | // 4. Define the AgentkitAction class 83 | export class EncodeFunctionDataAction implements AgentkitAction { 84 | public name = "encode_function_data"; 85 | public description = EncodeFunctionDataPrompt; 86 | public argsSchema = EncodeFunctionDataSchema; 87 | public func = encodeFunctionDataFunc; 88 | public smartAccountRequired = false; // This action does not require a wallet 89 | } 90 | 91 | // Optionally export the individual components if needed elsewhere 92 | // export { EncodeFunctionDataSchema, EncodeFunctionDataPrompt, encodeFunctionDataFunc }; 93 | -------------------------------------------------------------------------------- /agentkit-core/src/BaseActions/FomatHelpers.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { 3 | formatUnits, 4 | parseUnits, 5 | formatEther, 6 | parseEther, 7 | toHex, 8 | fromHex, 9 | toBytes, 10 | fromBytes, 11 | bytesToString, 12 | stringToBytes, 13 | bytesToHex, 14 | hexToBytes, 15 | Hex, 16 | isHex, 17 | } from "viem"; 18 | import { AgentkitAction } from "../agentkit"; 19 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 20 | 21 | // Common error handling function 22 | function formatError(toolName: string, error: unknown): string { 23 | const message = error instanceof Error ? error.message : String(error); 24 | console.error(`Error in ${toolName}:`, error); 25 | return `Error in ${toolName}: ${message}`; 26 | } 27 | 28 | // --- 1. Format Units --- 29 | 30 | export const FormatUnitsSchema = z.object({ 31 | value: z.string().regex(/^-?\d+$/, "Value must be a string representing an integer (wei)."), 32 | decimals: z.number().int().positive("Decimals must be a positive integer."), 33 | }); 34 | type FormatUnitsInput = z.infer; 35 | export const FormatUnitsPrompt = ` 36 | Name: format_units 37 | Description: Converts a numerical value (as a string in wei or smallest unit) into a human-readable string representation based on the specified number of decimals. 38 | Usage: Useful for displaying token balances or amounts stored as large integers in a user-friendly format (e.g., converting wei to Ether, or smallest token unit to standard unit). 39 | Input Parameters: 40 | - value (string, required): The numerical value in its smallest unit (e.g., wei) as a string. 41 | - decimals (number, required): The number of decimal places the unit uses (e.g., 18 for Ether, 6 for USDC). 42 | Output: 43 | - On success: Returns the formatted string. Example: "1.2345" 44 | - On failure: Returns an error message. 45 | `; 46 | export async function formatUnitsFunc( 47 | _wallet: ZeroXgaslessSmartAccount, 48 | { value, decimals }: FormatUnitsInput, 49 | ): Promise { 50 | try { 51 | const formatted = formatUnits(BigInt(value), decimals); 52 | return `Formatted Value: ${formatted}`; 53 | } catch (error: unknown) { 54 | return formatError("format_units", error); 55 | } 56 | } 57 | export class FormatUnitsAction implements AgentkitAction { 58 | public name = "format_units"; 59 | public description = FormatUnitsPrompt; 60 | public argsSchema = FormatUnitsSchema; 61 | public func = formatUnitsFunc; 62 | public smartAccountRequired = false; 63 | } 64 | 65 | // --- 2. Parse Units --- 66 | 67 | export const ParseUnitsSchema = z.object({ 68 | value: z 69 | .string() 70 | .regex(/^-?(\d+(\.\d+)?|\.\d+)$/, "Value must be a string representing a decimal number."), 71 | decimals: z.number().int().positive("Decimals must be a positive integer."), 72 | }); 73 | type ParseUnitsInput = z.infer; 74 | export const ParseUnitsPrompt = ` 75 | Name: parse_units 76 | Description: Converts a human-readable string representation of a value (e.g., "1.23") into its integer representation (e.g., wei) based on the specified number of decimals. 77 | Usage: Useful for converting user input (like "0.5" ETH) into the format required for blockchain transactions (wei). 78 | Input Parameters: 79 | - value (string, required): The human-readable decimal value as a string. 80 | - decimals (number, required): The number of decimal places the unit uses (e.g., 18 for Ether, 6 for USDC). 81 | Output: 82 | - On success: Returns the parsed value as a string integer. Example: "1230000000000000000" 83 | - On failure: Returns an error message. 84 | `; 85 | export async function parseUnitsFunc( 86 | _wallet: ZeroXgaslessSmartAccount, 87 | { value, decimals }: ParseUnitsInput, 88 | ): Promise { 89 | try { 90 | const parsed = parseUnits(value, decimals); 91 | return `Parsed Value (Wei): ${parsed.toString()}`; 92 | } catch (error: unknown) { 93 | return formatError("parse_units", error); 94 | } 95 | } 96 | export class ParseUnitsAction implements AgentkitAction { 97 | public name = "parse_units"; 98 | public description = ParseUnitsPrompt; 99 | public argsSchema = ParseUnitsSchema; 100 | public func = parseUnitsFunc; 101 | public smartAccountRequired = false; 102 | } 103 | 104 | // --- 3. Format Ether --- 105 | 106 | export const FormatEtherSchema = z.object({ 107 | value: z.string().regex(/^-?\d+$/, "Value must be a string representing an integer (wei)."), 108 | }); 109 | type FormatEtherInput = z.infer; 110 | export const FormatEtherPrompt = ` 111 | Name: format_ether 112 | Description: Converts a value in wei (as a string) into its Ether representation (string). Shortcut for format_units with decimals=18. 113 | Usage: Specifically for converting native currency wei values to Ether. 114 | Input Parameters: 115 | - value (string, required): The value in wei as a string. 116 | Output: 117 | - On success: Returns the formatted Ether string. Example: "1.2345" 118 | - On failure: Returns an error message. 119 | `; 120 | export async function formatEtherFunc( 121 | _wallet: ZeroXgaslessSmartAccount, 122 | { value }: FormatEtherInput, 123 | ): Promise { 124 | try { 125 | const formatted = formatEther(BigInt(value)); 126 | return `Formatted Ether: ${formatted}`; 127 | } catch (error: unknown) { 128 | return formatError("format_ether", error); 129 | } 130 | } 131 | export class FormatEtherAction implements AgentkitAction { 132 | public name = "format_ether"; 133 | public description = FormatEtherPrompt; 134 | public argsSchema = FormatEtherSchema; 135 | public func = formatEtherFunc; 136 | public smartAccountRequired = false; 137 | } 138 | 139 | // --- 4. Parse Ether --- 140 | 141 | export const ParseEtherSchema = z.object({ 142 | value: z 143 | .string() 144 | .regex(/^-?(\d+(\.\d+)?|\.\d+)$/, "Value must be a string representing a decimal number."), 145 | }); 146 | type ParseEtherInput = z.infer; 147 | export const ParseEtherPrompt = ` 148 | Name: parse_ether 149 | Description: Converts a string representation of Ether into its wei representation (as a string). Shortcut for parse_units with decimals=18. 150 | Usage: Specifically for converting Ether values (like user input) to wei for transactions. 151 | Input Parameters: 152 | - value (string, required): The Ether value as a string. 153 | Output: 154 | - On success: Returns the parsed value in wei as a string. Example: "1234500000000000000" 155 | - On failure: Returns an error message. 156 | `; 157 | export async function parseEtherFunc( 158 | _wallet: ZeroXgaslessSmartAccount, 159 | { value }: ParseEtherInput, 160 | ): Promise { 161 | try { 162 | const parsed = parseEther(value); 163 | return `Parsed Wei: ${parsed.toString()}`; 164 | } catch (error: unknown) { 165 | return formatError("parse_ether", error); 166 | } 167 | } 168 | export class ParseEtherAction implements AgentkitAction { 169 | public name = "parse_ether"; 170 | public description = ParseEtherPrompt; 171 | public argsSchema = ParseEtherSchema; 172 | public func = parseEtherFunc; 173 | public smartAccountRequired = false; 174 | } 175 | 176 | // --- 5. To Hex --- 177 | 178 | export const ToHexSchema = z.object({ 179 | value: z 180 | .union([ 181 | z.string(), 182 | z.number(), 183 | z.bigint(), 184 | z.boolean(), 185 | // z.instanceof(Uint8Array) // Difficult to represent Uint8Array as string input reliably 186 | ]) 187 | .describe("The value to convert to Hex. Can be string, number, bigint, or boolean."), 188 | // size: z.number().int().positive().optional().describe("The desired byte size of the hex value.") 189 | }); 190 | type ToHexInput = z.infer; 191 | export const ToHexPrompt = ` 192 | Name: to_hex 193 | Description: Converts a string, number, bigint, or boolean value into a hexadecimal string (0x...). 194 | Usage: Useful for converting various data types into the hex format commonly used in Ethereum. Note: Cannot directly convert byte arrays via agent input, use other tools first if needed. 195 | Input Parameters: 196 | - value (string | number | bigint | boolean, required): The value to encode. 197 | Output: 198 | - On success: Returns the hex string. Example: "Hex: 0x60" (for number 96) 199 | - On failure: Returns an error message. 200 | `; 201 | export async function toHexFunc( 202 | _wallet: ZeroXgaslessSmartAccount, 203 | { value }: ToHexInput, 204 | ): Promise { 205 | try { 206 | // Explicitly handle boolean as viem might require number/bigint/string/bytes 207 | const valueToConvert = typeof value === "boolean" ? (value ? 1 : 0) : value; 208 | const hexValue = toHex(valueToConvert as string | number | bigint | Hex); // Cast needed after boolean check 209 | return `Hex: ${hexValue}`; 210 | } catch (error: unknown) { 211 | return formatError("to_hex", error); 212 | } 213 | } 214 | export class ToHexAction implements AgentkitAction { 215 | public name = "to_hex"; 216 | public description = ToHexPrompt; 217 | public argsSchema = ToHexSchema; 218 | public func = toHexFunc; 219 | public smartAccountRequired = false; 220 | } 221 | 222 | // --- 6. From Hex --- 223 | 224 | export const FromHexSchema = z.object({ 225 | hex: z.string().refine(isHex, "Input must be a valid hex string (0x...)."), 226 | to: z 227 | .enum(["string", "number", "bigint", "boolean"]) 228 | .describe("The target type to convert the hex value to."), 229 | }); 230 | type FromHexInput = z.infer; 231 | export const FromHexPrompt = ` 232 | Name: from_hex 233 | Description: Converts a hexadecimal string (0x...) into a specified type (string, number, bigint, or boolean). 234 | Usage: Useful for decoding hex values received from the blockchain or other sources. 235 | Input Parameters: 236 | - hex (string, required): The hexadecimal string (must start with 0x). 237 | - to (string, required): The target type. Must be one of: "string", "number", "bigint", "boolean". 238 | Output: 239 | - On success: Returns the decoded value. Example: "Decoded Value: 96" (for "0x60", "number") 240 | - On failure: Returns an error message. 241 | `; 242 | export async function fromHexFunc( 243 | _wallet: ZeroXgaslessSmartAccount, 244 | { hex, to }: FromHexInput, 245 | ): Promise { 246 | try { 247 | const value = fromHex(hex as Hex, to); 248 | return `Decoded Value: ${value.toString()}`; // Ensure output is stringifiable 249 | } catch (error: unknown) { 250 | return formatError("from_hex", error); 251 | } 252 | } 253 | export class FromHexAction implements AgentkitAction { 254 | public name = "from_hex"; 255 | public description = FromHexPrompt; 256 | public argsSchema = FromHexSchema; 257 | public func = fromHexFunc; 258 | public smartAccountRequired = false; 259 | } 260 | 261 | // --- 7. To Bytes --- 262 | 263 | export const ToBytesSchema = z.object({ 264 | value: z 265 | .union([z.string(), z.number(), z.bigint()]) 266 | .describe("The value (string, number, bigint) to convert to a byte array."), 267 | // opts: z.object({ size: z.number().int().positive().optional(), signed: z.boolean().optional() }).optional().describe("Optional settings for size and signedness.") 268 | }); 269 | type ToBytesInput = z.infer; 270 | export const ToBytesPrompt = ` 271 | Name: to_bytes 272 | Description: Converts a string (hex or UTF-8), number, or bigint into a Uint8Array byte representation. The output format is not directly usable by the agent but this tool confirms the conversion process. Primarily useful for internal checks or confirming data before encoding. 273 | Usage: Use when needing to represent data as raw bytes, often as an intermediate step. Output is a description, not the raw bytes. 274 | Input Parameters: 275 | - value (string | number | bigint, required): The value to convert. Strings are interpreted as hex if they start with 0x, otherwise UTF-8. 276 | Output: 277 | - On success: Returns a confirmation message indicating the byte array was generated and its length. Example: "Value converted to byte array (Uint8Array) with length: 5" 278 | - On failure: Returns an error message. 279 | `; 280 | // This function won't return the actual Uint8Array to the agent, as it's not easily serializable/usable. 281 | // It will return a confirmation string instead. 282 | export async function toBytesFunc( 283 | _wallet: ZeroXgaslessSmartAccount, 284 | { value }: ToBytesInput, 285 | ): Promise { 286 | try { 287 | let bytes: Uint8Array; 288 | if (typeof value === "string") { 289 | bytes = stringToBytes(value); // handles hex and utf8 automatically via viem v2+ 290 | } else { 291 | // For number/bigint, need more context (size, signed). For simplicity, using basic toHex -> hexToBytes 292 | const hexVal = toHex(value as number | bigint); 293 | bytes = hexToBytes(hexVal); 294 | } 295 | // const bytes = toBytes(value as string | number | bigint /*, opts */); 296 | return `Value converted to byte array (Uint8Array) with length: ${bytes.length}`; 297 | } catch (error: unknown) { 298 | return formatError("to_bytes", error); 299 | } 300 | } 301 | 302 | export class ToBytesAction implements AgentkitAction { 303 | public name = "to_bytes"; 304 | public description = ToBytesPrompt; 305 | public argsSchema = ToBytesSchema; 306 | public func = toBytesFunc; 307 | public smartAccountRequired = false; 308 | } 309 | 310 | // --- 8. From Bytes --- 311 | 312 | export const FromBytesSchema = z.object({ 313 | // Represent bytes as hex for agent input 314 | hexBytes: z 315 | .string() 316 | .refine(isHex, "Input must be a valid hex string representing bytes (0x...)."), 317 | to: z 318 | .enum(["string", "number", "bigint", "boolean"]) 319 | .describe("The target type to convert the bytes to."), 320 | // opts: z.object({ size: z.number().int().positive().optional(), signed: z.boolean().optional() }).optional().describe("Optional settings for size and signedness for number/bigint conversion.") 321 | }); 322 | type FromBytesInput = z.infer; 323 | export const FromBytesPrompt = ` 324 | Name: from_bytes 325 | Description: Converts a byte array (represented as a hex string) into a specified type (string, number, bigint, or boolean). 326 | Usage: Use when you have raw byte data (as hex) and need to interpret it as a specific data type. 327 | Input Parameters: 328 | - hexBytes (string, required): The byte array represented as a hexadecimal string (0x...). 329 | - to (string, required): The target type. Must be one of: "string", "number", "bigint", "boolean". 330 | Output: 331 | - On success: Returns the decoded value as a string. Example: "Decoded Value: HelloWorld" (for 'string') or "Decoded Value: 96" (for 'number') 332 | - On failure: Returns an error message. 333 | `; 334 | export async function fromBytesFunc( 335 | _wallet: ZeroXgaslessSmartAccount, 336 | { hexBytes, to /*, opts */ }: FromBytesInput, 337 | ): Promise { 338 | try { 339 | const bytes = hexToBytes(hexBytes as Hex); 340 | let value: string | number | bigint | boolean; 341 | // viem's fromBytes is mainly for number/bigint with options. 342 | // For string, use bytesToString. For boolean, check if non-zero. 343 | switch (to) { 344 | case "string": 345 | value = bytesToString(bytes); 346 | break; 347 | case "number": 348 | case "bigint": 349 | case "boolean": { 350 | // Use fromHex for simplicity as it handles these conversions 351 | // Direct fromBytes requires size/signed options not easily managed here. 352 | const intermediateHex = bytesToHex(bytes); 353 | value = fromHex(intermediateHex, to); // Reuse fromHex logic 354 | break; 355 | } 356 | default: { 357 | return formatError("from_bytes", `Unsupported target type: ${to}`); 358 | } 359 | } 360 | 361 | //const value = fromBytes(bytes, { to: to as 'string' | 'number' | 'bigint' | 'boolean' /* , ...opts */ }); 362 | return `Decoded Value: ${value.toString()}`; 363 | } catch (error: unknown) { 364 | return formatError("from_bytes", error); 365 | } 366 | } 367 | export class FromBytesAction implements AgentkitAction { 368 | public name = "from_bytes"; 369 | public description = FromBytesPrompt; 370 | public argsSchema = FromBytesSchema; 371 | public func = fromBytesFunc; 372 | public smartAccountRequired = false; 373 | } 374 | 375 | // --- Exports --- 376 | // You might want to export these actions individually or as an array/object 377 | export const formatHelperActions = [ 378 | new FormatUnitsAction(), 379 | new ParseUnitsAction(), 380 | new FormatEtherAction(), 381 | new ParseEtherAction(), 382 | new ToHexAction(), 383 | new FromHexAction(), 384 | new ToBytesAction(), 385 | new FromBytesAction(), 386 | ]; 387 | -------------------------------------------------------------------------------- /agentkit-core/src/BaseActions/GetBalance.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 3 | import { getWalletBalance } from "../services"; 4 | import { AgentkitAction } from "../agentkit"; 5 | import { tokenMappings, commonTokens } from "../constants"; 6 | 7 | const GET_BALANCE_PROMPT = ` 8 | This tool gets the balance of the smart account that is already configured with the SDK. 9 | No additional wallet setup or private key generation is needed. 10 | 11 | You can check balances in three ways: 12 | 1. By default, it returns balances for all supported tokens on the current chain 13 | 2. By token ticker symbols (e.g., "ETH", "USDC", "USDT", "WETH", etc.) 14 | 3. By token contract addresses (e.g., "0x...") 15 | 16 | USAGE GUIDANCE: 17 | - When a user asks to check or get balances, use this tool immediately without asking for confirmation 18 | - If the user doesn't specify tokens, call the tool with no parameters to get ALL token balances 19 | - If the user mentions specific tokens by name (like "USDC" or "USDT"), use the tokenSymbols parameter 20 | - Only use tokenAddresses parameter if the user specifically provides contract addresses 21 | 22 | Note: This action works on supported networks only (Base, Fantom, Moonbeam, Metis, Avalanche, BSC). 23 | `; 24 | 25 | export const GetBalanceInput = z 26 | .object({ 27 | tokenAddresses: z 28 | .array(z.string()) 29 | .optional() 30 | .describe("Optional list of token contract addresses to get balances for"), 31 | tokenSymbols: z 32 | .array(z.string()) 33 | .optional() 34 | .describe( 35 | "Optional list of token symbols (e.g., 'USDC', 'USDT', 'WETH') to get balances for", 36 | ), 37 | }) 38 | .strip() 39 | .describe("Instructions for getting smart account balance"); 40 | 41 | /** 42 | * Resolves token symbols to their contract addresses based on the current chain 43 | * 44 | * @param wallet - The smart account to get chain information from 45 | * @param symbols - Array of token symbols to resolve 46 | * @returns Array of token addresses 47 | */ 48 | async function resolveTokenSymbols( 49 | wallet: ZeroXgaslessSmartAccount, 50 | symbols: string[], 51 | ): Promise<`0x${string}`[]> { 52 | const chainId = wallet.rpcProvider.chain?.id; 53 | if (!chainId || !tokenMappings[chainId]) { 54 | console.warn(`Chain ID ${chainId} not found in token mappings`); 55 | return []; 56 | } 57 | 58 | const chainTokens = tokenMappings[chainId]; 59 | const resolvedAddresses: `0x${string}`[] = []; 60 | 61 | for (const symbol of symbols) { 62 | const normalizedSymbol = symbol.toUpperCase(); 63 | if (chainTokens[normalizedSymbol]) { 64 | resolvedAddresses.push(chainTokens[normalizedSymbol]); 65 | } else { 66 | console.warn(`Token symbol ${normalizedSymbol} not found for chain ID ${chainId}`); 67 | } 68 | } 69 | 70 | return resolvedAddresses; 71 | } 72 | 73 | /** 74 | * Gets balance for the smart account. 75 | * 76 | * @param wallet - The smart account to get the balance for. 77 | * @param args - The input arguments for the action. 78 | * @returns A message containing the balance information. 79 | */ 80 | export async function getBalance( 81 | wallet: ZeroXgaslessSmartAccount, 82 | args: z.infer, 83 | ): Promise { 84 | try { 85 | let tokenAddresses: `0x${string}`[] = []; 86 | const smartAccount = await wallet.getAddress(); 87 | const chainId = wallet.rpcProvider.chain?.id; 88 | 89 | // If no specific tokens requested, get all tokens from tokenMappings for the current chain 90 | if ( 91 | (!args.tokenAddresses || args.tokenAddresses.length === 0) && 92 | (!args.tokenSymbols || args.tokenSymbols.length === 0) 93 | ) { 94 | if (chainId && tokenMappings[chainId]) { 95 | // Get all token addresses for the current chain 96 | tokenAddresses = [...tokenAddresses, ...Object.values(tokenMappings[chainId])]; 97 | } else { 98 | console.warn(`Chain ID ${chainId} not found in token mappings or is empty`); 99 | } 100 | } else { 101 | // Process token addresses if provided 102 | if (args.tokenAddresses && args.tokenAddresses.length > 0) { 103 | tokenAddresses = args.tokenAddresses.map(addr => addr as `0x${string}`); 104 | } 105 | 106 | // Process token symbols if provided 107 | if (args.tokenSymbols && args.tokenSymbols.length > 0) { 108 | const symbolAddresses = await resolveTokenSymbols(wallet, args.tokenSymbols); 109 | tokenAddresses = [...tokenAddresses, ...symbolAddresses]; 110 | } 111 | } 112 | 113 | // Remove duplicates 114 | tokenAddresses = [...new Set(tokenAddresses)]; 115 | 116 | const balances = await getWalletBalance( 117 | wallet, 118 | tokenAddresses.length > 0 ? tokenAddresses : undefined, 119 | ); 120 | if (!balances) { 121 | return "Error getting balance: No balance information returned from the provider"; 122 | } 123 | 124 | if (balances.length === 0) { 125 | return "No balances found for the requested tokens"; 126 | } 127 | 128 | // Format the balance response 129 | const balanceStrings = balances 130 | // Filter out zero balances unless explicitly requested specific tokens 131 | .filter(balance => { 132 | // If user requested specific tokens, show all balances including zeros 133 | if ( 134 | (args.tokenAddresses && args.tokenAddresses.length > 0) || 135 | (args.tokenSymbols && args.tokenSymbols.length > 0) 136 | ) { 137 | return true; 138 | } 139 | // Otherwise, only show non-zero balances 140 | return balance.formattedAmount !== "0" && balance.formattedAmount !== "0.0"; 141 | }) 142 | .map(balance => { 143 | // Try to find a symbol for this address 144 | const chainId = wallet.rpcProvider.chain?.id; 145 | let displayName = balance.address; 146 | 147 | // Special case for native token (ETH, BNB, etc.) 148 | if (balance.address.toLowerCase() === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") { 149 | // Use chain-specific native token name if available 150 | if (chainId === 56) { 151 | displayName = "BNB"; 152 | } else if (chainId === 43114) { 153 | displayName = "AVAX"; 154 | } else if (chainId === 250) { 155 | displayName = "FTM"; 156 | } else if (chainId === 1088) { 157 | displayName = "METIS"; 158 | } else if (chainId === 8453) { 159 | displayName = "ETH"; 160 | } else if (chainId === 1284) { 161 | displayName = "GLMR"; 162 | } else { 163 | displayName = "ETH"; 164 | } 165 | } else if (chainId && tokenMappings[chainId]) { 166 | const chainTokens = tokenMappings[chainId]; 167 | // Find token symbol by address 168 | for (const [symbol, address] of Object.entries(chainTokens)) { 169 | if (address.toLowerCase() === balance.address.toLowerCase()) { 170 | displayName = symbol; 171 | break; 172 | } 173 | } 174 | } 175 | 176 | return `${displayName}: ${balance.formattedAmount}`; 177 | }); 178 | 179 | // Sort balances alphabetically by token name for better readability 180 | balanceStrings.sort(); 181 | 182 | const responseTitle = 183 | tokenAddresses.length > 0 && !args.tokenAddresses?.length && !args.tokenSymbols?.length 184 | ? "All Token Balances:" 185 | : "Balances:"; 186 | 187 | if (balanceStrings.length === 0) { 188 | return `Smart Account: ${smartAccount}\n${responseTitle}\nNo non-zero balances found`; 189 | } 190 | 191 | return `Smart Account: ${smartAccount}\n${responseTitle}\n${balanceStrings.join("\n")}`; 192 | } catch (error) { 193 | console.error("Balance fetch error:", error); 194 | return `Error getting balance: ${error instanceof Error ? error.message : String(error)}`; 195 | } 196 | } 197 | 198 | /** 199 | * Get wallet balance action. 200 | */ 201 | export class GetBalanceAction implements AgentkitAction { 202 | public name = "get_balance"; 203 | public description = GET_BALANCE_PROMPT; 204 | public argsSchema = GetBalanceInput; 205 | public func = getBalance; 206 | public smartAccountRequired = true; 207 | } 208 | -------------------------------------------------------------------------------- /agentkit-core/src/BaseActions/GetStatusFromUserop.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount, UserOpReceipt } from "@0xgasless/smart-account"; 3 | import { AgentkitAction } from "../agentkit"; // Adjust path if necessary 4 | 5 | // Constants for polling logic (can be adjusted) 6 | const DEFAULT_WAIT_INTERVAL = 5000; // 5 seconds 7 | const DEFAULT_MAX_DURATION = 60000; // 60 seconds 8 | 9 | // 1. Define the schema for the input parameters 10 | export const GetTransactionStatusSchema = z.object({ 11 | userOpHash: z.string().regex(/^0x[a-fA-F0-9]{64}$/, "Invalid User Operation Hash format."), 12 | }); 13 | 14 | // Infer the type from the schema 15 | type GetTransactionStatusInput = z.infer; 16 | 17 | // 2. Define the prompt detailing the tool's use 18 | export const GetTransactionStatusPrompt = ` 19 | Name: get_transaction_status 20 | Description: Checks the status of a submitted User Operation (transaction) using its hash. It polls the bundler until the transaction is confirmed or a timeout is reached. 21 | Usage: Use this action after 'send_transaction' to determine if the transaction was successfully included in a block. Provide the 'userOpHash' returned by 'send_transaction'. 22 | Input Parameters: 23 | - userOpHash (string, required): The User Operation hash (0x...) obtained after submitting a transaction. 24 | Output: 25 | - If confirmed: Returns a confirmation message including the transaction hash and block number. Example: "Transaction confirmed! TxHash: 0x..., Block: 123456" 26 | - If still pending after timeout: Returns a message indicating it's still pending. Example: "Transaction is still pending after 60 seconds. UserOpHash: 0x..." 27 | - If failed: Returns an error message indicating the failure. Example: "Error: Transaction failed or was not found. UserOpHash: 0x..." 28 | - On error: Returns a detailed error message. Example: "Error: Failed to get transaction status: Bundler URL not configured." 29 | `; 30 | 31 | // 3. Define the core function logic - requires wallet for bundler access 32 | export async function getTransactionStatusFunc( 33 | wallet: ZeroXgaslessSmartAccount, // Wallet instance is passed first 34 | { userOpHash }: GetTransactionStatusInput, 35 | ): Promise { 36 | if (!wallet.bundler) { 37 | return "Error: Failed to get transaction status: Bundler not configured on the smart account."; 38 | } 39 | 40 | let totalDuration = 0; 41 | 42 | // Re-implement polling using the SDK's getUserOperationReceipt 43 | return new Promise(resolve => { 44 | const intervalId = setInterval(async () => { 45 | try { 46 | const receipt: UserOpReceipt | null = await wallet.bundler!.getUserOpReceipt(userOpHash); 47 | 48 | // Check if the receipt exists and indicates success 49 | if (receipt?.success) { 50 | const txHash = receipt.receipt?.transactionHash; 51 | const blockNumber = receipt.receipt?.blockNumber; 52 | if (txHash && blockNumber) { 53 | clearInterval(intervalId); 54 | resolve( 55 | `Transaction confirmed! TxHash: ${txHash}, Block: ${Number(blockNumber)}. UserOpHash: ${userOpHash}`, 56 | ); 57 | return; 58 | } else { 59 | // Still technically success, but maybe log a warning if details missing? 60 | clearInterval(intervalId); 61 | resolve( 62 | `Transaction succeeded but receipt details (TxHash, Block) missing. UserOpHash: ${userOpHash}`, 63 | ); 64 | return; 65 | } 66 | } else if (receipt && !receipt.success) { 67 | // Transaction included but failed 68 | clearInterval(intervalId); 69 | resolve( 70 | `Transaction failed. UserOpHash: ${userOpHash}. Reason: ${receipt.reason || "No reason provided"}.`, 71 | ); 72 | return; 73 | } 74 | // If receipt is null, it's still pending 75 | 76 | // Update duration and check timeout 77 | totalDuration += DEFAULT_WAIT_INTERVAL; 78 | if (totalDuration >= DEFAULT_MAX_DURATION) { 79 | clearInterval(intervalId); 80 | resolve( 81 | `Transaction is still pending after ${DEFAULT_MAX_DURATION / 1000} seconds. UserOpHash: ${userOpHash}`, 82 | ); 83 | return; 84 | } 85 | } catch (error: unknown) { 86 | console.error("Error polling transaction status:", error); 87 | clearInterval(intervalId); 88 | resolve( 89 | `Error polling transaction status for ${userOpHash}: ${error instanceof Error ? error.message : String(error)}`, 90 | ); 91 | return; // Exit promise on error 92 | } 93 | }, DEFAULT_WAIT_INTERVAL); 94 | }); 95 | } 96 | 97 | // 4. Define the AgentkitAction class 98 | export class GetTransactionStatusAction 99 | implements AgentkitAction 100 | { 101 | public name = "get_transaction_status"; 102 | public description = GetTransactionStatusPrompt; 103 | public argsSchema = GetTransactionStatusSchema; 104 | public func = getTransactionStatusFunc; 105 | public smartAccountRequired = true; // Requires wallet to get bundler URL 106 | } 107 | -------------------------------------------------------------------------------- /agentkit-core/src/BaseActions/ReadContract.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Abi, Address, isAddress } from "viem"; 3 | import { AgentkitAction } from "../agentkit"; 4 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 5 | 6 | // Helper to validate JSON strings (reuse if defined globally, or define here) 7 | const jsonString = z.string().refine( 8 | data => { 9 | try { 10 | JSON.parse(data); 11 | return true; 12 | } catch { 13 | return false; 14 | } 15 | }, 16 | { message: "Must be a valid JSON string" }, 17 | ); 18 | 19 | // Common error handling function (reuse if defined globally, or define here) 20 | function formatError(toolName: string, error: unknown): string { 21 | const message = error instanceof Error ? error.message : String(error); 22 | console.error(`Error in ${toolName}:`, error); 23 | return `Error in ${toolName}: ${message}`; 24 | } 25 | 26 | // 1. Define the schema for the input parameters 27 | export const ReadContractSchema = z.object({ 28 | contractAddress: z.string().refine(isAddress, "Invalid contract address format."), 29 | abiString: jsonString.describe( 30 | "The contract ABI fragment (or full ABI) as a JSON string array, containing the function to read.", 31 | ), 32 | functionName: z.string().describe("The name of the view or pure function to call."), 33 | argsString: jsonString 34 | .describe( 35 | "The arguments for the function as a JSON string array. Omit or use '[]' for functions with no arguments.", 36 | ) 37 | .optional(), 38 | }); 39 | 40 | // Infer the type from the schema 41 | type ReadContractInput = z.infer; 42 | 43 | // 2. Define the prompt detailing the tool's use 44 | export const ReadContractPrompt = ` 45 | Name: read_contract 46 | Description: Reads data from a specified function on a smart contract without sending a transaction (gasless view/pure call). 47 | Usage: Use this to fetch information stored on a contract, such as balances, owner addresses, configuration settings, token URIs, etc. Only works for functions that do not modify state (marked as 'view' or 'pure' in the ABI). 48 | Input Parameters: 49 | - contractAddress (string, required): The address of the contract to read from. 50 | - abiString (string, required): A JSON string representation of the contract's ABI, or at least the fragment defining the function you want to call. Example: '[{"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"stateMutability":"view","type":"function"}]' 51 | - functionName (string, required): The exact name of the 'view' or 'pure' function to call. Example: "symbol" 52 | - argsString (string, optional): The arguments for the function, provided as a JSON string array. Order and types must match the ABI. Example: '["0xSomeAddress"]' or '[]' if no arguments. 53 | Output: 54 | - On success: Returns the result read from the contract, converted to a string. Complex results (structs, arrays) will be JSON stringified. Example: "Result: USDC" or "Result: 1000000000000000000" or "Result: [\\"value1\\", \\"value2\\"]" 55 | - On failure: Returns an error message detailing the issue. Example: "Error: read_contract: Function 'getOwner' not found on ABI." or "Error: read_contract: Invalid contract address." 56 | `; 57 | 58 | // 3. Define the core function logic - requires wallet for RPC provider 59 | export async function readContractFunc( 60 | wallet: ZeroXgaslessSmartAccount, // Wallet instance is passed first 61 | { contractAddress, abiString, functionName, argsString }: ReadContractInput, 62 | ): Promise { 63 | try { 64 | // Ensure the RPC provider is available 65 | const rpcProvider = wallet.rpcProvider; 66 | if (!rpcProvider) { 67 | return formatError("read_contract", "RPC Provider not found on the wallet instance."); 68 | } 69 | 70 | // Parse ABI and arguments 71 | const abi = JSON.parse(abiString) as Abi; 72 | // biome-ignore lint/suspicious/noExplicitAny: 73 | const args = argsString ? (JSON.parse(argsString) as any[]) : []; 74 | 75 | // Perform the read operation 76 | const result = await rpcProvider.readContract({ 77 | address: contractAddress as Address, // Cast after validation 78 | abi: abi, 79 | functionName: functionName, 80 | args: args, 81 | }); 82 | 83 | // Format the result as a string for the agent 84 | let resultString: string; 85 | if (typeof result === "bigint") { 86 | resultString = result.toString(); 87 | } else if (typeof result === "object" && result !== null) { 88 | // Attempt to stringify complex objects/arrays, handle BigInts within 89 | resultString = JSON.stringify(result, (_, value) => 90 | typeof value === "bigint" ? value.toString() : value, 91 | ); 92 | } else { 93 | // Handle primitives (string, number, boolean, null, undefined) 94 | resultString = String(result); 95 | } 96 | 97 | return `Result: ${resultString}`; 98 | } catch (error: unknown) { 99 | return formatError("read_contract", error); 100 | } 101 | } 102 | 103 | // 4. Define the AgentkitAction class 104 | export class ReadContractAction implements AgentkitAction { 105 | public name = "read_contract"; 106 | public description = ReadContractPrompt; 107 | public argsSchema = ReadContractSchema; 108 | public func = readContractFunc; 109 | public smartAccountRequired = true; // Requires wallet for the RPC Provider 110 | } 111 | -------------------------------------------------------------------------------- /agentkit-core/src/BaseActions/SendTransaction.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount, Transaction, PaymasterMode } from "@0xgasless/smart-account"; 3 | import { AgentkitAction } from "../agentkit"; 4 | 5 | export const SendTransactionSchema = z.object({ 6 | to: z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid 'to' address format."), 7 | data: z 8 | .string() 9 | .regex(/^0x[a-fA-F0-9]*$/, "Invalid 'data' hex format.") 10 | .optional(), 11 | value: z.string().regex(/^\d+$/, "Invalid 'value' format, should be a string of wei.").optional(), 12 | }); 13 | 14 | // Infer the type from the schema 15 | export type SendTransactionInput = z.infer; 16 | 17 | // 2. Define the prompt detailing the tool's use 18 | export const SendTransactionPrompt = ` 19 | Name: send_transaction 20 | Description: Sends a transaction using the 0xgasless Smart Account. This action submits a transaction to the blockchain to interact with contracts or transfer native tokens. 21 | Usage: Use this action when you need to execute a blockchain transaction, such as calling a contract function or sending the native currency (e.g., ETH, MATIC). 22 | Input Parameters: 23 | - to (string, required): The destination address for the transaction (e.g., contract address or recipient EOA). Must be a valid Ethereum address (0x...). 24 | - data (string, optional): The encoded function data for contract interactions. Required when calling a contract function. Must be a hex string (0x...). Use 'encode_function_data' tool to generate this if needed. 25 | - value (string, optional): The amount of native currency (in wei) to send with the transaction. Defaults to "0" if not provided. Must be a string representing an integer. 26 | Output: 27 | - On success: Returns a confirmation message including the User Operation Hash (userOpHash). Example: "Transaction submitted successfully! User Operation Hash: 0x..." 28 | - On failure: Returns an error message detailing the issue. Example: "Error: Failed to send transaction: Invalid 'to' address." 29 | 30 | Note: This action only *submits* the transaction. Use the 'get_transaction_status' action with the returned userOpHash to check if the transaction has been confirmed on the blockchain. 31 | `; 32 | 33 | // 3. Define the core function logic 34 | export async function sendTransactionFunc( 35 | wallet: ZeroXgaslessSmartAccount, // The wallet instance is passed implicitly by the agent runner 36 | { to, data, value }: SendTransactionInput, 37 | ): Promise { 38 | try { 39 | // Construct the transaction object 40 | const tx: Transaction = { 41 | to: to as `0x${string}`, // Cast to expected type after validation 42 | data: data as `0x${string}` | undefined, // Cast to expected type after validation 43 | value: value ? BigInt(value) : BigInt(0), // Convert validated string to bigint 44 | }; 45 | 46 | // Send the transaction using the 0xgasless SDK 47 | const request = await wallet.sendTransaction(tx, { 48 | paymasterServiceData: { 49 | mode: PaymasterMode.SPONSORED, // Use sponsored mode for gasless 50 | }, 51 | }); 52 | 53 | // Handle potential errors returned by the SDK 54 | if (request.error) { 55 | return `Error: Failed to send transaction: ${request.error.message || request.error}`; 56 | } 57 | 58 | // Return success message with the userOpHash 59 | return `Transaction submitted successfully! User Operation Hash: ${request.userOpHash}\nUse 'get_transaction_status' to check confirmation.`; 60 | } catch (error: unknown) { 61 | // Catch any other exceptions during the process 62 | console.error("Error in sendTransactionFunc:", error); 63 | return `Error: Failed to send transaction: ${error instanceof Error ? error.message : String(error)}`; 64 | } 65 | } 66 | 67 | export class SendTransactionAction implements AgentkitAction { 68 | public name = "send_transaction"; 69 | public description = SendTransactionPrompt; // Use the prompt string 70 | public argsSchema = SendTransactionSchema; // Use the zod schema 71 | public func = sendTransactionFunc; 72 | public smartAccountRequired = true; // This action needs the smart account wallet 73 | } 74 | -------------------------------------------------------------------------------- /agentkit-core/src/BaseActions/SignMessage.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 3 | import { AgentkitAction } from "../agentkit"; // Adjust path if necessary 4 | 5 | // 1. Define the schema for the input parameters 6 | export const SignMessageSchema = z.object({ 7 | message: z.string().min(1, "Message cannot be empty."), 8 | }); 9 | 10 | // Infer the type from the schema 11 | type SignMessageInput = z.infer; 12 | 13 | // 2. Define the prompt detailing the tool's use 14 | export const SignMessagePrompt = ` 15 | Name: sign_message 16 | Description: Signs an arbitrary message using the 0xgasless Smart Account's private key. 17 | Usage: Use this action when an application or protocol requires you to prove ownership of your address by signing a specific message. 18 | Input Parameters: 19 | - message (string, required): The message string that needs to be signed. 20 | Output: 21 | - On success: Returns the signature as a hexadecimal string (0x...). Example: "Signature: 0x..." 22 | - On failure: Returns an error message detailing the issue. Example: "Error: Failed to sign message: User rejected signing." 23 | `; 24 | 25 | // 3. Define the core function logic 26 | export async function signMessageFunc( 27 | wallet: ZeroXgaslessSmartAccount, // The wallet instance is passed implicitly 28 | { message }: SignMessageInput, 29 | ): Promise { 30 | try { 31 | // Sign the message using the smart account's underlying signer 32 | const signature = await wallet.signMessage(message); 33 | 34 | // Return the signature 35 | return `Signature: ${signature}`; 36 | } catch (error: unknown) { 37 | // Catch any exceptions during the signing process 38 | console.error("Error in signMessageFunc:", error); 39 | return `Error: Failed to sign message: ${error instanceof Error ? error.message : String(error)}`; 40 | } 41 | } 42 | 43 | // 4. Define the AgentkitAction class 44 | export class SignMessageAction implements AgentkitAction { 45 | public name = "sign_message"; 46 | public description = SignMessagePrompt; 47 | public argsSchema = SignMessageSchema; 48 | public func = signMessageFunc; 49 | public smartAccountRequired = true; // Signing requires the smart account 50 | } 51 | -------------------------------------------------------------------------------- /agentkit-core/src/BaseActions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./FomatHelpers"; 2 | export * from "./EncodeFunctionData"; 3 | export * from "./GetStatusFromUserop"; 4 | export * from "./SendTransaction"; 5 | export * from "./SignMessage"; 6 | -------------------------------------------------------------------------------- /agentkit-core/src/actions/checkTransactionAction.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 3 | import { AgentkitAction } from "../agentkit"; 4 | import { waitForTransaction } from "../services"; 5 | 6 | const CHECK_TRANSACTION_PROMPT = ` 7 | This tool checks the status of a previously submitted transaction using its User Operation Hash. 8 | It will attempt to get the transaction receipt and confirmation status. 9 | 10 | Required parameters: 11 | - userOpHash: The User Operation Hash returned when the transaction was submitted 12 | - confirmations: (Optional) Number of block confirmations to wait for (default: 1) 13 | - maxDuration: (Optional) Maximum time to wait in milliseconds (default: 30000) 14 | - interval: (Optional) How often to check status in milliseconds (default: 5000) 15 | `; 16 | 17 | export const CheckTransactionInput = z 18 | .object({ 19 | userOpHash: z.string().describe("The User Operation Hash to check"), 20 | confirmations: z 21 | .number() 22 | .optional() 23 | .describe("Number of block confirmations to wait for (default: 1)"), 24 | maxDuration: z 25 | .number() 26 | .optional() 27 | .describe("Maximum time to wait in milliseconds (default: 30000)"), 28 | interval: z 29 | .number() 30 | .optional() 31 | .describe("How often to check status in milliseconds (default: 5000)"), 32 | }) 33 | .strip() 34 | .describe("Instructions for checking transaction status"); 35 | 36 | /** 37 | * Checks the status of a transaction using its User Operation Hash. 38 | * 39 | * @param wallet - The smart account to use for checking status. 40 | * @param args - The input arguments containing the userOpHash and optional parameters. 41 | * @returns A message containing the transaction status. 42 | */ 43 | export async function checkTransactionStatus( 44 | wallet: ZeroXgaslessSmartAccount, 45 | args: z.infer, 46 | ): Promise { 47 | try { 48 | const status = await waitForTransaction(wallet, args.userOpHash, { 49 | confirmations: args.confirmations, 50 | maxDuration: args.maxDuration, 51 | interval: args.interval, 52 | }); 53 | 54 | switch (status.status) { 55 | case "confirmed": 56 | return ` 57 | Transaction confirmed! 58 | Block Number: ${status.blockNumber} 59 | Block Confirmations: ${status.blockConfirmations} 60 | Receipt: ${JSON.stringify(status.receipt, null, 2)} 61 | `; 62 | 63 | case "pending": 64 | return ` 65 | Transaction is still pending. 66 | ${status.error ? `Note: ${status.error}` : ""} 67 | You can try checking again with a longer maxDuration. 68 | `; 69 | 70 | case "failed": 71 | return ` 72 | Transaction failed! 73 | Error: ${status.error} 74 | `; 75 | 76 | default: 77 | return `Unknown transaction status`; 78 | } 79 | } catch (error) { 80 | return `Error checking transaction status: ${error}`; 81 | } 82 | } 83 | 84 | /** 85 | * Check transaction status action. 86 | */ 87 | export class CheckTransactionAction implements AgentkitAction { 88 | public name = "check_transaction_status"; 89 | public description = CHECK_TRANSACTION_PROMPT; 90 | public argsSchema = CheckTransactionInput; 91 | public func = checkTransactionStatus; 92 | public smartAccountRequired = true; 93 | } 94 | -------------------------------------------------------------------------------- /agentkit-core/src/actions/getAddressAction.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 3 | import { AgentkitAction } from "../agentkit"; 4 | 5 | const GET_ADDRESS_PROMPT = ` 6 | This tool retrieves the smart account address that is already configured with the SDK. 7 | No additional wallet setup or private key generation is needed. 8 | 9 | USAGE GUIDANCE: 10 | - When a user asks for their wallet address, account address, or smart account address, use this tool immediately 11 | - No parameters are needed to retrieve the address 12 | - The address can be used for receiving tokens or for verification purposes 13 | - This is a read-only operation that doesn't modify any blockchain state 14 | 15 | Note: This action works on all supported networks (Base, Fantom, Moonbeam, Metis, Avalanche, BSC). 16 | `; 17 | 18 | export const GetAddressInput = z 19 | .object({}) 20 | .strip() 21 | .describe("No input required to get the smart account address"); 22 | 23 | /** 24 | * Gets the smart account address. 25 | * 26 | * @returns A message containing the smart account address. 27 | */ 28 | export async function getAddress( 29 | wallet: ZeroXgaslessSmartAccount, 30 | args: z.infer, 31 | ): Promise { 32 | try { 33 | const smartAccount = await wallet.getAddress(args); 34 | 35 | return `Smart Account: ${smartAccount}`; 36 | } catch (error) { 37 | console.error("Error getting address:", error); 38 | return `Error getting address: ${error instanceof Error ? error.message : String(error)}`; 39 | } 40 | } 41 | 42 | /** 43 | * Get smart account address action. 44 | */ 45 | export class GetAddressAction implements AgentkitAction { 46 | public name = "get_address"; 47 | public description = GET_ADDRESS_PROMPT; 48 | public argsSchema = GetAddressInput; 49 | public func = getAddress; 50 | public smartAccountRequired = true; 51 | } 52 | -------------------------------------------------------------------------------- /agentkit-core/src/actions/getBalanceAction.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 3 | import { getWalletBalance } from "../services"; 4 | import { AgentkitAction } from "../agentkit"; 5 | import { tokenMappings, commonTokens } from "../constants"; 6 | 7 | const GET_BALANCE_PROMPT = ` 8 | This tool gets the balance of the smart account that is already configured with the SDK. 9 | No additional wallet setup or private key generation is needed. 10 | 11 | You can check balances in three ways: 12 | 1. By default, it returns balances for all supported tokens on the current chain 13 | 2. By token ticker symbols (e.g., "ETH", "USDC", "USDT", "WETH", etc.) 14 | 3. By token contract addresses (e.g., "0x...") 15 | 16 | USAGE GUIDANCE: 17 | - When a user asks to check or get balances, use this tool immediately without asking for confirmation 18 | - If the user doesn't specify tokens, call the tool with no parameters to get ALL token balances 19 | - If the user mentions specific tokens by name (like "USDC" or "USDT"), use the tokenSymbols parameter 20 | - Only use tokenAddresses parameter if the user specifically provides contract addresses 21 | 22 | Note: This action works on supported networks only (Base, Fantom, Moonbeam, Metis, Avalanche, BSC). 23 | `; 24 | 25 | export const GetBalanceInput = z 26 | .object({ 27 | tokenAddresses: z 28 | .array(z.string()) 29 | .optional() 30 | .describe("Optional list of token contract addresses to get balances for"), 31 | tokenSymbols: z 32 | .array(z.string()) 33 | .optional() 34 | .describe( 35 | "Optional list of token symbols (e.g., 'USDC', 'USDT', 'WETH') to get balances for", 36 | ), 37 | }) 38 | .strip() 39 | .describe("Instructions for getting smart account balance"); 40 | 41 | /** 42 | * Resolves token symbols to their contract addresses based on the current chain 43 | * 44 | * @param wallet - The smart account to get chain information from 45 | * @param symbols - Array of token symbols to resolve 46 | * @returns Array of token addresses 47 | */ 48 | async function resolveTokenSymbols( 49 | wallet: ZeroXgaslessSmartAccount, 50 | symbols: string[], 51 | ): Promise<`0x${string}`[]> { 52 | const chainId = wallet.rpcProvider.chain?.id; 53 | if (!chainId || !tokenMappings[chainId]) { 54 | console.warn(`Chain ID ${chainId} not found in token mappings`); 55 | return []; 56 | } 57 | 58 | const chainTokens = tokenMappings[chainId]; 59 | const resolvedAddresses: `0x${string}`[] = []; 60 | 61 | for (const symbol of symbols) { 62 | const normalizedSymbol = symbol.toUpperCase(); 63 | if (chainTokens[normalizedSymbol]) { 64 | resolvedAddresses.push(chainTokens[normalizedSymbol]); 65 | } else { 66 | console.warn(`Token symbol ${normalizedSymbol} not found for chain ID ${chainId}`); 67 | } 68 | } 69 | 70 | return resolvedAddresses; 71 | } 72 | 73 | /** 74 | * Gets balance for the smart account. 75 | * 76 | * @param wallet - The smart account to get the balance for. 77 | * @param args - The input arguments for the action. 78 | * @returns A message containing the balance information. 79 | */ 80 | export async function getBalance( 81 | wallet: ZeroXgaslessSmartAccount, 82 | args: z.infer, 83 | ): Promise { 84 | try { 85 | let tokenAddresses: `0x${string}`[] = []; 86 | const smartAccount = await wallet.getAddress(); 87 | const chainId = wallet.rpcProvider.chain?.id; 88 | 89 | // If no specific tokens requested, get all tokens from tokenMappings for the current chain 90 | if ( 91 | (!args.tokenAddresses || args.tokenAddresses.length === 0) && 92 | (!args.tokenSymbols || args.tokenSymbols.length === 0) 93 | ) { 94 | if (chainId && tokenMappings[chainId]) { 95 | // Get all token addresses for the current chain 96 | tokenAddresses = [...tokenAddresses, ...Object.values(tokenMappings[chainId])]; 97 | } else { 98 | console.warn(`Chain ID ${chainId} not found in token mappings or is empty`); 99 | } 100 | } else { 101 | // Process token addresses if provided 102 | if (args.tokenAddresses && args.tokenAddresses.length > 0) { 103 | tokenAddresses = args.tokenAddresses.map(addr => addr as `0x${string}`); 104 | } 105 | 106 | // Process token symbols if provided 107 | if (args.tokenSymbols && args.tokenSymbols.length > 0) { 108 | const symbolAddresses = await resolveTokenSymbols(wallet, args.tokenSymbols); 109 | tokenAddresses = [...tokenAddresses, ...symbolAddresses]; 110 | } 111 | } 112 | 113 | // Remove duplicates 114 | tokenAddresses = [...new Set(tokenAddresses)]; 115 | 116 | const balances = await getWalletBalance( 117 | wallet, 118 | tokenAddresses.length > 0 ? tokenAddresses : undefined, 119 | ); 120 | if (!balances) { 121 | return "Error getting balance: No balance information returned from the provider"; 122 | } 123 | 124 | if (balances.length === 0) { 125 | return "No balances found for the requested tokens"; 126 | } 127 | 128 | // Format the balance response 129 | const balanceStrings = balances 130 | // Filter out zero balances unless explicitly requested specific tokens 131 | .filter(balance => { 132 | // If user requested specific tokens, show all balances including zeros 133 | if ( 134 | (args.tokenAddresses && args.tokenAddresses.length > 0) || 135 | (args.tokenSymbols && args.tokenSymbols.length > 0) 136 | ) { 137 | return true; 138 | } 139 | // Otherwise, only show non-zero balances 140 | return balance.formattedAmount !== "0" && balance.formattedAmount !== "0.0"; 141 | }) 142 | .map(balance => { 143 | // Try to find a symbol for this address 144 | const chainId = wallet.rpcProvider.chain?.id; 145 | let displayName = balance.address; 146 | 147 | // Special case for native token (ETH, BNB, etc.) 148 | if (balance.address.toLowerCase() === "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee") { 149 | // Use chain-specific native token name if available 150 | if (chainId === 56) { 151 | displayName = "BNB"; 152 | } else if (chainId === 43114) { 153 | displayName = "AVAX"; 154 | } else if (chainId === 250) { 155 | displayName = "FTM"; 156 | } else if (chainId === 1088) { 157 | displayName = "METIS"; 158 | } else if (chainId === 8453) { 159 | displayName = "ETH"; 160 | } else if (chainId === 1284) { 161 | displayName = "GLMR"; 162 | } else { 163 | displayName = "ETH"; 164 | } 165 | } else if (chainId && tokenMappings[chainId]) { 166 | const chainTokens = tokenMappings[chainId]; 167 | // Find token symbol by address 168 | for (const [symbol, address] of Object.entries(chainTokens)) { 169 | if (address.toLowerCase() === balance.address.toLowerCase()) { 170 | displayName = symbol; 171 | break; 172 | } 173 | } 174 | } 175 | 176 | return `${displayName}: ${balance.formattedAmount}`; 177 | }); 178 | 179 | // Sort balances alphabetically by token name for better readability 180 | balanceStrings.sort(); 181 | 182 | const responseTitle = 183 | tokenAddresses.length > 0 && !args.tokenAddresses?.length && !args.tokenSymbols?.length 184 | ? "All Token Balances:" 185 | : "Balances:"; 186 | 187 | if (balanceStrings.length === 0) { 188 | return `Smart Account: ${smartAccount}\n${responseTitle}\nNo non-zero balances found`; 189 | } 190 | 191 | return `Smart Account: ${smartAccount}\n${responseTitle}\n${balanceStrings.join("\n")}`; 192 | } catch (error) { 193 | console.error("Balance fetch error:", error); 194 | return `Error getting balance: ${error instanceof Error ? error.message : String(error)}`; 195 | } 196 | } 197 | 198 | /** 199 | * Get wallet balance action. 200 | */ 201 | export class GetBalanceAction implements AgentkitAction { 202 | public name = "get_balance"; 203 | public description = GET_BALANCE_PROMPT; 204 | public argsSchema = GetBalanceInput; 205 | public func = getBalance; 206 | public smartAccountRequired = true; 207 | } 208 | -------------------------------------------------------------------------------- /agentkit-core/src/actions/getTokenDetailsAction.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 3 | import { fetchTokenDetails } from "../services"; 4 | import { AgentkitAction } from "../agentkit"; 5 | 6 | const GET_TOKEN_DETAILS_PROMPT = ` 7 | This tool will fetch details about an ERC20 token including: 8 | - Token name 9 | - Token symbol 10 | - Decimals 11 | - Contract address 12 | - Chain ID 13 | 14 | Provide the token contract address to get its details. 15 | `; 16 | 17 | export const GetTokenDetailsInput = z 18 | .object({ 19 | tokenAddress: z.string().describe("The ERC20 token contract address"), 20 | }) 21 | .strip() 22 | .describe("Instructions for getting token details"); 23 | 24 | /** 25 | * Gets details about an ERC20 token. 26 | * 27 | * @param wallet - The smart account to use for querying. 28 | * @param args - The input arguments containing the token address. 29 | * @returns A message containing the token details. 30 | */ 31 | export async function getTokenDetails( 32 | wallet: ZeroXgaslessSmartAccount, 33 | args: z.infer, 34 | ): Promise { 35 | try { 36 | const details = await fetchTokenDetails(wallet, args.tokenAddress); 37 | if (!details) { 38 | return "Error getting token details"; 39 | } 40 | return ` 41 | Token Details: 42 | Name: ${details.name} 43 | Symbol: ${details.symbol} 44 | Decimals: ${details.decimals} 45 | Address: ${details.address} 46 | Chain ID: ${details.chainId} 47 | `; 48 | } catch (error) { 49 | return `Error getting token details: ${error}`; 50 | } 51 | } 52 | 53 | /** 54 | * Get token details action. 55 | */ 56 | export class GetTokenDetailsAction implements AgentkitAction { 57 | public name = "get_token_details"; 58 | public description = GET_TOKEN_DETAILS_PROMPT; 59 | public argsSchema = GetTokenDetailsInput; 60 | public func = getTokenDetails; 61 | public smartAccountRequired = true; 62 | } 63 | -------------------------------------------------------------------------------- /agentkit-core/src/actions/index.ts: -------------------------------------------------------------------------------- 1 | import { GetBalanceAction } from "./getBalanceAction"; 2 | import { SmartTransferAction } from "./smartTransferAction"; 3 | import { GetTokenDetailsAction } from "./getTokenDetailsAction"; 4 | import { CheckTransactionAction } from "./checkTransactionAction"; 5 | import { SmartSwapAction } from "./smartSwapAction"; 6 | import { AgentkitAction, ActionSchemaAny } from "../agentkit"; 7 | import { GetAddressAction } from "./getAddressAction"; 8 | 9 | export function getAllAgentkitActions(): AgentkitAction[] { 10 | return [ 11 | new GetBalanceAction(), 12 | new GetAddressAction(), 13 | new GetTokenDetailsAction(), 14 | new CheckTransactionAction(), 15 | new SmartTransferAction(), 16 | new SmartSwapAction(), 17 | ]; 18 | } 19 | 20 | export const AGENTKIT_ACTIONS = getAllAgentkitActions(); 21 | -------------------------------------------------------------------------------- /agentkit-core/src/actions/smartSwapAction/dln.debridge.finance.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "paths": { 4 | 5 | "/v1.0/supported-chains": { 6 | "get": { 7 | "operationId": "AppControllerV10_getSupportedChainResponse", 8 | "summary": "", 9 | "deprecated": true, 10 | "parameters": [], 11 | "responses": { 12 | "200": { 13 | "description": "", 14 | "content": { 15 | "application/json": { 16 | "schema": { 17 | "$ref": "#/components/schemas/SupportedChainsResponse" 18 | } 19 | } 20 | } 21 | } 22 | }, 23 | "tags": [ 24 | "utils" 25 | ] 26 | } 27 | }, 28 | "/v1.0/supported-chains-info": { 29 | "get": { 30 | "operationId": "AppControllerV10_getSupportedChainInfoResponse", 31 | "parameters": [], 32 | "responses": { 33 | "200": { 34 | "description": "", 35 | "content": { 36 | "application/json": { 37 | "schema": { 38 | "$ref": "#/components/schemas/SupportedChainsInfoResponse" 39 | } 40 | } 41 | } 42 | } 43 | }, 44 | "tags": [ 45 | "utils" 46 | ] 47 | } 48 | }, 49 | "/v1.0/token-list": { 50 | "get": { 51 | "operationId": "AppControllerV10_getTokens", 52 | "parameters": [ 53 | { 54 | "name": "chainId", 55 | "required": true, 56 | "in": "query", 57 | "description": "ID of a chain", 58 | "examples": { 59 | "1": { 60 | "summary": "Ethereum", 61 | "value": "1" 62 | }, 63 | "10": { 64 | "summary": "Optimism", 65 | "value": "10" 66 | }, 67 | "56": { 68 | "summary": "BNB Chain", 69 | "value": "56" 70 | }, 71 | "137": { 72 | "summary": "Polygon", 73 | "value": "137" 74 | }, 75 | "250": { 76 | "summary": "Fantom", 77 | "value": "250" 78 | }, 79 | "8453": { 80 | "summary": "Base", 81 | "value": "8453" 82 | }, 83 | "42161": { 84 | "summary": "Arbitrum One", 85 | "value": "42161" 86 | }, 87 | "43114": { 88 | "summary": "Avalanche", 89 | "value": "43114" 90 | }, 91 | "59144": { 92 | "summary": "Linea", 93 | "value": "59144" 94 | }, 95 | "7565164": { 96 | "summary": "Solana", 97 | "value": "7565164" 98 | }, 99 | "100000001": { 100 | "summary": "Neon", 101 | "value": "100000001" 102 | }, 103 | "100000002": { 104 | "summary": "Gnosis", 105 | "value": "100000002" 106 | }, 107 | "100000004": { 108 | "summary": "Metis", 109 | "value": "100000004" 110 | }, 111 | "100000005": { 112 | "summary": "Bitrock", 113 | "value": "100000005" 114 | }, 115 | "100000006": { 116 | "summary": "CrossFi", 117 | "value": "100000006" 118 | }, 119 | "100000010": { 120 | "summary": "zkEvmCronos", 121 | "value": "100000010" 122 | }, 123 | "100000013": { 124 | "summary": "Story", 125 | "value": "100000013" 126 | }, 127 | "100000014": { 128 | "summary": "Sonic", 129 | "value": "100000014" 130 | }, 131 | "100000017": { 132 | "summary": "Abstract", 133 | "value": "100000017" 134 | }, 135 | "100000020": { 136 | "summary": "Berachain", 137 | "value": "100000020" 138 | } 139 | }, 140 | "schema": { 141 | "enum": [ 142 | "1", 143 | "10", 144 | "56", 145 | "137", 146 | "250", 147 | "8453", 148 | "42161", 149 | "43114", 150 | "59144", 151 | "7565164", 152 | "100000001", 153 | "100000002", 154 | "100000004", 155 | "100000005", 156 | "100000006", 157 | "100000010", 158 | "100000013", 159 | "100000014", 160 | "100000017", 161 | "100000020" 162 | ], 163 | "type": "string" 164 | } 165 | } 166 | ], 167 | "responses": { 168 | "200": { 169 | "description": "", 170 | "content": { 171 | "application/json": { 172 | "schema": { 173 | "$ref": "#/components/schemas/TokenListResponse" 174 | } 175 | } 176 | } 177 | } 178 | }, 179 | "tags": [ 180 | "utils" 181 | ] 182 | } 183 | }, 184 | "/v1.0/chain/transaction": { 185 | "get": { 186 | "operationId": "SingleSwapControllerV10_getChainTransaction", 187 | "parameters": [ 188 | { 189 | "name": "chainId", 190 | "required": true, 191 | "in": "query", 192 | "description": "An ID of a chain, a chain where the swap must be performed", 193 | "examples": { 194 | "1": { 195 | "summary": "Ethereum", 196 | "value": "1" 197 | }, 198 | "10": { 199 | "summary": "Optimism", 200 | "value": "10" 201 | }, 202 | "56": { 203 | "summary": "BNB Chain", 204 | "value": "56" 205 | }, 206 | "137": { 207 | "summary": "Polygon", 208 | "value": "137" 209 | }, 210 | "250": { 211 | "summary": "Fantom", 212 | "value": "250" 213 | }, 214 | "8453": { 215 | "summary": "Base", 216 | "value": "8453" 217 | }, 218 | "42161": { 219 | "summary": "Arbitrum One", 220 | "value": "42161" 221 | }, 222 | "43114": { 223 | "summary": "Avalanche", 224 | "value": "43114" 225 | }, 226 | "59144": { 227 | "summary": "Linea", 228 | "value": "59144" 229 | }, 230 | "7565164": { 231 | "summary": "Solana", 232 | "value": "7565164" 233 | }, 234 | "100000001": { 235 | "summary": "Neon", 236 | "value": "100000001" 237 | }, 238 | "100000002": { 239 | "summary": "Gnosis", 240 | "value": "100000002" 241 | }, 242 | "100000004": { 243 | "summary": "Metis", 244 | "value": "100000004" 245 | }, 246 | "100000005": { 247 | "summary": "Bitrock", 248 | "value": "100000005" 249 | }, 250 | "100000006": { 251 | "summary": "CrossFi", 252 | "value": "100000006" 253 | }, 254 | "100000010": { 255 | "summary": "zkEvmCronos", 256 | "value": "100000010" 257 | }, 258 | "100000013": { 259 | "summary": "Story", 260 | "value": "100000013" 261 | }, 262 | "100000014": { 263 | "summary": "Sonic", 264 | "value": "100000014" 265 | }, 266 | "100000017": { 267 | "summary": "Abstract", 268 | "value": "100000017" 269 | }, 270 | "100000020": { 271 | "summary": "Berachain", 272 | "value": "100000020" 273 | } 274 | }, 275 | "schema": { 276 | "enum": [ 277 | "1", 278 | "10", 279 | "56", 280 | "137", 281 | "250", 282 | "8453", 283 | "42161", 284 | "43114", 285 | "59144", 286 | "7565164", 287 | "100000001", 288 | "100000002", 289 | "100000004", 290 | "100000005", 291 | "100000006", 292 | "100000010", 293 | "100000013", 294 | "100000014", 295 | "100000017", 296 | "100000020" 297 | ], 298 | "type": "string" 299 | } 300 | }, 301 | { 302 | "name": "tokenIn", 303 | "required": true, 304 | "in": "query", 305 | "description": "An address of an input token to swap", 306 | "examples": { 307 | "usdt": { 308 | "summary": "USDT token on Ethereum", 309 | "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7" 310 | }, 311 | "native": { 312 | "summary": "Native coin (e.g., ETH on Ethereum, BNB on BNB Chain)", 313 | "value": "0x0000000000000000000000000000000000000000" 314 | }, 315 | "native_solana": { 316 | "summary": "Native token on SOLANA", 317 | "value": "11111111111111111111111111111111" 318 | }, 319 | "usdc_solana": { 320 | "summary": "USDC token on SOLANA", 321 | "value": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" 322 | } 323 | }, 324 | "schema": { 325 | "type": "string" 326 | } 327 | }, 328 | { 329 | "name": "tokenInAmount", 330 | "required": true, 331 | "in": "query", 332 | "description": "An amount of input tokens to swap", 333 | "examples": { 334 | "usdt": { 335 | "summary": "50 USDT (decimals=6)", 336 | "value": 50000000 337 | }, 338 | "busd": { 339 | "summary": "50 BUSD (decimals=18)", 340 | "value": 50000000000000000000 341 | }, 342 | "eth": { 343 | "summary": "0.1 ETH (decimals=18)", 344 | "value": 100000000000000000 345 | }, 346 | "sol": { 347 | "summary": "0.1 SOL (decimals=9)", 348 | "value": "100000000" 349 | } 350 | }, 351 | "schema": { 352 | "type": "string" 353 | } 354 | }, 355 | { 356 | "name": "slippage", 357 | "required": false, 358 | "in": "query", 359 | "description": "A slippage constraint (in %) is a safeguard during swaps (on both source and destination chains, if applicable). It is also used to calculate the minimum possible outcome during estimation. This property can be set to \"auto\" so that the API will suggest the best possible slippage.", 360 | "schema": { 361 | "default": "auto", 362 | "type": "string" 363 | } 364 | }, 365 | { 366 | "name": "tokenOut", 367 | "required": true, 368 | "in": "query", 369 | "description": "An address of a target token.", 370 | "examples": { 371 | "usdc": { 372 | "summary": "USDC token on the Ethereum chain", 373 | "value": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" 374 | }, 375 | "native": { 376 | "summary": "Native coin (e.g., ETH on Ethereum, BNB on BNB Chain)", 377 | "value": "0x0000000000000000000000000000000000000000" 378 | }, 379 | "usdc_solana": { 380 | "summary": "USDC token on SOLANA", 381 | "value": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" 382 | } 383 | }, 384 | "schema": { 385 | "type": "string" 386 | } 387 | }, 388 | { 389 | "name": "tokenOutRecipient", 390 | "required": true, 391 | "in": "query", 392 | "schema": { 393 | "type": "string" 394 | } 395 | }, 396 | { 397 | "name": "affiliateFeePercent", 398 | "required": false, 399 | "in": "query", 400 | "description": "\n The share of the input amount to be distributed to the `affiliateFeeRecipient` (if given) address as an affiliate fee.\n \n\n \nIf you are building on top of deSwap, you might want to take a small fee relative to the amount of a token a user provides for a swap. To achieve this, set this parameter to a desired % (e.g. `0.1` stands for 0.1%; the max value is `10` which stands for 10% of `srcAmountInParam`) and specify your wallet address in the `affiliateFeeRecipient` parameter.\n \n\n \nFor example, setting this parameter to `0.1` means that when the user gives 5000 USDT then you will receive 5 USDT immediately after transaction is being confirmed.\n ", 401 | "examples": { 402 | "no_fee": { 403 | "summary": "No affiliate fee", 404 | "value": 0 405 | }, 406 | "fee_0.1": { 407 | "summary": "Set 0.1% affiliate fee", 408 | "value": 0.1 409 | }, 410 | "fee_1": { 411 | "summary": "Set 1% affiliate fee", 412 | "value": 1 413 | } 414 | }, 415 | "schema": { 416 | "type": "number" 417 | } 418 | }, 419 | { 420 | "name": "affiliateFeeRecipient", 421 | "required": false, 422 | "in": "query", 423 | "description": "An address (on an origin chain) that will receive affiliate fees according to the `affiliateFeePercent` parameter. For solana affiliate fee you have to create & provide Jupiter's referral key (https://referral.jup.ag/dashboard).", 424 | "schema": { 425 | "type": "string" 426 | } 427 | }, 428 | { 429 | "name": "senderAddress", 430 | "required": false, 431 | "in": "query", 432 | "description": "Address (on the source chain) who submits input tokens for a cross-chain swap", 433 | "schema": { 434 | "type": "string" 435 | } 436 | } 437 | ], 438 | "responses": { 439 | "200": { 440 | "description": "", 441 | "content": { 442 | "application/json": { 443 | "schema": { 444 | "$ref": "#/components/schemas/ChainTransactionResponseV10" 445 | } 446 | } 447 | } 448 | }, 449 | "400": { 450 | "description": "The request is malformed, has missing parameters, or has incorrect or contradicting parameter values. Possible error ids: \n \n - INVALID_QUERY_PARAMETERS\n \n \n - AFFILIATE_FEE_PERCENT_NOT_SET\n \n \n - AFFILIATE_FEE_RECIPIENT_NOT_SET\n \n \n - UNSUPPORTED_TOKEN_IN\n \n \n - UNSUPPORTED_TOKEN_OUT\n \n", 451 | "content": { 452 | "application/json": { 453 | "schema": { 454 | "$ref": "#/components/schemas/BadRequestResponse" 455 | } 456 | } 457 | } 458 | }, 459 | "500": { 460 | "description": "The server has encountered a situation it does not know how to handle. Possible error ids: \n \n - INTERNAL_SERVER_ERROR\n \n \n - INTERNAL_SDK_ERROR\n \n", 461 | "content": { 462 | "application/json": { 463 | "schema": { 464 | "$ref": "#/components/schemas/InternalServerErrorResponse" 465 | } 466 | } 467 | } 468 | } 469 | }, 470 | "tags": [ 471 | "single chain swap" 472 | ] 473 | } 474 | }, 475 | "/v1.0/chain/estimation": { 476 | "get": { 477 | "operationId": "SingleSwapControllerV10_getChainEstimation", 478 | "parameters": [ 479 | { 480 | "name": "chainId", 481 | "required": true, 482 | "in": "query", 483 | "description": "An ID of a chain, a chain where the swap must be performed", 484 | "examples": { 485 | "1": { 486 | "summary": "Ethereum", 487 | "value": "1" 488 | }, 489 | "10": { 490 | "summary": "Optimism", 491 | "value": "10" 492 | }, 493 | "56": { 494 | "summary": "BNB Chain", 495 | "value": "56" 496 | }, 497 | "137": { 498 | "summary": "Polygon", 499 | "value": "137" 500 | }, 501 | "250": { 502 | "summary": "Fantom", 503 | "value": "250" 504 | }, 505 | "8453": { 506 | "summary": "Base", 507 | "value": "8453" 508 | }, 509 | "42161": { 510 | "summary": "Arbitrum One", 511 | "value": "42161" 512 | }, 513 | "43114": { 514 | "summary": "Avalanche", 515 | "value": "43114" 516 | }, 517 | "59144": { 518 | "summary": "Linea", 519 | "value": "59144" 520 | }, 521 | "7565164": { 522 | "summary": "Solana", 523 | "value": "7565164" 524 | }, 525 | "100000001": { 526 | "summary": "Neon", 527 | "value": "100000001" 528 | }, 529 | "100000002": { 530 | "summary": "Gnosis", 531 | "value": "100000002" 532 | }, 533 | "100000004": { 534 | "summary": "Metis", 535 | "value": "100000004" 536 | }, 537 | "100000005": { 538 | "summary": "Bitrock", 539 | "value": "100000005" 540 | }, 541 | "100000006": { 542 | "summary": "CrossFi", 543 | "value": "100000006" 544 | }, 545 | "100000010": { 546 | "summary": "zkEvmCronos", 547 | "value": "100000010" 548 | }, 549 | "100000013": { 550 | "summary": "Story", 551 | "value": "100000013" 552 | }, 553 | "100000014": { 554 | "summary": "Sonic", 555 | "value": "100000014" 556 | }, 557 | "100000017": { 558 | "summary": "Abstract", 559 | "value": "100000017" 560 | }, 561 | "100000020": { 562 | "summary": "Berachain", 563 | "value": "100000020" 564 | } 565 | }, 566 | "schema": { 567 | "enum": [ 568 | "1", 569 | "10", 570 | "56", 571 | "137", 572 | "250", 573 | "8453", 574 | "42161", 575 | "43114", 576 | "59144", 577 | "7565164", 578 | "100000001", 579 | "100000002", 580 | "100000004", 581 | "100000005", 582 | "100000006", 583 | "100000010", 584 | "100000013", 585 | "100000014", 586 | "100000017", 587 | "100000020" 588 | ], 589 | "type": "string" 590 | } 591 | }, 592 | { 593 | "name": "tokenIn", 594 | "required": true, 595 | "in": "query", 596 | "description": "An address of an input token to swap", 597 | "examples": { 598 | "usdt": { 599 | "summary": "USDT token on Ethereum", 600 | "value": "0xdAC17F958D2ee523a2206206994597C13D831ec7" 601 | }, 602 | "native": { 603 | "summary": "Native coin (e.g., ETH on Ethereum, BNB on BNB Chain)", 604 | "value": "0x0000000000000000000000000000000000000000" 605 | }, 606 | "native_solana": { 607 | "summary": "Native token on SOLANA", 608 | "value": "11111111111111111111111111111111" 609 | }, 610 | "usdc_solana": { 611 | "summary": "USDC token on SOLANA", 612 | "value": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" 613 | } 614 | }, 615 | "schema": { 616 | "type": "string" 617 | } 618 | }, 619 | { 620 | "name": "tokenInAmount", 621 | "required": true, 622 | "in": "query", 623 | "description": "An amount of input tokens to swap", 624 | "examples": { 625 | "usdt": { 626 | "summary": "50 USDT (decimals=6)", 627 | "value": 50000000 628 | }, 629 | "busd": { 630 | "summary": "50 BUSD (decimals=18)", 631 | "value": 50000000000000000000 632 | }, 633 | "eth": { 634 | "summary": "0.1 ETH (decimals=18)", 635 | "value": 100000000000000000 636 | }, 637 | "sol": { 638 | "summary": "0.1 SOL (decimals=9)", 639 | "value": "100000000" 640 | } 641 | }, 642 | "schema": { 643 | "type": "string" 644 | } 645 | }, 646 | { 647 | "name": "slippage", 648 | "required": false, 649 | "in": "query", 650 | "description": "A slippage constraint (in %) is a safeguard during swaps (on both source and destination chains, if applicable). It is also used to calculate the minimum possible outcome during estimation. This property can be set to \"auto\" so that the API will suggest the best possible slippage.", 651 | "schema": { 652 | "default": "auto", 653 | "type": "string" 654 | } 655 | }, 656 | { 657 | "name": "tokenOut", 658 | "required": true, 659 | "in": "query", 660 | "description": "An address of a target token.", 661 | "examples": { 662 | "usdc": { 663 | "summary": "USDC token on the Ethereum chain", 664 | "value": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" 665 | }, 666 | "native": { 667 | "summary": "Native coin (e.g., ETH on Ethereum, BNB on BNB Chain)", 668 | "value": "0x0000000000000000000000000000000000000000" 669 | }, 670 | "usdc_solana": { 671 | "summary": "USDC token on SOLANA", 672 | "value": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" 673 | } 674 | }, 675 | "schema": { 676 | "type": "string" 677 | } 678 | }, 679 | { 680 | "name": "affiliateFeePercent", 681 | "required": false, 682 | "in": "query", 683 | "description": "\n The share of the input amount to be distributed to the `affiliateFeeRecipient` (if given) address as an affiliate fee.\n \n\n \nIf you are building on top of deSwap, you might want to take a small fee relative to the amount of a token a user provides for a swap. To achieve this, set this parameter to a desired % (e.g. `0.1` stands for 0.1%; the max value is `10` which stands for 10% of `srcAmountInParam`) and specify your wallet address in the `affiliateFeeRecipient` parameter.\n \n\n \nFor example, setting this parameter to `0.1` means that when the user gives 5000 USDT then you will receive 5 USDT immediately after transaction is being confirmed.\n ", 684 | "examples": { 685 | "no_fee": { 686 | "summary": "No affiliate fee", 687 | "value": 0 688 | }, 689 | "fee_0.1": { 690 | "summary": "Set 0.1% affiliate fee", 691 | "value": 0.1 692 | }, 693 | "fee_1": { 694 | "summary": "Set 1% affiliate fee", 695 | "value": 1 696 | } 697 | }, 698 | "schema": { 699 | "type": "number" 700 | } 701 | }, 702 | { 703 | "name": "affiliateFeeRecipient", 704 | "required": false, 705 | "in": "query", 706 | "description": "An address (on an origin chain) that will receive affiliate fees according to the `affiliateFeePercent` parameter.", 707 | "schema": { 708 | "type": "string" 709 | } 710 | } 711 | ], 712 | "responses": { 713 | "200": { 714 | "description": "", 715 | "content": { 716 | "application/json": { 717 | "schema": { 718 | "$ref": "#/components/schemas/ChainEstimationResponseV10" 719 | } 720 | } 721 | } 722 | }, 723 | "400": { 724 | "description": "The request is malformed, has missing parameters, or has incorrect or contradicting parameter values. Possible error ids: \n \n - INVALID_QUERY_PARAMETERS\n \n \n - AFFILIATE_FEE_PERCENT_NOT_SET\n \n \n - AFFILIATE_FEE_RECIPIENT_NOT_SET\n \n \n - UNSUPPORTED_TOKEN_IN\n \n \n - UNSUPPORTED_TOKEN_OUT\n \n", 725 | "content": { 726 | "application/json": { 727 | "schema": { 728 | "$ref": "#/components/schemas/BadRequestResponse" 729 | } 730 | } 731 | } 732 | }, 733 | "500": { 734 | "description": "The server has encountered a situation it does not know how to handle. Possible error ids: \n \n - INTERNAL_SERVER_ERROR\n \n \n - INTERNAL_SDK_ERROR\n \n", 735 | "content": { 736 | "application/json": { 737 | "schema": { 738 | "$ref": "#/components/schemas/InternalServerErrorResponse" 739 | } 740 | } 741 | } 742 | } 743 | }, 744 | "tags": [ 745 | "single chain swap" 746 | ] 747 | } 748 | } 749 | } 750 | } -------------------------------------------------------------------------------- /agentkit-core/src/actions/smartSwapAction/index.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Transaction, ZeroXgaslessSmartAccount } from "@0xgasless/smart-account"; 3 | import { AgentkitAction } from "../../agentkit"; 4 | import { 5 | sendTransaction, 6 | formatTokenAmount, 7 | resolveTokenSymbol, 8 | checkAndApproveTokenAllowance, 9 | } from "../../services"; 10 | 11 | const SWAP_PROMPT = ` 12 | This tool allows you to perform gasless token swaps on supported chains. 13 | 14 | You can swap tokens in two ways: 15 | 1. Using token addresses (e.g., "0x...") 16 | 2. Using token symbols (e.g., "ETH", "USDC", "USDT", "WETH", etc.) 17 | 18 | USAGE GUIDANCE: 19 | - Provide either tokenIn/tokenOut addresses OR tokenInSymbol/tokenOutSymbol 20 | - Specify the amount to swap (in the input token's units) 21 | - Optionally set a custom slippage (default is "auto") 22 | - Optionally set 'approveMax: true' to approve maximum token allowance (default is false) 23 | 24 | EXAMPLES: 25 | - Swap by address: "Swap 10 from 0x123... to 0x456..." 26 | - Swap by symbol: "Swap 10 USDC to ETH" 27 | - With max approval: "Swap 10 USDT to ETH with approveMax: true" 28 | 29 | Note: This action works on supported networks only (Base, Fantom, Moonbeam, Metis, Avalanche, BSC). 30 | All swaps are gasless - no native tokens needed for gas fees. 31 | The transaction will be submitted and the tool will wait for confirmation by default. 32 | `; 33 | 34 | export const SmartSwapInput = z 35 | .object({ 36 | tokenIn: z 37 | .string() 38 | .optional() 39 | .describe("The address of the input token (token you're selling)"), 40 | tokenOut: z 41 | .string() 42 | .optional() 43 | .describe("The address of the output token (token you're buying)"), 44 | tokenInSymbol: z 45 | .string() 46 | .optional() 47 | .describe("The symbol of the input token (e.g., 'ETH', 'USDC')"), 48 | tokenOutSymbol: z 49 | .string() 50 | .optional() 51 | .describe("The symbol of the output token (e.g., 'ETH', 'USDC')"), 52 | amount: z.string().describe("The amount of input token to swap"), 53 | slippage: z 54 | .string() 55 | .optional() 56 | .default("auto") 57 | .describe("Slippage tolerance in percentage (e.g., '0.5') or 'auto'"), 58 | approveMax: z 59 | .boolean() 60 | .optional() 61 | .default(false) 62 | .describe("Whether to approve maximum token allowance"), 63 | }) 64 | .strip() 65 | .describe("Instructions for swapping tokens"); 66 | 67 | export async function smartSwap( 68 | wallet: ZeroXgaslessSmartAccount, 69 | args: z.infer, 70 | ): Promise { 71 | try { 72 | const chainId = wallet.rpcProvider.chain?.id; 73 | if (!chainId) { 74 | return "Error: Unable to determine chain ID from wallet"; 75 | } 76 | 77 | // Resolve token addresses from symbols if provided 78 | let tokenInAddress = args.tokenIn; 79 | let tokenOutAddress = args.tokenOut; 80 | if (args.tokenInSymbol && !tokenInAddress) { 81 | const resolved = await resolveTokenSymbol(wallet, args.tokenInSymbol); 82 | if (!resolved) { 83 | return `Error: Could not resolve token symbol "${args.tokenInSymbol}" to an address on chain ${chainId}`; 84 | } 85 | tokenInAddress = resolved; 86 | } 87 | if (args.tokenOutSymbol && !tokenOutAddress) { 88 | const resolved = await resolveTokenSymbol(wallet, args.tokenOutSymbol); 89 | if (!resolved) { 90 | return `Error: Could not resolve token symbol "${args.tokenOutSymbol}" to an address on chain ${chainId}`; 91 | } 92 | tokenOutAddress = resolved; 93 | } 94 | if (!tokenInAddress || !tokenOutAddress) { 95 | return "Error: Both input and output token addresses are required"; 96 | } 97 | 98 | // Get token details for better user feedback 99 | let tokenInDetails; 100 | let tokenOutDetails; 101 | 102 | // Format the amount with proper decimals 103 | const formattedAmount = await formatTokenAmount( 104 | wallet, 105 | tokenInAddress as `0x${string}`, 106 | args.amount, 107 | ); 108 | 109 | // Construct the API URL with query parameters 110 | const baseUrl = "https://dln.debridge.finance/v1.0/chain/transaction"; 111 | const queryParams = new URLSearchParams({ 112 | chainId: chainId.toString(), 113 | tokenIn: tokenInAddress, 114 | tokenInAmount: formattedAmount, 115 | tokenOut: tokenOutAddress, 116 | tokenOutRecipient: await wallet.getAddress(), 117 | slippage: args.slippage || "auto", 118 | affiliateFeePercent: "0", 119 | }); 120 | const formedDebridgeApiUrl = `${baseUrl}?${queryParams.toString()}`; 121 | 122 | // First try to get an estimation to check if the swap is possible 123 | try { 124 | const estimationUrl = formedDebridgeApiUrl.replace("/transaction", "/estimation"); 125 | const estimationResponse = await fetch(estimationUrl); 126 | let parsedEstimation = await estimationResponse.json(); 127 | parsedEstimation = parsedEstimation.estimation; 128 | 129 | if (!estimationResponse.ok) { 130 | if ( 131 | parsedEstimation.errorMessage?.includes("insufficient liquidity") || 132 | parsedEstimation.errorMessage?.includes("no route found") 133 | ) { 134 | return `Swap not available: Insufficient liquidity or no route found between these tokens.`; 135 | } 136 | 137 | if (parsedEstimation.errorMessage?.includes("amount too small")) { 138 | return `Swap not available: The amount is too small. Please try a larger amount.`; 139 | } 140 | 141 | console.warn("Estimation failed:", parsedEstimation); 142 | } 143 | tokenInDetails = parsedEstimation.tokenIn; 144 | tokenOutDetails = parsedEstimation.tokenOut; 145 | } catch (error) { 146 | console.warn("Error checking swap estimation:", error); 147 | // Continue even if estimation fails 148 | } 149 | 150 | const debridgeResponse = await fetch(formedDebridgeApiUrl); 151 | const transactionData = await debridgeResponse.json(); 152 | 153 | if (!debridgeResponse.ok || transactionData.errorCode == 0) { 154 | // Handle specific error cases 155 | if ( 156 | transactionData.errorMessage?.includes("insufficient liquidity") || 157 | transactionData.errorMessage?.includes("no route found") 158 | ) { 159 | return `Swap failed: Insufficient liquidity or no route found between these tokens.`; 160 | } 161 | 162 | if (transactionData.errorMessage?.includes("amount too small")) { 163 | return `Swap failed: The amount is too small. Please try a larger amount.`; 164 | } 165 | 166 | if (transactionData.errorMessage?.includes("Bad Request")) { 167 | return `Swap failed: Invalid request parameters. Please check your token addresses and amount. 168 | 169 | For BNB Chain (56), make sure: 170 | 1. The token amount is not too small (try at least 0.01 USDT) 171 | 2. There is sufficient liquidity for this pair 172 | 3. You have enough balance of the input token`; 173 | } 174 | 175 | if ( 176 | transactionData.errorMessage?.includes("transfer amount exceeds allowance") || 177 | transactionData.errorMessage?.includes("BEP20: transfer amount exceeds allowance") 178 | ) { 179 | return `Swap failed: Token allowance error. You need to approve the swap contract to spend your tokens. 180 | 181 | Please try again with "approveMax: true" parameter to automatically approve token spending. 182 | Example: "Swap 0.01 USDT to WETH with approveMax: true"`; 183 | } 184 | 185 | return `Swap failed: ${transactionData.errorMessage || debridgeResponse.statusText}\nDetails: ${JSON.stringify(transactionData.details || {})}`; 186 | } 187 | if (!tokenInDetails || !tokenOutDetails) { 188 | tokenInDetails = transactionData.tokenIn; 189 | tokenOutDetails = transactionData.tokenOut; 190 | } 191 | 192 | // If we only got an estimation without transaction data 193 | if (!transactionData.tx) { 194 | const inSymbol = tokenInDetails?.symbol || args.tokenInSymbol || "tokens"; 195 | const outSymbol = tokenOutDetails?.symbol || args.tokenOutSymbol || "tokens"; 196 | 197 | return `Swap estimation:\nInput: ${transactionData.tokenIn.amount} ${transactionData.tokenIn.symbol || inSymbol}\nExpected Output: ${transactionData.tokenOut.amount} ${transactionData.tokenOut.symbol || outSymbol}\nMin Output: ${transactionData.tokenOut.minAmount} ${transactionData.tokenOut.symbol || outSymbol}\nRecommended Slippage: ${transactionData.recommendedSlippage}%`; 198 | } 199 | 200 | // For non-native tokens, we need to check and approve allowance if needed 201 | if (tokenInAddress !== "0x0000000000000000000000000000000000000000") { 202 | const spenderAddress = transactionData.tx.to as `0x${string}`; 203 | 204 | // Check and approve token allowance 205 | const approvalResult = await checkAndApproveTokenAllowance( 206 | wallet, 207 | tokenInAddress as `0x${string}`, 208 | spenderAddress, 209 | BigInt(formattedAmount), 210 | args.approveMax, 211 | ); 212 | 213 | if (!approvalResult.success) { 214 | return `Failed to approve token spending: ${approvalResult.error}`; 215 | } 216 | 217 | // If an approval transaction was sent (it now waits by default), 218 | // check its result. 219 | if (approvalResult.userOpHash) { 220 | // The `sendTransaction` within `approveToken` (called by `checkAndApproveTokenAllowance`) 221 | // now waits for confirmation. We just need to check the final result. 222 | // The `approvalResult` reflects the final status. 223 | console.log(`Token approval successful. UserOpHash: ${approvalResult.userOpHash}`); 224 | } 225 | } 226 | 227 | const txResponse = await sendTransaction(wallet, transactionData.tx as Transaction); 228 | 229 | if (!txResponse.success) { 230 | if ( 231 | typeof txResponse.error === "string" && 232 | (txResponse.error.includes("transfer amount exceeds allowance") || 233 | txResponse.error.includes("BEP20: transfer amount exceeds allowance")) 234 | ) { 235 | return `Swap failed: Token allowance error. You need to approve the swap contract to spend your tokens. 236 | 237 | Please try again with "approveMax: true" parameter to automatically approve token spending. 238 | Example: "Swap 0.01 USDT to WETH with approveMax: true"`; 239 | } 240 | return `Swap failed: ${ 241 | typeof txResponse.error === "string" ? txResponse.error : JSON.stringify(txResponse.error) 242 | }`; 243 | } 244 | 245 | // Remove the logic block for `if (args.wait)` and `waitForTransaction` 246 | // Update the success message based on the awaited result from sendTransaction 247 | const inSymbol = tokenInDetails?.symbol || args.tokenInSymbol || "tokens"; 248 | const outSymbol = tokenOutDetails?.symbol || args.tokenOutSymbol || "tokens"; 249 | 250 | return `Swap successful!\nInput: ${args.amount} ${inSymbol}\n(Approximate) Output: ${tokenOutDetails?.amount || "?"} ${outSymbol}\nTx Hash: ${txResponse.txHash}`; 251 | } catch (error) { 252 | console.error("Smart Swap Error:", error); 253 | return `An unexpected error occurred during the swap: ${error instanceof Error ? error.message : String(error)}`; 254 | } 255 | } 256 | 257 | export class SmartSwapAction implements AgentkitAction { 258 | public name = "smart_swap"; 259 | public description = SWAP_PROMPT; 260 | public argsSchema = SmartSwapInput; 261 | public func = smartSwap; 262 | public smartAccountRequired = true; 263 | } 264 | -------------------------------------------------------------------------------- /agentkit-core/src/actions/smartTransferAction.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ZeroXgaslessSmartAccount, Transaction } from "@0xgasless/smart-account"; 3 | import { encodeFunctionData, parseEther, parseUnits } from "viem"; 4 | import { TokenABI } from "../constants"; 5 | import { sendTransaction } from "../services"; 6 | import { AgentkitAction } from "../agentkit"; 7 | 8 | const SMART_TRANSFER_PROMPT = ` 9 | This tool will transfer an ERC20 token or native currency from the wallet to another onchain address using gasless transactions. 10 | 11 | It takes the following inputs: 12 | - amount: The amount to transfer 13 | - tokenAddress: The token contract address (use 'eth' for native currency transfers) 14 | - destination: Where to send the funds (must be a valid onchain address) 15 | 16 | Important notes: 17 | - Gasless transfers are only available on supported networks: Avalanche C-Chain, Metis chain, BASE, BNB chain, FANTOM, Moonbeam. 18 | - The transaction will be submitted and the tool will wait for confirmation by default. 19 | `; 20 | 21 | /** 22 | * Input schema for smart transfer action. 23 | */ 24 | export const SmartTransferInput = z 25 | .object({ 26 | amount: z.string().describe("The amount of tokens to transfer"), 27 | tokenAddress: z 28 | .string() 29 | .describe("The token contract address or 'eth' for native currency transfers"), 30 | destination: z.string().describe("The recipient address"), 31 | }) 32 | .strip() 33 | .describe("Instructions for transferring tokens from a smart account to an onchain address"); 34 | 35 | /** 36 | * Transfers assets using gasless transactions. 37 | * 38 | * @param wallet - The smart account to transfer from. 39 | * @param args - The input arguments for the action. 40 | * @returns A message containing the transfer details. 41 | */ 42 | export async function smartTransfer( 43 | wallet: ZeroXgaslessSmartAccount, 44 | args: z.infer, 45 | ): Promise { 46 | try { 47 | const isEth = args.tokenAddress.toLowerCase() === "eth"; 48 | let tx: Transaction; 49 | 50 | if (isEth) { 51 | // Native ETH transfer 52 | tx = { 53 | to: args.destination as `0x${string}`, 54 | data: "0x", 55 | value: parseEther(args.amount), 56 | }; 57 | } else { 58 | // ERC20 token transfer 59 | const decimals = await wallet.rpcProvider.readContract({ 60 | abi: TokenABI, 61 | address: args.tokenAddress as `0x${string}`, 62 | functionName: "decimals", 63 | }); 64 | const data = encodeFunctionData({ 65 | abi: TokenABI, 66 | functionName: "transfer", 67 | args: [ 68 | args.destination as `0x${string}`, 69 | parseUnits(args.amount, (decimals as number) || 18), 70 | ], 71 | }); 72 | 73 | tx = { 74 | to: args.tokenAddress as `0x${string}`, 75 | data, 76 | value: 0n, 77 | }; 78 | } 79 | 80 | const response = await sendTransaction(wallet, tx); 81 | 82 | if (!response || !response.success) { 83 | return `Transaction failed: ${response?.error || "Unknown error"}`; 84 | } 85 | 86 | return `The transaction has been confirmed on the blockchain. Successfully transferred ${args.amount} ${ 87 | isEth ? "ETH" : `tokens from contract ${args.tokenAddress}` 88 | } to ${args.destination}. Transaction Hash: ${response.txHash}`; 89 | } catch (error) { 90 | return `Error transferring the asset: ${error}`; 91 | } 92 | } 93 | 94 | /** 95 | * Smart transfer action. 96 | */ 97 | export class SmartTransferAction implements AgentkitAction { 98 | public name = "smart_transfer"; 99 | public description = SMART_TRANSFER_PROMPT; 100 | public argsSchema = SmartTransferInput; 101 | public func = smartTransfer; 102 | public smartAccountRequired = true; 103 | } 104 | -------------------------------------------------------------------------------- /agentkit-core/src/agentkit.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { Account, createPublicClient, createWalletClient, http, PublicClient } from "viem"; 3 | import { mnemonicToAccount, privateKeyToAccount } from "viem/accounts"; 4 | import { ZeroXgaslessSmartAccount, createSmartAccountClient } from "@0xgasless/smart-account"; 5 | 6 | import { supportedChains } from "./constants"; 7 | 8 | // biome-ignore lint/suspicious/noExplicitAny: 9 | export type ActionSchemaAny = z.ZodObject; 10 | 11 | /** 12 | * Represents the base structure for Agentkit Actions. 13 | */ 14 | export interface AgentkitAction { 15 | /** 16 | * The name of the action 17 | */ 18 | name: string; 19 | 20 | /** 21 | * A description of what the action does 22 | */ 23 | description: string; 24 | 25 | /** 26 | * Schema for validating action arguments 27 | */ 28 | argsSchema: TActionSchema; 29 | 30 | /** 31 | * Indicates whether a smart account is required for this action 32 | */ 33 | smartAccountRequired?: boolean; 34 | 35 | /** 36 | * The function to execute for this action 37 | */ 38 | func: (wallet: ZeroXgaslessSmartAccount, args: z.infer) => Promise; 39 | // | ((wallet: PublicClient, args: z.infer) => Promise) 40 | // | ((args: z.infer) => Promise); 41 | } 42 | 43 | /** 44 | * Configuration options for the Agentkit 45 | */ 46 | export interface PublicAgentOptions { 47 | chainID: number; 48 | rpcUrl?: string; 49 | } 50 | 51 | /** 52 | * Configuration options for the Agentkit with a Smart Account 53 | */ 54 | export interface SmartAgentOptions extends PublicAgentOptions { 55 | mnemonicPhrase?: string; 56 | accountPath?: number; 57 | privateKey?: `0x${string}`; 58 | apiKey: string; 59 | } 60 | 61 | export class Agentkit { 62 | private publicClient: PublicClient; 63 | private smartAccount?: ZeroXgaslessSmartAccount; 64 | 65 | public constructor(config: PublicAgentOptions) { 66 | if (!supportedChains[config.chainID]) { 67 | throw new Error(`Chain ID ${config.chainID} is not supported`); 68 | } 69 | 70 | // Configure public client 71 | this.publicClient = createPublicClient({ 72 | chain: supportedChains[config.chainID], 73 | transport: config.rpcUrl ? http(config.rpcUrl) : http(), 74 | }); 75 | } 76 | 77 | public static async configureWithWallet(config: SmartAgentOptions): Promise { 78 | if (!config.apiKey || config.apiKey === "") { 79 | throw new Error("API_KEY is required for smart agent configuration"); 80 | } 81 | 82 | const agentkit = new Agentkit(config); 83 | 84 | try { 85 | let account: Account; 86 | if (config.privateKey) { 87 | account = privateKeyToAccount(config.privateKey); 88 | } else if (config.mnemonicPhrase) { 89 | account = mnemonicToAccount(config.mnemonicPhrase, { 90 | accountIndex: config.accountPath || 0, 91 | }); 92 | } else { 93 | throw new Error("Either privateKey or mnemonicPhrase must be provided"); 94 | } 95 | 96 | // Create wallet client 97 | const wallet = createWalletClient({ 98 | account, 99 | chain: supportedChains[config.chainID], 100 | transport: config.rpcUrl ? http(config.rpcUrl) : http(), 101 | }); 102 | 103 | // Configure smart account 104 | const bundlerUrl = `https://bundler.0xgasless.com/${config.chainID}`; 105 | const paymasterUrl = `https://paymaster.0xgasless.com/v1/${config.chainID}/rpc/${config.apiKey}`; 106 | 107 | agentkit.smartAccount = await createSmartAccountClient({ 108 | bundlerUrl, 109 | paymasterUrl, 110 | chainId: config.chainID, 111 | signer: wallet, 112 | }); 113 | } catch (error) { 114 | throw new Error(`Failed to initialize smart account: ${error}`); 115 | } 116 | 117 | return agentkit; 118 | } 119 | 120 | async run( 121 | action: AgentkitAction, 122 | args: TActionSchema, 123 | ): Promise { 124 | if (!this.smartAccount) { 125 | return `Unable to run Action: ${action.name}. A Smart Account is required. Please configure Agentkit with a Wallet to run this action.`; 126 | } 127 | return await ( 128 | action.func as (account: ZeroXgaslessSmartAccount, args: TActionSchema) => Promise 129 | )(this.smartAccount, args); 130 | } 131 | 132 | async getAddress(): Promise { 133 | if (!this.smartAccount) { 134 | throw new Error("Smart account not configured"); 135 | } 136 | return await this.smartAccount.getAddress(); 137 | } 138 | 139 | async getChainId(): Promise { 140 | if (!this.smartAccount) { 141 | throw new Error("Smart account not configured"); 142 | } 143 | return this.smartAccount.SmartAccountConfig.chainId; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /agentkit-core/src/constants.ts: -------------------------------------------------------------------------------- 1 | import { avalanche, fantom, moonbeam, metis, base, bsc, Chain } from "viem/chains"; 2 | 3 | export const supportedChains: Record = { 4 | 8453: base, 5 | 250: fantom, 6 | 1284: moonbeam, 7 | 1088: metis, 8 | 43114: avalanche, 9 | 56: bsc, 10 | }; 11 | 12 | // Token mappings by chain ID and ticker symbol 13 | export const tokenMappings: Record> = { 14 | // Avalanche (43114) 15 | 43114: { 16 | USDT: "0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7", 17 | USDC: "0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e", 18 | WAVAX: "0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7", 19 | "BTC.E": "0x152b9d0fdc40c096757f570a51e494bd4b943e50", 20 | BUSD: "0x9c9e5fd8bbc25984b178fdce6117defa39d2db39", 21 | WETH: "0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab", 22 | "USDC.E": "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664", 23 | WBTC: "0x50b7545627a5162f82a992c33b87adc75187b218", 24 | DAI: "0xd586e7f844cea2f87f50152665bcbc2c279d8d70", 25 | }, 26 | // BNB Chain (56) 27 | 56: { 28 | USDT: "0x55d398326f99059ff775485246999027b3197955", 29 | WBNB: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c", 30 | WETH: "0x2170ed0880ac9a755fd29b2688956bd959f933f8", 31 | BUSD: "0xe9e7cea3dedca5984780bafc599bd69add087d56", 32 | CAKE: "0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82", 33 | SOL: "0x570a5d26f7765ecb712c0924e4de545b89fd43df", 34 | TST: "0x86bb94ddd16efc8bc58e6b056e8df71d9e666429", 35 | DAI: "0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3", 36 | TON: "0x76a797a59ba2c17726896976b7b3747bfd1d220f", 37 | PEPE: "0x25d887ce7a35172c62febfd67a1856f20faebb00", 38 | }, 39 | // Add other chains as needed 40 | 8453: {}, // Base 41 | 250: {}, // Fantom 42 | 1284: {}, // Moonbeam 43 | 1088: {}, // Metis 44 | }; 45 | 46 | // Common tokens that exist on most chains (for easier reference) 47 | export const commonTokens = ["ETH", "USDT", "USDC", "DAI", "WETH", "WBTC", "BUSD"]; 48 | 49 | export const BASE_CONTEXT = ` 50 | You are a smart account built by 0xgasless Smart SDK. You are capable of gasless blockchain interactions. You can perform actions without requiring users to hold native tokens for gas fees via erc-4337 account abstraction standard. 51 | 52 | Capabilities: 53 | - Check balances of ETH and any ERC20 tokens by symbol (e.g., "USDC", "USDT") or address 54 | - Transfer tokens gaslessly 55 | - Perform token swaps without gas fees 56 | - Create and deploy new smart accounts 57 | 58 | Important Information: 59 | - The wallet is already configured with the SDK. DO NOT generate or mention private keys when using any tools. 60 | - You can only operate on supported networks: Base (8453), Fantom (250), Moonbeam (1284), Metis (1088), Avalanche (43114), and BSC (56) 61 | - All transactions are gasless - users don't need native tokens to perform actions 62 | - Default RPC uses Ankr's free tier which has rate limitations 63 | 64 | When interacting with tokens: 65 | - Always verify token addresses are valid 66 | - Check token balances before transfers 67 | - Use proper decimal precision for token amounts 68 | - You can use token symbols like "USDC", "USDT", "WETH" instead of addresses on supported chains 69 | 70 | You can assist users by: 71 | 1. Getting wallet balances - when asked about balances, immediately check them without asking for confirmation 72 | - For common tokens, use their symbols (e.g., "USDC", "USDT", "WETH") instead of addresses 73 | - For other tokens, you can use their contract addresses 74 | 2. Executing token transfers 75 | 3. Performing token swaps 76 | 4. Creating new smart accounts 77 | 5. Checking transaction status 78 | 79 | Please ensure all addresses and token amounts are properly validated before executing transactions.`; 80 | 81 | export const TokenABI = [ 82 | { 83 | inputs: [ 84 | { 85 | internalType: "string", 86 | name: "name", 87 | type: "string", 88 | }, 89 | { 90 | internalType: "string", 91 | name: "symbol", 92 | type: "string", 93 | }, 94 | ], 95 | stateMutability: "payable", 96 | type: "constructor", 97 | }, 98 | { 99 | anonymous: false, 100 | inputs: [ 101 | { 102 | indexed: true, 103 | internalType: "address", 104 | name: "owner", 105 | type: "address", 106 | }, 107 | { 108 | indexed: true, 109 | internalType: "address", 110 | name: "spender", 111 | type: "address", 112 | }, 113 | { 114 | indexed: false, 115 | internalType: "uint256", 116 | name: "value", 117 | type: "uint256", 118 | }, 119 | ], 120 | name: "Approval", 121 | type: "event", 122 | }, 123 | { 124 | anonymous: false, 125 | inputs: [ 126 | { 127 | indexed: true, 128 | internalType: "address", 129 | name: "from", 130 | type: "address", 131 | }, 132 | { 133 | indexed: true, 134 | internalType: "address", 135 | name: "to", 136 | type: "address", 137 | }, 138 | { 139 | indexed: false, 140 | internalType: "uint256", 141 | name: "value", 142 | type: "uint256", 143 | }, 144 | ], 145 | name: "Transfer", 146 | type: "event", 147 | }, 148 | { 149 | inputs: [ 150 | { 151 | internalType: "address", 152 | name: "owner", 153 | type: "address", 154 | }, 155 | { 156 | internalType: "address", 157 | name: "spender", 158 | type: "address", 159 | }, 160 | ], 161 | name: "allowance", 162 | outputs: [ 163 | { 164 | internalType: "uint256", 165 | name: "", 166 | type: "uint256", 167 | }, 168 | ], 169 | stateMutability: "view", 170 | type: "function", 171 | }, 172 | { 173 | inputs: [ 174 | { 175 | internalType: "address", 176 | name: "spender", 177 | type: "address", 178 | }, 179 | { 180 | internalType: "uint256", 181 | name: "amount", 182 | type: "uint256", 183 | }, 184 | ], 185 | name: "approve", 186 | outputs: [ 187 | { 188 | internalType: "bool", 189 | name: "", 190 | type: "bool", 191 | }, 192 | ], 193 | stateMutability: "nonpayable", 194 | type: "function", 195 | }, 196 | { 197 | inputs: [ 198 | { 199 | internalType: "address", 200 | name: "account", 201 | type: "address", 202 | }, 203 | ], 204 | name: "balanceOf", 205 | outputs: [ 206 | { 207 | internalType: "uint256", 208 | name: "", 209 | type: "uint256", 210 | }, 211 | ], 212 | stateMutability: "view", 213 | type: "function", 214 | }, 215 | { 216 | inputs: [], 217 | name: "decimals", 218 | outputs: [ 219 | { 220 | internalType: "uint8", 221 | name: "", 222 | type: "uint8", 223 | }, 224 | ], 225 | stateMutability: "view", 226 | type: "function", 227 | }, 228 | { 229 | inputs: [ 230 | { 231 | internalType: "address", 232 | name: "spender", 233 | type: "address", 234 | }, 235 | { 236 | internalType: "uint256", 237 | name: "subtractedValue", 238 | type: "uint256", 239 | }, 240 | ], 241 | name: "decreaseAllowance", 242 | outputs: [ 243 | { 244 | internalType: "bool", 245 | name: "", 246 | type: "bool", 247 | }, 248 | ], 249 | stateMutability: "nonpayable", 250 | type: "function", 251 | }, 252 | { 253 | inputs: [ 254 | { 255 | internalType: "address", 256 | name: "spender", 257 | type: "address", 258 | }, 259 | { 260 | internalType: "uint256", 261 | name: "addedValue", 262 | type: "uint256", 263 | }, 264 | ], 265 | name: "increaseAllowance", 266 | outputs: [ 267 | { 268 | internalType: "bool", 269 | name: "", 270 | type: "bool", 271 | }, 272 | ], 273 | stateMutability: "nonpayable", 274 | type: "function", 275 | }, 276 | { 277 | inputs: [], 278 | name: "name", 279 | outputs: [ 280 | { 281 | internalType: "string", 282 | name: "", 283 | type: "string", 284 | }, 285 | ], 286 | stateMutability: "view", 287 | type: "function", 288 | }, 289 | { 290 | inputs: [], 291 | name: "symbol", 292 | outputs: [ 293 | { 294 | internalType: "string", 295 | name: "", 296 | type: "string", 297 | }, 298 | ], 299 | stateMutability: "view", 300 | type: "function", 301 | }, 302 | { 303 | inputs: [], 304 | name: "totalSupply", 305 | outputs: [ 306 | { 307 | internalType: "uint256", 308 | name: "", 309 | type: "uint256", 310 | }, 311 | ], 312 | stateMutability: "view", 313 | type: "function", 314 | }, 315 | { 316 | inputs: [ 317 | { 318 | internalType: "address", 319 | name: "to", 320 | type: "address", 321 | }, 322 | { 323 | internalType: "uint256", 324 | name: "amount", 325 | type: "uint256", 326 | }, 327 | ], 328 | name: "transfer", 329 | outputs: [ 330 | { 331 | internalType: "bool", 332 | name: "", 333 | type: "bool", 334 | }, 335 | ], 336 | stateMutability: "nonpayable", 337 | type: "function", 338 | }, 339 | { 340 | inputs: [ 341 | { 342 | internalType: "address", 343 | name: "from", 344 | type: "address", 345 | }, 346 | { 347 | internalType: "address", 348 | name: "to", 349 | type: "address", 350 | }, 351 | { 352 | internalType: "uint256", 353 | name: "amount", 354 | type: "uint256", 355 | }, 356 | ], 357 | name: "transferFrom", 358 | outputs: [ 359 | { 360 | internalType: "bool", 361 | name: "", 362 | type: "bool", 363 | }, 364 | ], 365 | stateMutability: "nonpayable", 366 | type: "function", 367 | }, 368 | ]; 369 | 370 | export const Permit2Abi = [ 371 | "function approve(address token, address spender, uint160 amount, uint48 expiration) external", 372 | "function allowance(address owner, address token, address spender) external view returns (uint160, uint48, uint48)", 373 | ] as const; 374 | -------------------------------------------------------------------------------- /agentkit-core/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module agentkit-core 3 | * Order List of files 4 | * constants 5 | * services 6 | * agentkit 7 | * actions 8 | * langchain 9 | */ 10 | export * from "./actions"; 11 | export * from "./agentkit"; 12 | export * from "./langchain"; 13 | -------------------------------------------------------------------------------- /agentkit-core/src/langchain.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StructuredToolInterface, 3 | BaseToolkit as Toolkit, 4 | StructuredTool, 5 | } from "@langchain/core/tools"; 6 | import { AGENTKIT_ACTIONS } from "./actions"; 7 | import { Agentkit, AgentkitAction } from "./agentkit"; 8 | import { z } from "zod"; 9 | 10 | /** 11 | * 0xgasless Agentkit Toolkit. 12 | * 13 | * Security Note: This toolkit contains tools that can perform gasless 14 | * transactions on supported EVM chains using account abstraction. 15 | * Tools can read and modify blockchain state through operations like 16 | * token transfers, swaps, and smart contract deployments. 17 | * 18 | * Setup: 19 | * You will need to configure the following: 20 | * ```typescript 21 | * const config = { 22 | * chainID: 8453, // Base chain ID 23 | * apiKey: "your-0xgasless-api-key", 24 | * privateKey: "0x..." // or mnemonicPhrase: "..." 25 | * }; 26 | * const agentkit = await Agentkit.configureWithWallet(config); 27 | * const toolkit = new AgentkitToolkit(agentkit); 28 | * const tools = toolkit.getTools(); 29 | * ``` 30 | * 31 | * Important: Once configured, the wallet is ready to use with all tools. 32 | * No additional wallet setup or private key generation is needed when using the tools. 33 | * 34 | * Available tools include: 35 | * - get_balance: Check ETH and token balances (uses the already configured wallet) 36 | * - smart_transfer: Transfer tokens gaslessly 37 | * - swap: Perform token swaps without gas fees 38 | * - deploy_token: Deploy new ERC20 tokens 39 | * 40 | * Supported Networks: 41 | * - Base (8453) 42 | * - Fantom (250) 43 | * - Moonbeam (1284) 44 | * - Metis (1088) 45 | * - Avalanche (43114) 46 | * - BSC (56) 47 | */ 48 | export class AgentkitToolkit extends Toolkit { 49 | tools: StructuredToolInterface[]; 50 | 51 | /** 52 | * Creates a new 0xgasless Toolkit instance 53 | * 54 | * @param agentkit - 0xgasless agentkit instance 55 | */ 56 | constructor(agentkit: Agentkit) { 57 | super(); 58 | this.tools = AGENTKIT_ACTIONS.map(action => new AgentkitTool(action, agentkit)); 59 | } 60 | 61 | getTools(): StructuredToolInterface[] { 62 | return this.tools; 63 | } 64 | } 65 | 66 | // biome-ignore lint/suspicious/noExplicitAny: 67 | type ActionSchemaAny = z.ZodObject; 68 | 69 | /** 70 | * This tool allows agents to interact with the 0xgasless library and control an MPC Wallet onchain. 71 | * 72 | * To use this tool, you must first set as environment variables: 73 | * ```bash 74 | * Required: 75 | * export 0xGASLESS_API_KEY="your-0xgasless-api-key" 76 | * export 0xGASLESS_CHAIN_ID="your-0xgasless-chain-id" 77 | * export 0xGASLESS_PRIVATE_KEY="your-0xgasless-private-key" 78 | * 79 | * Optional: 80 | * export 0xGASLESS_MNEMONIC_PHRASE="your-0xgasless-mnemonic-phrase" 81 | * export 0xGASLESS_RPC_URL="your-0xgasless-rpc-url" 82 | * ``` 83 | */ 84 | 85 | export class AgentkitTool extends StructuredTool { 86 | /** 87 | * Schema definition for the tool's input 88 | */ 89 | public schema: TActionSchema; 90 | 91 | /** 92 | * The name of the tool 93 | */ 94 | public name: string; 95 | 96 | /** 97 | * The description of the tool 98 | */ 99 | public description: string; 100 | 101 | /** 102 | * The Agentkit instance 103 | */ 104 | private agentkit: Agentkit; 105 | 106 | /** 107 | * The Agentkit Action 108 | */ 109 | private action: AgentkitAction; 110 | 111 | /** 112 | * Constructor for the Agentkit Tool class 113 | * 114 | * @param action - The Agentkit action to execute 115 | * @param agentkit - The Agentkit wrapper to use 116 | */ 117 | constructor(action: AgentkitAction, agentkit: Agentkit) { 118 | super(); 119 | this.action = action; 120 | this.agentkit = agentkit; 121 | this.name = action.name; 122 | this.description = action.description; 123 | this.schema = action.argsSchema; 124 | } 125 | 126 | /** 127 | * Executes the Agentkit action with the provided input 128 | * 129 | * @param input - An object containing either instructions or schema-validated arguments 130 | * @returns A promise that resolves to the result of the Agentkit action 131 | * @throws {Error} If the Agentkit action fails 132 | */ 133 | protected async _call( 134 | input: z.infer & Record, 135 | ): Promise { 136 | try { 137 | // biome-ignore lint/suspicious/noExplicitAny: 138 | let args: any; 139 | 140 | // If we have a schema, try to validate against it 141 | if (this.schema) { 142 | try { 143 | const validatedInput = this.schema.parse(input); 144 | args = validatedInput; 145 | } catch (error) { 146 | // If schema validation fails, fall back to instructions-only mode 147 | args = input; 148 | console.error(`Error validating input for ${this.name}: ${error}`); 149 | } 150 | } 151 | // No schema, use instructions mode 152 | else { 153 | args = input; 154 | } 155 | 156 | return await this.agentkit.run(this.action, args); 157 | } catch (error: unknown) { 158 | if (error instanceof Error) { 159 | return `Error executing ${this.name}: ${error.message}`; 160 | } 161 | return `Error executing ${this.name}: Unknown error occurred`; 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /agentkit-core/src/services.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ZeroXgaslessSmartAccount, 3 | Transaction, 4 | PaymasterMode, 5 | BalancePayload, 6 | UserOpResponse, 7 | UserOpReceipt, 8 | } from "@0xgasless/smart-account"; 9 | import { TokenABI, tokenMappings } from "./constants"; 10 | import { generatePrivateKey } from "viem/accounts"; 11 | import { encodeFunctionData, getContract, parseUnits } from "viem"; 12 | import { TransactionResponse, TransactionStatus, TokenDetails } from "./types"; 13 | 14 | const DEFAULT_WAIT_INTERVAL = 5000; // 5 seconds 15 | const DEFAULT_MAX_DURATION = 30000; // 30 seconds 16 | 17 | export function createWallet() { 18 | const wallet = generatePrivateKey(); 19 | return wallet; 20 | } 21 | 22 | /** 23 | * Sends a transaction without waiting for confirmation 24 | * @param wallet The smart account to send the transaction from 25 | * @param tx The transaction to send 26 | * @returns The user operation response or false if the request failed 27 | */ 28 | export async function sendTransaction( 29 | wallet: ZeroXgaslessSmartAccount, 30 | tx: Transaction, 31 | ): Promise { 32 | try { 33 | const request = await wallet.sendTransaction(tx, { 34 | paymasterServiceData: { 35 | mode: PaymasterMode.SPONSORED, 36 | }, 37 | }); 38 | 39 | if (request.error) { 40 | return { 41 | success: false, 42 | error: request.error, 43 | }; 44 | } 45 | 46 | const receipt = await request.wait(); 47 | 48 | if (receipt.reason) { 49 | return { 50 | success: false, 51 | error: receipt.reason, 52 | }; 53 | } 54 | if (receipt.success && receipt.receipt?.transactionHash) { 55 | return { 56 | success: true, 57 | txHash: receipt.receipt?.transactionHash, 58 | message: `Transaction confirmed!\nTx Hash: ${receipt.receipt?.transactionHash}\n\n.`, 59 | receipt, 60 | }; 61 | } 62 | return { 63 | success: false, 64 | error: `Transaction failed: ${receipt.reason}`, 65 | }; 66 | } catch (error) { 67 | return { 68 | success: false, 69 | error: `Transaction Error: ${error}`, 70 | }; 71 | } 72 | } 73 | 74 | /** 75 | * Waits for a transaction to be confirmed and returns the status 76 | * @param wallet The smart account to use for checking status 77 | * @param userOpHashOrResponse The user operation hash or response object 78 | * @param options Optional configuration for waiting 79 | * @returns The transaction status 80 | */ 81 | export async function waitForTransaction( 82 | wallet: ZeroXgaslessSmartAccount, 83 | userOpHashOrResponse: string | UserOpResponse, 84 | options: { 85 | confirmations?: number; 86 | maxDuration?: number; 87 | interval?: number; 88 | } = {}, 89 | ): Promise { 90 | const { 91 | confirmations = 1, 92 | maxDuration = DEFAULT_MAX_DURATION, 93 | interval = DEFAULT_WAIT_INTERVAL, 94 | } = options; 95 | 96 | // Extract userOpHash from response if needed 97 | const userOpHash = 98 | typeof userOpHashOrResponse === "string" 99 | ? userOpHashOrResponse 100 | : userOpHashOrResponse.userOpHash; 101 | 102 | let totalDuration = 0; 103 | 104 | return new Promise(resolve => { 105 | const intervalId = setInterval(async () => { 106 | try { 107 | // Get the receipt from bundler 108 | const bundlerUrl = wallet.bundler?.getBundlerUrl(); 109 | if (!bundlerUrl) { 110 | throw new Error("Bundler URL not found"); 111 | } 112 | const response = await fetch(bundlerUrl, { 113 | method: "POST", 114 | headers: { 115 | "Content-Type": "application/json", 116 | }, 117 | body: JSON.stringify({ 118 | method: "eth_getUserOperationReceipt", 119 | params: [userOpHash], 120 | id: Date.now(), 121 | jsonrpc: "2.0", 122 | }), 123 | }); 124 | 125 | if (!response.ok) { 126 | throw new Error(`Failed to fetch receipt: ${response.statusText}`); 127 | } 128 | 129 | const data = await response.json(); 130 | const receipt = data.result as UserOpReceipt; 131 | 132 | if (receipt?.receipt?.blockNumber) { 133 | if (confirmations > 1) { 134 | // Check block confirmations if required 135 | const latestBlock = await wallet.rpcProvider.getBlockNumber(); 136 | const confirmedBlocks = Number(latestBlock) - receipt.receipt.blockNumber; 137 | 138 | if (confirmedBlocks >= confirmations) { 139 | clearInterval(intervalId); 140 | resolve({ 141 | status: "confirmed", 142 | receipt, 143 | blockNumber: receipt.receipt.blockNumber, 144 | blockConfirmations: confirmedBlocks, 145 | }); 146 | return; 147 | } 148 | } else { 149 | clearInterval(intervalId); 150 | resolve({ 151 | status: "confirmed", 152 | receipt, 153 | blockNumber: receipt.receipt.blockNumber, 154 | blockConfirmations: 1, 155 | }); 156 | return; 157 | } 158 | } 159 | 160 | // Update duration and check timeout 161 | totalDuration += interval; 162 | if (totalDuration >= maxDuration) { 163 | clearInterval(intervalId); 164 | resolve({ 165 | status: "pending", 166 | error: `Exceeded maximum duration (${maxDuration / 1000} sec) waiting for transaction`, 167 | }); 168 | } 169 | } catch (error) { 170 | clearInterval(intervalId); 171 | resolve({ 172 | status: "failed", 173 | error: error instanceof Error ? error.message : "Unknown error occurred", 174 | }); 175 | } 176 | }, interval); 177 | }); 178 | } 179 | 180 | export async function getDecimals( 181 | wallet: ZeroXgaslessSmartAccount, 182 | tokenAddress: string, 183 | ): Promise { 184 | const decimals = (await wallet.rpcProvider.readContract({ 185 | abi: TokenABI, 186 | address: tokenAddress as `0x${string}`, 187 | functionName: "decimals", 188 | })) as bigint; 189 | if (!decimals || decimals === BigInt(0)) { 190 | return false; 191 | } 192 | return decimals; 193 | } 194 | 195 | export async function getWalletBalance( 196 | wallet: ZeroXgaslessSmartAccount, 197 | tokenAddress?: `0x${string}`[], 198 | ): Promise { 199 | const balance = await wallet.getBalances(tokenAddress); 200 | if (!balance) { 201 | return false; 202 | } 203 | return balance; 204 | } 205 | 206 | export async function fetchTokenDetails( 207 | wallet: ZeroXgaslessSmartAccount, 208 | tokenAddress: string, 209 | ): Promise { 210 | const tokenContract = getContract({ 211 | abi: TokenABI, 212 | address: tokenAddress as `0x${string}`, 213 | client: wallet.rpcProvider, 214 | }); 215 | const name = await tokenContract.read.name(); 216 | const symbol = await tokenContract.read.symbol(); 217 | const decimals = await tokenContract.read.decimals(); 218 | if (!name || !symbol || !decimals) { 219 | return false; 220 | } 221 | return { 222 | name, 223 | symbol, 224 | decimals, 225 | address: tokenAddress as `0x${string}`, 226 | chainId: wallet.rpcProvider.chain?.id ?? 0, 227 | } as TokenDetails; 228 | } 229 | 230 | /** 231 | * Resolves token symbols to their contract addresses based on the current chain 232 | * 233 | * @param wallet - The smart account to get chain information from 234 | * @param symbol - Token symbol to resolve 235 | * @returns Token address or null if not found 236 | */ 237 | export async function resolveTokenSymbol( 238 | wallet: ZeroXgaslessSmartAccount, 239 | symbol: string, 240 | ): Promise<`0x${string}` | null> { 241 | const chainId = wallet.rpcProvider.chain?.id; 242 | if (!chainId || !tokenMappings[chainId]) { 243 | console.warn(`Chain ID ${chainId} not found in token mappings`); 244 | return null; 245 | } 246 | 247 | const chainTokens = tokenMappings[chainId]; 248 | const normalizedSymbol = symbol.toUpperCase(); 249 | 250 | if (chainTokens[normalizedSymbol]) { 251 | return chainTokens[normalizedSymbol]; 252 | } 253 | 254 | // Special case for native token (ETH, AVAX, BNB, etc.) 255 | if ( 256 | normalizedSymbol === "ETH" || 257 | normalizedSymbol === "AVAX" || 258 | normalizedSymbol === "BNB" || 259 | normalizedSymbol === "FTM" || 260 | normalizedSymbol === "METIS" || 261 | normalizedSymbol === "GLMR" 262 | ) { 263 | return "0x0000000000000000000000000000000000000000"; 264 | } 265 | 266 | console.warn(`Token symbol ${normalizedSymbol} not found for chain ID ${chainId}`); 267 | return null; 268 | } 269 | 270 | /** 271 | * Format amount with proper decimals for the API 272 | * 273 | * @param wallet - The smart account to use for querying 274 | * @param tokenAddress - The token address 275 | * @param amount - The human-readable amount (e.g., "0.001") 276 | * @returns The amount formatted with proper decimals 277 | */ 278 | export async function formatTokenAmount( 279 | wallet: ZeroXgaslessSmartAccount, 280 | tokenAddress: `0x${string}`, 281 | amount: string, 282 | ): Promise { 283 | try { 284 | // For native token (address 0x0) 285 | if (tokenAddress === "0x0000000000000000000000000000000000000000") { 286 | return parseUnits(amount, 18).toString(); 287 | } 288 | 289 | // Get token decimals 290 | const decimals = await getDecimals(wallet, tokenAddress); 291 | if (!decimals) { 292 | throw new Error(`Could not get decimals for token ${tokenAddress}`); 293 | } 294 | 295 | // Parse the amount with proper decimals 296 | return parseUnits(amount, Number(decimals)).toString(); 297 | } catch (error) { 298 | console.error("Error formatting token amount:", error); 299 | // Fallback to assuming 18 decimals if we can't get the actual decimals 300 | return parseUnits(amount, 18).toString(); 301 | } 302 | } 303 | 304 | /** 305 | * Check token allowance for a spender 306 | * 307 | * @param wallet - The smart account to use 308 | * @param tokenAddress - The token address to check allowance for 309 | * @param spenderAddress - The address that needs allowance 310 | * @returns Current allowance as bigint or false if failed 311 | */ 312 | export async function checkTokenAllowance( 313 | wallet: ZeroXgaslessSmartAccount, 314 | tokenAddress: `0x${string}`, 315 | spenderAddress: `0x${string}`, 316 | ): Promise { 317 | try { 318 | // Skip for native token 319 | if ( 320 | tokenAddress === "0x0000000000000000000000000000000000000000" || 321 | tokenAddress === "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" 322 | ) { 323 | return BigInt(0); 324 | } 325 | const userAddress = await wallet.getAddress(); 326 | const allowance = (await wallet.rpcProvider.readContract({ 327 | abi: TokenABI, 328 | address: tokenAddress, 329 | functionName: "allowance", 330 | args: [userAddress, spenderAddress], 331 | })) as bigint; 332 | 333 | return allowance; 334 | } catch (error) { 335 | console.error("Error checking token allowance:", error); 336 | return BigInt(0); 337 | } 338 | } 339 | 340 | /** 341 | * Approve token spending for a spender 342 | * 343 | * @param wallet - The smart account to use 344 | * @param tokenAddress - The token address to approve 345 | * @param spenderAddress - The address to approve spending for 346 | * @param amount - The amount to approve (or max uint256 for unlimited) 347 | * @returns Transaction response 348 | */ 349 | export async function approveToken( 350 | wallet: ZeroXgaslessSmartAccount, 351 | tokenAddress: `0x${string}`, 352 | spenderAddress: `0x${string}`, 353 | amount: bigint, 354 | ): Promise { 355 | try { 356 | // Skip approval for native token 357 | if ( 358 | tokenAddress === "0x0000000000000000000000000000000000000000" || 359 | tokenAddress === "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" 360 | ) { 361 | return { success: true, userOpHash: "" }; 362 | } 363 | 364 | // Create approval transaction 365 | const tx = { 366 | to: tokenAddress, 367 | data: encodeFunctionData({ 368 | abi: TokenABI, 369 | functionName: "approve", 370 | args: [spenderAddress, amount], 371 | }), 372 | value: BigInt(0), 373 | }; 374 | 375 | // Send approval transaction 376 | return await sendTransaction(wallet, tx); 377 | } catch (error) { 378 | console.error("Error approving token:", error); 379 | return { 380 | success: false, 381 | userOpHash: "", 382 | error: error instanceof Error ? error.message : String(error), 383 | }; 384 | } 385 | } 386 | 387 | /** 388 | * Check and approve token allowance if needed 389 | * 390 | * @param wallet - The smart account to use 391 | * @param tokenAddress - The token address to check allowance for 392 | * @param spenderAddress - The address that needs allowance 393 | * @param amount - The amount that needs to be approved 394 | * @param approveMax - Whether to approve maximum amount 395 | * @returns Success status and any error message 396 | */ 397 | export async function checkAndApproveTokenAllowance( 398 | wallet: ZeroXgaslessSmartAccount, 399 | tokenAddress: `0x${string}`, 400 | spenderAddress: `0x${string}`, 401 | amount: bigint, 402 | approveMax: boolean = false, 403 | ): Promise { 404 | if ( 405 | tokenAddress === "0x0000000000000000000000000000000000000000" || 406 | tokenAddress === "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" 407 | ) { 408 | return { success: true, userOpHash: "" }; 409 | } 410 | const currentAllowance = await checkTokenAllowance(wallet, tokenAddress, spenderAddress); 411 | console.log(`Current allowance: ${currentAllowance}, Required: ${amount}`); 412 | if (currentAllowance >= amount && !approveMax) { 413 | console.log("Allowance is sufficient, no need to approve"); 414 | return { success: true, userOpHash: "" }; 415 | } 416 | const maxUint256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); 417 | const approvalAmount = approveMax ? maxUint256 : amount; 418 | return await approveToken(wallet, tokenAddress, spenderAddress, approvalAmount); 419 | } 420 | -------------------------------------------------------------------------------- /agentkit-core/src/types.ts: -------------------------------------------------------------------------------- 1 | import { UserOpReceipt } from "@0xgasless/smart-account"; 2 | 3 | export type TransactionResponse = { 4 | success: boolean; 5 | userOpHash?: string; 6 | txHash?: string; 7 | error?: string | { message: string; code: number }; 8 | message?: string; 9 | receipt?: UserOpReceipt; 10 | }; 11 | 12 | export type TokenDetails = { 13 | name: string; 14 | symbol: string; 15 | decimals: bigint; 16 | address: `0x${string}`; 17 | chainId: number; 18 | }; 19 | 20 | export type TransactionStatus = { 21 | status: "pending" | "confirmed" | "failed"; 22 | receipt?: UserOpReceipt; 23 | error?: string; 24 | blockNumber?: number; 25 | blockConfirmations?: number; 26 | }; 27 | -------------------------------------------------------------------------------- /agentkit-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src", 6 | "paths": { 7 | "~/*": ["src/*"] 8 | }, 9 | "typeRoots": ["./node_modules/@types"], 10 | "baseUrl": ".", 11 | "declaration": true, 12 | "declarationDir": "./dist/types" 13 | }, 14 | "include": ["src/**/*.ts"], 15 | "exclude": ["src/tests"] 16 | } 17 | -------------------------------------------------------------------------------- /agentkit-demo/.env.sample: -------------------------------------------------------------------------------- 1 | OPENROUTER_API_KEY= 2 | PRIVATE_KEY= 3 | CHAIN_ID= 4 | RPC_URL= 5 | API_KEY= -------------------------------------------------------------------------------- /agentkit-demo/.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | 3 | **/.python-version 4 | 5 | # IDE 6 | .idea/* 7 | .vscode/* 8 | 9 | ## Emacs 10 | *~ 11 | \#*\# 12 | .\#* 13 | **/.projectile 14 | 15 | # Python cache files 16 | **/__pycache__/ 17 | 18 | # Wallet data 19 | **/wallet_data.txt 20 | 21 | # Tools 22 | **/.pytest_cache 23 | 24 | **/.ruff_cache 25 | 26 | **/.mypy_cache 27 | 28 | # Build 29 | **/_build/ 30 | 31 | **/build/ 32 | 33 | **/dist/ 34 | 35 | # Virtual Environments 36 | **/venv/ 37 | **/.venv/ 38 | 39 | # Environment Configurations 40 | **/env/ 41 | **/.env/ 42 | **/.env 43 | **/.env.local/ 44 | **/.env.test/ 45 | 46 | # Logs 47 | logs 48 | *.log 49 | npm-debug.log* 50 | yarn-debug.log* 51 | yarn-error.log* 52 | lerna-debug.log* 53 | .pnpm-debug.log* 54 | 55 | # IDE 56 | .idea/* 57 | .vscode/* 58 | 59 | ## Emacs 60 | *~ 61 | \#*\# 62 | .\#* 63 | **/.projectile 64 | 65 | # Build outputs 66 | dist 67 | out 68 | .next 69 | .nuxt 70 | build/Release 71 | .turbo/ 72 | 73 | # Coverage 74 | coverage 75 | **/.coverage 76 | *.lcov 77 | .nyc_output 78 | lib-cov 79 | 80 | # Dependencies 81 | node_modules/ 82 | jspm_packages/ 83 | bower_components 84 | web_modules/ 85 | .pnp.* 86 | 87 | # Cache 88 | .npm 89 | .eslintcache 90 | .stylelintcache 91 | .parcel-cache 92 | .cache 93 | .temp 94 | .rpt2_cache/ 95 | .rts2_cache_cjs/ 96 | .rts2_cache_es/ 97 | .rts2_cache_umd/ 98 | 99 | # IDE 100 | .vscode 101 | .vscode-test 102 | .idea 103 | .tern-port 104 | 105 | # Environment Configurations 106 | **/env/ 107 | **/.env/ 108 | **/.env 109 | **/.env.local/ 110 | **/.env.test/ 111 | 112 | # Runtime data 113 | pids 114 | *.pid 115 | *.seed 116 | *.pid.lock 117 | *.tsbuildinfo 118 | .node_repl_history 119 | 120 | # Yarn v2 121 | .yarn/cache 122 | .yarn/unplugged 123 | .yarn/build-state.yml 124 | .yarn/install-state.gz 125 | 126 | # Misc 127 | .DS_Store 128 | **/*_local* 129 | api.json 130 | 131 | # JSDoc 132 | docs/ -------------------------------------------------------------------------------- /agentkit-demo/README.md: -------------------------------------------------------------------------------- 1 | # 0xgasless AgentKit Langchain Extension Examples - Chatbot Typescript 2 | 3 | This example demonstrates an agent setup as a terminal style chatbot with access to the full set of 0xgasless AgentKit actions. 4 | 5 | ## Ask the chatbot to engage in the Web3 ecosystem! 6 | 7 | - "Transfer a portion of your ETH to john2879.base.eth" 8 | - "Deploy an NFT that will go super viral!" 9 | - "Choose a name for yourself and register a Basename for your wallet" 10 | - "Deploy an ERC-20 token with total supply 1 billion" 11 | 12 | ## Requirements 13 | 14 | - [Bun](https://bun.sh/docs/installation) 15 | - [0xgasless API Key](https://dashboard.0xgasless.com/) 16 | - [OpenAI API Key](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) 17 | 18 | ## Installation 19 | 20 | ```bash 21 | bun install 22 | ``` 23 | 24 | ## Run the Chatbot 25 | 26 | ### Set ENV Vars 27 | 32 | 33 | - Ensure the following ENV Vars are set: 34 | - "0XGASLESS_API_KEY" 35 | - "OPENAI_API_KEY" 36 | - "OPENROUTER_API_KEY" 37 | - "PRIVATE_KEY" 38 | - "CHAIN_ID" 39 | - "RPC_URL" 40 | - "API_KEY" 41 | 42 | ```bash 43 | bun run index.ts 44 | ``` 45 | 46 | ## License 47 | 48 | MIT -------------------------------------------------------------------------------- /agentkit-demo/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xgasless/agentkit/d332905cd7cc6d8653de543370305ccdb83425ba/agentkit-demo/bun.lockb -------------------------------------------------------------------------------- /agentkit-demo/index.ts: -------------------------------------------------------------------------------- 1 | import { Agentkit, AgentkitToolkit } from "@0xgasless/agentkit"; 2 | // import { Agentkit, AgentkitToolkit } from "@0xgas/agentkit"; 3 | import { HumanMessage } from "@langchain/core/messages"; 4 | import { StructuredToolInterface } from "@langchain/core/tools"; 5 | import { MemorySaver } from "@langchain/langgraph"; 6 | import { createReactAgent } from "@langchain/langgraph/prebuilt"; 7 | import { ChatOpenAI } from "@langchain/openai"; 8 | import * as dotenv from "dotenv"; 9 | import * as readline from "readline"; 10 | 11 | dotenv.config(); 12 | 13 | function validateEnvironment(): void { 14 | const missingVars: string[] = []; 15 | 16 | const requiredVars = ["OPENROUTER_API_KEY", "PRIVATE_KEY", "RPC_URL", "API_KEY", "CHAIN_ID"]; 17 | 18 | requiredVars.forEach(varName => { 19 | if (!process.env[varName]) { 20 | missingVars.push(varName); 21 | } 22 | }); 23 | 24 | if (missingVars.length > 0) { 25 | console.error("Error: Required environment variables are not set"); 26 | missingVars.forEach(varName => { 27 | console.error(`${varName}=your_${varName.toLowerCase()}_here`); 28 | }); 29 | process.exit(1); 30 | } 31 | 32 | if (!process.env.CHAIN_ID) { 33 | console.warn("Warning: CHAIN_ID not set, defaulting to base-sepolia"); 34 | } 35 | } 36 | 37 | validateEnvironment(); 38 | 39 | async function initializeAgent() { 40 | try { 41 | const llm = new ChatOpenAI({ 42 | model: "gpt-4o", 43 | openAIApiKey: process.env.OPENROUTER_API_KEY, 44 | configuration: { 45 | baseURL: "https://openrouter.ai/api/v1", 46 | }, 47 | }); 48 | 49 | // Initialize 0xGasless AgentKit 50 | const agentkit = await Agentkit.configureWithWallet({ 51 | privateKey: process.env.PRIVATE_KEY as `0x${string}`, 52 | rpcUrl: process.env.RPC_URL, 53 | apiKey: process.env.API_KEY as string, 54 | chainID: Number(process.env.CHAIN_ID) || 8453, // Base Sepolia 55 | }); 56 | 57 | // Initialize AgentKit Toolkit and get tools 58 | const agentkitToolkit = new AgentkitToolkit(agentkit); 59 | const tools = agentkitToolkit.getTools() as StructuredToolInterface[]; 60 | 61 | const memory = new MemorySaver(); 62 | const agentConfig = { configurable: { thread_id: "0xGasless AgentKit Chatbot Example!" } }; 63 | 64 | const agent = createReactAgent({ 65 | llm, 66 | tools, 67 | checkpointSaver: memory, 68 | messageModifier: ` 69 | You are a helpful agent that can interact with EVM chains using 0xGasless smart accounts. You can perform 70 | gasless transactions using the account abstraction wallet. 71 | 72 | IMPORTANT: The wallet is already configured with the SDK. DO NOT generate or mention private keys when using any tools. 73 | 74 | You can check balances of ETH and any ERC20 token by symbol or contract address. When checking balances: 75 | - When a user asks to check or get balances, use the get_balance tool immediately without asking for confirmation 76 | - If the user doesn't specify tokens, just call the tool with no parameters to get the ETH balance 77 | - If the user mentions specific tokens by name (like "USDC" or "USDT"), use the tokenSymbols parameter 78 | - Only use tokenAddresses parameter if the user specifically provides contract addresses 79 | 80 | If someone asks you to do something you can't do with your currently available tools, you must say so. 81 | Be concise and helpful with your responses. 82 | `, 83 | }); 84 | 85 | return { agent, config: agentConfig }; 86 | } catch (error) { 87 | console.error("Failed to initialize agent:", error); 88 | throw error; 89 | } 90 | } 91 | 92 | // For runAutonomousMode, runChatMode, chooseMode and main functions, reference: 93 | 94 | /** 95 | * Run the agent autonomously with specified intervals 96 | * 97 | * @param agent - The agent executor 98 | * @param config - Agent configuration 99 | * @param interval - Time interval between actions in seconds 100 | */ 101 | 102 | // async function runAutonomousMode(agent: any, config: any, interval = 10) { 103 | // console.log("Starting autonomous mode..."); 104 | 105 | // // eslint-disable-next-line no-constant-condition 106 | // while (true) { 107 | // try { 108 | // const thought = 109 | // "Be creative and do something interesting on the blockchain. " + 110 | // "Choose an action or set of actions and execute it that highlights your abilities."; 111 | 112 | // const stream = await agent.stream({ messages: [new HumanMessage(thought)] }, config); 113 | 114 | // for await (const chunk of stream) { 115 | // if ("agent" in chunk) { 116 | // console.log(chunk.agent.messages[0].content); 117 | // } else if ("tools" in chunk) { 118 | // console.log(chunk.tools.messages[0].content); 119 | // } 120 | // console.log("-------------------"); 121 | // } 122 | 123 | // await new Promise(resolve => setTimeout(resolve, interval * 1000)); 124 | // } catch (error) { 125 | // if (error instanceof Error) { 126 | // console.error("Error:", error.message); 127 | // } 128 | // process.exit(1); 129 | // } 130 | // } 131 | // } 132 | 133 | /** 134 | * Run the agent interactively based on user input 135 | * 136 | * @param agent - The agent executor 137 | * @param config - Agent configuration 138 | */ 139 | //biome-ignore lint/suspicious/noExplicitAny: 140 | async function runChatMode(agent: any, config: any) { 141 | console.log("Starting chat mode... Type 'exit' to end."); 142 | 143 | const rl = readline.createInterface({ 144 | input: process.stdin, 145 | output: process.stdout, 146 | }); 147 | 148 | const question = (prompt: string): Promise => 149 | new Promise(resolve => rl.question(prompt, resolve)); 150 | 151 | try { 152 | while (true) { 153 | const userInput = await question("\nPrompt: "); 154 | 155 | if (userInput.toLowerCase() === "exit") { 156 | break; 157 | } 158 | 159 | const stream = await agent.stream({ messages: [new HumanMessage(userInput)] }, config); 160 | 161 | for await (const chunk of stream) { 162 | if ("agent" in chunk) { 163 | console.log(chunk.agent.messages[0].content); 164 | } else if ("tools" in chunk) { 165 | console.log(chunk.tools.messages[0].content); 166 | } 167 | console.log("-------------------"); 168 | } 169 | } 170 | } catch (error) { 171 | if (error instanceof Error) { 172 | console.error("Error:", error.message); 173 | } 174 | process.exit(1); 175 | } finally { 176 | rl.close(); 177 | } 178 | } 179 | 180 | /** 181 | * Choose whether to run in autonomous or chat mode based on user input 182 | * 183 | * @returns Selected mode 184 | */ 185 | // async function chooseMode(): Promise<"chat" | "auto"> { 186 | // const rl = readline.createInterface({ 187 | // input: process.stdin, 188 | // output: process.stdout, 189 | // }); 190 | 191 | // const question = (prompt: string): Promise => 192 | // new Promise(resolve => rl.question(prompt, resolve)); 193 | 194 | // // eslint-disable-next-line no-constant-condition 195 | // while (true) { 196 | // console.log("\nAvailable modes:"); 197 | // console.log("1. chat - Interactive chat mode"); 198 | // console.log("2. auto - Autonomous action mode"); 199 | 200 | // const choice = (await question("\nChoose a mode (enter number or name): ")) 201 | // .toLowerCase() 202 | // .trim(); 203 | 204 | // if (choice === "1" || choice === "chat") { 205 | // rl.close(); 206 | // return "chat"; 207 | // } else if (choice === "2" || choice === "auto") { 208 | // rl.close(); 209 | // return "auto"; 210 | // } 211 | // console.log("Invalid choice. Please try again."); 212 | // } 213 | // } 214 | 215 | /** 216 | * Start the chatbot agent 217 | */ 218 | async function main() { 219 | try { 220 | const { agent, config } = await initializeAgent(); 221 | // const mode = await chooseMode(); 222 | 223 | await runChatMode(agent, config); 224 | // if (mode === "chat") { 225 | // } else { 226 | // await runAutonomousMode(agent, config); 227 | // } 228 | } catch (error) { 229 | if (error instanceof Error) { 230 | console.error("Error:", error.message); 231 | } 232 | process.exit(1); 233 | } 234 | } 235 | 236 | if (require.main === module) { 237 | console.log("Starting Agent..."); 238 | main().catch(error => { 239 | console.error("Fatal error:", error); 240 | process.exit(1); 241 | }); 242 | } 243 | -------------------------------------------------------------------------------- /agentkit-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@0xgasless/agentkit-example", 3 | "description": "0xgasless Agentkit Node.js SDK Chatbot Example", 4 | "version": "1.0.0", 5 | "author": { 6 | "name": "Permissionless Puter", 7 | "email": "puter@prmsnls.xyz", 8 | "url": "https://bento.me/puter" 9 | }, 10 | "repository": "https://github.com/0xgasless/agentkit", 11 | "license": "MIT", 12 | "scripts": { 13 | "lint": "biome lint --write .", 14 | "format": "biome format --write ." 15 | }, 16 | "dependencies": { 17 | "@0xgasless/agentkit": "link:@0xgasless/agentkit", 18 | "@0xgasless/smart-account": "^0.0.13", 19 | "@langchain/core": "^0.3.19", 20 | "@langchain/langgraph": "^0.2.21", 21 | "@langchain/openai": "^0.3.14", 22 | "axios": "^1.7.9", 23 | "dotenv": "^16.4.5", 24 | "merkletreejs": "^0.4.1", 25 | "tslib": "^2.8.1", 26 | "viem": "^2.22.17", 27 | "zod": "^3.22.4" 28 | }, 29 | "devDependencies": { 30 | "@biomejs/biome": "1.9.4" 31 | } 32 | } -------------------------------------------------------------------------------- /agentkit-demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "rootDir": ".", 6 | "strict": true, 7 | "resolveJsonModule": true, 8 | "esModuleInterop": true, 9 | "moduleResolution": "node", 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "declaration": true, 13 | "noImplicitAny": false, 14 | "removeComments": false, 15 | "preserveSymlinks": true, 16 | }, 17 | "include": [ 18 | "*.ts" 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "dist", 23 | "**/tests/**/**" 24 | ] 25 | } -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, 4 | "files": { "ignoreUnknown": false, "ignore": [] }, 5 | "formatter": { 6 | "enabled": true, 7 | "useEditorconfig": true, 8 | "formatWithErrors": false, 9 | "indentStyle": "space", 10 | "indentWidth": 2, 11 | "lineEnding": "lf", 12 | "lineWidth": 100, 13 | "attributePosition": "auto", 14 | "bracketSpacing": true, 15 | "ignore": [ 16 | "**/docs/", 17 | "**/dist/", 18 | "**/coverage/", 19 | "**/.github/", 20 | "src/client", 21 | "**/**/*.json", 22 | "**/*.md" 23 | ] 24 | }, 25 | "organizeImports": { "enabled": true }, 26 | "linter": { 27 | "enabled": true, 28 | "rules": { 29 | "recommended": false, 30 | "complexity": { 31 | "noBannedTypes": "error", 32 | "noExtraBooleanCast": "error", 33 | "noMultipleSpacesInRegularExpressionLiterals": "error", 34 | "noUselessCatch": "error", 35 | "noUselessTypeConstraint": "error", 36 | "noWith": "error" 37 | }, 38 | "correctness": { 39 | "noConstAssign": "error", 40 | "noConstantCondition": "error", 41 | "noEmptyCharacterClassInRegex": "error", 42 | "noEmptyPattern": "error", 43 | "noGlobalObjectCalls": "error", 44 | "noInnerDeclarations": "error", 45 | "noInvalidConstructorSuper": "error", 46 | "noNewSymbol": "error", 47 | "noNonoctalDecimalEscape": "error", 48 | "noPrecisionLoss": "error", 49 | "noSelfAssign": "error", 50 | "noSetterReturn": "error", 51 | "noSwitchDeclarations": "error", 52 | "noUndeclaredVariables": "error", 53 | "noUnreachable": "error", 54 | "noUnreachableSuper": "error", 55 | "noUnsafeFinally": "error", 56 | "noUnsafeOptionalChaining": "error", 57 | "noUnusedLabels": "error", 58 | "noUnusedVariables": "error", 59 | "useArrayLiterals": "off", 60 | "useIsNan": "error", 61 | "useValidForDirection": "error", 62 | "useYield": "error" 63 | }, 64 | "style": { 65 | "noNamespace": "error", 66 | "useAsConstAssertion": "error", 67 | "useBlockStatements": "off" 68 | }, 69 | "suspicious": { 70 | "noAsyncPromiseExecutor": "error", 71 | "noCatchAssign": "error", 72 | "noClassAssign": "error", 73 | "noCompareNegZero": "error", 74 | "noControlCharactersInRegex": "error", 75 | "noDebugger": "error", 76 | "noDuplicateCase": "error", 77 | "noDuplicateClassMembers": "error", 78 | "noDuplicateObjectKeys": "error", 79 | "noDuplicateParameters": "error", 80 | "noEmptyBlockStatements": "error", 81 | "noExplicitAny": "error", 82 | "noExtraNonNullAssertion": "error", 83 | "noFallthroughSwitchClause": "error", 84 | "noFunctionAssign": "error", 85 | "noGlobalAssign": "error", 86 | "noImportAssign": "error", 87 | "noMisleadingCharacterClass": "error", 88 | "noMisleadingInstantiator": "error", 89 | "noPrototypeBuiltins": "error", 90 | "noRedeclare": "error", 91 | "noShadowRestrictedNames": "error", 92 | "noSparseArray": "error", 93 | "noUnsafeDeclarationMerging": "error", 94 | "noUnsafeNegation": "error", 95 | "useGetterReturn": "error", 96 | "useValidTypeof": "error" 97 | } 98 | } 99 | }, 100 | "javascript": { 101 | "formatter": { 102 | "jsxQuoteStyle": "double", 103 | "quoteProperties": "asNeeded", 104 | "trailingCommas": "all", 105 | "semicolons": "always", 106 | "arrowParentheses": "asNeeded", 107 | "bracketSameLine": false, 108 | "quoteStyle": "double", 109 | "attributePosition": "auto", 110 | "bracketSpacing": true 111 | } 112 | }, 113 | "overrides": [ 114 | { 115 | "include": ["*.ts", "*.tsx", "*.mts", "*.cts"], 116 | "linter": { 117 | "rules": { 118 | "correctness": { 119 | "noConstAssign": "off", 120 | "noGlobalObjectCalls": "off", 121 | "noInvalidBuiltinInstantiation": "off", 122 | "noInvalidConstructorSuper": "off", 123 | "noNewSymbol": "off", 124 | "noSetterReturn": "off", 125 | "noUndeclaredVariables": "off", 126 | "noUnreachable": "off", 127 | "noUnreachableSuper": "off" 128 | }, 129 | "style": { "noArguments": "error", "noVar": "error", "useConst": "error" }, 130 | "suspicious": { 131 | "noDuplicateClassMembers": "off", 132 | "noDuplicateObjectKeys": "off", 133 | "noDuplicateParameters": "off", 134 | "noFunctionAssign": "off", 135 | "noImportAssign": "off", 136 | "noRedeclare": "off", 137 | "noUnsafeNegation": "off", 138 | "useGetterReturn": "off" 139 | } 140 | } 141 | } 142 | } 143 | ] 144 | } 145 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xgasless/agentkit/d332905cd7cc6d8653de543370305ccdb83425ba/bun.lockb -------------------------------------------------------------------------------- /jest.config.base.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "node", 4 | extensionsToTreatAsEsm: [".ts"], 5 | coveragePathIgnorePatterns: ["node_modules", "dist", "docs"], 6 | collectCoverage: true, 7 | collectCoverageFrom: ["./src/**"], 8 | coverageReporters: ["html"], 9 | verbose: true, 10 | maxWorkers: 1, 11 | coverageThreshold: { 12 | "./src/**": { 13 | branches: 77, 14 | functions: 85, 15 | statements: 85, 16 | lines: 85, 17 | }, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "0xgasless-agentkit-monorepo", 3 | "private": true, 4 | "workspaces": [ 5 | "agentkit-core" 6 | ], 7 | "scripts": { 8 | "build": "turbo run build", 9 | "test": "turbo run test", 10 | "lint": "turbo run lint", 11 | "clean": "turbo run clean", 12 | "docs": "turbo run docs", 13 | "dev": "turbo run dev", 14 | "format": "biome format --write \"**/*.{ts,js,cjs,json,md}\"", 15 | "lint:fix": "biome lint --write \"**/*.{ts,js,cjs,json,md}\"", 16 | "test:types": "turbo run test:types" 17 | }, 18 | "license": "Apache-2.0", 19 | "repository": "https://github.com/0xgasless/0xgasless-agentkit", 20 | "devDependencies": { 21 | "@biomejs/biome": "1.9.4", 22 | "@types/jest": "^29.5.14", 23 | "@types/node": "^20.12.11", 24 | "jest": "^29.7.0", 25 | "ts-jest": "^29.2.5", 26 | "turbo": "^2.3.3", 27 | "typedoc": "^0.27.2", 28 | "typescript": "^5.4.5" 29 | }, 30 | "packageManager": ">bun@1.1.19" 31 | } -------------------------------------------------------------------------------- /repo-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xgasless/agentkit/d332905cd7cc6d8653de543370305ccdb83425ba/repo-banner.png -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "outDir": "./dist", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "moduleResolution": "node", 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "declaration": true, 14 | "noImplicitAny": false, 15 | "removeComments": false 16 | }, 17 | "include": ["src/**/*.ts"], 18 | "exclude": ["node_modules", "dist", "**/tests/**/**"] 19 | } 20 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalDependencies": [".env"], 4 | "tasks": { 5 | "build": { 6 | "dependsOn": ["^build"], 7 | "outputs": ["dist/**"] 8 | }, 9 | "test": { 10 | "dependsOn": ["build"], 11 | "inputs": ["src/**/*.ts", "test/**/*.ts"] 12 | }, 13 | "lint": { 14 | "outputs": [] 15 | }, 16 | "lint:fix": { 17 | "cache": false 18 | }, 19 | "format": { 20 | "cache": false 21 | }, 22 | "format:check": { 23 | "cache": false 24 | }, 25 | "clean": { 26 | "cache": false 27 | }, 28 | "docs": { 29 | "outputs": ["docs/**"] 30 | }, 31 | "dev": { 32 | "cache": false, 33 | "persistent": true 34 | }, 35 | "test:types": { 36 | "dependsOn": ["build"] 37 | } 38 | } 39 | } 40 | --------------------------------------------------------------------------------