ERC-1167:最小代理合约

原文章:https://eips.ethereum.org/EIPS/eip-1167

简要说明:为了以简单且廉价的方式在不可变的情况下克隆合约功能,该标准规定了一种最小字节码实现,将所有调用委托给了一个已知的固定地址。

摘要

通过在已知的最小字节码重定向实现上进行标准化,该标准允许用户和第三方工具(例如:etherscan)简单地发现一个合约将始终以已知方式重定向,并依赖于目标合约中代码的行为作为重定向合约的行文。具体而言,工具可以查询重定向地址处的字节码,以确定将运行的代码位置,并且可以依赖于关于该代码的表示(经过验证的源代码、第三方审计等)。此实现将所有调用和 100% 的 gas 转发到实际合约,然后将返回值传递回调用者。在实施出现还原时,还会传递回还原与有效负载数据(用于带有消息的还原)

动机

该标准支持这样一种情况,即希望在最小化副作用(例如内存槽覆盖)和低 Gas 成本部署重复合约的情况下克隆精确合约功能

规范:

标准克隆合约的确切字节码如下:363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3,其中索引10 - 29(包括 10 和 29)的字节被替换为实际合约的 20 个字节地址。

可以在optionlity/clone-factory github 仓库中找到此项参考的实现

原理

这一努力的目标是:

  • 廉价部署(低 Gas 用于部署 clons)
  • 支持在创建交易中初始化 clones (通过工厂合约模型)
  • 简单的克隆字节码,以鼓励直接进行字节码查询(参考 clone-factory 项目中的 CloneProbe.sol)
  • 可靠、锁定行为 - 这不是设计用于处理升级性问题,也不应该如此,因为我们正在寻求更强大的表示
  • 小操作开销 - 每次调用都会增加一个调用成本
  • 处理回滚消息返回错误

向后兼容性

没有向后兼容性问题。可能有一些系统正在使用早期版本的代理合约字节码。它们将不符合此标准。

测试案例

测试案例包括:

  • 无参数调用
  • 带参数调用

  • 具有固定长度返回值的调用

  • 具有可变长度返回值的调用

  • 具有回滚的调用(确认已转移回滚有效载荷)

  • 这些案例的测试包含在参考实现项目中。

实施

部署字节码不包括在此规范中。代理合约参考实现中定义了一种方法。

开始代理

标准部署的代理合约代码的反汇编(从r2中提取并编辑以包含堆栈可视化)

|           0x00000000      36             calldatasize          cds
|           0x00000001      3d             returndatasize        0 cds
|           0x00000002      3d             returndatasize        0 0 cds
|           0x00000003      37             calldatacopy          
|           0x00000004      3d             returndatasize        0
|           0x00000005      3d             returndatasize        0 0 
|           0x00000006      3d             returndatasize        0 0 0
|           0x00000007      36             calldatasize          cds 0 0 0
|           0x00000008      3d             returndatasize        0 cds 0 0 0
|           0x00000009      73bebebebebe.  push20 0xbebebebe     0xbebe 0 cds 0 0 0
|           0x0000001e      5a             gas                   gas 0xbebe 0 cds 0 0 0
|           0x0000001f      f4             delegatecall          suc 0
|           0x00000020      3d             returndatasize        rds suc 0
|           0x00000021      82             dup3                  0 rds suc 0
|           0x00000022      80             dup1                  0 0 rds suc 0
|           0x00000023      3e             returndatacopy        suc 0
|           0x00000024      90             swap1                 0 suc
|           0x00000025      3d             returndatasize        rds 0 suc
|           0x00000026      91             swap2                 suc 0 rds
|           0x00000027      602b           push1 0x2b            0x2b suc 0 rds
|       ,=< 0x00000029      57             jumpi                 0 rds
|       |   0x0000002a      fd             revert
|       `-> 0x0000002b      5b             jumpdest              0 rds
\           0x0000002c      f3             return

注意:为了尽可能减少燃气成本,上述字节码依赖于 EIP-211 规范,在调用框架内的任何调用之前,returndatasize返回零。returndatasizedup* 使用更少的燃气。

虚拟地址优化

通过在以前导零字节开头的虚荣合约部署地址上安装主合约,可以进一步优化代理部署。通过生成一个包含 Z 个前导 0 字节的主合约虚荣地址,在其地址中用 pushN(其中 N 是 20 - Z)替换 push20 操作码,您可以缩短代理字节码,并在之后跟随 N 个非零地址字节。在这种情况下,还会将回退跳转地址减少 Z。以下是一个示例,其中 Z = 4:

|           0x00000000      36             calldatasize          cds
|           0x00000001      3d             returndatasize        0 cds
|           0x00000002      3d             returndatasize        0 0 cds
|           0x00000003      37             calldatacopy          
|           0x00000004      3d             returndatasize        0
|           0x00000005      3d             returndatasize        0 0 
|           0x00000006      3d             returndatasize        0 0 0
|           0x00000007      36             calldatasize          cds 0 0 0
|           0x00000008      3d             returndatasize        0 cds 0 0 0
|           0x00000009      6fbebebebebe.  push16 0xbebebebe     0xbebe 0 cds 0 0 0
|           0x0000001a      5a             gas                   gas 0xbebe 0 cds 0 0 0
|           0x0000001b      f4             delegatecall          suc 0
|           0x0000001c      3d             returndatasize        rds suc 0
|           0x0000001d      82             dup3                  0 rds suc 0
|           0x0000001e      80             dup1                  0 0 rds suc 0
|           0x0000001f      3e             returndatacopy        suc 0
|           0x00000020      90             swap1                 0 suc
|           0x00000021      3d             returndatasize        rds 0 suc
|           0x00000022      91             swap2                 suc 0 rds
|           0x00000023      6027           push1 0x27            0x27 suc 0 rds
|       ,=< 0x00000025      57             jumpi                 0 rds
|       |   0x00000026      fd             revert
|       `-> 0x00000027      5b             jumpdest              0 rds
\           0x00000028      f3             return

这将节省4个字节的代理合约大小(每次部署都能节省),对运行时的燃气成本没有任何影响。