Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/0x-stan/euler-module-proxy
https://github.com/0x-stan/euler-module-proxy
Last synced: 9 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/0x-stan/euler-module-proxy
- Owner: 0x-stan
- Created: 2022-09-13T02:52:54.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2022-09-18T04:53:33.000Z (about 2 years ago)
- Last Synced: 2024-10-13T14:07:41.113Z (24 days ago)
- Language: Solidity
- Size: 1.5 MB
- Stars: 1
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Euler Module Proxy
A simply exmaple to learn Euler's upgradable module proxy.
## Quick Start
install
```bash
forge install
```test
```bash
forge test
```run script
```bash
# deploy contracts
forge script script/DeployModuleProxyScript.s.sol
```## Simple Proxy
Proxy -> Implementation
Single Proxy and Implementation contract.
## Module Proxy
```bash
MainProxy
├── InstallerModule
│ ├── InstallerModuleProxy
│ └── InstallerModuleImplementation
├── AddModule
│ ├── AddModuleProxy
│ └── AddModuleImplementation
└── MultiplyModule
├── MultiplyModuleProxy
└── MultiplyModuleImplementation
```MainProxy is the main entry, each module has a ModuleProxy contract and a ModuleImplementation contract.
### deploy process
1. deploy InstallerModuleImplementation
2. deploy MainProxy with InstallerModuleImplementation address
3. deploy AddModuleImplementation and MultiplyModuleImplementation
4. call InstallerModuleProxy installModules function to install add and multiply modules. It will deploy a ModuleProxy for each module, and mapping ModuleImplementation to ModuleProxyWhen call function on ModuleProxy, ModuleProxy will call main Proxy's dispatch function, and dispatch will delegatecall ModuleImplementation's logic function.
Notice: MainProxy use call assembly code, and ModuleProxy use delegatecall assembly code.
```solidity
// src/ModuleProxy/Proxy.sol
function dispatch() external {
...assembly {
...
// insize = payloadSize + 20(calleraddress)
let result := delegatecall(gas(), moduleImpl, 0, add(payloadSize, 20), 0, 0)
...
}}
// src/ModuleProxy/ModuleProxy.sol
fallback() external {
...assembly {
...
// 24 = 4 dispatch selector + 20 caller address
// insize = calldatasize + 24
let result := call(gas(), creator_, 0, 0, add(24, calldatasize()), 0, 0)
...
}}
```
## Dive Into Euler's ModuleProxy
传统的 Proxy 模式是没有模块化的,Euler 对其进行改进,使得不同模块可以单独升级。整体的架构是一个主入口代理合约 MainProxy,一个 Installer 模块,以及其他的功能模块,其中每个模块都有一个 Implementation 合约(执行逻辑)和一个 ModuleProxy 合约(代理)
![](https://github.com/0x-stan/euler-module-proxy/blob/main/img/Euler-module-proxy-01.png)
### 合约的部署流程
1. 部署 Installer 模块的 Implementation(执行逻辑合约)
2. 传入 1 的地址作为入参,部署 MainProxy 主入口合约
3. MainProxy 造函数会通过 ModuleProxy 模板创建一个 Installer 模块的 Proxy 合约,并将两者地址绑定(mapping)![](https://github.com/0x-stan/euler-module-proxy/blob/main/img/Euler-module-proxy-02.png)
### 功能模块安装/升级流程
1. 部署新模块的 Implementation 合约(可部署多个模块)
2. 将新模块的地址数组传入 Installer Proxy,调用 InstallModules()函数
3. 函数内部会遍历这些模块,并为其创建模块的 ModuleProxy 合约
4. 将每个新模块的 Implementation 和 ModuleProxy 绑定(mapping),完成新模块安装![](https://github.com/0x-stan/euler-module-proxy/blob/main/img/Euler-module-proxy-03.png)
### 用户调用模块功能接口流程
1. 向模块的 ModuleProxy 发起调用(注意不是 MainProxy)
2. ModuleProxy 会使用 call 操作码来调用 MainProxy 的 dispatch 函数
3. dispatch 函数会使用 delegatecall 操作码来调用模块的 Implementation 执行逻辑合约![](https://github.com/0x-stan/euler-module-proxy/blob/main/img/Euler-module-proxy-04.png)
调用过程非常绕,但是这么做有很大的优势。由于最终是由 MainProxy 使用 delegatecall 调用具体逻辑,所以 Euler 合约的主要状态都存储在主入口代理合约 MainProxy 上,而模块的安装/升级并不会影响协议的状态。
### 主要代码逻辑
下面来看一下主要的代码逻辑,首先是 ModuleProxy 的 fallback,当我们发起调用时,会进入这里的 assembly 代码块,会先将我们的 calldata 重组:
```bash
dispatch selector + calldata + caller address
```然后使用 call 操作码调用 MainProxy,由于所有模块的 Proxy 都是由 MainProxy 创建,所以模块 Proxy 的 creator 就是 MainProxy
![](https://github.com/0x-stan/euler-module-proxy/blob/main/img/Euler-module-proxy-05.png)
接下来进入 MainProxy 的 dispatch 函数,首先会检查 calldata 长度,因为从 ModuleProxy 发送过来的调用都会进行重组,所以合法的调用长度是大于等于 28 字节
```bash
4 dispatch selector+4 some function selector + (payload) + 20 caller address
```接着使用 delegatecall 操作码调用模块的执行逻辑合约
![](https://github.com/0x-stan/euler-module-proxy/blob/main/img/Euler-module-proxy-06.png)
> 注意:ModuleProxy 中使用 call 调用,而 MainProxy 中使用 delegatecall 调用,所以函数运行逻辑在模块的 Implementation 中,而所使用的状态在 MainProxy 中
还有一个很精彩的地方:event log 记录在模块的 Proxy 中。在 fallback 中有一个条件语句,当使用模块中类似 emitViaProxy_Transfer 函数发出 event log 时,日志会被分门别类的记录在不同模块的 Proxy 中,而不会全部堆叠在 MainProxy 中。
![](https://github.com/0x-stan/euler-module-proxy/blob/main/img/Euler-module-proxy-07.png)
![](https://github.com/0x-stan/euler-module-proxy/blob/main/img/Euler-module-proxy-08.png)
## Flow
[Flow Board link](https://whimsical.com/euler-module-proxy-BhhPNejt2rbLgx1jntXAsh)
![Euler-Module-Proxy-Flow](./img/Euler-Module-Proxy%402x.png)