代理协议

代理是不可升级的桩合约,有两个工作:

  • 将外部用户的方法调用转发到主Euler合约

  • 接收来自主Euler合约的方法调用并按照指示记录事件

尽管代理本身是不可升级的,但它们与 Euler 的模块系统集成在一起,于是就可以升级了。

以下协议都使用自定义组装例程,而不是solidity ABI 编码器/解码器。虽然我们不会掉以轻心,但将其保持在纯solidity中的测量开销太高了。为了弥补这种可能令人遗憾的汇编使用,本文档详细解释了协议。

代理 -> Euler

To the calldata received in a fallback, the proxy prepends the 4-byte selector for dispatch() (0xe9c4a3ac), and appends its view of msg.sender:

对于在回退中收到的调用数据,代理在 dispatch() (0xe9c4a3ac) 前添加 4 字节选择器,并附加其对 msg.sender 的视图:

[dispatch() selector (4 bytes)][calldata (N bytes)][msg.sender (20 bytes)]

This data is then passed to the Euler contract with a CALL (not DELEGATECALL).

然后将此数据通过CALL(而不是DELEGATECALL)传递给Euler合约。

Euler -> 模块

dispatch() 方法中,Euler合约查找 msg.senderits 视图,该视图对应于代理地址。

然后在trustedSenders映射中查找假定的代理地址,该映射必须存在,否则调用将被复原。通过在moduleId字段中有一个非零条目来确定它是否存在(模块必须具有非零 ID)。

可以将代理地址添加到 trustedSenders 的唯一方法是Euler合约本身创建它(使用 contracts/Base.sol 中的 _createProxy 函数)。

在单独代理模块的情况下,trustedSenders 中的相同slot也将包含模块实现的地址。如果不是(即多代理模块),则必须通过在 moduleLookup 映射中的附加查找来寻找模块实现。这是因为在升级期间,单独代理模块只需要更新对应的一点,而多代理模块则需要更新 trustedSenders 中的每个相应条目。

此时我们知道消息来自合法代理,因此可以假设最后 20 个字节对应于调用代理的实际msg.sender。检查调用数据的长度。它至少应该是 4 + 4 + 20 字节长,对应于:

  • 4 个字节用于 dispatch() 选择器。

  • 用于调用代理的选择器的 4 个字节(模块中不支持非标准 ABI 调用和回退方法)。

  • msg.sender 结尾的 20 个字节。

Euler 合约将接收到的 calldata 并剥离 dispatch() 选择器,然后附加 msg.senderits 视图(汇编中的caller()),它对应于代理的地址。这导致以下结果:

[original calldata (N bytes)][original msg.sender (20 bytes)][proxy addr (20 bytes)]

然后,这些数据通过 DELEGATECALL 发送到模块实现,因此模块实现的代码在主Euler合约的存储环境文中执行。

模块实现将使用solidity ABI 解码器解压原始调用数据,忽略末尾的40 个字节。

模块不被允许访问 msg.sender。相反,他们应该使用 contracts/BaseModule.sol 中的 unpackTrailingParamMsgSender() 助手,它将从尾随调用数据中检索消息发送者。

当模块需要访问代理地址时,有一个复合帮助器 unpackTrailingParams() 返回两个尾随参数。 msg.sender 仍然不允许用于此,因为模块可以通过批处理调用,而不是通过代理。

模块 -> 代理

当一个模块直接发出一个日志(或solidity级别的“事件”)时,它将从主Euler合约的地址发生。这对于许多日志来说都无碍,但在某些情况下,比如模块正实施 ERC-20 标准时,就不太行了。在这些情况下,有必要从代理本身的地址发出日志。

为了做到这一点,Euler合约(特别是其中一个模块)对代理地址执行CALL

当代理看到Euler合约(它的创建者)对其回退的调用时,它知道不要重新进入Euler合约。相反,它将此调用解释为发出日志消息的指令。这是调用数据的格式:

[number of topics as uint8 (1 byte)][topic #i (32 bytes)]{0,4}[extra log data (N bytes)]

The proxy unpacks this message and executes the appropriate log instruction, log0, log1, etc, depending on the number of topics.

代理解包此消息并根据主题的数量执行适当的日志指令log0log1等。

Last updated