I’ve recently “upgraded” my deployment setup to use Hardhat and the latest @openzeppelin/contracts-upgradeable package.
Unsurprisingly, this means some changes to my smart contract. Unfortunately, while compilation works, I cannot get my contract to successfully deploy, as this error is returned:
{ Error: multiple matching functions (argument="name", value="initialize", code=INVALID_ARGUMENT, version=abi/5.0.10)
at Logger.makeError (/contract/node_modules/@ethersproject/logger/src.ts/index.ts:205:28)
at Logger.throwError (/contract/node_modules/@ethersproject/logger/src.ts/index.ts:217:20)
at Logger.throwArgumentError (/contract/node_modules/@ethersproject/logger/src.ts/index.ts:221:21)
at Interface.getFunction (/contract/node_modules/@ethersproject/abi/src.ts/interface.ts:196:24)
at getInitializerData (/contract/node_modules/@openzeppelin/hardhat-upgrades/src/deploy-proxy.ts:74:46)
at Proxy.deployProxy (/contract/node_modules/@openzeppelin/hardhat-upgrades/src/deploy-proxy.ts:54:18)
at processTicksAndRejections (internal/process/task_queues.js:86:5)
reason: 'multiple matching functions',
code: 'INVALID_ARGUMENT',
argument: 'name',
value: 'initialize' }
Contract code looks like this, and I’m not sure where I’ve gone wrong:
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/presets/ERC721PresetMinterPauserAutoIdUpgradeable.sol";
contract TEST is Initializable, ERC721PresetMinterPauserAutoIdUpgradeable {
function initialize() public initializer {
__ERC721PresetMinterPauserAutoId_init(
"name",
"symbol",
"baseURI"
);
}
...
}
That makes sense, but it didn’t solve my problem. I had to completely rename the contract’s initialize() function to something different, then call it with: await upgrades.deployProxy(TEST, [], { initializer: 'TESTInit' });
That works, and everything is running as expected, but verification isn’t working at all and I’m not sure why.
You’d think this would work, but it does not, as override can’t figure out what it’s supposed to be overriding. Very odd.
import "@openzeppelin/contracts-upgradeable/presets/ERC721PresetMinterPauserAutoIdUpgradeable.sol";
contract TEST is ERC721PresetMinterPauserAutoIdUpgradeable {
function initialize() public override initializer {
__ERC721PresetMinterPauserAutoId_init(
"TEST",
"TEST",
"https://TEST.testnet.TEST.com/"
);
}
...
}
Re: Verification: The address it spits out post deployment is the DEPLOYED_CONTRACT_ADDRESS, right? I can see that address is verified in Etherscan already through a “Similar Match,” but I don’t see any of my “custom” functions in there and verification consistently fails with:
Error in plugin @nomiclabs/hardhat-etherscan: The address provided as argument contains a contract, but its bytecode doesn't match any of your local contracts.
Possible causes are:
- Contract code changed after the deployment was executed. This includes code for seemingly unrelated contracts.
- A solidity file was added, moved, deleted or renamed after the deployment was executed. This includes files for seemingly unrelated contracts.
- Solidity compiler settings were modified after the deployment was executed (like the optimizer, target EVM, etc.).
- The given address is wrong.
- The selected network (rinkeby) is wrong.
I’m wondering if it has something to do with constructor arguments since initializers are involved? I’ve not had problems verifying single-file or contracts that don’t inherit other libraries.
You (@abcoathup) were right, as usual. The “implementation contract” is not the same as DEPLOYED_CONTRACT_ADDRESS. The “implementation contract” is what needs to be verified and can be found one of two ways:
Explore the ./.openzeppelin/NETWORK.json file for the deployed contract, but it’s tough to know which entry might be the right one if you’ve got more than one compilation/deployment.
As per your directions, in Etherscan, under “more options” you can click Is this a proxy and it will ask you to verify your implementation contract… THEN you click through to the next screen to see the address of the implementation contract that needs verification. This IMPLEMENTATION_ADDRESS will match with the appropriate address in the ./openzeppelin/NETWORK.json file.
Then run: npx hardhat verify --network rinkeby IMPLEMENTATION_ADDRESS
and you’re off to the races!
The reason you’re getting a function name clash is that the two initialize functions have different signatures. This is also the reason why you can’t override.
One way around this is to specify the full function signature. If I’m not mistaken, you should be able to use options { initializer: 'initialize()' }. Note the parentheses.