I've worked through all of the proxy documentation, a bunch of forum posts, and a number of the workshops on the site and am currently unable to find a good example of how to effectively test the Proxy -> Implementation pattern in Hardhat. It seems like I'm missing something obvious but without any examples of how the proxy is implemented I can't seem to figure out what's missing.
This is a pretty barebones example that should work but fails with this call revert exception:
Error: call revert exception (method="version()", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.5.0)
Code to reproduce
TestProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/Proxy.sol";
contract TestProxy is Proxy {
address public implementation;
constructor(address implementation_) {
implementation = implementation_;
}
function _implementation() internal view virtual override returns (address) {
return implementation;
}
}
ERC1155TestV1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import '@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol';
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract ERC1155TestV1 is Initializable, ERC1155Upgradeable, UUPSUpgradeable, OwnableUpgradeable {
function initialize() initializer public {
__ERC1155_init("");
__Ownable_init();
__UUPSUpgradeable_init();
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() initializer {}
function _authorizeUpgrade(address) internal override onlyOwner {}
function setURI(string memory newuri) public onlyOwner {
_setURI(newuri);
}
function version() public pure returns (string memory) {
return "1";
}
}
TestProxy.js
const { expect } = require("chai");
const { ethers, upgrades } = require("hardhat");
describe("Test a proxy", () => {
before(async () => {
[owner, sender] = await ethers.getSigners();
// instantiate the testproxy contract and deploy
ERC1155TestV1 = await ethers.getContractFactory("ERC1155TestV1");
IMPLEMENTATION = await upgrades.deployProxy(ERC1155TestV1, { kind: 'uups' });
console.log(`IMPLEMENTATION contract: ${IMPLEMENTATION.address}`);
// instantiate the proxy contract and deploy
TestProxy = await ethers.getContractFactory("TestProxy");
PROXY = await TestProxy.deploy(IMPLEMENTATION.address);
console.log(`PROXY contract: ${PROXY.address}`);
// use our proxy contract to get version of the implementation contract
let version = await ERC1155TestV1.attach(PROXY.address).version(); // FAILS HERE
console.log(`VERSION: ${version}`);
});
describe("Verify project deployment", async () => {
it("Should verify that the project contract was deployed and initialized", async () => {
expect(IMPLEMENTATION.address).to.be.properAddress;
expect(PROXY.address).to.be.properAddress;
});
});
});
I've also attempted to implement ERC1967Proxy
which results in a different error but amounts to the same problem: failure with no explanation.
Error: Transaction reverted without a reason string
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract TestProxy is ERC1967Proxy {
constructor (address _implementation, bytes memory _data) ERC1967Proxy(_implementation, _data) {}
function getImplementation() public view returns (address) {
return _getImplementation();
}
function upgradeTo(address newImplementation) public {
_upgradeTo(newImplementation);
}
}
Environment
{
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.4",
"@nomiclabs/hardhat-etherscan": "^3.0.0",
"@nomiclabs/hardhat-waffle": "^2.0.2",
"@openzeppelin/hardhat-upgrades": "^1.13.0",
"chai": "^4.3.6",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.5.3",
"hardhat": "^2.8.3"
},
"dependencies": {
"@openzeppelin/contracts": "^4.5.0",
"@openzeppelin/contracts-upgradeable": "^4.5.1",
"hardhat-gas-reporter": "^1.0.7",
"hardhat-watcher": "^2.1.1"
}
}