I have a test project with proxy pattern (fallback/delegate call). The purpose is to have upgradeable contracts.
Here is the proxy contract:
// 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())
}
}
}
}
Implementation contract:
// 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";
}
}
Test:
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");
});
});
Fallback function is not executing when I call a function that does not exist in the contract.
After executing tests via npx hardhat test I get an error:
TypeError: storage.hello is not a function
And the log message in fallback is not printed.
I have also tried using receive function and payable modifier with fallback but it didn't help.
I hope I was clear enough.
Thanks in advance for any suggestions.
Best Answer
This fails because the abi of StorageContract has no
hello
function. So ethers.js doesn't populate the storage object with ahello
function, and your call tostorage.hello()
fails.You are on the good track given that
storage
is indeed a proxy forstorageImpl
, you only need to let ethers "know" about it. One way of solving it is to create an instance ofstorageImplementation
attached to the address ofstorage
:I hope this answers your question.