> For the complete documentation index, see [llms.txt](https://zenofchain.gitbook.io/euler-wen-dang-+-bai-pi-shu/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://zenofchain.gitbook.io/euler-wen-dang-+-bai-pi-shu/kai-fa-zhe/dai-li-xie-yi.md).

# 代理协议

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

* 将外部用户的方法调用转发到主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.sender` 的 *its* 视图，该视图对应于代理地址。

然后在`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.sender` 的 *its* 视图（汇编中的`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.

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