You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.3 KiB

Using with Upgrades

If you are deploying upgradeable contracts, such as using OpenZeppelin Upgrade Plugins, you will need to use the upgradeable variant of ERC721A.

For more information, please refer to OpenZeppelin's documentation.

Since v4, the upgradeable variant uses the Diamond storage pattern as defined in EIP-2535.

Installation

npm install --save-dev erc721a-upgradeable

Usage

The package shares the same directory layout as the main ERC721A package, but every file and contract has the suffix Upgradeable.

Constructors are replaced by internal initializer functions following the naming convention __{ContractName}__init.

These functions are internal, and you must define your own public initializer function that calls the parent class' initializer.

pragma solidity ^0.8.4;

import 'erc721a-upgradeable/contracts/ERC721AUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';

contract Something is ERC721AUpgradeable, OwnableUpgradeable {
    // Take note of the initializer modifiers.
    // - `initializerERC721A` for `ERC721AUpgradeable`.
    // - `initializer` for OpenZeppelin's `OwnableUpgradeable`.
    function initialize() initializerERC721A initializer public {
        __ERC721A_init('Something', 'SMTH');
        __Ownable_init();
    }

    function mint(uint256 quantity) external payable {
        // `_mint`'s second argument now takes in a `quantity`, not a `tokenId`.
        _mint(msg.sender, quantity);
    }

    function adminMint(uint256 quantity) external payable onlyOwner {
        _mint(msg.sender, quantity);
    }
}

If using with another upgradeable library, please do use their respective initializer modifier on the initialize() function, in addition to the initializerERC721A modifier.

Deployment

If you are using hardhat, you can deploy it using OpenZeppelin Upgrade Plugins.

npm install --save-dev @openzeppelin/hardhat-upgrades

Deploy Script

// scripts/deploy.js
const { ethers, upgrades } = require('hardhat');
const fs = require('fs');

async function main () {
    const Something = await ethers.getContractFactory('Something');
    console.log('Deploying...');
    const something = await upgrades.deployProxy(
        Something, 
        [], 
        { initializer: 'initialize' }
    );
    await something.deployed();
    const addresses = {
        proxy: something.address,
        admin: await upgrades.erc1967.getAdminAddress(something.address), 
        implementation: await upgrades.erc1967.getImplementationAddress(
            something.address)
    };
    console.log('Addresses:', addresses);

    try { 
        await run('verify', { address: addresses.implementation });
    } catch (e) {}

    fs.writeFileSync('deployment-addresses.json', JSON.stringify(addresses));
}

main();

Upgrade Script

// scripts/upgrade.js
const { ethers, upgrades } = require('hardhat');
const fs = require('fs');

async function main () {
    const Something = await ethers.getContractFactory('Something');
    console.log('Upgrading...');
    let addresses = JSON.parse(fs.readFileSync('deployment-addresses.json'));
    await upgrades.upgradeProxy(addresses.proxy, Something);
    console.log('Upgraded');

    addresses = {
        proxy: addresses.proxy,
        admin: await upgrades.erc1967.getAdminAddress(addresses.proxy), 
        implementation: await upgrades.erc1967.getImplementationAddress(
            addresses.proxy)
    };
    console.log('Addresses:', addresses);
    
    try { 
        await run('verify', { address: addresses.implementation });
    } catch (e) {}

    fs.writeFileSync('deployment-addresses.json', JSON.stringify(addresses));
}

main();

Local

Add the following to your hardhat.config.js:

// hardhat.config.js
require("@nomiclabs/hardhat-waffle");
require('@openzeppelin/hardhat-upgrades');

module.exports = {
    solidity: "0.8.11"
};

Deploy

npx hardhat run --network localhost scripts/deploy.js

Upgrade

npx hardhat run --network localhost scripts/upgrade.js

Testnet / Mainnet

We will use the Goerli testnet as an example.

Install the following packages if they are not already installed:

npm install --save-dev @nomiclabs/hardhat-etherscan
npm install --save-dev dotenv

Add the following to your environment file .env:

ETHERSCAN_KEY="Your Etherscan API Key"
PRIVATE_KEY="Your Wallet Private Key"
RPC_URL_GOERLI="https://Infura Or Alchemy URL With API Key"

Add the following to your hardhat.config.js:

// hardhat.config.js
require("@nomiclabs/hardhat-waffle");
require('dotenv').config();
require('@openzeppelin/hardhat-upgrades');
require("@nomiclabs/hardhat-etherscan");

module.exports = {
	solidity: "0.8.11",
	networks: {
		goerli: {
			url: process.env.RPC_URL_GOERLI,
			accounts: [process.env.PRIVATE_KEY]
		}
	},
	etherscan: {
		// Your API key for Etherscan
		// Obtain one at https://etherscan.io/
		apiKey: process.env.ETHERSCAN_KEY
	}
};

Deploy

npx hardhat run --network goerli scripts/deploy.js

Upgrade

npx hardhat run --network goerli scripts/upgrade.js