Agent on Web3——x402 支付、Session Keys、链上行动、Virtuals
x402(Coinbase + Cloudflare 2025)协议;ERC-7715 session keys;ERC-4337 smart accounts;Virtuals/ElizaOS/GOAT 框架定位
日期: 2026-10-09 方向: AI系统工程 / Agent 阶段: Phase 3 - Agent架构与多Agent (Day 149-162) 标签: #x402 #SessionKeys #ERC7715 #ERC4337 #Virtuals #ElizaOS #OnchainAgent
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | x402(Coinbase + Cloudflare 2025)协议;ERC-7715 session keys;ERC-4337 smart accounts;Virtuals/ElizaOS/GOAT 框架定位 |
| 实操 | 实现一个 agent:① 用 session key 限权;② 调付费 API 时自动 x402 settle;③ 链上 swap 前 simulate + sign |
| 产出 | onchain_agent.py(约 600 行)+ session key 演示 |
一、x402 协议——agent 经济基础设施
1.1 背景
HTTP 状态码 402(Payment Required)保留了 30 多年没真正用。2025 年 5 月 Coinbase + Cloudflare 推出 x402 协议:基于 HTTP 402 + 稳定币 onchain settlement,让 API 可以按调用收费,agent 可以无 KYC 自动支付。
1.2 流程
agent ──GET /api/data──► server
◄──402 Payment Required + payment_requirements──
│ {"asset":"USDC","amount":"0.001",
│ "facilitator":"https://x402.coinbase.com",
│ "chain":"base","recipient":"0x..."}
│
│ agent: sign EIP-3009 transferWithAuthorization
│
──GET /api/data + X-PAYMENT header──► server
│ X-PAYMENT: base64(signed_authorization)
│
│ server validates with facilitator
│
◄──200 OK──
1.3 关键特性
- No-KYC——agent 钱包就是 ID
- Sub-cent——最小 $0.0001 调用
- Atomic——支付与 API 调用在一个 HTTP 往返
- EIP-3009——稳定币原生支持 transferWithAuthorization
- Facilitator——Coinbase 提供 settlement,server 不需要自己跑链
- Chain:Base, Solana, etc.
1.4 适合场景
- AI 模型调用按 token 收费
- 数据 API 按 query 收费
- 代理 agent 互相付费
- 防 DDoS(每个请求要钱)
二、Session Keys(ERC-7715)
2.1 问题
传统:agent 需要私钥才能签交易。但永远不应该把主钱包私钥给 agent——一旦泄漏全财产丢失。
2.2 ERC-7715 Session Keys
Session key = 临时、范围受限的密钥,由主账户授权,只能做指定动作。
2.3 限制维度
- 合约白名单:仅能调 Uniswap router
- 方法白名单:仅 swapExactTokensForTokens
- 金额上限:单笔 < 1 ETH,日累 < 5 ETH
- 时间窗口:从 T 起 7 天内有效
- 次数上限:100 次
- Receiver 白名单:只能转给 self
2.4 配套技术栈
- ERC-4337(账户抽象):smart account 是合约,可以编程化定义"什么 key 能做什么"
- EIP-7702(2025 主网):让 EOA 也能临时 act as smart account,session key UX 大改善
- Permissions(Privy / ZeroDev / Biconomy SDK):包装 session key 创建 + 验证
2.5 为什么对 agent 关键
- 主账户在 hardware wallet 永远离线
- 给 agent 一个有限 session key
- agent 跑飞最多损失"上限金额"
- key 过期自动失效
三、ERC-4337 Smart Accounts
3.1 ELI5
EOA = 一个私钥控制一切。Smart Account = 一个合约控制资产,合约逻辑可任意:
- 多签
- Session keys
- 社交恢复
- 限额
- Plugin 系统(Kernel / Safe 7579)
3.2 关键参与者
- UserOperation:用户的 intent
- Bundler:把多个 UserOp 打包成一笔 tx
- EntryPoint:链上验证 + 执行入口
- Paymaster:第三方付 gas(让 agent 用稳定币付 gas,不需要 ETH)
3.3 SDK 选择
| SDK | 特色 |
|---|---|
| viem + permissionless | 标准、灵活 |
| ZeroDev SDK | session key 完善 |
| Biconomy | paymaster 完善 |
| Privy | embedded wallet UX |
| Alchemy AA SDK | 集成度高 |
四、Onchain Agent 框架
4.1 Virtuals Protocol
- 2024 出,专做 agent token + agent commerce
- 框架 G.A.M.E(Generative Autonomous Multimodal Entity)
- 每个 agent 有 ERC-20 token,可被持有/治理
- 主要在 Base 上
4.2 ElizaOS(前 ai16z)
- 开源 agent 框架(TypeScript)
- 多 plugin 生态(Twitter/Discord/Solana/EVM)
- 角色驱动(character files)
- 链无关(plugins 决定)
4.3 GOAT (Great Onchain Agent Toolkit)
- Crossmint 维护
- 给 LLM agent 一系列 onchain "actions"(like tools)
- 支持 Solana / EVM / Sui / Cosmos
- 与 Vercel AI SDK / LangChain 集成
4.4 Olas Network
- DAO 化的 agent infrastructure
- Pearl(runtime)+ Mech(service marketplace)
- 链上 service 注册 + 收益分配
4.5 选型
| 你要做 | 选 |
|---|---|
| Twitter / 社交 agent + token | Virtuals 或 ElizaOS |
| 纯 onchain action (DeFi / NFT) | GOAT + viem |
| 去中心化 agent service | Olas |
| 自定义业务 agent | viem + 自家 framework |
五、架构图
┌──────────────────────────────────────────────────────────────────┐
│ Onchain Agent (Day 161) │
│ │
│ User wallet (cold/hardware) │
│ │ │
│ │ signs once: createSessionKey(scope, limit, expiry) │
│ ▼ │
│ Smart Account (ERC-4337 / 7702) │
│ │ │
│ │ holds: │
│ │ - main key (cold) │
│ │ - session key #SK-1 (agent uses this) │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Agent Runtime │ │
│ │ │ │
│ │ tools: │ │
│ │ - get_balance(addr) (read RPC) │ │
│ │ - get_quote(token, amt) (Uniswap) │ │
│ │ - simulate_swap(...) (eth_call + bundler sim)│ │
│ │ - execute_swap(...) (sign with SK + bundler)│ │
│ │ - call_paid_api(url) (auto x402 if 402) │ │
│ │ │ │
│ │ guardrails: │ │
│ │ - all destructive: simulate first │ │
│ │ - amount > $5k: require user re-confirm │ │
│ │ - per-task USD cap │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ Bundler ─────► EntryPoint ─────► Smart Account ─────► DEX │
└──────────────────────────────────────────────────────────────────┘
六、代码——onchain_agent.py
# onchain_agent.py
"""
Day 161 - An onchain agent with:
- Session key (ERC-7715-style scope limits)
- x402 auto-settle for paid APIs
- Simulate-before-execute for swaps
- Per-task budget guardrail
Pip install:
pip install web3 anthropic httpx eth-account
"""
from __future__ import annotations
import base64
import json
import os
import time
from dataclasses import dataclass, field
from typing import Any, Callable, Optional
import httpx
from anthropic import Anthropic
from eth_account import Account
from eth_account.messages import encode_typed_data
from web3 import Web3
# ====================================================================
# Config
# ====================================================================
RPC_URL = os.environ.get("RPC_URL", "https://mainnet.base.org")
w3 = Web3(Web3.HTTPProvider(RPC_URL))
USDC = Web3.to_checksum_address("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913") # Base USDC
# ====================================================================
# Session Key
# ====================================================================
@dataclass
class SessionKey:
address: str # session key EOA
private_key: str # held by agent runtime ONLY
smart_account: str # the smart account this SK is registered on
allowed_targets: set[str] # contract addresses agent can call
allowed_selectors: set[str] # 4-byte fn selectors (e.g. swap)
max_per_call_usd: float
max_per_day_usd: float
used_today_usd: float = 0.0
expires_ts: int = 0
@classmethod
def create(
cls,
smart_account: str,
allowed_targets: list[str],
allowed_selectors: list[str],
max_per_call_usd: float = 1000,
max_per_day_usd: float = 5000,
valid_days: int = 7,
) -> "SessionKey":
acct = Account.create()
return cls(
address=acct.address,
private_key=acct.key.hex(),
smart_account=smart_account,
allowed_targets={Web3.to_checksum_address(a) for a in allowed_targets},
allowed_selectors=set(allowed_selectors),
max_per_call_usd=max_per_call_usd,
max_per_day_usd=max_per_day_usd,
expires_ts=int(time.time()) + valid_days * 86400,
)
def authorize(self, target: str, selector: str, value_usd: float) -> Optional[str]:
"""Return None if allowed, else error string."""
if time.time() > self.expires_ts:
return "session_expired"
target = Web3.to_checksum_address(target)
if target not in self.allowed_targets:
return f"target_not_allowed: {target}"
if selector not in self.allowed_selectors:
return f"selector_not_allowed: {selector}"
if value_usd > self.max_per_call_usd:
return f"per_call_limit: ${value_usd} > ${self.max_per_call_usd}"
if self.used_today_usd + value_usd > self.max_per_day_usd:
return f"daily_limit: would exceed ${self.max_per_day_usd}"
return None
def commit(self, value_usd: float):
self.used_today_usd += value_usd
# ====================================================================
# x402 client
# ====================================================================
@dataclass
class X402Wallet:
"""Holds a USDC-funded EOA used for x402 micropayments."""
private_key: str
address: str
@classmethod
def from_env(cls) -> "X402Wallet":
pk = os.environ["X402_PRIVATE_KEY"]
return cls(private_key=pk, address=Account.from_key(pk).address)
def sign_authorization(self, requirements: dict) -> str:
"""EIP-3009 transferWithAuthorization signature."""
# USDC token contract on Base
domain = {
"name": "USD Coin",
"version": "2",
"chainId": int(requirements.get("chain_id", 8453)),
"verifyingContract": requirements["asset_address"],
}
types = {
"TransferWithAuthorization": [
{"name": "from", "type": "address"},
{"name": "to", "type": "address"},
{"name": "value", "type": "uint256"},
{"name": "validAfter", "type": "uint256"},
{"name": "validBefore", "type": "uint256"},
{"name": "nonce", "type": "bytes32"},
],
}
message = {
"from": self.address,
"to": requirements["recipient"],
"value": int(float(requirements["amount"]) * 10**6), # USDC has 6 decimals
"validAfter": 0,
"validBefore": int(time.time()) + 600,
"nonce": os.urandom(32).hex(),
}
signed = Account.sign_typed_data(self.private_key, domain, types, message)
# Encode for X-PAYMENT header
payload = {
"scheme": "exact",
"domain": domain,
"message": message,
"signature": signed.signature.hex(),
}
return base64.b64encode(json.dumps(payload).encode()).decode()
class X402Client:
def __init__(self, wallet: X402Wallet, max_per_call_usd: float = 0.50):
self.wallet = wallet
self.max_per_call_usd = max_per_call_usd
self.client = httpx.Client(timeout=30)
def get(self, url: str) -> dict:
r = self.client.get(url)
if r.status_code == 200:
return r.json()
if r.status_code == 402:
req = r.json() # payment_requirements
amount = float(req["amount"])
if amount > self.max_per_call_usd:
raise RuntimeError(f"x402 amount ${amount} exceeds cap ${self.max_per_call_usd}")
x_payment = self.wallet.sign_authorization(req)
r2 = self.client.get(url, headers={"X-PAYMENT": x_payment})
r2.raise_for_status()
return r2.json()
r.raise_for_status()
# ====================================================================
# Tools
# ====================================================================
@dataclass
class Tool:
name: str
description: str
input_schema: dict
handler: Callable[[dict], Any]
def _t_get_balance(args):
addr = Web3.to_checksum_address(args["address"])
bal = w3.eth.get_balance(addr)
return {"address": addr, "eth_wei": bal, "eth": bal / 1e18}
def _t_get_usdc_balance(args):
USDC_ABI = [{"constant": True, "inputs": [{"name": "owner", "type": "address"}],
"name": "balanceOf", "outputs": [{"name": "", "type": "uint256"}],
"type": "function"}]
c = w3.eth.contract(address=USDC, abi=USDC_ABI)
bal = c.functions.balanceOf(Web3.to_checksum_address(args["address"])).call()
return {"usdc": bal / 1e6}
def _t_simulate_swap(args):
"""eth_call simulate without spending gas. Returns expected min_out."""
# Stub: production calls Uniswap quoter
return {"min_out_estimate": float(args["amount_in"]) * 0.998}
# ====================================================================
# Onchain Agent
# ====================================================================
ONCHAIN_TOOLS = [
Tool(name="get_balance", description="Get ETH balance of an address.",
input_schema={"type":"object","properties":{"address":{"type":"string"}},"required":["address"]},
handler=_t_get_balance),
Tool(name="get_usdc_balance", description="Get USDC balance.",
input_schema={"type":"object","properties":{"address":{"type":"string"}},"required":["address"]},
handler=_t_get_usdc_balance),
Tool(name="simulate_swap",
description="Simulate (does NOT execute) a Uniswap swap. Returns expected output. ALWAYS call before execute_swap.",
input_schema={"type":"object","properties":{
"from_token":{"type":"string"}, "to_token":{"type":"string"},
"amount_in":{"type":"string"}, "recipient":{"type":"string"},
},"required":["from_token","to_token","amount_in","recipient"]},
handler=_t_simulate_swap),
]
class OnchainAgent:
def __init__(self, session_key: SessionKey, x402: X402Client | None = None,
task_budget_usd: float = 5.0):
self.client = Anthropic()
self.session_key = session_key
self.x402 = x402
self.task_budget_usd = task_budget_usd
self.tools = {t.name: t for t in ONCHAIN_TOOLS}
self._spent_usd = 0.0
SYSTEM = (
"You are an onchain DeFi assistant. You have access to read-only tools "
"and a session key with strict limits. RULES:\n"
"1. ALWAYS simulate_swap before any execute_swap.\n"
"2. Reject swaps where simulated min_out is < 99% of expected.\n"
"3. NEVER hardcode addresses; always read from user input.\n"
"4. If session key denies an action, report and stop.\n"
"5. Each swap > $1000 USD requires explicit user re-confirmation.\n"
)
def execute_swap_with_session_key(self, target: str, selector: str,
calldata_hex: str, value_usd: float) -> dict:
# Authorize via session key
err = self.session_key.authorize(target, selector, value_usd)
if err:
return {"status": "rejected", "reason": err}
# In production: build UserOperation, sign with SK, send via bundler
# Here we stub the broadcast
self.session_key.commit(value_usd)
self._spent_usd += value_usd
return {"status": "submitted", "tx_hash": "0x" + os.urandom(32).hex(),
"remaining_daily_usd": self.session_key.max_per_day_usd - self.session_key.used_today_usd}
def run(self, task: str, max_iter: int = 8):
msgs = [{"role": "user", "content": task}]
for i in range(max_iter):
if self._spent_usd > self.task_budget_usd:
return {"error": "task_budget_exceeded"}
resp = self.client.messages.create(
model="claude-opus-4-7",
max_tokens=2048,
system=self.SYSTEM,
tools=[{"name":t.name,"description":t.description,
"input_schema":t.input_schema} for t in self.tools.values()],
messages=msgs,
)
msgs.append({"role":"assistant","content":resp.content})
if resp.stop_reason == "end_turn":
final = "".join(b.text for b in resp.content if b.type == "text")
return {"final": final, "spent_usd": self._spent_usd}
if resp.stop_reason == "tool_use":
tool_results = []
for b in resp.content:
if b.type == "tool_use":
try:
out = self.tools[b.name].handler(b.input)
tool_results.append({"type":"tool_result","tool_use_id":b.id,
"content":json.dumps(out)})
except Exception as e:
tool_results.append({"type":"tool_result","tool_use_id":b.id,
"content":f"error: {e}", "is_error": True})
msgs.append({"role":"user","content":tool_results})
return {"error": "max_iter"}
# ====================================================================
# Demo
# ====================================================================
if __name__ == "__main__":
sk = SessionKey.create(
smart_account="0x" + "0" * 40,
allowed_targets=["0x1111" + "1" * 36], # uniswap router placeholder
allowed_selectors=["0xb6f9de95"], # swapExactETHForTokens (example)
max_per_call_usd=500,
max_per_day_usd=2000,
valid_days=7,
)
print("Created session key:", sk.address)
print(" expires:", time.strftime("%Y-%m-%d", time.localtime(sk.expires_ts)))
agent = OnchainAgent(session_key=sk, task_budget_usd=2.0)
out = agent.run("Check the ETH balance of 0x0000000000000000000000000000000000000001 and tell me if it's > 1 ETH.")
print(json.dumps(out, indent=2, default=str))
# Demo session key denial
err = sk.authorize(target="0xdeadbeef" + "0"*32, selector="0xdead0000", value_usd=10000)
print("Denial demo:", err)
七、金融领域应用
7.1 链上财富管理 agent
- 用户给 session key(仅 swap、单笔 $5k,每日 $20k)
- Agent 跑策略:rebalance / yield optimize / DCA
- 主钱包永远在 cold storage,agent 只能动 session 范围
7.2 自动化稳定币运营
- 跨多个 yield 协议比较 APY
- Agent 自动 move funds(限于 USDC、限于白名单协议)
- x402 调用 yield oracle
7.3 RWA 流动性 routing
- 链上代币化美债 + 链上稳定币 + DEX
- Agent 监控价差,动态分配
- 合规约束写进 session key(只能调白名单合约 → 已 KYC 的协议)
八、生产经验与陷阱
-
私钥保管失误 Session key private 也是钱——泄漏者可用满范围+limit。最少:HSM / KMS / encrypted at rest + IAM。理想:threshold signing(MPC)。
-
Simulate 与实际不一致 Block N simulate 通过,N+1 才 broadcast,状态变了 revert(前面有 large swap 改 price)。措施:simulate 时机离 broadcast 最近 + 链上 minOut 强约束。
-
x402 价格欺诈 恶意 server 发 402 amount=$1 实际服务值 $0.001。客户端必须有 max_per_call cap + 信誉机制。
-
Session key permission 失效 主账户撤销了 session 但 agent 不知道,反复尝试。Agent 收到 revert 后要识别 "permission revoked" 并停止。
-
Bundler 拒绝 gas estimation 偏低 / Paymaster 拒付。需要 fallback 路径(换 bundler,或降级到 EOA send)。
-
重放攻击 x402 nonce 重复使用,server 重复扣钱。EIP-3009 nonce 必须 random + server 缓存检查。
-
MEV / 三明治 agent 在 mempool 暴露 swap 被夹。措施:private mempool(Flashbots)、TWAP、或用 CoW Protocol(intent-based)。
-
跨链状态 Agent 读 chain A 状态,到 chain B 行动,桥延迟期间状态变。措施:单链 atomic 优先,跨链用 7683 intent。
九、Cost & Latency
| 操作 | 成本 |
|---|---|
| LLM iter(opus 4-7) | $0.03-0.10 |
| RPC read | 免费/一次 |
| Bundler simulate | 免费(链下) |
| ERC-4337 UserOp | gas + paymaster fee(10-50% 溢价) |
| x402 micro call | $0.0001-$0.01 + ~50ms USDC settle |
| Session key creation | 1 笔交易 + ~$0.5-3 gas(L2) |
| 延迟 | 数值 |
|---|---|
| LLM iter | 5-15s |
| Bundler submit | 2-5s |
| L2 finalize | 5-15s |
| x402 settle | 50-200ms |
完整一次 "agent 决定 + 链上执行" 30-60s。生产里大量动作可批量到一笔 UserOp。
十、关键速查
Session key scope 推荐设置(不同任务)
| 任务 | 推荐 scope |
|---|---|
| 自动 yield rebalance | 协议白名单 + 单笔 $5k + 日 $50k + 7 天 |
| 自动 DCA | swap target 白名单 + 固定金额 + 每天 1 次 |
| 风控自动平仓 | 单笔金额大但仅 sell 方向 + 短期(24h) |
| 试运行 / 测试 | 主网 small 金额 + 严格白名单 + 1 天 |
Onchain agent 安全 checklist
- 主账户私钥不接触 agent runtime
- Session key 范围最小化
- 每个 destructive call simulate first
- 单笔 / 日累 USD cap
- Task USD budget cap
- Audit log every onchain action
- 异常 revert 触发立刻停止 + 通知
- x402 amount cap
- 大额(> threshold)人工 re-confirm
十一、面试题
Q1: 为什么不能直接给 agent 主钱包私钥?
A: 安全上不可接受。Agent runtime 含 LLM + tool 调用 + 网络,攻击面巨大(prompt injection / RCE / dependency 漏洞)。一旦 runtime 被攻陷,主钱包全部资产丢失。Session key(ERC-7715)+ smart account(ERC-4337)允许"仅授权 agent 在范围内动钱",损失上限明确。这与传统软件"least privilege"原则同构。
Q2: x402 解决什么问题?为什么不用 Stripe?
A: ① No-KYC——agent 没法过 Stripe KYC;② Sub-cent——Stripe 最低 $0.30,x402 可以 $0.0001;③ Atomic——支付与服务一个 HTTP 往返;④ 机器友好——agent 钱包就是 ID;⑤ 跨链可移植。Stripe 适合人类买东西,x402 适合 agent 间机器消费。
Q3: 一个 agent 用 session key 跑了一周后,最大风险是什么?
A: ① Smart contract 漏洞——session key 本身没漏,但 agent 调的协议被黑(agent 资金在该协议中);② 预言机操纵——agent 据 price 决策,price 被操纵;③ MEV/三明治——swap 被夹,每次损失;④ session key 私钥泄漏——HSM / MPC 必要;⑤ 错误持续触发——agent 每天都做错事但在 limit 内,不报警。Mitigation:每日预算 + 异常告警 + 人工 review。
Q4: 设计一个 RWA 自动运营 agent,session key scope 怎么设?
A: ① 资产侧:白名单稳定币(USDC/PYUSD)+ 白名单 RWA token(已合规);② 协议侧:白名单 yield 协议(已审计 + 监管友好);③ 金额:单笔 ≤ AUM 的 5%,日 ≤ 20%;④ 方法:仅 deposit/withdraw/swap,不能 borrow;⑤ 时间:30 天 expiry;⑥ 接收方:仅 self-account;⑦ 强制 simulate;⑧ 大额(> 10% AUM)必须人工二次签。
Q5: Virtuals / ElizaOS / GOAT,企业 PM 应该选哪个?
A: 取决于产品。① 想要 token 化 agent + 社交 → Virtuals(但 token 化在很多司法区受限);② 自定义 character/plugin、TypeScript 团队 → ElizaOS;③ 想给现有 LLM stack(LangChain / Vercel AI)加 onchain action → GOAT;④ 企业内部不暴露公开 → 自建,不要 lock-in 任何框架。"用 framework"不等于"成功",PM 要关注:合规、可审计、能被金融团队信任。
十二、深入:x402 流程实战
12.1 完整 HTTP 序列
Request 1 (no payment):
GET https://data.api/quote?symbol=BTC HTTP/1.1
Accept: application/json
Response 1:
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"x402Version": 1,
"accepts": [{
"scheme": "exact",
"network": "base",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"maxAmountRequired": "1000", // 0.001 USDC (6 decimals)
"resource": "/quote?symbol=BTC",
"description": "Real-time BTC quote",
"mimeType": "application/json",
"payTo": "0xRecipient...",
"maxTimeoutSeconds": 60,
"extra": {"name": "USD Coin", "version": "2"}
}]
}
Request 2 (with payment):
GET https://data.api/quote?symbol=BTC HTTP/1.1
X-PAYMENT: <base64(scheme="exact", signature, message)>
Response 2:
HTTP/1.1 200 OK
X-PAYMENT-RESPONSE: <base64(facilitator_settlement_proof)>
Content-Type: application/json
{"symbol": "BTC", "price": 105_320, "ts": 1733...}
12.2 Facilitator 的角色
- 验证 EIP-3009 签名
- broadcast settlement 上链
- 给 server 一个"已收到钱"凭证
- Server 不需要自己跑节点
12.3 为什么不直接 settle 在 server?
- Server 通常是 SaaS / API provider,无意维护链节点
- Facilitator 提供 settlement-as-a-service,类似 Stripe 之于卡支付
- 缺点:信任 facilitator(但 settlement 本身上链可审)
十三、Session Key 的链下管理设计
13.1 私钥生命周期
[Create] 主账户签授权 tx → onchain registry 记 SK 公钥 + scope
[Store] SK 私钥加密存 KMS / HSM
[Use] Agent runtime 取 SK 私钥(in-memory)→ 签 UserOp
[Rotate] 每 N 天创建新 SK,回收旧 SK
[Revoke] 主账户签 revoke tx → registry 标记 SK 失效
13.2 KMS 集成示例(AWS)
import boto3
kms = boto3.client("kms")
def sign_with_kms(key_id: str, message_hash: bytes) -> bytes:
resp = kms.sign(
KeyId=key_id,
Message=message_hash,
MessageType="DIGEST",
SigningAlgorithm="ECDSA_SHA_256",
)
return resp["Signature"]
私钥永远不离开 KMS。Agent runtime 只能 request signing。即使容器被攻陷,攻击者拿不到私钥本体(只能在 IAM 允许范围内签)。
13.3 紧急 kill switch
// Smart account with kill switch
function emergencyRevokeAll() external onlyMainKey {
sessionKeys.revokeAll();
}
主账户随时可一键 revoke 所有 SK。结合监控:异常 swap 大小、异常频率触发自动 alarm + 人工 kill。
十四、链上 agent 经济模型
14.1 Agent 的角色谱
| 角色 | 例子 | 商业模式 |
|---|---|---|
| 服务商:agent 提供服务收钱 | trading bot、data agent | x402 / 订阅 |
| 代理人:代表 user 行动 | 财富管理 agent | 用户付订阅 / 业绩分成 |
| 市场参与者:自营资产管理 | "agent token" funds | 治理 token 升值 |
| 协调者:撮合多 agent | Olas mech | 分润 |
14.2 Agent token 模型(Virtuals 风格)
- 每个 agent 发行 token
- Token holder 共享 agent 收入(10-30% airdrop / 收入分配)
- Agent 性能 → token 升值
- 风险:监管不确定(多数司法区视为 security)
14.3 实际企业部署
传统金融机构通常不用 agent token。常见模式:
- Agent 内部部署,给客户经理用
- 收入按服务套餐而非 token
- 监管友好
十五、扩展练习
- 实现 paymaster 集成——用 USDC 付 gas 而非 ETH
- 加 EIP-7702 临时智能账户——让普通 EOA 用 session key
- 实现 batch transactions——多个 swap 打包成 1 笔 UserOp
- 加 simulation against forked mainnet——hardhat fork + simulate
- 写 monitoring dashboard——session key usage / 余额 / 失败率
- 实现 multi-sig session key——agent 提案,多个 sig 才执行
- 接 CoW Protocol——用 intent-based 避免 MEV
- 写 emergency revoke flow——异常时主账户一键 kill all SK
明日预告
Day 162: Week 24 复习——Multi-agent 系统整合 v1
- 把 Day 149-161 的所有概念整合
- 一个完整的"金融研究 + 链上执行"multi-agent v1
- Phase 3 中段总结