iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0
Web 3

Smart Contract Development Breakdown系列 第 15

Day 15 - Contract Proxy: Minimal Proxies & UUPS

  • 分享至 

  • xImage
  •  

Others Proxy Pattern 1 - Minimal Proxies & UUPS

Synchronization Link Tree


Intro.

這兩天會快速地介紹三種 Proxy,當然現在也不只有這幾種 Proxy 模式(可見這兩天 Reference Others 處),我只是挑一些很常見的來講而已!

Minimal Proxies(EIP-1167)

Minimal Proxy 的目標是在同樣一段程式碼或者一份合約會被 Deploy 數次時,能否透過一個標準來節省將同樣的 bytecode 多次存在鏈上的步驟,以模組化和節省成本為設計目標,Minimal Proxy 便出現了。相關的實作可以在 optionality/clone-factory 找到。

Minimal Proxy 在許多專案的合約中也會叫做:Clone Factory、Proxy Factory

簡單來說實作方式是我們可以 deploy minimal proxy contract 來指向已經實作函式 body 的合約地址,如此一來也能透過 delegatecall 把 state 存在 minimal proxy contract 上。一個知名的例子是 Uniswap 的流動池,每一個 pool 都可以指向真正實作了 liquity pool function 的那一個合約。

當我們在 minimal proxy contract 中呼叫了 createClone() 以後就會製造出一個指向 Implementation Contract 的 Proxy Contract。

Minimal Proxy 和可升級模式的 Proxy 其實沒什麼關係,只是一種最小化代理合約的 Patterns,在 Minimal Proxy Contract 並不會實作換 code 或換 address 的程式碼,只有初始設定的 creation code、implementation contract address、delegatecall 使用而已。

資料流的呼叫方式為:

  1. Caller(EOA) 對 Minimal Proxy Contract 進行呼叫(附帶資料)
  2. Minimal Proxy Contract 對 Implementation Contract 進行呼叫(delegate call)
  3. Implementation Contract 回傳資料給 Minimal Proxy Contract,Minimal Proxy Contract 修改狀態
  4. Minimal Proxy Contract 回傳結果給 Caller

我們可以看一下 EIP-1167: Minimal Proxy Contract 提供的例子,以下是一個 minimal proxy contract 的程式碼:

pragma solidity ^0.4.23;

/*
The MIT License (MIT)
*/
//solhint-disable max-line-length
//solhint-disable no-inline-assembly

contract CloneFactory {

  function createClone(address target) internal returns (address result) {
    bytes20 targetBytes = bytes20(target);
    assembly {
      let clone := mload(0x40)
      mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
      mstore(add(clone, 0x14), targetBytes)
      mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
      result := create(0, clone, 0x37)
    }
  }
}

0x40 是一個魔法 slot,是一個 free memory pointer,會儲存當前的 free memory 位置。

而這段程式碼經過編譯之後得到真正部署到鏈上的 bytecode:

0x3d602d80600a3d3981f3363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3

這段 bytecode 主要由三個部分構成:

  1. Creation Code
3d602d80600a3d3981f3
  1. Implementation Contract Address
bebebebebebebebebebebebebebebebebebebebe
  1. DELEGATECALL instruction.
5af43d82803e903d91602b57fd5bf3

我們可以發現 Minimal Proxy Contract 中沒有 constructor 也沒有初始化的宣告,因為 Creation Code 的目的為:Copy the runtime code into memory and return it。這邊的 runtime code 指的是當我們呼叫了合約中的函式,則這段程式碼就會被執行。

UUPS(EIP-1822)

複習一下可升級合約的模式:

                         delegatecall
EOA  ---------->  Proxy  -----------> Implementation
             (storage layer)          (logic layer)

Transparent vs UUPS Proxies

原本在 OpenZeppelin 中的 proxy 是 Transparant Proxy,而最近他們也轉向推薦使用 UUPS Proxies,可以讓整個系統設計更輕量化與多變。EIP-1822: Universal Upgradeable Proxy Standard (UUPS) 把 Proxy 中原本 Transparant Proxy 中會定義的「升級邏輯」移動到 Implementation Contract 中。也就是說同樣的介面(內容)之下,UUPS 可以省下部署 Admin、升級邏輯的部分 Gas。而由於升級於否被放在實作合約中,其中的安全機制為在 implementation 中加入一個 flag mechanism 來確保當前的升級函式能否被執行。

Overview

一個 UUPS 合約中的 Logic Contract 有兩個重要元素:

  1. initialize():由於在 upgradable contracts 中沒有 constructor 的存在,這邊 initialize() 會代替 constructor 的作用。
  2. _authorizeUpgrade():讓升級可行,也就是上面提到的 flag mechanism。

同時如果我們繼承了這三個 Library 可以幫助建立 Proxy 並管理 UUPS 的合約們:

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"

在使用 Hardhat-upgrades 時記得要使用 kind:'uups' 來指定 UUPS Proxy。


Closing

Reference


最後歡迎大家拍打餵食大學生0x2b83c71A59b926137D3E1f37EF20394d0495d72d


上一篇
Day 14 - Multi Signature
下一篇
Day 16 - Contract Proxy: Diamond Pattern
系列文
Smart Contract Development Breakdown30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言