I'm deploying my MyToken
contract like so:
const ERC721 = await ethers.getContractFactory("MyContract");
const erc721 = await upgrades.deployProxy(ERC721, [], { kind: "uups" })
This gives me two addresses:
1/ Address A for the original contract
2/ Address B for the proxy contract
Now when I try to deploy ProxyTest
like so:
const proxy = await ethers.getContractFactory("ProxyTest");
const proxyDeployed2 = await proxy.deploy("...")
when I call await proxy.deploy
with Address A, this works, but it doesn't work with Address B and fails with:
error={"name":"ProviderError","code":-32603,"_isProviderError":true,"data":{"message":"Error: VM Exception while processing transaction: reverted with reason string 'Address: low-level delegate call failed'","data":"0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000027416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c656400000000000000000000000000000000000000000000000000"}}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.6.8)
at step (/Users/pw/Documents/coding/contracts/node_modules/@ethersproject/providers/lib/json-rpc-provider.js:48:23)
at EthersProviderWrapper.<anonymous> (/Users/pw/Documents/coding/contracts/node_modules/@ethersproject/providers/src.ts/json-rpc-provider.ts:603:20)
at checkError (/Users/pw/Documents/coding/contracts/node_modules/@ethersproject/providers/src.ts/json-rpc-provider.ts:78:20)
at Logger.throwError (/Users/pw/Documents/coding/contracts/node_modules/@ethersproject/logger/src.ts/index.ts:273:20)
at Logger.makeError (/Users/pw/Documents/coding/contracts/node_modules/@ethersproject/logger/src.ts/index.ts:261:28) {
reason: "Error: VM Exception while processing transaction: reverted with reason string 'Address: low-level delegate call failed'",
code: 'UNPREDICTABLE_GAS_LIMIT',
method: 'estimateGas',
Any idea why this is? I want to utilize the original proxy contract as a way to upgrade the implementation MyToken
contract - when I do make this upgrade I don't want to be responsible for upgrading all the ProxyTest
contracts, so I would rather have ProxyTest
point to the UUPS upgradeable proxy (vs. the original MyToken
).
Let me know if there's anything I can do here.
Code to reproduce
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() initializer public {
__ERC721_init("MyToken", "MTK");
__Ownable_init();
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
}
contract ProxyTest is Proxy {
constructor(address addr) {
assert(
_IMPLEMENTATION_SLOT ==
bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
);
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = addr;
Address.functionDelegateCall(
addr,
abi.encodeWithSignature("initialize()")
);
}
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Returns the current implementation address.
*/
function implementation() public view returns (address) {
return _implementation();
}
function _implementation() internal view override returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
}
Environment
Hardhat