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.
210 lines
5.3 KiB
Markdown
210 lines
5.3 KiB
Markdown
# Using with Upgrades
|
|
|
|
If you are deploying upgradeable contracts,
|
|
such as using [OpenZeppelin Upgrade Plugins](https://docs.openzeppelin.com/upgrades-plugins/1.x/),
|
|
you will need to use the upgradeable variant of ERC721A.
|
|
|
|
For more information, please refer to
|
|
[OpenZeppelin's documentation](https://docs.openzeppelin.com/contracts/4.x/upgradeable).
|
|
|
|
Since v4, the upgradeable variant uses the Diamond storage pattern as defined in [EIP-2535](https://eips.ethereum.org/EIPS/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.
|
|
|
|
```solidity
|
|
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](https://docs.openzeppelin.com/upgrades-plugins/1.x/).
|
|
|
|
```
|
|
npm install --save-dev @openzeppelin/hardhat-upgrades
|
|
```
|
|
|
|
**Deploy Script**
|
|
|
|
```javascript
|
|
// 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**
|
|
|
|
```javascript
|
|
// 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`:
|
|
|
|
```javascript
|
|
// 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`:
|
|
|
|
```javascript
|
|
// 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
|
|
```
|