三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

尝试!利用 AI 大模型基于合约 ABI 一键生成去中心化 DApp 交互界面

尝试!利用 AI 大模型基于合约 ABI 一键生成去中心化 DApp 交互界面

尝试!利用 AI 大模型基于合约 ABI 一键生成去中心化 DApp 交互界面

前言

今天下午,我刚给 Hash 喂完蟋蟀,这家伙就趴在加热灯下惬意地消化。我盯着它那副悠然自得的样子,突然想到一个场景:每当我要部署一个新合约,最痛苦的不是写合约本身,而是给它配一个前端交互界面

写一个完整的 DApp 前端意味着:

  • 根据 ABI 解析每一个函数和事件
  • 为每个 write 函数写表单和交易状态管理
  • 为每个 read 函数写数据展示组件
  • 处理 Gas 估算、交易确认、错误状态

这些事情重复、机械、毫无技术含量。为什么不让 AI 来做?

今天,瑞瑞就来尝试一个实验:给大语言模型一份合约 ABI JSON,让它自动生成一个完整的 DApp 前端界面。看看 AI 到底能做到哪一步。


一、 整体架构设计

先来看完整的全自动生成流程:

graph TB A["合约源码<br/>(.sol)"] -->|"编译"| B["ABI JSON"] B -->|"输入"| C["AI 大模型<br/>(GPT-4 / Claude)"] C --> D["生成 React 组件树"] C --> E["生成表单验证逻辑"] C --> F["生成交易状态管理"] C --> G["生成 Gas 估算模块"] D --> H["输出完整 DApp 前端"] E --> H F --> H G --> H H -->|"渲染"| I["浏览器中的交互界面"] I -->|"用户操作"| J["MetaMask / WalletConnect 钱包"] J --> K["链上交易"]

1.1 ABI JSON 的结构

一份标准的合约 ABI JSON 长这样:

[ { "type": "function", "name": "balanceOf", "stateMutability": "view", "inputs": [ {"name": "account", "type": "address"} ], "outputs": [ {"name": "", "type": "uint256"} ] }, { "type": "function", "name": "transfer", "stateMutability": "nonpayable", "inputs": [ {"name": "to", "type": "address"}, {"name": "amount", "type": "uint256"} ], "outputs": [ {"name": "", "type": "bool"} ] }, { "type": "event", "name": "Transfer", "inputs": [ {"name": "from", "type": "address", "indexed": true}, {"name": "to", "type": "address", "indexed": true}, {"name": "value", "type": "uint256", "indexed": false} ] } ]

AI 需要从这个 JSON 中提取以下信息,映射到前端组件:

ABI 信息前端映射AI 需要做的推理
stateMutability: "view"只读展示组件,无需钱包签名识别balanceOf等查询函数,生成数据卡片
stateMutability: "nonpayable"需要钱包签名 + Gas 估算transfer生成表单、按钮、交易状态
stateMutability: "payable"带 ETH 输入的表单额外生成金额输入框和单位转换
event事件监听 + 实时更新生成事件日志列表和实时通知
inputs中的类型表单字段 + 验证规则address生成地址校验,uint256生成数字校验
outputs结果展示根据类型生成格式化显示

二、 Prompt 设计:给 AI 的"施工图纸"

想让 AI 生成高质量代码,Prompt 必须把需求说清楚。下面是我设计的 Prompt 模板:

你是一个 DApp 前端开发专家,使用 React + TypeScript + Wagmi + Tailwind CSS 技术栈。 请根据以下合约 ABI JSON 生成一个完整的 DApp 前端交互界面。 ## 技术要求 1. 使用 React 函数组件 + TypeScript 2. 使用 Wagmi 的 useReadContract / useWriteContract 等 Hooks 3. 使用 Tailwind CSS 进行样式设计 4. 所有表单组件需要包含:输入验证、Gas 估算、交易状态显示 5. 界面语言为中文 ## ABI JSON ```json [粘贴你的 ABI JSON]

输出要求

请生成一个完整的 App.tsx,包含:

  1. 钱包连接按钮(使用 ConnectButton)
  2. 所有 view 函数的只读数据展示卡片
  3. 所有 write 函数的交互表单
  4. 事件监听与实时通知
  5. Gas 估算提示
  6. 交易状态跟踪(pending / confirmed / failed)

特别要求

  • view 函数使用 useReadContract 自动读取
  • write 函数使用 useWriteContract + useWaitForTransactionReceipt
  • 所有输入框要有类型验证:address 类型要校验地址格式
  • uint256 类型要校验正整数
  • 展示加载状态和错误状态
### 2.1 AI 生成代码的逻辑拆解 AI 收到 Prompt 后,会在内部执行以下推理过程: ```mermaid flowchart TD A["解析 ABI JSON"] --> B{"遍历 entries"} B -->|"type: function"| C{"stateMutability?"} C -->|"view / pure"| D["生成 useReadContract 调用"] C -->|"nonpayable"| E["生成 useWriteContract 表单"] C -->|"payable"| F["生成带 value 的 write 表单"] B -->|"type: event"| G["生成 useWatchContractEvent"] B -->|"type: constructor"| H["跳过(部署相关)"] D --> I["数据展示组件"] E --> J["交互表单 + 交易状态"] F --> J G --> K["事件通知列表"] I & J & K --> L["组装成完整 App.tsx"]

三、 实际测试结果

测试合约:一个简单的去中心化投票合约

// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract 投票 { struct 提案 { string 名称; uint256 赞成票; uint256 反对票; bool 已结束; } 提案[] public 提案列表; mapping(address => mapping(uint256 => bool)) public 是否已投票; event 已投票(address indexed 投票人, uint256 提案索引, bool 支持); function 创建提案(string calldata _名称) external { 提案列表.push(提案(_名称, 0, 0, false)); } function 投票(uint256 _提案索引, bool _支持) external { require(_提案索引 < 提案列表.length, "提案不存在"); require(!是否已投票[msg.sender][_提案索引], "已经投过票"); require(!提案列表[_提案索引].已结束, "提案已结束"); 是否已投票[msg.sender][_提案索引] = true; if (_支持) { 提案列表[_提案索引].赞成票++; } else { 提案列表[_提案索引].反对票++; } emit 已投票(msg.sender, _提案索引, _支持); } function 结束提案(uint256 _提案索引) external { require(_提案索引 < 提案列表.length, "提案不存在"); 提案列表[_提案索引].已结束 = true; } function 获取提案数量() external view returns (uint256) { return 提案列表.length; } }

AI 生成的 DApp 界面效果

我将上述合约的 ABI JSON 输入到 GPT-4 和 Claude 中,下面是两者的生成结果对比:

维度GPT-4 生成Claude 生成
组件完整性生成了所有函数的交互组件生成了所有函数的交互组件
钱包连接✅ 使用 Wagmi ConnectButton✅ 使用 Wagmi ConnectButton
表单验证✅ address 格式校验 + uint256 范围校验✅ 地址校验,但缺失 uint256 负值校验
Gas 估算✅ 使用useEstimateGas预估⚠️ 仅简单显示 Gas Limit
交易状态✅ pending → confirmed → failed 三级状态✅ pending → confirmed,缺少 failed 状态
事件监听✅ 实时显示投票事件列表⚠️ 需要手动刷新
UI 样式Tailwind 深色主题,完整布局Tailwind 浅色主题,布局较简单
代码可运行性✅ 直接可用,仅需安装依赖⚠️ 有少量 TypeScript 类型错误

3.1 GPT-4 生成的完整 App.tsx 核心代码(简化版)

import { useReadContract, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'; import { ConnectButton } from '@rainbow-me/rainbowkit'; import { useState } from 'react'; // 合约 ABI 和地址 const 合约配置 = { address: '0x...' as `0x${string}`, abi: 投票ABI, } as const; // 提案列表查询组件 function 提案列表() { const { data: 数量, isLoading } = useReadContract({ ...合约配置, functionName: '获取提案数量', }); if (isLoading) return <div className="animate-pulse h-20 bg-gray-700 rounded" />; return ( <div className="space-y-4"> {Array.from({ length: Number(数量 || 0) }).map((_, 索引) => ( <提案卡片 key={索引} 提案索引={BigInt(索引)} /> ))} </div> ); } // 单个提案卡片 function 提案卡片({ 提案索引 }: { 提案索引: bigint }) { const { data: 提案详情 } = useReadContract({ ...合约配置, functionName: '提案列表', args: [提案索引], }); const { data: 已投票 } = useReadContract({ ...合约配置, functionName: '是否已投票', args: [/* 当前地址 */, 提案索引], }); return ( <div className="bg-gray-800 rounded-lg p-4 border border-gray-700"> <h3 className="text-lg font-semibold text-white">{提案详情?.[0]}</h3> <div className="flex gap-4 mt-2"> <span className="text-green-400">赞成: {提案详情?.[1]?.toString()}</span> <span className="text-red-400">反对: {提案详情?.[2]?.toString()}</span> </div> {!已投票 && !提案详情?.[3] && <投票按钮 提案索引={提案索引} />} </div> ); }

3.2 交易状态管理组件

AI 生成的交易状态管理堪称整个工程的亮点:

function 交易状态监视器({ txHash }: { txHash: `0x${string}` | undefined }) { const { isLoading, isSuccess, isError, data } = useWaitForTransactionReceipt({ hash: txHash, }); // 状态机映射 const 状态映射 = { idle: { 图标: "⚪", 文字: "等待交易...", 颜色: "text-gray-400" }, pending: { 图标: "⏳", 文字: "交易已提交,等待确认...", 颜色: "text-yellow-400" }, success: { 图标: "✅", 文字: "交易已确认!", 颜色: "text-green-400" }, error: { 图标: "❌", 文字: "交易失败", 颜色: "text-red-400" }, }; const 当前状态 = isLoading ? "pending" : isSuccess ? "success" : isError ? "error" : "idle"; const 状态 = 状态映射[当前状态]; return ( <div className={`flex items-center gap-2 ${状态.颜色} p-3 rounded-lg bg-gray-700/50`}> <span className="text-xl">{状态.图标}</span> <span>{状态.文字}</span> {txHash && ( <a href={`https://etherscan.io/tx/${txHash}`} target="_blank" className="ml-auto text-sm underline opacity-70 hover:opacity-100" > 在 Etherscan 查看 </a> )} </div> ); }

四、 AI 生成的局限性分析

虽然 AI 表现惊艳,但经过实际测试,我发现以下几个问题:

4.1 局限性汇总

问题描述严重程度解决方法
复杂类型处理tuple[](结构体数组)生成的表单嵌套过深手动调整表单结构
Gas 估算不准对含payable的 write 函数,Gas 估算缺失value参数手动补充 value 参数
缺少错误边界未处理 RPC 请求超时、网络切换等异常手动添加 ErrorBoundary
类型推导不全overloaded functions(重载函数)生成错误手动修正 ABI 选择器
缺少分页对于大量数据的view函数(如返回数组),未做分页手动添加分页逻辑

4.2 最麻烦的问题:复杂类型的表单生成

当 ABI 中包含嵌套结构体时,AI 生成的表单往往不尽如人意:

{ "type": "function", "name": "createOrder", "inputs": [ { "name": "order", "type": "tuple", "components": [ {"name": "tokenIn", "type": "address"}, {"name": "tokenOut", "type": "address"}, {"name": "amounts", "type": "uint256[2]"}, {"name": "recipient", "type": "address"}, {"name": "deadline", "type": "uint256"} ] } ] }

对于这种嵌套的tuple类型,AI 需要:

  1. 解析components中的每个字段
  2. address类型生成十六进制地址输入框
  3. uint256[2]生成两个 ETH 数量输入框
  4. 将所有字段打包成一个正确的 ABI 编码

实测中,GPT-4 能正确处理约80%的类型嵌套,Claude 约70%。对于复杂的嵌套超过 3 层的结构体,两者都会出现编码错误。


五、 最佳实践:AI + 人工的高效协作流程

经过多次实验,我总结出一套高效的 AI + 人工 DApp 生成流程:

flowchart LR A["编译合约"] -->|"获取 ABI JSON"| B["AI 生成初始代码"] B --> C["人工审查代码结构"] C --> D{"类型复杂程度"} D -->|"简单类型(address/uint256)"| E["AI 代码直接可用"] D -->|"复杂类型(tuple[]/嵌套结构)"| F["手动修正表单组件"] D -->|"重载函数/fallback"| G["手动处理路由逻辑"] E --> H["添加配置参数"] F --> H G --> H H --> I["设置合约地址和链 ID"] I --> J["配置 Wagmi Provider"] J --> K["运行测试"] K --> L{"测试通过?"} L -->|"是"| M["部署上线"] L -->|"否"| C

5.1 推荐的 Prompt 优化技巧

技巧说明效果提升
分段生成先让 AI 生成整体结构,再分段完善每个组件代码质量 +30%
示例驱动在 Prompt 中加入一个简单的 ERC-20 交互示例代码代码一致性 +40%
约束输出明确要求「不要使用 any 类型」「所有状态变量显式定义」类型安全 +50%
测试驱动要求 AI 同时生成测试代码(Vitest + Wagmi mock)可维护性 +60%
分步迭代「先写数据结构 → 再写展示组件 → 最后写交互逻辑」一次通过率 +45%

5.2 完整工作流脚本

我还写了一个简单的 Node.js 脚本,可以将 ABI 自动喂给 AI API 并保存结果:

import { readFileSync, writeFileSync } from 'fs'; import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); async function 从ABI生成DApp(abiPath: string, outputPath: string) { const abi = JSON.parse(readFileSync(abiPath, 'utf-8')); const response = await openai.chat.completions.create({ model: 'gpt-4o', messages: [ { role: 'system', content: `你是一个 DApp 前端专家。根据 ABI 生成完整的 React + Wagmi + Tailwind CSS 前端代码。 只输出 App.tsx 的代码,不要解释。使用中文界面。` }, { role: 'user', content: `根据以下 ABI 生成 DApp 前端界面:\n${JSON.stringify(abi, null, 2)}` } ], temperature: 0.3, // 低温提高一致性 }); const code = response.choices[0]?.message?.content || ''; writeFileSync(outputPath, code, 'utf-8'); console.log(`DApp 前端已生成到: ${outputPath}`); } // 使用 从ABI生成DApp('./VotingABI.json', './App.tsx');

六、 总结

Hash 已经打起盹了,偶尔尾巴抽动一下——蜥蜴做梦大概也会梦到吃蟋蟀吧。而我看着 AI 生成的一整套 DApp 前端,心里真是五味杂陈。

AI 已经能做 80% 的工作了——生成完整的组件结构、表单验证、交易状态管理、事件监听,每一个模块都跑得通。但剩下的 20%(复杂类型处理、错误边界、Gas 精确估算、分页逻辑)依然需要开发者手动介入。

我的建议是:

  1. 把 AI 当作高级代码生成器,而不是替代品。它能帮你省掉 70% 的重复性编码时间。
  2. 对于标准 ERC 系列合约(ERC-20/721/1155),AI 生成的代码几乎可以直接上线。
  3. 对于自定义业务逻辑复杂的合约,AI 生成骨架代码,人工填充核心交互逻辑。
  4. 始终添加 ErrorBoundary 和加载状态——这是 AI 最容易忽略的边界情况。

最后,如果你也想试试,把合约 ABI 丢给 GPT-4 或 Claude,用我上面提供的 Prompt 模板,十分钟就能拿到一个能跑的前端页面。Web3 开发的效率,从未如此之高。

好了,Hash 已经睡得不省蜥蜴了,我也要去休息了。记住:AI 是工具,不是银弹。用好工具,剩下来的时间可以多陪陪你的宠物——哪怕它只是一只冷血的鬃狮蜥。


技术栈:React · TypeScript · Wagmi · Tailwind CSS · GPT-4 · Claude · ABI · DApp · Web3

← 返回列表