{"slug": "building-ai-agents-that-interact-with-blockchain-a-deep-technical-guide-using", "title": "Building AI Agents That Interact With Blockchain: A Deep Technical Guide Using LangChain", "summary": "A developer built a production-grade AI agent that interacts with blockchain using LangChain's agent framework, ethers.js, and custom tools. The agent can read on-chain data, interact with smart contracts, and execute DeFi operations. The guide provides step-by-step instructions for creating tools to fetch ETH balances, ERC-20 token balances, and call read-only contract functions.", "body_md": "Most tutorials on AI agents stop at chat interfaces and RAG pipelines. This one doesn't.\n\nThis guide walks through building a production-grade AI agent that can read on-chain data, interact with smart contracts, and execute DeFi operations — using LangChain's agent framework, ethers.js, and a set of custom tools you'll write from scratch.\n\nBy the end, you'll have an agent that can:\n\n```\nmkdir ai-blockchain-agent && cd ai-blockchain-agent\nnpm init -y\nnpm install langchain @langchain/openai @langchain/core ethers dotenv\nnpm install -D typescript ts-node @types/node hardhat\nnpx tsc --init\n```\n\n**.env:**\n\n```\nOPENAI_API_KEY=your_openai_key\nALCHEMY_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/your_key\nPRIVATE_KEY=your_wallet_private_key\njs\n// src/provider.ts\nimport { ethers } from \"ethers\";\nimport * as dotenv from \"dotenv\";\ndotenv.config();\n\nexport const provider = new ethers.JsonRpcProvider(process.env.ALCHEMY_RPC_URL);\n\nexport const signer = new ethers.Wallet(\n  process.env.PRIVATE_KEY!,\n  provider\n);\n```\n\nLangChain agents are only as powerful as their tools. Every on-chain operation becomes a tool the agent can call. Each tool needs a name, description (the LLM reads this to decide when to use it), and a func.\n\n``` js\n// src/tools/getBalance.ts\nimport { DynamicTool } from \"@langchain/core/tools\";\nimport { provider } from \"../provider\";\nimport { ethers } from \"ethers\";\n\nexport const getBalanceTool = new DynamicTool({\n  name: \"get_eth_balance\",\n  description:\n    \"Get the ETH balance of a wallet address. Input should be a valid Ethereum address.\",\n  func: async (address: string) => {\n    try {\n      const balance = await provider.getBalance(address);\n      return `Balance: ${ethers.formatEther(balance)} ETH`;\n    } catch (err) {\n      return `Error fetching balance: ${err}`;\n    }\n  },\n});\njs\n// src/tools/getTokenBalance.ts\nimport { DynamicStructuredTool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { ethers } from \"ethers\";\nimport { provider } from \"../provider\";\n\nconst ERC20_ABI = [\n  \"function balanceOf(address owner) view returns (uint256)\",\n  \"function decimals() view returns (uint8)\",\n  \"function symbol() view returns (string)\",\n];\n\nexport const getTokenBalanceTool = new DynamicStructuredTool({\n  name: \"get_token_balance\",\n  description:\n    \"Get the ERC-20 token balance for a wallet. Provide the token contract address and wallet address.\",\n  schema: z.object({\n    tokenAddress: z.string().describe(\"ERC-20 token contract address\"),\n    walletAddress: z.string().describe(\"Wallet address to check\"),\n  }),\n  func: async ({ tokenAddress, walletAddress }) => {\n    try {\n      const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);\n      const [balance, decimals, symbol] = await Promise.all([\n        contract.balanceOf(walletAddress),\n        contract.decimals(),\n        contract.symbol(),\n      ]);\n      const formatted = ethers.formatUnits(balance, decimals);\n      return `${formatted} ${symbol}`;\n    } catch (err) {\n      return `Error: ${err}`;\n    }\n  },\n});\n```\n\nThis is where it gets powerful — the agent can query any contract if you give it an ABI.\n\n``` js\n// src/tools/readContract.ts\nimport { DynamicStructuredTool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { ethers } from \"ethers\";\nimport { provider } from \"../provider\";\n\nexport const readContractTool = new DynamicStructuredTool({\n  name: \"read_contract\",\n  description:\n    \"Call a read-only function on any smart contract. Provide the contract address, ABI as a JSON string, function name, and arguments array.\",\n  schema: z.object({\n    contractAddress: z.string(),\n    abi: z.string().describe(\"JSON string of the contract ABI\"),\n    functionName: z.string(),\n    args: z.array(z.string()).default([]),\n  }),\n  func: async ({ contractAddress, abi, functionName, args }) => {\n    try {\n      const parsedAbi = JSON.parse(abi);\n      const contract = new ethers.Contract(contractAddress, parsedAbi, provider);\n      const result = await contract[functionName](...args);\n      return `Result: ${result.toString()}`;\n    } catch (err) {\n      return `Error: ${err}`;\n    }\n  },\n});\njs\n// src/tools/getGasPrice.ts\nimport { DynamicTool } from \"@langchain/core/tools\";\nimport { provider } from \"../provider\";\nimport { ethers } from \"ethers\";\n\nexport const getGasPriceTool = new DynamicTool({\n  name: \"get_gas_price\",\n  description: \"Get the current gas price on Ethereum mainnet in Gwei.\",\n  func: async () => {\n    try {\n      const feeData = await provider.getFeeData();\n      const gasPriceGwei = ethers.formatUnits(feeData.gasPrice!, \"gwei\");\n      const maxFeeGwei = ethers.formatUnits(feeData.maxFeePerGas!, \"gwei\");\n      return `Base gas price: ${parseFloat(gasPriceGwei).toFixed(2)} Gwei | Max fee: ${parseFloat(maxFeeGwei).toFixed(2)} Gwei`;\n    } catch (err) {\n      return `Error: ${err}`;\n    }\n  },\n});\njs\n// src/tools/getSwapQuote.ts\nimport { DynamicStructuredTool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\nimport { ethers } from \"ethers\";\nimport { provider } from \"../provider\";\n\n// Uniswap V3 Quoter contract on mainnet\nconst QUOTER_ADDRESS = \"0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6\";\nconst QUOTER_ABI = [\n  \"function quoteExactInputSingle(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint160 sqrtPriceLimitX96) external returns (uint256 amountOut)\",\n];\n\nexport const getSwapQuoteTool = new DynamicStructuredTool({\n  name: \"get_swap_quote\",\n  description:\n    \"Get a Uniswap V3 swap quote. Returns how many tokens you'd receive for a given input amount.\",\n  schema: z.object({\n    tokenIn: z.string().describe(\"Input token contract address\"),\n    tokenOut: z.string().describe(\"Output token contract address\"),\n    fee: z.number().describe(\"Pool fee tier: 500, 3000, or 10000\"),\n    amountIn: z.string().describe(\"Amount in (human-readable, e.g. '1.5')\"),\n    decimalsIn: z.number().default(18),\n    decimalsOut: z.number().default(18),\n  }),\n  func: async ({ tokenIn, tokenOut, fee, amountIn, decimalsIn, decimalsOut }) => {\n    try {\n      const quoter = new ethers.Contract(QUOTER_ADDRESS, QUOTER_ABI, provider);\n      const amountInWei = ethers.parseUnits(amountIn, decimalsIn);\n      const amountOut = await quoter.quoteExactInputSingle.staticCall(\n        tokenIn,\n        tokenOut,\n        fee,\n        amountInWei,\n        0\n      );\n      const formatted = ethers.formatUnits(amountOut, decimalsOut);\n      return `You would receive approximately ${formatted} tokens for ${amountIn} input tokens.`;\n    } catch (err) {\n      return `Error getting quote: ${err}`;\n    }\n  },\n});\n```\n\nNow plug all tools into a LangChain agent with memory and a system prompt that defines its behavior.\n\n``` js\n// src/agent.ts\nimport { ChatOpenAI } from \"@langchain/openai\";\nimport { AgentExecutor, createOpenAIFunctionsAgent } from \"langchain/agents\";\nimport { ChatPromptTemplate, MessagesPlaceholder } from \"@langchain/core/prompts\";\nimport { BufferMemory } from \"langchain/memory\";\n\nimport { getBalanceTool } from \"./tools/getBalance\";\nimport { getTokenBalanceTool } from \"./tools/getTokenBalance\";\nimport { readContractTool } from \"./tools/readContract\";\nimport { getGasPriceTool } from \"./tools/getGasPrice\";\nimport { getSwapQuoteTool } from \"./tools/getSwapQuote\";\n\nconst tools = [\n  getBalanceTool,\n  getTokenBalanceTool,\n  readContractTool,\n  getGasPriceTool,\n  getSwapQuoteTool,\n];\n\nconst llm = new ChatOpenAI({\n  modelName: \"gpt-4o\",\n  temperature: 0,\n  openAIApiKey: process.env.OPENAI_API_KEY,\n});\n\nconst prompt = ChatPromptTemplate.fromMessages([\n  [\n    \"system\",\n    `You are a DeFi-native AI agent with direct access to the Ethereum blockchain.\nYou can read wallet balances, token holdings, smart contract state, gas prices, and DEX swap quotes.\nAlways verify addresses before acting. When providing numbers, format them clearly with appropriate units.\nNever guess on-chain data — always use your tools to fetch it.\nIf a user asks you to perform a transaction, confirm all parameters before proceeding.`,\n  ],\n  new MessagesPlaceholder(\"chat_history\"),\n  [\"human\", \"{input}\"],\n  new MessagesPlaceholder(\"agent_scratchpad\"),\n]);\n\nconst memory = new BufferMemory({\n  memoryKey: \"chat_history\",\n  returnMessages: true,\n});\n\nexport async function createBlockchainAgent() {\n  const agent = await createOpenAIFunctionsAgent({ llm, tools, prompt });\n\n  return new AgentExecutor({\n    agent,\n    tools,\n    memory,\n    verbose: true,\n    maxIterations: 10,\n  });\n}\njs\n// src/index.ts\nimport { createBlockchainAgent } from \"./agent\";\nimport * as dotenv from \"dotenv\";\ndotenv.config();\n\nasync function main() {\n  const agent = await createBlockchainAgent();\n\n  const queries = [\n    \"What is the ETH balance of 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045?\",\n    \"What's the current gas price on Ethereum?\",\n    \"How much USDC would I get if I swap 1 ETH on Uniswap V3? USDC address is 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, WETH is 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, use the 500 fee tier.\",\n  ];\n\n  for (const query of queries) {\n    console.log(`\\n🧠 Query: ${query}`);\n    const result = await agent.invoke({ input: query });\n    console.log(`✅ Response: ${result.output}`);\n  }\n}\n\nmain().catch(console.error);\n```\n\n**Run it:**\n\n`npx ts-node src/index.ts`\n\nNever test write operations on mainnet directly. Fork it locally:\n\n`npm install -D hardhat @nomicfoundation/hardhat-toolbox`\n\nnpx hardhat node --fork https://eth-mainnet.g.alchemy.com/v2/your_key\n\nThen in .env, point your ALCHEMY_RPC_URL to [http://127.0.0.1:8545](http://127.0.0.1:8545) for local testing. Your agent will interact with a real state snapshot without risking real funds.\n\nThis agent is a foundation. Production extensions include:\n\nThe gap between AI agents and blockchain infrastructure is closing fast. LangChain's tool abstraction makes it straightforward to expose any on-chain operation as a callable function — the agent handles reasoning, sequencing, and decision-making on top.\n\nThe architecture above is minimal by design. The real complexity in production systems isn't wiring the tools — it's designing safe execution boundaries, handling RPC failures gracefully, and making sure the agent can't be prompted into signing something it shouldn't.\n\nBuild the guardrails before you build the features.\n\nI'm Fahad Arif — ** Blockchain Developer**, Smart Contract Auditor, and DeFi Consultant. I build and secure production Web3 systems across EVM chains and Solana. More at my website.", "url": "https://wpnews.pro/news/building-ai-agents-that-interact-with-blockchain-a-deep-technical-guide-using", "canonical_source": "https://dev.to/fahadarif/building-ai-agents-that-interact-with-blockchain-a-deep-technical-guide-using-langchain-1e1c", "published_at": "2026-06-22 09:51:55+00:00", "updated_at": "2026-06-22 10:10:14.927340+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "developer-tools", "large-language-models"], "entities": ["LangChain", "ethers.js", "OpenAI", "Alchemy", "Hardhat"], "alternates": {"html": "https://wpnews.pro/news/building-ai-agents-that-interact-with-blockchain-a-deep-technical-guide-using", "markdown": "https://wpnews.pro/news/building-ai-agents-that-interact-with-blockchain-a-deep-technical-guide-using.md", "text": "https://wpnews.pro/news/building-ai-agents-that-interact-with-blockchain-a-deep-technical-guide-using.txt", "jsonld": "https://wpnews.pro/news/building-ai-agents-that-interact-with-blockchain-a-deep-technical-guide-using.jsonld"}}