2026/10/15 23:03:02

ERC-8183:为AI Agent经济补齐链上授权与预算控制的关键拼图

ERC-8183:为AI Agent经济补齐链上授权与预算控制的关键拼图 Agent 经济的讨论热闹了大半年我身边不少人都在做链上自动交易、治理投票助手、自动领取空投这类脚本。但聊到最后几乎都会卡在同一个地方一个 AI Agent 到底凭什么、以什么名义、花谁的钱去执行链上操作ERC-8183 这个提案就是冲着这个问题来的圈子里称它是“给 Agent 经济补上最后一块拼图”。它解决的不是让 Agent 变聪明的问题而是让 Agent 在链上有权限、有预算、有刹车机制地替人干活。这篇文章我会按自己的理解拆解这个提案的设计逻辑并附上一套可以在本地跑起来的最小实现适合正在做 Agent 基建、DeFi 自动化工具或者想把自己业务自动化起来的开发者参考。1. Agent 经济的缺口到底在哪如果你做过链上自动化一定熟悉这么一套老办法用户给合约授权一个额度然后你的脚本调用transferFrom把币转走。这套逻辑服务的是“单笔资产转移”但 Agent 经济里需要的根本不是一次性转账而是“替我连续完成一项任务”。举个例子。我想让一个 Agent 帮我把钱包里的资金分散到五个策略池期间要往返多次先从金库取币、兑换成对应资产、再分别转入五个池子、最后更新我的仓位记录。用传统授权模型我得为每一步单独授权要么就得把手里的私钥整个交给 Agent。前者烦琐到不现实后者等于把家门钥匙直接塞给陌生人安全上完全站不住。更深层的问题在于代理权的定义缺失。链上合约有明确的权限边界但“一个 Agent 在什么条件下、花多少钱、做哪些操作、多久之后失效”这件事没有任何标准可循。每个团队都在自己造轮子有的用链下服务器硬攒签名有的把用户私钥托管在云端有的干脆让用户授权一个超大的approve然后自求多福。这些方案在 demo 里都能跑一旦放进真实资金环境审计第一关就过不去。ERC-8183 想做的就是把“用户授权 Agent 代替自己执行链上操作”这整件事标准化明确 Agent 能调哪些方法、最多花费多少、什么时候过期、用户如何事中撤销以及每一笔调用如何被验证和记账。你可以把它理解成一个介于“普通授权”和“完全托管”之间的可控代理层。没有这层基础设施Agent 经济就始终停留在“替人看行情的玩具”层面落不到真实资产管理上。2. ERC-8183 的核心设计思路2.1 从“资产转移授权”升级为“操作授权”传统授权模型只关心“谁可以动我的钱”ERC-8183 把问题升级成了“谁可以以我的身份做哪些操作”。这个区别非常关键。approve授权的是一个金额上限而 ERC-8183 授权的是一个执行边界。边界里既有资金上限也有方法白名单。Agent 不能拿着授权去调用任意合约的任意函数它只能调用用户预先允许的合约和方法。比如我允许 Agent 调用某个聚合器的swap函数那么它就不能顺手调用同一个合约里其他的管理函数。粒度上做到了“函数级”比“额度级”精确一个量级。在实现上这需要一个可验证的授权凭证。ERC-8183 的做法是让用户创建一条授权记录grant记录里包含了被授权的 Agent 地址、预算上限、允许调用的目标合约、允许调用的函数选择器、有效期和单次调用上限。所有调用都必须携带这条记录的编号合约在收到调用请求时先校验 Agent 身份和记录是否匹配再校验预算是否充足。整个流程在链上完成验证用户不需要把私钥给 Agent。2.2 会话凭证与上下文Agent 如何证明“我被授权了”有了授权记录Agent 在调用时必须证明“我就是那个被授权的 Agent”。这一步依赖链下签名。用户创建授权记录后Agent 手里会拿到一个 grant ID同时自己保留一把独立的签名私钥。每次执行任务时Agent 构造一条调用消息包含目标地址、payload、预算消耗、nonce 和过期时间用这把私钥签名后提交到链上合约。合约校验签名与授权记录中的 Agent 地址一致后才继续执行。这里有个细节值得多说一句签名私钥和资产私钥是分离的。用户的资产私钥永远留在自己手里Agent 只有一把功能受限的“操作钥匙”。哪怕这把钥匙泄露攻击者也只能在授权边界内操作而且用户随时可以撤销。这比托管私钥的方案安全一个级别。nonce 和过期时间的作用是防重放。每一条调用消息都有唯一的 nonce已经被执行过的 nonce 不能再次使用。过期时间则限制签名有效期避免一条老签名在几个月后被重新提交。这两点在实际使用中极其重要后面讲踩坑的时候我会专门展开。2.3 可撤销、可续期、可审计的标准三件套任何代理权设计如果缺了“撤销”就不能算完整。ERC-8183 把撤销做成了标准动作用户随时可以调用接口终止一条授权记录撤销后 Agent 的所有待执行签名立刻失效。设计上还把撤销设计成 calldata 级也就是说不需要额外链下同步Agent 下一笔调用会直接被合约拒绝。可续期是另一个刚需。很多任务不是一次性完成而是长期运行的比如治理代理人每周为用户投票或者定投策略按月执行。ERC-8183 允许用户在授权过期前续期并不需要重新走一遍完整授权流程。续期时可以同时调整预算上限和有效期相当于把授权记录升级了。可审计则体现在事件日志上。每一次 Agent 调用都会发出事件包含 grant ID、目标合约、消耗金额、调用结果。用户随时可以在区块浏览器里翻出自己名下所有 Agent 的行为记录每一笔钱花在哪里、调了什么方法、结果如何一目了然。链上行为天然透明但前提是标准先定义好日志格式否则每个协议打出来的日志都不一样查起来非常痛苦。3. 一个最小实现接口、数据结构和核心逻辑纸上谈兵没有意义我按自己的理解写了一个最小可用版本。代码量不大核心逻辑集中在授权创建、调用校验和预算扣减三个动作。3.1 接口约定我先把接口画出来。这不是标准原文而是我对 ERC-8183 的个人复刻用来跑通流程。interface IERC8183 { struct Grant { address agent; // 被授权的 Agent 地址 address owner; // 授权人 uint96 budget; // 总预算单位 wei uint96 spent; // 已花费 uint48 expiresAt; // 过期时间戳 uint16 callLimit; // 最大调用次数 uint16 callCount; // 已调用次数 address target; // 唯一允许调用的目标合约 bytes4[] selectors; // 允许调用的函数选择器列表 bool active; // 是否有效 } event GrantCreated(uint256 indexed grantId, address indexed owner, address indexed agent); event AgentCall(uint256 indexed grantId, address target, bytes4 selector, uint256 cost); event GrantRevoked(uint256 indexed grantId); function createGrant( address agent, uint96 budget, uint48 duration, uint16 callLimit, address target, bytes4[] calldata selectors ) external returns (uint256 grantId); function execute( uint256 grantId, bytes calldata payload, uint96 cost, bytes calldata signature ) external returns (bytes memory result); function revoke(uint256 grantId) external; }这个接口把授权记录设计成“单目标合约”模式一条 grant 只能对应一个目标合约。这样在审计时思路特别清晰用户也容易判断“我到底把这个 Agent 放进了哪一间房间”。3.2 授权创建流程用户调用createGrant传入 Agent 地址、预算、时长、调用次数上限、目标合约和允许的 selector 列表。合约生成一个新的 grant ID把信息存进grants映射然后发出GrantCreated事件。预算在这里以原生 ETH 为计量单位。实际使用中如果要扩展到 ERC-20需要额外传 token 地址我会在第四节展开这个问题。单次调用上限通过费用参数控制也就是转账金额。整个授权创建不要求用户进行任何转账用户只是“划出了一个许可范围”真正的资金是在 Agent 执行调用时才流动。3.3 调用执行与校验逻辑执行函数是整套设计的核心校验顺序我按审计逻辑排好了先查授权是否被撤销、是否过期再验签名、验方法、验额度最后扣钱执行。每一步都能独立失败方便定位问题。function execute( uint256 grantId, bytes calldata payload, uint96 cost, bytes calldata signature ) external returns (bytes memory result) { Grant storage grant grants[grantId]; require(grant.active, grant not active); require(block.timestamp grant.expiresAt, grant expired); require(msg.sender grant.agent, not authorized agent); bytes32 digest keccak256(abi.encodePacked( \x19Ethereum Signed Message:\n32, keccak256(abi.encode(grantId, payload, cost, nonces[grantId])) )); address signer recover(digest, signature); require(signer grant.owner, bad signature); bytes4 selector bytes4(payload); require(isAllowed(grant, selector), selector not allowed); require(grant.spent cost grant.budget, budget exceeded); require(grant.callCount grant.callLimit, call limit reached); nonces[grantId] 1; bool success; (success, result) grant.target.call{value: cost}(payload); require(success, target call failed); grant.spent cost; grant.callCount 1; emit AgentCall(grantId, grant.target, selector, cost); }注意这里我把签名方设计成了 owner也就是用户必须在链下为 Agent 的每次调用预先签名。这有一个好处用户可以在签名前检查 payload 内容但也有一个坏处Agent 没法自主执行。实际生产里更灵活的方案是让 Agent 用自己的私钥签名用户只签授权创建和 grant 配置二者可以混用。我在简化实现里选了前者逻辑线更短。另一个细节selector bytes4(payload)的写法要求 payload 是标准的 4 字节选择器加参数编码。如果你的 Agent 调用带复杂参数拆分参数做边界校验会复杂很多。为了保持最小实现可读性这里只在函数级做了白名单校验参数级校验可以在每个具体业务的 manager 合约里去做。3.4 撤销与事件revoke函数逻辑很简单只有授权人自己能把 grant 置为不可用同时发出事件。撤销之后Agent 如果再尝试执行第一步的active校验就把请求拦住了整个调用天然失效不需要额外向 Agent 下发“你已经被解除授权”的通知。这是链上状态机最省心的一个特性状态变更即时生效所有参与者共享同一份状态。4. Agent 侧接入的真实工作4.1 签名机制与上下文封装我在实际写 Agent 集成的时候发现最容易出乱子的是签名上下文。同一个 grant 下会有很多笔调用每笔调用如果只有 payload 和签名是防不了重放的攻击者把旧调用重新提交一遍又一遍预算会被偷花光。所以签名消息里必须包含四个要素grantId、payload、费用、nonce。其中 nonce 可以由合约维护也可以由 Agent 维护但建议由合约维护。合约在每次执行后自增 nonce这样即使 Agent 那边状态不同步也不会出现重复签名被二次使用的情况。链上是唯一事实来源不要依赖 Agent 的本地状态做安全判断这是我踩过坑之后才有的体会。另外如果你的合约在多个链上部署签名消息里还要塞chainId和合约地址。跨链重放攻击是真实存在的Agent 在一条链上生成的有效签名被攻击者在另一条链上原封不动地重放了一遍照样能把预算花掉。加上链标识是最便宜、最有效的防御。4.2 计价与限额怎么定预算才有实操意义预算字段我用了一个简单的uint96 budget但生产环境里这不够用。面对现实世界你至少会遇到三种计价需求一种是固定金额Agent 每次调用消耗 0.1 ETH预算上限 1 ETH允许调用十次。这是我们实现里最基础的模型。一种是可变成本Agent 的执行路径不确定比如兑换路径可能经过两个池子也可能经过三个池子每次实际消耗都不同。这种场景单靠调用方预估费用不可靠合约可以在执行后按实际回传成本进行扣减或者让目标合约自己向授权合约报告消耗。两种方式各有利弊前者逻辑简单但可能低估费用后者准确但会把复杂度转嫁给业务合约。还有一种是跨资产Agent 要动用用户的多种 ERC-20 和 ETH。这种情况下授权记录里需要挂一个“资产清单”每一种资产独立设置预算上限。我的建议是不要用单一货币折算所有资产因为价格波动会把你的风控模型搅乱。分币种设限超限即失败简单又稳。4.3 Agent 的钱包管理前面提到过Agent 不应该持有用户资产私钥。实际工程里Agent 的签名私钥建议放在独立的 MPC 服务或硬件安全模块里进程被入侵和签名私钥泄露这两件事要尽量隔离。我在本地测试时用的是最简单的方式给 Agent 生成一个独立的 EOA专门用于 execute 调用和成员签名。但这个 EOA 本身不需要任何余额因为价值转移是从授权人账户走的或者由授权合约代为扣款。这个设计挺反直觉的但很安全Agent 地址的私钥泄露攻击者也只能在用户授权的目标和限额内调用无法直接转走资产。4.4 用户侧交互设计用户侧不能直接把创建授权变成一笔让人看懵的交易。我在设计前端交互时推荐三步走第一步展示权限边界明确告诉用户这个 Agent 能调用哪个合约、哪些函数、预算多少、多长时间有效。这一步用自然语言展示不要直接给人看 selector 十六进制。第二步让用户签名授权记录并提供预算。可以走 EIP-712 类型的结构化签名用户用钱包确认时能清晰看到权限内容不需要先支付 gas。第三步Agent 上线后实时展示执行流水。用户随时能看到每一次调用的成本和结果并且界面上一直放着一个大大的“撤销授权”按钮。这个按钮一定要直接调用revoke而不是先发请求给后端再等确认。5. 我自己实测下来最容易踩的坑5.1 重放攻击最隐蔽的预算刺客起初我以为加了 nonce 就万事大吉但问题出在非ce 的生成位置上。有一版我图省事直接把 nonce 塞在 Agent 本地数据库里结果 Agent 服务重启后 nonce 回滚旧签名又被提交了一遍。几笔重复的兑换调用在几分钟内把预算花了个干净。自那以后我老实了nonce 必须由合约维护或者至少由合约对账校验。本地状态在任何分布式系统里都只能当辅助不能当安全边界。5.2 只校验函数不校验参数照样出问题有一版我想当然地认为只要 Agent 只能调用swap函数就安全了。但swap的函数签名里有个参数是“接收代币的地址”。Agent 可以把收益转到任意地址包括攻击者指定的地址。这是函数级授权的天然盲区。补救办法有两个方向要么在授权合约里实现“参数模板”把关键参数改成只能取某几个白名单值要么在目标合约侧加一个授权模块只接受特定来源的调用并且校验内部参数。我的建议是后者因为把参数规则写进通用授权合约会越写越复杂最终变成没人敢碰的巨型代码库。5.3 预算扣减与实际执行成本不一致另一个常见问题是预估费用和实际费用不一致。Agent 构造调用时Charge 用的是预估成本但目标合约真正消耗的 gas 或转账金额可能会更高。如果合约在调用前就锁死费用实际不够的时候就会失败如果按最终消耗扣又可能出现 Agent 恶意高报费用把预算套走。我的处理方式是双轨制合约执行时锁定一个最大费用值cost目标合约执行完后实际消耗如果小于锁定值多出的部分立刻退回授权人的可消费余额。锁定时校验spent cost budget退钱时只调 spent。这样的体验比较好Agent 不会因为预估值偏低而频繁失败用户也不会因为预估值偏高而被过度扣款。5.4 不要把业务逻辑和授权逻辑混在同一个合约里我最早做的版本把授权校验、业务执行、资产结算全写在一个合约里看起来效率很高但每次换业务逻辑都要重新部署整个授权合约。后来我改成“授权合约只管权限和预算业务合约只管具体执行”也就是让授权合约以代理身份调用目标合约二者通过接口解耦。这样目标合约可以独立迭代授权模块稳定不动安全性也好审计得多。5.5 事件日志要早设计别等上线后补Agent 调用的可追溯性完全取决于事件里存了什么。我一开始只记录grantId和target排查问题的时候发现根本不知道某笔调用具体做了什么。后来我把事件扩展为目标合约、选择器、费用、结果状态、调用时间五要素排查问题的效率高了很多。日志字段设计千万别省上线之后补日志参数是要动合约的成本极高。6. 哪些场景适合拿 ERC-8183 打底6.1 DeFi 自动交易与策略执行DeFi 是 ERC-8183 最直接的落地场景。用户可以授权一个 Agent 在特定 DEX 上执行兑换但限制它只能操作白名单里的池子并设置每日预算上限。Agent 根据行情信号自行调仓用户不需要盯盘也不需要把资产转进某个保险箱合约资产始终留存在自己的钱包体系内只是代理权被标准化了。实际收益模型清晰审计成本也低。6.2 DAO 治理中的代理人机制DAO 治理投票是个被严重低估的 Agent 场景。很多持币人根本没时间跟踪提案更别说逐条分析投票。通过 ERC-8183 这类授权用户可以委托 Agent 在特定范围内的提案上代为投票同时设置单次投票的预算上限比如给 Agent 的操作 gas 补贴上限。它比传统的“投票委托”更精细用户可以限制 Agent 只对其中的技术类提案投票不碰治理参数调整类提案。参数级授权在这里变成了现实刚需。6.3 内容分发与自动结算如果你的业务里有大量“按效果付费”的分发场景比如内容创作者通过 Agent 自动发布内容并领取分润那么 ERC-8183 可以直接用于搭建自动结算层。内容的每次发布消耗一次调用额度分润则由目标合约按规则打款给创作者整条链路不需要人工干预也不需要信任一个中心化第三方定期记账。6.4 订阅型 Agent 服务我还见过一种模式用户以月费形式订阅一个分析 AgentAgent 每周末替用户生成链上活动报告并批量为用户执行定投计划。这里 ERC-8183 可以作为“订阅接入层”用户首次授权一整年预算后续由 Agent 自动执行月费不足时授权人可以直接撤销授权等于退订。整个订阅生命周期都记账上用户对资金去向有完整的审计轨迹解决了不少服务商和用户之间的信任问题。我自己做了几轮实验之后对这个提案的感受是技术实现难度其实不大难的是把权限模型想清楚。Agent 经济增长到今天缺的不再是更好的模型或更聪明的提示词而是可信的执行层。ERC-8183 的价值恰恰是让“我允许这个 Agent 花我的钱、做我限定的事、超过边界就停”这件事变成链上常识。你如果正在做 Agent 相关项目不妨从最小授权模型开始跑通流程然后再一点点加上预算维度、参数维度和时间维度。这套骨架搭好了后面往里填什么业务都不会歪。