Proxy-Contracts

了解 ERC1967Proxy 內部

  • August 26, 2021

我有一個可以儲存/檢索值的邏輯合約(Box.sol),我想通過 ERC1967Proxy 與之互動

問題: 直接更新一次盒子值後,通過代理檢索值返回預設值,而不是因為它是一個不同的盒子實例

行為:呼叫 proxy.retrieve 返回 0(預設框值)

預期:呼叫 proxy.retrieve 返回 5(之前手動更新的值)


這就是我所做的

  1. 部署邏輯合約 (Box.sol) => 儲存值為 0
  2. 直接與邏輯合約互動 => 儲存值被更新(例如:5)
  3. 在建構子中使用邏輯合約地址部署代理合約(MyProxy.sol)

代理參數

  1. 通過 remix,我使用 Box ABI 檢索具有代理合約地址的值(從文件資源管理器選項卡中選擇 Box.sol,從事務選項卡中選擇“地址”按鈕,但使用代理的地址而不是部署的地址盒子)

最終部署狀態

已部署的合約


盒子.sol

// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Box {
   uint256 private _value;

   // Emitted when the stored value changes
   event ValueChanged(uint256 value);

   // Stores a new value in the contract
   function store(uint256 value) public {
       _value = value;
       emit ValueChanged(value);
   }

   // Reads the last stored value
   function retrieve() public view returns (uint256) {
       return _value;
   }
   
}

MyProxy.sol

// contracts/Box.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract MyProxy is ERC1967Proxy {
  
   constructor(address _logic, bytes memory _data)  ERC1967Proxy(_logic, _data) {}
 
   // debug method to expose the address
   function getImplementationAddress() public view returns(address) {
       return ERC1967Upgrade._getImplementation();
   }   
}

Mikko Ohtamaa是對的。他的回答為我指明了正確的方向:

有幫助的文件是關於代理升級的 OpenZeppelin 文件

需要注意的一個非常重要的事情是程式碼使用了 EVM 的委託呼叫操作碼,該操作碼在呼叫者狀態的上下文中執行被呼叫者的程式碼。

所以這裡發生的是,執行的程式碼是由邏輯合約的字節碼創建的,但它是在代理儲存狀態下執行的,這也是為什麼你需要一個初始化程序而不是使用建構子的原因(參見建構子警告

您誤解瞭如何使用代理合約處理狀態儲存。

部署邏輯合約 (Box.sol) => 儲存值為 0

直接與邏輯合約互動 => 儲存值被更新(例如:5)

這兩個動作都需要通過代理合約發生。永遠不要直接與實現合約互動。這樣狀態處理就得到了正確的管理。

引用自:https://ethereum.stackexchange.com/questions/107791