# Upgradeable NFT Smart Contract

**URL:** <https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191>\
**Category:** Upgrades\
**Tags:** erc721, proxies\
**Created:** [November 17, 2022, 3:32pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191 "2022-11-17T15:32:20Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 17, 2022, 3:32pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/1 "2022-11-17T15:32:20Z")

</div>

Can someone advise me on how to make the Openzeppelin NFT ERC721 upgradeable?

I currently have a constructor with the following attributes:

```auto
constructor() public ERC721(" ", " ") Pausable() { 
    _baseUri = "";
    _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    _setupRole(PAUSER_ROLE, msg.sender);
    _setupRole(MINTER_ROLE, msg.sender);
    _setupRole(BURNER_ROLE, msg.sender);
    _setupRole(OPERATOR_ROLE, msg.sender);
  }

```

Do i need remove the constructor and set the above commands inside an "initializer" function?

Thanks

---

<div class="post-metadata">

**Author:** ![Skyge](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/skyge/32/343_2.png) [@Skyge](https://forum.openzeppelin.com/u/Skyge)\
**Post date:** [November 18, 2022, 9:37am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/2 "2022-11-18T09:37:52Z")

</div>

Yes, and maybe you can have a look at this docs at the same time: [Writing Upgradeable Contracts - OpenZeppelin Docs](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable)

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 18, 2022, 3:58pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/3 "2022-11-18T15:58:32Z")

</div>

Right, thanks. So something like this would suffice?

After importing "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"

```auto
contract ERC721 is Initializable

function initialize() public initializer {
    _baseUri = "";
    _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    _setupRole(PAUSER_ROLE, msg.sender);
    _setupRole(MINTER_ROLE, msg.sender);
    _setupRole(BURNER_ROLE, msg.sender);
    _setupRole(OPERATOR_ROLE, msg.sender);
  }

```

Would this work?

---

<div class="post-metadata">

**Author:** ![Skyge](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/skyge/32/343_2.png) [@Skyge](https://forum.openzeppelin.com/u/Skyge)\
**Post date:** [November 21, 2022, 1:18am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/4 "2022-11-21T01:18:04Z")

</div>

Emmm, you can have a look at the new modifier `onlyInitializing`: [https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v4.4.1](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v4.4.1)

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 21, 2022, 9:38am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/5 "2022-11-21T09:38:45Z")

</div>

OK great, does this include importing the source file ?

And what version of Solidity is compatible with this?

"[@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v4.8.0/contracts/proxy/utils/Initializable.sol)";

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 21, 2022, 2:26pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/6 "2022-11-21T14:26:05Z")

</div>

Just so you are aware, my Solidity compiler does not recognise this command.

```auto
CompileError: DeclarationError: Identifier not found or not unique.
  --> project:/contracts/ERC721CarbonAsset.sol:26:32:
   |
26 | function initialize() internal onlyInitializing {
   | ^^^^^^^^^^^^^^^^

```

Is there a reason why?

---

<div class="post-metadata">

**Author:** ![Skyge](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/skyge/32/343_2.png) [@Skyge](https://forum.openzeppelin.com/u/Skyge)\
**Post date:** [November 23, 2022, 12:53am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/7 "2022-11-23T00:53:05Z")

</div>

> [@Skyge](#):
>
> [https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v4.4.1](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/releases/tag/v4.4.1)

I think v4.4.1 and above.

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 23, 2022, 1:35pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/8 "2022-11-23T13:35:43Z")

</div>

I am currently using v4.8.0, so i shouldn't have any problems right?

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 23, 2022, 1:36pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/9 "2022-11-23T13:36:07Z")

</div>

yet it still wont recognise the command...?

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 24, 2022, 10:23am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/10 "2022-11-24T10:23:02Z")

</div>

Sorry, managed to sort it now. Compiler recognises the command! Thanks @Skyge

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 24, 2022, 6:43pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/11 "2022-11-24T18:43:44Z")

</div>

Hi, can you kindly tell me if the below contract overall is correct? Was i right to comment out the Constructor?

```auto
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract ERC721 is Initializable, ERC721URIStorageUpgradeable, PausableUpgradeable, AccessControlUpgradeable {
  
  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
  bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
  bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
  bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
  
  // Base URI
  string private _baseUri;
  address _forwarder;
  
  mapping(uint256 => string) private _digests;
  mapping(uint256 => string) private _infoRoots;
  
  // Addresses under operator control
  mapping(address => bool) private _operatorEnabled;
  
  // constructor() public ERC721("", "") Pausable() { 

function initialize() onlyInitializing internal {
  
    _baseUri = "";
    _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    _setupRole(PAUSER_ROLE, msg.sender);
    _setupRole(MINTER_ROLE, msg.sender);
    _setupRole(BURNER_ROLE, msg.sender);
    _setupRole(OPERATOR_ROLE, msg.sender);
  }
  
  function pause() external onlyRole(PAUSER_ROLE) {
    _pause();
  }

  function unpause() external onlyRole(PAUSER_ROLE) {
    _unpause();
  }

  /**
   * @dev See {ERC20-_beforeTokenTransfer}.
   * Taken from ERC20Pausable
   *
   * Requirements:
   *
   * - the contract must not be paused.
   */
  function _beforeTokenTransfer(address from, address to, uint256 amount, uint256 batchSize) internal virtual override {
    super._beforeTokenTransfer(from, to, amount, batchSize);
    require(!paused(), "ERC721Pausable: token transfer while paused");
  }
  
  function mint(address to, uint256 tokenId, string memory tokenUri, string memory _digest) public onlyRole(MINTER_ROLE) {
    _mint(to, tokenId);
    _setTokenURI(tokenId, tokenUri);
    _digests[tokenId] = _digest;
  }

  function burn(uint256 tokenId) public onlyRole(BURNER_ROLE) {
    _burn(tokenId);
  }
  
  function setBaseURI(string memory uri) external onlyRole(OPERATOR_ROLE) {
    _baseUri = uri;
  }

  /**
   * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
   * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
   * by default, can be overriden in child contracts.
   */
  function _baseURI() internal view virtual override returns (string memory) {
    return _baseUri;
  }

  function infoRoot(uint256 tokenId) external view virtual returns (string memory) {
    require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token");

    string memory _infoRoot = _infoRoots[tokenId];

    // If there is no infoRoot set, return an empty string.
    if (bytes(_infoRoot).length == 0) {
      return "";
    }

    return _infoRoot;
  }

  function setInfoRoot(uint256 tokenId, string memory _infoRoot) external onlyRole(OPERATOR_ROLE) whenNotPaused() {
    require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
    _infoRoots[tokenId] = _infoRoot;
  }
  
  function digest(uint256 tokenId) external view virtual returns (string memory) {
    require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token");

    string memory _digest = _digests[tokenId];

    // If there is no digest set, return an empty string.
    if (bytes(_digest).length == 0) {
      return "";
    }

    return _digest;
  }
  
  function setdigest(uint256 tokenId, string memory _digest) external onlyRole(OPERATOR_ROLE) whenNotPaused() {
    require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
    _digests[tokenId] = _digest;
  }

  // Operator initiatiated token transfer
  function operatorTransfer(address recipient, uint256 tokenId) external onlyRole(OPERATOR_ROLE) whenNotPaused() returns (bool) {
    address owner = ownerOf(tokenId);
    require(isOperatorControlled(owner), "ERC721: sender not under operator control");
    // Reset appoval
    _approve(msg.sender, tokenId);
    transferFrom(owner, recipient, tokenId);
    return true;
  }
  
  // Address owner can enable their address for operator control
  // Default state is operator disabled
  function enableOperatorControl() external whenNotPaused() returns (bool) {
    require(msgSender() != address(0), "ERC20: owner is a zero address");
    require(!isOperatorControlled(msgSender()), "ERC20: owner already under operator control");
    _operatorEnabled[msgSender()] = true;
    return true;
  }
  
  // Operator role can remove operator control from an address
  function disableOperatorControl(address owner) external onlyRole(OPERATOR_ROLE) whenNotPaused() returns (bool) {
    require(owner != address(0), "ERC721: owner is a zero address");
    require(isOperatorControlled(owner), "ERC721: owner not under operator control");
    _operatorEnabled[owner] = false;
    return true;
  }
  
  function isOperatorControlled(address owner) public view returns (bool) {
    require(owner != address(0), "ERC721: owner is a zero address");
    return _operatorEnabled[owner];
  }

  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721Upgradeable, AccessControlUpgradeable) returns (bool) {
      return super.supportsInterface(interfaceId);
  }

  function msgSender() internal view returns(address sender) {
    if(msg.sender == _forwarder) {
      bytes memory array = msg.data;
      uint256 index = msg.data.length;
      assembly {
          // Load the 32 bytes word from memory with the address on the lower 20 bytes, and mask those.
          sender := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
      }
    } else {
        sender = msg.sender;
    }
    return sender;
  }

  function setForwarder(address forwarder) external onlyRole(OPERATOR_ROLE) returns (bool) {
    _forwarder = forwarder;
    return true;
  }
  
  function getForwarder() external view returns (address) {
    return _forwarder;
  }
}

```

---

<div class="post-metadata">

**Author:** ![Jadedvi104](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/jadedvi104/32/10363_2.png) [@Jadedvi104](https://forum.openzeppelin.com/u/Jadedvi104)\
**Post date:** [November 25, 2022, 2:38pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/12 "2022-11-25T14:38:30Z")

</div>

Why do you need upgradable contracts what are the goals here ?

---

<div class="post-metadata">

**Author:** ![Skyge](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/skyge/32/343_2.png) [@Skyge](https://forum.openzeppelin.com/u/Skyge)\
**Post date:** [November 26, 2022, 2:13am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/13 "2022-11-26T02:13:12Z")

</div>

Ohhh, my fault, it is recommended to use the modifier `initializer`.

---

<div class="post-metadata">

**Author:** ![Skyge](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/skyge/32/343_2.png) [@Skyge](https://forum.openzeppelin.com/u/Skyge)\
**Post date:** [November 26, 2022, 2:16am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/14 "2022-11-26T02:16:30Z")

</div>

You need to write some test cases before deploying contracts, I think no one can guarantee a contract is absolutely safe.

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 26, 2022, 9:28pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/15 "2022-11-26T21:28:57Z")

</div>

Well it’s simply to ensure that if we want to add new features, fix bugs or mitigate attacks, we can do so through the proxy pattern.

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 26, 2022, 9:29pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/16 "2022-11-26T21:29:21Z")

</div>

Sorry, where do I need to place that?

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 26, 2022, 9:31pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/17 "2022-11-26T21:31:32Z")

</div>

Yes of course that’s the plan

---

<div class="post-metadata">

**Author:** ![Skyge](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/skyge/32/343_2.png) [@Skyge](https://forum.openzeppelin.com/u/Skyge)\
**Post date:** [November 28, 2022, 2:17am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/18 "2022-11-28T02:17:52Z")

</div>

> [@Golanger](#):
>
> `function initialize() onlyInitializing internal {`

function initialize() initializer {}

---

<div class="post-metadata">

**Author:** ![Golanger](https://avatars.discourse-cdn.com/v4/letter/g/22d042/32.png) [@Golanger](https://forum.openzeppelin.com/u/Golanger)\
**Post date:** [November 28, 2022, 8:46am UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/19 "2022-11-28T08:46:57Z")

</div>

Don’t you need to use onlyInitializing to mitigate security? Doesn’t that ensure that it can only be called once and also not from other outside contracts/malicious actors?

---

<div class="post-metadata">

**Author:** ![Skyge](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/skyge/32/343_2.png) [@Skyge](https://forum.openzeppelin.com/u/Skyge)\
**Post date:** [November 28, 2022, 3:44pm UTC](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191/20 "2022-11-28T15:44:04Z")

</div>

I am sorry for misleading you, for the final deployed contract, just use `initializer`

[Next page](https://forum.openzeppelin.com/t/upgradeable-nft-smart-contract/33191.md?page=2)
