Solidity
回退未執行
我有一個帶有代理模式的測試項目(回退/委託呼叫)。目的是擁有可升級的合約。這是代理契約:
// 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"); }); });
我希望這回答了你的問題。