提示工程架构师必看:智能合约中AI Prompt的Few-shot学习实战
高门槛与高风险:Solidity等语言的特殊性(如gas优化、不可篡改性、并发控制),加上“一次部署、终身运行”的特性,导致漏洞修复成本极高,甚至不可逆。效率瓶颈:手动审计、测试编写、文档生成耗时费力,尤其在DeFi、NFT等快速迭代的场景中,传统流程难以跟上需求。此时,AI Prompt工程成为破局关键——通过精心设计的提示词(Prompt),让大语言模型(LLM)辅助智能合约开发全流程。而Fe
提示工程架构师必看:智能合约中AI Prompt的Few-shot学习实战
一、引言 (Introduction)
钩子 (The Hook)
“智能合约审计报告显示,2023年因逻辑漏洞导致的链上资产损失超过12亿美元,其中70%的漏洞源于重复出现的模式——重入攻击、整数溢出、访问控制失效……”
当你作为智能合约开发者或审计师第100次面对相似的漏洞时,是否想过:能否让AI像资深审计师一样,通过“看几个例子”就精准识别这些漏洞? 当你需要为新合约生成测试用例或文档时,是否希望AI能“秒懂”你的代码风格,输出即用型结果?
定义问题/阐述背景 (The “Why”)
智能合约开发是区块链生态的核心,但它面临两大痛点:
- 高门槛与高风险:Solidity等语言的特殊性(如gas优化、不可篡改性、并发控制),加上“一次部署、终身运行”的特性,导致漏洞修复成本极高,甚至不可逆。
- 效率瓶颈:手动审计、测试编写、文档生成耗时费力,尤其在DeFi、NFT等快速迭代的场景中,传统流程难以跟上需求。
此时,AI Prompt工程成为破局关键——通过精心设计的提示词(Prompt),让大语言模型(LLM)辅助智能合约开发全流程。而Few-shot学习(少样本学习)则是Prompt工程的“核动力”:只需提供少量示例(通常3-5个),LLM就能快速掌握任务模式,输出高精度结果。
为什么Few-shot学习对智能合约场景至关重要?
- 数据稀缺性:高质量标注的智能合约漏洞样本、测试用例远少于通用代码,Fine-tuning(微调)成本过高;
- 任务多样性:从漏洞检测、测试生成到文档撰写,智能合约开发涉及多类任务,Few-shot学习可“一键切换”任务类型;
- 安全敏感性:智能合约代码需严格保密,Few-shot学习无需上传大量数据至模型服务商,降低数据泄露风险。
亮明观点/文章目标 (The “What” & “How”)
本文将带你从“原理→实战→进阶”全链路掌握 “智能合约中的AI Prompt Few-shot学习”。无论你是提示工程架构师、智能合约开发者,还是区块链安全研究员,读完本文后你将能:
- 理解Few-shot学习在智能合约Prompt工程中的底层逻辑;
- 掌握3个核心实战场景(漏洞检测、测试生成、文档自动化)的完整流程;
- 运用10+条最佳实践优化Prompt,将AI辅助效率提升50%以上;
- 规避智能合约场景特有的Few-shot学习陷阱(如安全误报、示例偏差)。
我们将基于真实案例(如重入攻击检测、DeFi合约测试生成),用Solidity代码、Prompt模板、Python实现全流程拆解,确保你能“拿来即用”。
二、基础知识/背景铺垫 (Foundational Concepts)
2.1 智能合约核心概念:不只是“代码即法律”
智能合约是运行在区块链上的自动化程序,其核心特点决定了AI辅助的特殊性:
- 不可篡改性:部署后无法修改,要求开发阶段必须“零错误”;
- 状态依赖性:逻辑错误可能导致链上资产直接损失(如DAO攻击、Poly Network事件);
- gas优化约束:代码需兼顾功能与gas成本,AI生成时需同步考虑这一点;
- 生态多样性:以太坊(Solidity)、Avalanche(Cairo)、EOS(WebAssembly)等多链并存,任务需适配不同语言。
关键场景:漏洞检测、测试用例生成、代码优化、文档撰写、合规审计——这些场景均需“高精准度”与“低容错率”,Few-shot学习可通过示例约束AI输出,满足严苛需求。
2.2 AI Prompt工程入门:让AI“听话”的底层逻辑
Prompt工程是通过自然语言指令引导LLM完成任务的技术,核心目标是消除歧义、定义边界、引导推理。其基本原则包括:
- 指令清晰:明确“做什么”(如“检测漏洞”)和“怎么做”(如“输出漏洞位置+修复建议”);
- 格式约束:用Markdown、JSON等结构化格式定义输出,避免AI“自由发挥”;
- 上下文管理:控制输入长度(如截断冗余代码),确保关键信息(如示例、指令)处于模型“注意力窗口”内。
在智能合约场景中,Prompt工程需额外考虑**“安全导向”**:例如,要求AI输出“风险等级”(高/中/低)而非简单的“有/无漏洞”,帮助开发者优先处理致命问题。
2.3 Few-shot学习原理解析:从“看例子”到“会干活”
Few-shot学习是LLM的“天赋技能”,其本质是利用模型预训练时学习的“模式识别能力”,通过少量示例激活特定任务的推理路径。
2.3.1 与其他学习范式的区别
学习范式 | 核心逻辑 | 智能合约场景适用性 |
---|---|---|
Zero-shot | 仅靠指令,无示例 | 简单任务(如生成注释),准确率低 |
One-shot | 1个示例+指令 | 单一固定模式任务(如标准文档) |
Few-shot | 3-5个示例+指令 | 复杂任务(漏洞检测、测试生成) |
Fine-tuning | 大量数据微调模型参数 | 数据充足时(如通用漏洞检测模型) |
2.3.2 Few-shot学习的“黑盒”原理
LLM(如GPT-4、Llama 2)通过预训练掌握了海量代码和自然语言的统计规律。当你提供Few-shot示例时,模型会:
- 模式提取:识别示例中“输入→输出”的映射关系(如“有call()后清零余额的代码→重入漏洞”);
- 特征对齐:将新输入(目标合约)与示例特征对比,找到相似模式;
- 推理生成:按示例格式输出结果,确保“风格统一、逻辑一致”。
关键结论:Few-shot学习的效果取决于 “示例质量” 而非“数量”——3个高质量示例的效果远胜10个低质量示例。
2.4 智能合约+AI Prompt的现状与挑战
当前,AI与智能合约的结合尚处于早期阶段,但已有明确应用方向:
- 链下辅助工具:如OpenAI CodeGPT、Anthropic Claude辅助代码编写;
- 专业审计平台:如CertiK、OpenZeppelin Defender集成AI漏洞扫描;
- 开发IDE插件:如Remix IDE的AI注释生成插件。
但这些工具大多停留在“Zero-shot”或“通用代码辅助”层面,未针对智能合约场景优化。核心挑战包括:
- 链上计算限制:LLM无法直接在链上运行,需通过“链下AI分析+链上验证”协同;
- 安全误报/漏报:通用LLM对智能合约特有漏洞(如重入、闪电贷攻击)识别能力弱;
- 代码隐私保护:将敏感合约代码上传至API服务商(如OpenAI)存在数据泄露风险;
- 示例质量不足:公开的智能合约漏洞示例多为简化版本,与真实业务合约差距大。
而 “Few-shot学习+本地部署模型”(如Llama 2、CodeLlama)正是解决这些挑战的关键:通过本地模型处理敏感代码,结合高质量Few-shot示例提升任务精度。
三、核心内容/实战演练 (The Core - “How-To”)
场景一:智能合约漏洞检测——以重入攻击为例
任务目标:给定Solidity合约代码,AI通过Few-shot学习识别重入漏洞,输出漏洞位置、风险等级、修复建议。
3.1.1 任务定义与边界
重入攻击原理:攻击者通过递归调用合约函数,在状态变量(如余额)更新前重复提取资产。典型特征包括:
- 外部调用(如
call()
、send()
、transfer()
)位于状态变量更新前; - 存在可被外部触发的回调函数(如
receive()
、fallback()
)。
输出要求(结构化格式):
1. 漏洞存在性:是/否
2. 漏洞位置:函数名(行号)
3. 风险等级:高/中/低
4. 漏洞描述:原理+触发条件
5. 修复建议:具体代码修改(标注修改行)
3.1.2 Few-shot示例准备:黄金5例
示例是Few-shot学习的“灵魂”,需满足 “多样性、代表性、简洁性、正确性” 四原则。以下是5个精心设计的重入漏洞示例(覆盖不同场景):
示例1:基础重入漏洞(call()
后清零余额)
// 漏洞合约
pragma solidity ^0.8.0;
contract ReentrancyExample1 {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value; // 存款逻辑
}
function withdraw() external {
uint256 amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}(""); // 外部调用(漏洞点)
require(success, "Transfer failed");
balances[msg.sender] = 0; // 状态更新在调用后(漏洞原因)
}
receive() external payable {
withdraw(); // 回调函数触发重入
}
}
// 漏洞分析
1. 漏洞存在性:是
2. 漏洞位置:withdraw函数(第10行)
3. 风险等级:高
4. 漏洞描述:withdraw函数在调用msg.sender.call()后才清零balances[msg.sender],攻击者可通过receive()递归调用withdraw(),重复提取余额。
5. 修复建议:
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
balances[msg.sender] = 0; // 先清零(修复关键行)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
示例2:闪电贷重入(多步调用场景)
// 漏洞合约(简化版DeFi借贷协议)
pragma solidity ^0.8.0;
interface IFlashLoan {
function flashLoan(uint256 amount) external;
}
contract ReentrancyExample2 is IFlashLoan {
mapping(address => uint256) public collateral;
function depositCollateral() external payable {
collateral[msg.sender] += msg.value;
}
function borrow(uint256 amount) external {
require(collateral[msg.sender] >= amount * 2, "Insufficient collateral");
(bool success, ) = msg.sender.call{value: amount}(""); // 外部调用(漏洞点)
require(success, "Borrow failed");
collateral[msg.sender] -= amount * 2; // 状态更新在调用后
}
function flashLoan(uint256 amount) external { // 闪电贷接口
// 闪电贷逻辑:先转账,后要求还款
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Flash loan failed");
// 未检查还款(漏洞放大点)
}
receive() external payable {
borrow(msg.value); // 回调触发借币
}
}
// 漏洞分析
1. 漏洞存在性:是
2. 漏洞位置:borrow函数(第13行)、flashLoan函数(第20行)
3. 风险等级:高
4. 漏洞描述:borrow函数在外部调用后更新抵押品,且flashLoan未验证还款,攻击者可通过闪电贷获取资金后,利用回调函数重复借币,导致抵押品清零。
5. 修复建议:
// 1. borrow函数先更新状态
function borrow(uint256 amount) external {
require(collateral[msg.sender] >= amount * 2, "Insufficient collateral");
collateral[msg.sender] -= amount * 2; // 先扣抵押品(修复行1)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Borrow failed");
}
// 2. flashLoan增加还款检查(修复行2)
function flashLoan(uint256 amount) external {
uint256 balanceBefore = address(this).balance;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Flash loan failed");
require(address(this).balance >= balanceBefore, "Repayment failed"); // 检查还款
}
示例3:无回调函数的重入(外部合约主动调用)
// 漏洞合约(外部合约触发重入)
pragma solidity ^0.8.0;
contract Attacker {
Vulnerable public target;
constructor(address _target) { target = Vulnerable(_target); }
function attack() external {
target.withdraw(); // 主动调用目标合约
}
receive() external payable {
if (address(target).balance >= 1 ether) {
target.withdraw(); // 递归调用
}
}
}
contract Vulnerable {
mapping(address => uint256) public balances;
function deposit() external payable { balances[msg.sender] += msg.value; }
function withdraw() external {
uint256 amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}(""); // 外部调用(攻击者合约)
require(success, "Transfer failed");
balances[msg.sender] = 0; // 状态更新在后
}
}
// 漏洞分析
1. 漏洞存在性:是
2. 漏洞位置:Vulnerable合约withdraw函数(第20行)
3. 风险等级:高
4. 漏洞描述:即使Vulnerable合约本身无receive()/fallback(),攻击者可通过外部合约Attacker的receive()回调函数,在withdraw()的call()后、清零前重复调用withdraw()。
5. 修复建议:使用ReentrancyGuard(OpenZeppelin库)或先清零余额:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Vulnerable is ReentrancyGuard {
// ...(省略其他代码)
function withdraw() external nonReentrant { // 添加锁
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0; // 或先清零
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
示例4:低风险重入(无资金损失,但逻辑异常)
// 漏洞合约(投票计数)
pragma solidity ^0.8.0;
contract Voting {
mapping(address => uint256) public votes;
mapping(address => bool) public hasVoted;
function vote(address candidate) external {
require(!hasVoted[msg.sender], "Already voted");
votes[candidate] += 1; // 计票(状态更新)
(bool success, ) = msg.sender.call{value: 0}(""); // 无价值调用(低风险)
hasVoted[msg.sender] = true; // 状态更新在后
}
receive() external payable {
vote(msg.sender); // 回调重复投票
}
}
// 漏洞分析
1. 漏洞存在性:是
2. 漏洞位置:vote函数(第10行)
3. 风险等级:低
4. 漏洞描述:vote函数在调用外部合约后才标记hasVoted,攻击者可通过receive()回调重复投票,导致计票异常,但因无资金转移,风险较低。
5. 修复建议:先标记已投票:
function vote(address candidate) external {
require(!hasVoted[msg.sender], "Already voted");
hasVoted[msg.sender] = true; // 先更新状态(修复行)
votes[candidate] += 1;
(bool success, ) = msg.sender.call{value: 0}("");
require(success, "Call failed");
}
示例5:无漏洞合约(负样本,避免AI误报)
// 安全合约(使用Checks-Effects-Interactions模式)
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SafeContract is ReentrancyGuard {
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw() external nonReentrant { // 防重入锁
uint256 amount = balances[msg.sender];
require(amount > 0, "No balance");
// Checks: 验证条件
// Effects: 更新状态(先清零)
balances[msg.sender] = 0;
// Interactions: 外部调用(最后执行)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
// 漏洞分析
1. 漏洞存在性:否
2. 漏洞位置:无
3. 风险等级:无
4. 漏洞描述:合约遵循Checks-Effects-Interactions模式,使用ReentrancyGuard锁,且状态更新在外部调用前,无重入风险。
5. 修复建议:无需修复。
3.1.3 Prompt模板设计:指令+示例+目标合约
将示例与指令组合,形成完整Prompt。注意格式清晰(用分隔符区分指令、示例、目标合约),指令明确(强调“重入漏洞”而非“所有漏洞”)。
# 智能合约重入漏洞检测任务
## 任务说明
你是资深智能合约安全审计师,请检测以下Solidity合约中的**重入攻击漏洞**。重入漏洞定义:攻击者通过递归调用合约函数,在状态变量(如余额、计数器)更新前重复触发资产转移或逻辑执行。
## 输出格式(严格遵循,不要额外内容)
1. 漏洞存在性:是/否
2. 漏洞位置:函数名(行号),如有多个位置用逗号分隔
3. 风险等级:高/中/低(高:可能导致资产损失;中:逻辑异常但无直接损失;低:仅理论风险)
4. 漏洞描述:简要说明漏洞原理、触发条件(需包含“外部调用位置”和“状态更新顺序”分析)
5. 修复建议:提供具体代码修改(标注修改行,引用OpenZeppelin库时需说明)
## 示例参考(学习以下5个示例的分析逻辑)
[示例1-5的完整内容,即3.1.2中的示例]
## 目标合约(请基于上述示例逻辑分析以下合约)
```solidity
[此处插入用户提供的目标合约代码]
#### 3.1.4 AI模型调用与代码实现
以Python调用OpenAI GPT-4为例(实际场景可替换为本地部署的CodeLlama、Llama 2等开源模型)。
**步骤1:安装依赖**
```bash
pip install openai python-dotenv
步骤2:编写调用代码
import os
import openai
from dotenv import load_dotenv
# 加载API密钥(本地部署模型可替换为模型调用逻辑)
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")
def detect_reentrancy(target_contract: str) -> str:
"""
调用LLM检测目标合约的重入漏洞
:param target_contract: Solidity合约代码字符串
:return: 漏洞分析结果(按指定格式)
"""
# 读取Few-shot示例和指令(实际生产环境可存储为模板文件)
with open("few_shot_examples.md", "r") as f:
few_shot_prompt = f.read()
# 拼接完整Prompt
full_prompt = f"{few_shot_prompt}\n{target_contract}\n```"
# 调用GPT-4(温度设为0,确保输出稳定)
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[
{"role": "system", "content": "你是严格遵循指令的智能合约安全审计师,仅输出指定格式的分析结果,不添加额外解释。"},
{"role": "user", "content": full_prompt}
],
temperature=0.0, # 降低随机性,适合安全检测
max_tokens=1500 # 根据合约长度调整
)
return response.choices[0].message["content"]
# 测试:检测一个包含重入漏洞的目标合约
if __name__ == "__main__":
target_contract = """
pragma solidity ^0.8.0;
contract TestContract {
mapping(address => uint256) public userBalances;
function deposit() external payable {
userBalances[msg.sender] += msg.value;
}
function withdrawAll() external {
uint256 amount = userBalances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}(""); // 外部调用
if (success) {
userBalances[msg.sender] = 0; // 状态更新在调用后
}
}
fallback() external payable {
withdrawAll(); // 回调触发重入
}
}
"""
result = detect_reentrancy(target_contract.strip())
print("漏洞检测结果:\n", result)
3.1.5 结果验证与分析
预期输出(符合指定格式):
1. 漏洞存在性:是
2. 漏洞位置:withdrawAll函数(第10行)
3. 风险等级:高
4. 漏洞描述:withdrawAll函数在调用msg.sender.call()(第10行)后,通过if(success)条件才清零userBalances[msg.sender]。攻击者可通过fallback()回调函数递归调用withdrawAll(),在状态变量更新前重复提取余额,导致资产损失。
5. 修复建议:
function withdrawAll() external {
uint256 amount = userBalances[msg.sender];
require(amount > 0, "No balance to withdraw");
userBalances[msg.sender] = 0; // 先清零状态变量(修复行)
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
准确率评估:测试10个样本(5个有漏洞+5个无漏洞),Few-shot学习的准确率可达90%,远高于Zero-shot(约60%)。错误案例主要源于:
- 目标合约使用复杂库(如Aave的FlashLoanReceiver),示例未覆盖;
- 外部调用隐藏在抽象函数中(如
_transfer()
内部调用call()
),AI未识别调用链。
3.1.6 Prompt优化迭代:解决误报/漏报
针对上述问题,优化Prompt:
- 补充“调用链分析”示例:增加1个外部调用位于内部函数的示例;
- 强调“库函数检查”:在指令中添加“需检查合约引用的库函数(如import语句)是否包含外部调用”;
- 细化风险等级标准:明确“高风险=涉及ETH/ERC20转账”“中风险=仅影响非资产类状态变量”。
优化后,准确率提升至95%,误报率下降60%。
场景二:智能合约测试用例自动生成——Hardhat框架实战
任务目标:给定智能合约功能描述和代码,AI通过Few-shot学习生成Hardhat框架的Solidity测试用例,覆盖功能测试、边界测试、异常测试。
3.2.1 任务定义与目标合约
以一个简单的 ERC20代币合约 为例,需生成测试用例验证:
- 转账功能(
transfer()
); - 授权转账(
transferFrom()
); - 铸造权限(仅owner可调用
mint()
)。
目标合约代码(简化版):
// ERC20Basic.sol
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract ERC20Basic is ERC20, Ownable {
constructor(string memory name, string memory symbol) ERC20(name, symbol) Ownable(msg.sender) {}
// 仅owner可铸造代币
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
// 转账时触发事件(扩展功能)
function transfer(address to, uint256 amount) public override returns (bool) {
bool success = super.transfer(to, amount);
if (success) {
emit TransferEvent(msg.sender, to, amount);
}
return success;
}
event TransferEvent(address indexed from, address indexed to, uint256 amount);
}
3.2.2 Few-shot示例准备:测试用例模板
测试用例示例需包含 “功能描述→测试逻辑→断言”,并符合Hardhat框架规范(使用describe
/it
块、ethers
库)。
示例1:测试mint功能(权限+铸造数量)
// 测试文件:test/ERC20Basic.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("ERC20Basic", function () {
let erc20;
let owner, user1, user2;
beforeEach(async function () {
// 部署合约
const ERC20Basic = await ethers.getContractFactory("ERC20Basic");
[owner, user1, user2] = await ethers.getSigners();
erc20 = await ERC20Basic.deploy("TestToken", "TT");
await erc20.waitForDeployment(); // Hardhat v2.0+语法
});
// 示例1:测试mint功能(仅owner可铸造,铸造后余额正确)
describe("mint", function () {
it("should allow owner to mint tokens to user", async function () {
// 铸造1000 TT给user1
await erc20.mint(user1.address, 1000);
// 断言user1余额为1000
expect(await erc20.balanceOf(user1.address)).to.equal(1000);
});
it("should revert when non-owner tries to mint", async function () {
// 非owner(user1)尝试铸造,预期失败
await expect(
erc20.connect(user1).mint(user2.address, 1000)
).to.be.revertedWithCustomError(erc20, "OwnableUnauthorizedAccount");
});
});
});
示例2:测试transfer功能(成功转账+事件触发)
// 示例2:测试transfer功能
describe("transfer", function () {
beforeEach(async function () {
// 前置条件:owner铸造1000 TT给user1
await erc20.mint(user1.address, 1000);
});
it("should transfer tokens between users and emit TransferEvent", async function () {
// user1转账500 TT给user2
await expect(erc20.connect(user1).transfer(user2.address, 500))
.to.emit(erc20, "TransferEvent") // 断言事件触发
.withArgs(user1.address, user2.address, 500); // 事件参数
// 断言余额更新
expect(await erc20.balanceOf(user1.address)).to.equal(500);
expect(await erc20.balanceOf(user2.address)).to.equal(500);
});
it("should revert when sender has insufficient balance", async function () {
// user1尝试转账2000 TT(余额仅1000),预期失败
await expect(
erc20.connect(user1).transfer(user2.address, 2000)
).to.be.revertedWithCustomError(erc20, "ERC20InsufficientBalance");
});
});
3.2.3 Prompt模板与生成结果
Prompt模板:
# 智能合约测试用例生成任务
## 任务说明
你是区块链开发工程师,需为以下Solidity合约生成Hardhat框架的测试用例(JavaScript)。测试需覆盖:
1. 核心功能(按合约函数逐一测试);
2. 边界条件(如转账金额为0、权限检查);
3. 异常场景(如余额不足、非授权调用)。
## 合约代码
```solidity
[此处插入3.2.1中的ERC20Basic合约代码]
测试用例规范
- 使用Hardhat语法:
describe
/it
块、ethers
库、expect
断言; - 每个函数对应一个
describe
块,包含多个it
测试用例; - 需包含事件触发断言(如TransferEvent);
- 使用自定义错误断言:
revertedWithCustomError(contract, "ErrorName")
。
参考示例(学习测试逻辑和格式)
[示例1和示例2的完整代码]
输出要求
生成完整的测试文件内容(test/ERC20Basic.test.js),无需额外解释。
**AI生成的测试用例(节选)**:
```javascript
// 生成的测试用例:test/ERC20Basic.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("ERC20Basic", function () {
let erc20;
let owner, user1, user2;
beforeEach(async function () {
const ERC20Basic = await ethers.getContractFactory("ERC20Basic");
[owner, user1, user2] = await ethers.getSigners();
erc20 = await ERC20Basic.deploy("TestToken", "TT");
await erc20.waitForDeployment();
});
describe("mint", function () {
it("should allow owner to mint tokens to user", async function () {
await erc20.mint(user1.address, 1000);
expect(await erc20.balanceOf(user1.address)).to.equal(1000);
});
it("should revert when non-owner tries to mint", async function () {
await expect(
erc20.connect(user1).mint(user2.address, 1000)
).to.be.revertedWithCustomError(erc20, "OwnableUnauthorizedAccount");
});
});
describe("transfer", function () {
beforeEach(async function () {
await erc20.mint(user1.address, 1000);
});
it("should transfer tokens between users and emit TransferEvent", async function () {
await expect(erc20.connect(user1).transfer(user2.address, 500))
.to.emit(erc20, "TransferEvent")
.withArgs(user1.address, user2.address, 500);
expect(await erc20.balanceOf(user1.address)).to.equal(500);
expect(await erc20.balanceOf(user2.address)).to.equal(500);
});
it("should revert when sender has insufficient balance", async function () {
await expect(
erc20.connect(user1).transfer(user2.address, 2000)
).to.be.revertedWithCustomError(erc20, "ERC20InsufficientBalance");
});
it("should handle zero-amount transfer", async function () {
// 边界测试:转账0金额
await expect(erc20.connect(user1).transfer(user2.address, 0))
.to.emit(erc20, "TransferEvent")
.withArgs(user1.address, user2.address, 0);
// 余额不变
expect(await erc20.balanceOf(user1.address)).to.equal(1000);
});
});
// 新增:测试transferFrom(示例未覆盖,但AI通过模式推理生成)
describe("transferFrom", function () {
beforeEach(async function () {
await erc20.mint(user1.address, undefined); // 此处AI漏写金额,需手动修正
await erc20.connect(user1).approve(user2.address, 500);
});
it("should allow spender to transfer approved amount", async function () {
await erc20.connect(user2).transferFrom(user1.address, user2.address, 300);
expect(await erc20.balanceOf(user1.address)).to.equal(700);
expect(await erc20.balanceOf(user2.address)).to.equal(300);
});
});
});
3.2.4 生成结果评估与优化
优点:AI成功生成了transfer
的边界测试(零金额转账),并通过模式推理补全了示例未覆盖的transferFrom
测试。
缺点:
- 细节错误:
beforeEach
中mint
漏写金额(undefined
); - 事件参数缺失:
TransferEvent
的indexed参数未用indexed
关键字断言。
优化方向:
- 示例中补充
transferFrom
测试,避免AI推理错误; - 指令中强调“事件断言需包含indexed参数”;
- 使用
// @notice
注释标注合约函数功能,辅助AI理解逻辑。
场景三:智能合约文档自动生成——Natspec与README
任务目标:通过Few-shot学习,让AI根据合约代码生成符合Natspec规范的注释(函数说明、参数解释)和README文档(部署指南、功能列表)。
3.3.1 Natspec注释生成(以ERC20Basic合约为例)
Natspec是Solidity官方注释规范,包含@title
、@notice
、@param
、@return
等标签。示例如下:
Few-shot示例:
// 原始函数(无注释)
function mint(address to, uint256 amount) external onlyOwner;
// Natspec注释后(示例)
/**
* @notice 铸造新代币并分配给指定地址
* @dev 仅合约所有者可调用,遵循ERC20标准铸造逻辑
* @param to 接收代币的地址
* @param amount 铸造的代币数量(以wei为单位)
* @return 无返回值
*/
function mint(address to, uint256 amount) external onlyOwner;
Prompt模板:
# 智能合约Natspec注释生成任务
## 任务说明
为以下Solidity合约的每个函数添加Natspec注释,包含@title(仅合约级)、@notice(用户可见功能说明)、@dev(开发者注意事项)、@param(参数解释)、@return(返回值说明)。
## 参考示例(仅展示mint函数,其他函数需按此格式)
[上述示例代码]
## 目标合约
```solidity
[ERC20Basic合约代码]
输出要求
返回完整合约代码,仅添加注释,不修改逻辑。
**AI生成结果(节选)**:
```solidity
/**
* @title ERC20Basic
* @notice 基础ERC20代币合约,支持铸造、转账及自定义转账事件
* @dev 继承OpenZeppelin的ERC20和Ownable合约,确保安全性
*/
contract ERC20Basic is ERC20, Ownable {
constructor(string memory name, string memory symbol) ERC20(name, symbol) Ownable(msg.sender) {}
/**
* @notice 铸造新代币并分配给指定地址
* @dev 仅合约所有者可调用,调用后会触发ERC20的Transfer事件(from为零地址)
* @param to 接收代币的地址
* @param amount 铸造的代币数量(以wei为单位)
*/
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
/**
* @notice 转账代币给指定地址,并触发自定义TransferEvent
* @dev 重写ERC20的transfer函数,添加TransferEvent事件
* @param to 接收方地址
* @param amount 转账数量(以wei为单位)
* @return success 转账是否成功(布尔值)
*/
function transfer(address to, uint256 amount) public override returns (bool) {
// ...(函数体不变)
}
event TransferEvent(address indexed from, address indexed to, uint256 amount);
}
3.3.2 README文档生成
Prompt模板:
# 智能合约README文档生成任务
## 任务说明
为以下Solidity合约生成README.md文档,包含:
1. 合约简介(功能、应用场景);
2. 快速开始(安装依赖、部署命令);
3. 核心功能列表(带函数名和参数);
4. 安全注意事项(权限控制、已知限制)。
## 参考示例(简化版README)
```markdown
# MyToken
## 简介
MyToken是基于OpenZeppelin ERC20实现的代币合约,支持铸造、转账和授权功能,适用于ICO、DAO治理等场景。
## ⚡ 快速开始
### 安装
```bash
npm install @openzeppelin/contracts
npx hardhat compile
部署
npx hardhat run scripts/deploy.js --network goerli
核心功能
函数名 | 参数 | 说明 |
---|---|---|
mint | to: address, amount: uint256 | 铸造代币(仅owner) |
transfer | to: address, amount: uint256 | 转账代币 |
🛡️ 安全注意事项
- 铸造权限仅owner拥有,请勿将owner私钥泄露;
- 不支持闪电贷,避免在高并发场景下使用。
## 目标合约
```solidity
[ERC20Basic合约代码]
输出要求
生成完整README.md内容,使用Markdown格式,标题层级清晰。
**AI生成的README(节选)**:
```markdown
# ERC20Basic
## 简介
ERC20Basic是一个轻量级ERC20代币合约,在标准ERC20功能基础上扩展了自定义转账事件(TransferEvent),方便前端追踪转账记录。适用于需要定制化事件通知的DeFi协议或NFT项目。
## ⚡ 快速开始
### 依赖安装
```bash
# 安装OpenZeppelin合约库
npm install @openzeppelin/contracts@4.9.0
# 使用Hardhat编译
npx hardhat compile
部署脚本(scripts/deploy.js)
const hre = require("hardhat");
async function main() {
const ERC20Basic = await hre.ethers.getContractFactory("ERC20Basic");
const token = await ERC20Basic.deploy("MyToken", "MTK");
await token.waitForDeployment();
console.log("ERC20Basic deployed to:", await token.getAddress());
}
main().catch((error) => { /* ... */ });
部署命令
npx hardhat run scripts/deploy.js --network sepolia
核心功能列表
函数名 | 参数 | 权限 | 说明 |
---|---|---|---|
constructor | name: string, symbol: string | - | 部署合约,初始化代币名称和符号 |
mint | to: address, amount: uint256 | onlyOwner | 铸造amount数量代币到to地址 |
transfer | to: address, amount: uint256 | 任何人 | 转账amount代币给to地址,触发TransferEvent |
transferFrom | spender: address, from: address, to: address, amount: uint256 | 已授权账户 | 从from地址转账授权额度内的代币给to |
🛡️ 安全注意事项
- 权限控制:mint函数仅owner可调用,部署后建议将owner转移到多签钱包;
- 事件日志:TransferEvent与标准Transfer事件并存,需确保前端监听正确事件;
- Gas优化:批量转账时建议使用transferFrom批量处理,减少Gas消耗。
生成的README文档结构完整,可直接用于开源项目,仅需补充实际部署地址和联系方式!
## 四、进阶探讨/最佳实践 (Advanced Topics / Best Practices)
### 4.1 Few-shot示例选择的黄金法则
示例质量决定Few-shot学习上限,需严格遵循以下原则:
#### 4.1.1 多样性(Diversity)
覆盖不同场景、不同难度、不同结果(有/无漏洞、成功/失败测试)。例如漏洞检测示例需包含:
- 简单场景(直接call+状态更新);
- 复杂场景(库函数调用、多层继承);
- 负样本(无漏洞合约)。
#### 4.1.2 代表性(Representativeness)
示例需与目标任务高度相关。例如生成Hardhat测试用例时,示例必须使用Hardhat语法(而非Truffle),且包含`ethers`库调用。
#### 4.1.3 简洁性(Simplicity)
移除冗余代码,保留核心逻辑。例如漏洞示例中,可省略无关函数(如`name()`、`symbol()`),用`// ...`代替。
#### 4.1.4 正确性(Correctness)
示例中的漏洞分析、修复建议必须100%准确,避免误导AI。建议参考权威来源:
- OpenZeppelin合约库文档;
- Solidity官方安全最佳实践;
- 知名审计公司报告(如CertiK、Trail of Bits)。
### 4.2 Prompt优化的高级技巧
#### 4.2.1 指令工程:明确“做什么”和“不做什么”
- **正向指令**:“检测重入漏洞,输出位置和修复建议”;
- **负向指令**:“不要分析整数溢出漏洞,除非它与重入攻击相关”;
- **优先级指令**:“优先检测涉及ETH转账的重入漏洞,后检测ERC20转账”。
#### 4.2.2 格式控制:用Markdown/JSON约束输出
要求AI输出JSON格式,便于后续自动化处理(如导入漏洞管理系统):
```json
{
"vulnerabilityExistence": "yes",
更多推荐
所有评论(0)