一个完整的去中心化 AI 预言机
ERC-20 计费 · EIP-712 签名验证 · Agent 白名单 · 实时可视化
- 🔗 固定入口合约:所有请求统一经过
AIHub,地址永不变更,dApp 无缝集成 - 💰 ERC-20 计费:
OracleToken作为协议代币,支持一次性 Claim 空投,费用托管在FeeVault - 🤖 Agent 白名单:链上
AgentRegistry管理可信 AI 节点,Owner 一键启用/禁用 - 🔐 EIP-712 签名验证:Fulfillment 结构化签名,绑定
chainId + hub address,防跨链重放 - ⚡ 链下 AI 执行器:TypeScript executor 监听链上事件,调用 OpenAI / Anthropic 接口,签名回填
- 📊 实时可视化 Dashboard:用户面板 + 管理员控制台,覆盖请求流水、系统指标、费用统计
链上智能合约调用链下 AI 是一个真实存在的工程问题,但现有方案要么强依赖中心化 API 网关,要么没有统一的请求/结算标准。
AI Oracle 尝试解决三件事:
- 用固定地址入口合约屏蔽底层 AI 服务的变化,dApp 只需对接一个稳定接口
- 用 EIP-712 签名验证让链上合约能核实"这条回答确实来自授权的 Agent",而不是任意地址都能提交
- 用 ERC-20 费用模型 + FeeVault 托管实现请求方付款、Agent 结算、协议抽成的完整经济闭环
graph TB
subgraph Frontend["前端 (Next.js 15 / React 18)"]
UPanel[用户面板\nClaimAirdrop · RequestForm · RequestList]
APanel[管理员控制台\nAgentTable · SystemMetrics · RequestMonitor]
Hooks[wagmi Hooks\nuseAIHub · useAgentRegistry · useOracleToken]
UPanel & APanel --> Hooks
end
subgraph Executor["链下执行器 (TypeScript / Node)"]
Dispatcher[Dispatcher\n监听 RequestCreated 事件]
AIRunner[Executor\n调用 OpenAI / Anthropic]
Signer[Fulfiller\nEIP-712 签名 + fulfill 上链]
MetricsAPI[HTTP Server\n/metrics · /requests · /agents]
Dispatcher --> AIRunner --> Signer
end
subgraph Contracts["合约层 (Solidity 0.8.34 / Foundry)"]
Hub[AIHub\n固定入口]
Registry[AgentRegistry\n白名单管理]
Vault[FeeVault\n费用托管]
Token[OracleToken\nERC-20 + Claim]
end
Hooks -->|"request() · cancel()"| Hub
Hooks -->|"registerAgent() · setWhitelisted()"| Registry
Hooks -->|"claim() · approve()"| Token
Hooks -->|"GET /metrics"| MetricsAPI
Hub -->|"RequestCreated event"| Dispatcher
Signer -->|"fulfill(requestId, result, sig)"| Hub
Hub -->|"lock / settle / refund"| Vault
Hub -->|"isWhitelisted()"| Registry
sequenceDiagram
actor User
participant AIHub
participant FeeVault
participant Executor
participant AI Provider
participant Consumer
User->>AIHub: approve(hub, fee)
User->>AIHub: request(agentWallet, prompt, callback, fee, timeout)
AIHub->>FeeVault: transferFrom(user → vault) + lock(requestId, fee)
AIHub-->>Executor: emit RequestCreated(requestId, agentWallet, prompt)
Executor->>AI Provider: POST /v1/chat/completions (stream)
AI Provider-->>Executor: delta chunks → 拼接 result
Executor->>Executor: EIP-712 sign Fulfillment{requestId, resultHash, deadline, hub}
Executor->>AIHub: fulfill(requestId, result, agentSig)
AIHub->>AIHub: ecrecover(sig) == agentWallet ✓
AIHub->>FeeVault: settle(requestId, agent) → 90% agent / 10% protocol
AIHub->>Consumer: onAIResponse(requestId, result)
sequenceDiagram
actor User
participant AIHub
participant FeeVault
Note over User,FeeVault: 请求超过 timeout 仍未被 fulfill
User->>AIHub: cancel(requestId)
AIHub->>FeeVault: refund(requestId, requester)
FeeVault-->>User: 全额退还锁定费用
Executor 对每条回填消息签名,链上合约通过 ecrecover 验证签名者是否为注册的 Agent 钱包,防止任意地址伪造 AI 回答。
bytes32 public constant FULFILLMENT_TYPEHASH = keccak256(
"Fulfillment(uint256 requestId,bytes32 resultHash,uint256 deadline,address hub)"
);
// fulfill() 内部验证
bytes32 digest = _hashTypedDataV4(structHash); // 包含 chainId + verifyingContract
address recovered = digest.recover(agentSig);
if (recovered != req.agentWallet) revert InvalidSignature();签名绑定 chainId(via EIP-712 domain separator)+ hub address,同一签名无法在其他链或其他合约上重放。
// FeeVault.sol — fulfill 时按比例结算
uint256 public constant PROTOCOL_FEE_BPS = 1000; // 10%
function settle(uint256 requestId, address agent) external onlyHub {
uint256 total = _locked[requestId];
uint256 protocolCut = (total * PROTOCOL_FEE_BPS) / BPS_DENOMINATOR;
uint256 agentAmount = total - protocolCut;
protocolFees += protocolCut;
token.safeTransfer(agent, agentAmount);
}费用流转:requester → FeeVault(锁定)→ 90% Agent + 10% Protocol,超时取消则全额退款。
Executor 使用 viem watchContractEvent 订阅链上事件,事件触发后并发执行 AI 调用,结果签名后回填:
// dispatcher.ts
publicClient.watchContractEvent({
address: HUB_ADDRESS,
abi: AIHubABI,
eventName: "RequestCreated",
onLogs: handleRequestCreatedLogs,
});
// executor.ts — 流式调用,兼容仅支持 stream 的代理服务
const stream = await openai.chat.completions.create({ stream: true, ... });
let result = "";
for await (const chunk of stream) {
result += chunk.choices[0]?.delta?.content ?? "";
}Agent 敏感信息(API Key、接口地址)存储在链下 SQLite,链上只保存钱包地址和模型元数据。
Executor 内置 Express HTTP Server,暴露以下接口供前端轮询:
| 接口 | 说明 |
|---|---|
GET /metrics |
成功率、平均延迟、Pending 数、各 Agent 统计 |
GET /requests |
全量请求记录(含 AI 回答结果) |
GET /agents |
链下已注册的 Agent 配置列表 |
POST /agents |
注册 Agent 链下配置(endpoint + apiKey) |
- ReentrancyGuard:
request/fulfill/cancel全部加锁 - onlyHub 修饰符:FeeVault 所有资金操作只允许 AIHub 调用
- Callback 隔离:Consumer 回调失败不回滚 fulfillment,emit
CallbackFailed事件记录 - 超时机制:请求超时后只有 requester 本人可调用
cancel,防止资金永久锁定
| 决策 | 选择 | 原因 |
|---|---|---|
| 签名验证 | EIP-712 结构化签名 | 防重放、可读性强、与硬件钱包兼容 |
| 费用代币 | 自定义 ERC-20 + Claim 空投 | 本地测试无需外部依赖,一键领取降低使用门槛 |
| AI 调用 | 流式 + 非流式自适应 | 兼容不同代理服务的响应格式差异 |
| 链下存储 | better-sqlite3 | 轻量、零依赖、适合单机 executor 场景 |
| 前端合约交互 | wagmi v2 + viem | 类型安全、Hooks 封装、SSR 友好 |
| 编译器 | Solidity 0.8.34 | 最新稳定版,完整自定义错误和 EIP-712 支持 |
ai-oracle/
├── app/ Next.js 15 前端
│ └── src/
│ ├── app/ 页面路由(dashboard)
│ ├── components/
│ │ ├── layout/ Sidebar · WalletBar · RoleSwitcher
│ │ ├── user/ ClaimAirdrop · TokenBalance · RequestForm · RequestList · ResponseViewer
│ │ └── admin/ AgentTable · AgentRegisterForm · SystemMetrics · RequestMonitor · FeeStats
│ ├── hooks/ useAIHub · useAgentRegistry · useOracleToken · useRequestEvents · useMetrics
│ ├── lib/ wagmi 配置 · 合约地址与 ABI 导入
│ └── types/ oracle.ts · agent.ts
├── contracts/ Foundry 合约
│ ├── src/ OracleToken · AgentRegistry · FeeVault · AIHub 及接口
│ ├── test/ 单元测试 + 集成测试
│ └── script/ Deploy.s.sol · Seed.s.sol
├── executor/ TypeScript 链下执行器
│ └── src/ dispatcher · executor · fulfiller · metrics · db 层 · HTTP server
├── scripts/ dev.sh · deploy-local.sh · sync-abis.sh
└── deployments/ addresses.json(部署后自动生成)
- Node.js 20+、Foundry
# forge install 依赖 git submodule,必须先初始化
git init && git add . && git commit -m "init"
# 合约依赖
cd contracts
forge install foundry-rs/forge-std
forge install OpenZeppelin/openzeppelin-contracts
cd ..
# 链下执行器
cd executor && npm install && cd ..
# 前端
cd app && npm install && cd ..cp .env.example .env
# 编辑 .env,填入 AI 服务的 API Key(本地 dev 只需填 EXECUTOR_PRIVATE_KEY)chmod +x scripts/*.sh
bash scripts/dev.sh启动顺序:
- 启动
anvil --block-time 2 - 部署全套合约并生成
deployments/local/addresses.json - Seed 测试数据(注册 Agent、空投测试 Token)
- 同步 ABI 到 executor 和 app
- 写入
app/.env.local(合约地址注入前端) - 启动 executor:
http://localhost:3001 - 启动 Next.js:
http://localhost:3000
切换到 Admin 角色,点击 Register Agent,填入:
| 字段 | 说明 | 示例 |
|---|---|---|
| Agent Wallet | 收款钱包地址 | 0xf39F... |
| Model ID | 模型标识符 | gpt-4o |
| Provider | 服务商 | openai |
| Endpoint URL | 兼容 OpenAI 格式的接口地址 | http://127.0.0.1:8317/v1 |
| API Key | 服务商 API Key(存链下) | sk-... |
cd contracts
# 运行所有测试
forge test -vvv
# 完整流程集成测试
forge test --match-test test_fullFlow_requestFulfillCallback -vvv
# 取消 + 退款测试
forge test --match-test test_cancel -vvv
# Gas 报告
forge test --gas-report覆盖场景:完整 request → fulfill → callback 流程、EIP-712 签名验证、无效签名拒绝、重复 fulfill 防护、超时取消退款、Claim 空投重复领取防护。
MIT







