Solidity

回退未執行

  • May 16, 2022

我有一個帶有代理模式的測試項目(回退/委託呼叫)。目的是擁有可升級的合約。這是代理契約:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "hardhat/console.sol";

contract StorageContract {

   address private implementation;

   function setImplementation(address storageimplementation) external
   {
       implementation = storageimplementation;
   }


   fallback() external
   {
       console.log("executing fallback-------");
       delegate(implementation);
   }

   function delegate(address a) internal
   {
       assembly
       {
           calldatacopy(0, 0, calldatasize())

           let result := delegatecall(gas(), a, 0, calldatasize(), 0, 0)

           returndatacopy(0, 0, returndatasize())

           switch result
           case 0
           {
               revert(0, returndatasize())
           }
           default
           {
               return(0, returndatasize())
           }
       }
   }

}

執行契約:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "hardhat/console.sol";

contract StorageImplementation
{

   function add(uint256 a, uint256 b) public returns (uint256)
   {
       console.log("add function called");
       return a+b;
   }

   function hello() public returns (string memory)
   {
       console.log("hello function called");
       return "hello";
   }

}

測試:

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Storage", function () {
 it("delegatecall test", async function () {
  const StorageContract = await ethers.getContractFactory("StorageContract");
  const storage = await StorageContract.deploy();
  await storage.deployed();

  const StorageImplementation = await ethers.getContractFactory("StorageImplementation");
  const storageImpl = await StorageImplementation.deploy();
  await storageImpl.deployed();

  storage.setImplementation(storageImpl.address);

  let impl = await storage.getImplementation();
  console.log("impl:" + impl);

   let helloResp = await storage.hello();

   expect(helloResp).to.equal("hello");
 });
});

當我呼叫合約中不存在的函式時,備份函式未執行。

通過npx hardhat test執行測試後,出現錯誤:

類型錯誤:storage.hello 不是函式

並且不會列印回退中的日誌消息。

我也嘗試過使用帶有回退功能的接收功能和應付修飾符,但它沒有幫助。

我希望我足夠清楚。在此先感謝您的任何建議。

失敗是因為 StorageContract 的 abi 沒有hello功能。因此 ethers.js 不會使用hello函式填充儲存對象,並且您的呼叫storage.hello()失敗。

鑑於它storage確實是 的代理,您處於良好的軌道上storageImpl,您只需要讓乙太“知道”它。解決它的一種方法是創建一個storageImplementation附加到地址的實例storage

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Storage", function () {
 it("delegatecall test", async function () {
   const StorageContract = await ethers.getContractFactory("StorageContract");
   const storage = await StorageContract.deploy();
   await storage.deployed();

   const StorageImplementation = await ethers.getContractFactory(
     "StorageImplementation"
   );
   const storageImpl = await StorageImplementation.deploy();
   await storageImpl.deployed();

   storage.setImplementation(storageImpl.address);

   let impl = await storage.getImplementation();
   console.log("impl:" + impl);

   // Proxy handle : StorageImplementation attached to storage address
   // Trough proxy, you can interact with storage as if it was an instance
   // of StorageImplementation.
   const proxy = await StorageImplementation.attach(storage.address);
   let helloResp = await proxy.hello();

   expect(helloResp).to.equal("hello");
 });
});

我希望這回答了你的問題。

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