Reputation: 41
I have an ERC20 token already deployed on the Ropsten testnet with two versions.
V1 is a simple unproxied ERC20 token and looks like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, Ownable {
constructor() ERC20("MyToken", "MTK") {}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
I can interact with this contract using web3:
const Web3 = require('web3');
const MyToken = require('./build/contracts/MyToken.json');
const HDWalletProvider = require('@truffle/hdwallet-provider');
const provider = new HDWalletProvider(process.env.ACCOUNT_SECRET, process.env.INFURA_URL);
const web3 = new Web3(provider);
const contract = new web3.eth.Contract(MyToken.abi, process.env.CONTRACT_ADDRESS);
For example, here is a call that retrieves the owner of the contract:
await contract.methods.owner().call();
On the other hand, V2 is a UUPS upgradeable contract which looks like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.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, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() initializer {}
function initialize() initializer public {
__ERC20_init("MyToken", "MTK");
__Ownable_init();
__UUPSUpgradeable_init();
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
}
To interact with V2 using the same web3 nodejs code, I tried updating the build/abi as well as process.env.CONTRACT_ADDRESS
from V1's address to V2's. However, whenever I retrieve the owner using the same code, it always returns the zero address.
I think the call should be proxied or something, but I don't know how and I can't find resources (docs/tutorials) on this.
Contracts V1 and V2 are generated from wizard.openzeppelin.com. Nothing was modified.
V2 passes the get owner, symbol, and name truffle tests.
Upvotes: 0
Views: 948
Reputation: 43561
I'm not really sure what's the best practice regarding the initializer {}
modifier in constructor()
. My guess is that OpenZeppelin recommends to use it with constructor in case you're also setting other variables in constructor and not using other init function.
However, the effect of your implementation is that it simply sets the initialized
variable to true
without executing the top-level initialize()
function - effectively not setting the owner
variable (and others, such as name
and symbol
).
I was able to perform a quick fix by removing the initializer
modifier (since it's already with the initialize()
function, and calling the initialize()
from the constructor. Please check if there aren't any side effects to it.
// removed the modifier
// added the call to `initialize()`
constructor() {
initialize();
}
// stays the same
function initialize() initializer public {
Upvotes: 0