test: hardhat (#21)

* chore: update hardhat config

* test: add FleekERC721 tests and remove not used SitesNFTs suit

* test: verify ERC721 compatibility

* Content type on second abi.encodePacked call in tokenURI

* Fix abi encoding on tokenURI

* chore: update hardhat config

* test: add FleekERC721 tests and remove not used SitesNFTs suit

* test: verify ERC721 compatibility

* Content type on second abi.encodePacked call in tokenURI

* test: improve assertion using deep equality

* chore: remove 0.4.24 version from hardhat compilers

* refactor: clear empty bytes from bytes32

* refactor: change properties from bytes32 to string

Co-authored-by: janison <jsonsivar@gmail.com>
This commit is contained in:
Felipe Mendes 2022-12-06 17:39:08 -03:00 committed by GitHub
parent 700293b94b
commit ef8baad617
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 5347 additions and 9290 deletions

View File

@ -16,8 +16,8 @@ contract FleekERC721 is ERC721, FleekAccessControl {
event NewTokenName(uint256 indexed token, string indexed name); event NewTokenName(uint256 indexed token, string indexed name);
event NewTokenDescription(uint256 indexed token, string indexed description); event NewTokenDescription(uint256 indexed token, string indexed description);
event NewTokenImage(uint256 indexed token, string indexed image); event NewTokenImage(uint256 indexed token, string indexed image);
event NewTokenExternalURL(uint256 indexed token, bytes32 indexed external_url); event NewTokenExternalURL(uint256 indexed token, string indexed external_url);
event NewTokenENS(uint256 indexed token, bytes32 indexed ENS); event NewTokenENS(uint256 indexed token, string indexed ENS);
struct Build { struct Build {
string commit_hash; string commit_hash;
@ -25,12 +25,17 @@ contract FleekERC721 is ERC721, FleekAccessControl {
string author; string author;
} }
/**
* The properties are stored as string to keep consistency with
* other token contracts, we might consider changing for bytes32
* in the future due to gas optimization
*/
struct App { struct App {
string name; // Name of the site string name; // Name of the site
string description; // Description about the site string description; // Description about the site
string image; // Preview Image IPFS Link string image; // Preview Image IPFS Link
bytes32 external_url; // Site URL string external_url; // Site URL
bytes32 ENS; // ENS ID string ENS; // ENS ID
uint256 current_build; // The current build number (Increments by one with each change, starts at zero) uint256 current_build; // The current build number (Increments by one with each change, starts at zero)
mapping(uint256 => Build) builds; // Mapping to build details for each build number mapping(uint256 => Build) builds; // Mapping to build details for each build number
} }
@ -56,8 +61,8 @@ contract FleekERC721 is ERC721, FleekAccessControl {
string memory name, string memory name,
string memory description, string memory description,
string memory image, string memory image,
bytes32 external_url, string memory external_url,
bytes32 ENS, string memory ENS,
string memory commit_hash, string memory commit_hash,
string memory git_repository, string memory git_repository,
string memory author string memory author
@ -77,7 +82,7 @@ contract FleekERC721 is ERC721, FleekAccessControl {
// The mint interaction is considered to be the first build of the site. Updates from now on all increment the current_build by one and update the mapping. // The mint interaction is considered to be the first build of the site. Updates from now on all increment the current_build by one and update the mapping.
app.current_build = 0; app.current_build = 0;
app.builds[0] = Build(commit_hash, git_repository, author); app.builds[0] = Build(commit_hash, git_repository, author);
return tokenId; return tokenId;
} }
@ -97,12 +102,12 @@ contract FleekERC721 is ERC721, FleekAccessControl {
_requireMinted(tokenId); _requireMinted(tokenId);
address owner = ownerOf(tokenId); address owner = ownerOf(tokenId);
App storage app = _apps[tokenId]; App storage app = _apps[tokenId];
bytes memory dataURI = abi.encodePacked( bytes memory dataURI = abi.encodePacked(
'{', '{',
'"name":"', app.name, '",', '"name":"', app.name, '",',
'"description":"', app.description, '",', '"description":"', app.description, '",',
'"owner":"', abi.encodePacked(owner), '",', '"owner":"', Strings.toHexString(uint160(owner), 20), '",',
'"ENS":"', app.ENS, '",',
'"external_url":"', app.external_url, '",', '"external_url":"', app.external_url, '",',
'"image":"', app.image, '",', '"image":"', app.image, '",',
'"attributes": [', '"attributes": [',
@ -110,12 +115,12 @@ contract FleekERC721 is ERC721, FleekAccessControl {
'{"trait_type": "Commit Hash", "value":"', app.builds[app.current_build].commit_hash,'"},', '{"trait_type": "Commit Hash", "value":"', app.builds[app.current_build].commit_hash,'"},',
'{"trait_type": "Repository", "value":"', app.builds[app.current_build].git_repository,'"},', '{"trait_type": "Repository", "value":"', app.builds[app.current_build].git_repository,'"},',
'{"trait_type": "Author", "value":"', app.builds[app.current_build].author,'"},', '{"trait_type": "Author", "value":"', app.builds[app.current_build].author,'"},',
'{"trait_type": "Version", "value":"', app.current_build,'"}', '{"trait_type": "Version", "value":"', Strings.toString(app.current_build),'"}',
']', ']',
'}' '}'
); );
return string(abi.encodePacked(_baseURI(), Base64.encode(dataURI))); return string(abi.encodePacked(_baseURI(), Base64.encode((dataURI))));
} }
function addTokenController( function addTokenController(
@ -146,8 +151,8 @@ contract FleekERC721 is ERC721, FleekAccessControl {
function setTokenExternalURL( function setTokenExternalURL(
uint256 tokenId, uint256 tokenId,
bytes32 _tokenExternalURL string memory _tokenExternalURL
) public virtual payable requireTokenController(tokenId) { ) public virtual requireTokenController(tokenId) {
_requireMinted(tokenId); _requireMinted(tokenId);
_apps[tokenId].external_url = _tokenExternalURL; _apps[tokenId].external_url = _tokenExternalURL;
emit NewTokenExternalURL(tokenId, _tokenExternalURL); emit NewTokenExternalURL(tokenId, _tokenExternalURL);
@ -155,8 +160,8 @@ contract FleekERC721 is ERC721, FleekAccessControl {
function setTokenENS( function setTokenENS(
uint256 tokenId, uint256 tokenId,
bytes32 _tokenENS string memory _tokenENS
) public virtual payable requireTokenController(tokenId) { ) public virtual requireTokenController(tokenId) {
_requireMinted(tokenId); _requireMinted(tokenId);
_apps[tokenId].ENS = _tokenENS; _apps[tokenId].ENS = _tokenENS;
emit NewTokenENS(tokenId, _tokenENS); emit NewTokenENS(tokenId, _tokenENS);
@ -165,7 +170,7 @@ contract FleekERC721 is ERC721, FleekAccessControl {
function setTokenName( function setTokenName(
uint256 tokenId, uint256 tokenId,
string memory _tokenName string memory _tokenName
) public virtual payable requireTokenController(tokenId) { ) public virtual requireTokenController(tokenId) {
_requireMinted(tokenId); _requireMinted(tokenId);
_apps[tokenId].name = _tokenName; _apps[tokenId].name = _tokenName;
emit NewTokenName(tokenId, _tokenName); emit NewTokenName(tokenId, _tokenName);
@ -174,7 +179,7 @@ contract FleekERC721 is ERC721, FleekAccessControl {
function setTokenDescription( function setTokenDescription(
uint256 tokenId, uint256 tokenId,
string memory _tokenDescription string memory _tokenDescription
) public virtual payable requireTokenController(tokenId) { ) public virtual requireTokenController(tokenId) {
_requireMinted(tokenId); _requireMinted(tokenId);
_apps[tokenId].description = _tokenDescription; _apps[tokenId].description = _tokenDescription;
emit NewTokenDescription(tokenId, _tokenDescription); emit NewTokenDescription(tokenId, _tokenDescription);
@ -183,7 +188,7 @@ contract FleekERC721 is ERC721, FleekAccessControl {
function setTokenImage( function setTokenImage(
uint256 tokenId, uint256 tokenId,
string memory _tokenImage string memory _tokenImage
) public virtual payable requireTokenController(tokenId) { ) public virtual requireTokenController(tokenId) {
_requireMinted(tokenId); _requireMinted(tokenId);
_apps[tokenId].image = _tokenImage; _apps[tokenId].image = _tokenImage;
emit NewTokenImage(tokenId, _tokenImage); emit NewTokenImage(tokenId, _tokenImage);
@ -194,20 +199,22 @@ contract FleekERC721 is ERC721, FleekAccessControl {
string memory _commit_hash, string memory _commit_hash,
string memory _git_repository, string memory _git_repository,
string memory _author string memory _author
) public virtual payable requireTokenController(tokenId) { ) public virtual requireTokenController(tokenId) {
_requireMinted(tokenId); _requireMinted(tokenId);
_apps[tokenId].builds[++_apps[tokenId].current_build] = Build(_commit_hash, _git_repository, _author); _apps[tokenId].builds[++_apps[tokenId].current_build] = Build(_commit_hash, _git_repository, _author);
emit NewBuild(tokenId, _commit_hash); emit NewBuild(tokenId, _commit_hash);
} }
function burn(uint256 tokenId) public virtual payable requireTokenController(tokenId) { function burn(
uint256 tokenId
) public virtual requireTokenController(tokenId) {
require( require(
ownerOf(tokenId) == msg.sender, ownerOf(tokenId) == msg.sender,
"FleekERC721: must be token owner" "FleekERC721: must be token owner"
); );
super._burn(tokenId); super._burn(tokenId);
if (_apps[tokenId].external_url.length != 0) { if (bytes(_apps[tokenId].external_url).length != 0) {
delete _apps[tokenId]; delete _apps[tokenId];
} }
} }

View File

@ -1,123 +0,0 @@
require('@nomiclabs/hardhat-waffle');
require('@nomiclabs/hardhat-etherscan');
require('hardhat-deploy');
require('solidity-coverage');
require('hardhat-gas-reporter');
require('hardhat-contract-sizer');
require('dotenv').config();
/**
* @type import('hardhat/config').HardhatUserConfig
*/
const MAINNET_RPC_URL =
process.env.MAINNET_RPC_URL ||
process.env.ALCHEMY_MAINNET_RPC_URL ||
'https://eth-mainnet.alchemyapi.io/v2/your-api-key';
const GOERLI_RPC_URL =
process.env.GOERLI_RPC_URL ||
'https://eth-goerli.alchemyapi.io/v2/your-api-key';
const API_URL =
process.env.API_URL ||
'https://polygon-mainnet.alchemyapi.io/v2/your-api-key';
const POLYGON_MUMBAI_RPC_URL =
process.env.POLYGON_MUMBAI_RPC_URL ||
'https://polygon-mumbai.g.alchemy.com/v2/aIjNlC4r4aLYOHrdCTFT_JUX6OJsOsu0';
const PRIVATE_KEY = process.env.PRIVATE_KEY || '0x';
// optional
const MNEMONIC = process.env.MNEMONIC || 'your mnemonic';
// Your API key for Etherscan, obtain one at https://etherscan.io/
const ETHERSCAN_API_KEY =
process.env.ETHERSCAN_API_KEY || 'Your etherscan API key';
const POLYGONSCAN_API_KEY =
process.env.POLYGONSCAN_API_KEY || 'Your polygonscan API key';
const REPORT_GAS = process.env.REPORT_GAS || false;
module.exports = {
defaultNetwork: 'hardhat',
networks: {
hardhat: {
// // If you want to do some forking, uncomment this
// forking: {
// url: MAINNET_RPC_URL
// }
chainId: 31337,
},
localhost: {
chainId: 31337,
},
// goerli: {
// url: GOERLI_RPC_URL,
// accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
// // accounts: {
// // mnemonic: MNEMONIC,
// // },
// saveDeployments: true,
// chainId: 5,
// },
// mainnet: {
// url: MAINNET_RPC_URL,
// accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
// // accounts: {
// // mnemonic: MNEMONIC,
// // },
// saveDeployments: true,
// chainId: 1,
// },
// polygon: {
// url: POLYGON_MAINNET_RPC_URL,
// accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
// saveDeployments: true,
// chainId: 137,
// },
polygonMumbai: {
url: API_URL,
accounts: PRIVATE_KEY !== undefined ? [PRIVATE_KEY] : [],
saveDeployments: true,
chainId: 80001,
},
},
etherscan: {
// npx hardhat verify --network <NETWORK> <CONTRACT_ADDRESS> <CONSTRUCTOR_PARAMETERS>
apiKey: {
goerli: ETHERSCAN_API_KEY,
polygon: process.env.POLYSCAN_API,
polygonMumbai: process.env.POLYSCAN_API,
},
},
gasReporter: {
enabled: REPORT_GAS,
currency: 'USD',
outputFile: 'gas-report.txt',
noColors: true,
// coinmarketcap: process.env.COINMARKETCAP_API_KEY,
},
contractSizer: {
runOnCompile: false,
only: ['NftMarketplace'],
},
namedAccounts: {
deployer: {
default: 1, // here this will by default take the first account as deployer
1: 0,
},
privateKey: {
default: `privatekey://${PRIVATE_KEY}`,
},
},
solidity: {
compilers: [
{
version: '0.8.7',
},
{
version: '0.4.24',
},
],
},
mocha: {
timeout: 200000, // 200 seconds max for running tests
},
};

67
hardhat.config.ts Normal file
View File

@ -0,0 +1,67 @@
import '@nomiclabs/hardhat-ethers';
import '@nomiclabs/hardhat-web3';
import '@nomicfoundation/hardhat-chai-matchers';
import 'hardhat-deploy';
import 'solidity-coverage';
import 'hardhat-gas-reporter';
import 'hardhat-contract-sizer';
import * as dotenv from 'dotenv';
import { HardhatUserConfig } from 'hardhat/types';
dotenv.config();
const {
API_URL = 'https://polygon-mainnet.alchemyapi.io/v2/your-api-key',
PRIVATE_KEY,
REPORT_GAS,
} = process.env;
const config: HardhatUserConfig = {
defaultNetwork: 'hardhat',
networks: {
hardhat: {
chainId: 31337,
},
localhost: {
chainId: 31337,
},
polygonMumbai: {
url: API_URL,
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
saveDeployments: true,
chainId: 80001,
},
},
gasReporter: {
enabled: REPORT_GAS === 'true' || false,
currency: 'USD',
outputFile: 'gas-report.txt',
noColors: true,
// coinmarketcap: process.env.COINMARKETCAP_API_KEY,
},
contractSizer: {
runOnCompile: false,
only: ['NftMarketplace'],
},
namedAccounts: {
deployer: {
default: 1, // here this will by default take the first account as deployer
1: 0,
},
privateKey: {
default: `privatekey://${PRIVATE_KEY}`,
},
},
solidity: {
compilers: [
{
version: '0.8.7',
},
],
},
mocha: {
timeout: 200000, // 200 seconds max for running tests
},
};
export default config;

View File

@ -23,14 +23,16 @@
}, },
"homepage": "https://github.com/FleekHQ/contracts#readme", "homepage": "https://github.com/FleekHQ/contracts#readme",
"devDependencies": { "devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
"@nomicfoundation/hardhat-network-helpers": "^1.0.7",
"@nomicfoundation/hardhat-toolbox": "^2.0.0", "@nomicfoundation/hardhat-toolbox": "^2.0.0",
"@nomiclabs/hardhat-ethers": "^2.2.1", "@nomiclabs/hardhat-ethers": "^2.2.1",
"@nomiclabs/hardhat-etherscan": "^3.1.0", "@nomiclabs/hardhat-etherscan": "^3.1.0",
"@nomiclabs/hardhat-waffle": "^2.0.3", "@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/contracts": "^4.7.3", "@openzeppelin/contracts": "^4.7.3",
"@types/mocha": "^10.0.1",
"chai": "^4.3.6", "chai": "^4.3.6",
"dotenv": "^16.0.2", "dotenv": "^16.0.2",
"ethereum-waffle": "^3.4.4",
"ethers": "^5.7.2", "ethers": "^5.7.2",
"hardhat": "^2.11.2", "hardhat": "^2.11.2",
"hardhat-contract-sizer": "^2.6.1", "hardhat-contract-sizer": "^2.6.1",
@ -39,6 +41,9 @@
"minimist": "^1.2.7", "minimist": "^1.2.7",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0", "prettier-plugin-solidity": "^1.0.0",
"solidity-coverage": "^0.8.2" "solidity-coverage": "^0.8.2",
"ts-node": "^10.9.1",
"typescript": "^4.9.3",
"web3": "^1.8.1"
} }
} }

175
test/FleekERC721.ts Normal file
View File

@ -0,0 +1,175 @@
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { expect } from 'chai';
import { ethers } from 'hardhat';
import web3 from 'web3';
describe('FleekERC721', () => {
const COLLECTION_OWNER_ROLE = web3.utils.keccak256('COLLECTION_OWNER_ROLE');
const MINT_PARAMS = Object.freeze({
name: 'Fleek Test App',
description: 'Fleek Test App Description',
image: 'https://fleek.co/image.png',
ens: 'fleek.eth',
externalUrl: 'https://fleek.co',
commitHash: 'b72e47171746b6a9e29b801af9cb655ecf4d665c',
gitRepository: 'https://github.com/fleekxyz/contracts',
author: 'author',
});
const COLLECTION_PARAMS = Object.freeze({
name: 'FleekERC721',
symbol: 'FLEEK',
});
const defaultFixture = async () => {
// Contracts are deployed using the first signer/account by default
const [owner, otherAccount] = await ethers.getSigners();
const Contract = await ethers.getContractFactory('FleekERC721');
const contract = await Contract.deploy(
COLLECTION_PARAMS.name,
COLLECTION_PARAMS.symbol
);
return { owner, otherAccount, contract };
};
describe('Deployment', () => {
it('should assign the name and the symbol of the ERC721 contract', async () => {
const { contract } = await loadFixture(defaultFixture);
expect(await contract.name()).to.equal(COLLECTION_PARAMS.name);
expect(await contract.symbol()).to.equal(COLLECTION_PARAMS.symbol);
});
it('should assign the owner of the contract', async () => {
const { owner, contract } = await loadFixture(defaultFixture);
expect(
await contract.hasRole(COLLECTION_OWNER_ROLE, owner.address)
).to.equal(true);
});
it('should support ERC721 interface', async () => {
const { contract } = await loadFixture(defaultFixture);
expect(await contract.supportsInterface('0x80ac58cd')).to.equal(true);
});
});
describe('Minting', () => {
it('should be able to mint a new token', async () => {
const { owner, contract } = await loadFixture(defaultFixture);
const response = await contract.mint(
owner.address,
MINT_PARAMS.name,
MINT_PARAMS.description,
MINT_PARAMS.image,
MINT_PARAMS.externalUrl,
MINT_PARAMS.ens,
MINT_PARAMS.commitHash,
MINT_PARAMS.gitRepository,
MINT_PARAMS.author
);
expect(response.value).to.be.instanceOf(ethers.BigNumber);
expect(response.value.toNumber()).to.equal(0);
});
it('should not be able to mint a new token if not the owner', async () => {
const { otherAccount, contract } = await loadFixture(defaultFixture);
await expect(
contract
.connect(otherAccount)
.mint(
otherAccount.address,
MINT_PARAMS.name,
MINT_PARAMS.description,
MINT_PARAMS.image,
MINT_PARAMS.externalUrl,
MINT_PARAMS.ens,
MINT_PARAMS.commitHash,
MINT_PARAMS.gitRepository,
MINT_PARAMS.author
)
).to.be.revertedWith(
'FleekAccessControl: must have collection owner role'
);
});
});
describe('Token', () => {
let tokenId: number;
let fixture: Awaited<ReturnType<typeof defaultFixture>>;
before(async () => {
fixture = await loadFixture(defaultFixture);
const { contract } = fixture;
const response = await contract.mint(
fixture.owner.address,
MINT_PARAMS.name,
MINT_PARAMS.description,
MINT_PARAMS.image,
MINT_PARAMS.externalUrl,
MINT_PARAMS.ens,
MINT_PARAMS.commitHash,
MINT_PARAMS.gitRepository,
MINT_PARAMS.author
);
tokenId = response.value.toNumber();
});
it('should return the token URI', async () => {
const { contract } = fixture;
const tokenURI = await contract.tokenURI(tokenId);
const tokenURIDecoded = Buffer.from(
tokenURI.replace('data:application/json;base64,', ''),
'base64'
).toString('ascii');
const parsedURI = JSON.parse(tokenURIDecoded);
expect(parsedURI).to.eql({
owner: fixture.owner.address.toLowerCase(),
name: MINT_PARAMS.name,
description: MINT_PARAMS.description,
image: MINT_PARAMS.image,
external_url: MINT_PARAMS.externalUrl,
attributes: [
{
trait_type: 'ENS',
value: MINT_PARAMS.ens,
},
{
trait_type: 'Commit Hash',
value: MINT_PARAMS.commitHash,
},
{
trait_type: 'Repository',
value: MINT_PARAMS.gitRepository,
},
{
trait_type: 'Author',
value: MINT_PARAMS.author,
},
{
trait_type: 'Version',
value: '0',
},
],
});
});
it('should match the token owner', async () => {
const { contract, owner } = fixture;
const tokenOwner = await contract.ownerOf(tokenId);
expect(tokenOwner).to.equal(owner.address);
});
});
});

View File

@ -1,297 +0,0 @@
const { expect } = require('chai');
describe('SitesNFTs contract', function () {
describe('Deployment', () => {
it('Deployment should assign the name and the symbol of the ERC721 contract', async () => {
const [owner] = await ethers.getSigners();
const name = 'Sites NFTs';
const symbol = 'SNFT';
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const contractName = await hardhatSitesNFTs.name();
const contractSymbol = await hardhatSitesNFTs.symbol();
expect(contractName).to.equal(name);
expect(contractSymbol).to.equal(symbol);
});
it('Deployment should assign the deployer DEFAULT_ADMIN_ROLE', async () => {
const [owner] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const DEFAULT_ADMIN_ROLE_STRING = '';
const hasAdminRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE_STRING),
await owner.getAddress()
);
expect(hasAdminRole).to.equal(true);
});
it('Deployment should assign initial tokenId to 0', async () => {
const [owner] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const currentTokenId = await hardhatSitesNFTs.getCurrentTokenId();
expect(currentTokenId).to.equal(0);
});
});
describe('Access control', () => {
it('User with DEFAULT_ADMIN_ROLE should be able to assign MINTER_ROLE to another user', async () => {
const [owner, address1] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const MINTER_ROLE = 'MINTER_ROLE';
await hardhatSitesNFTs.grantRole(
ethers.utils.formatBytes32String(MINTER_ROLE),
await address1.getAddress()
);
const hasMinterRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(MINTER_ROLE),
await address1.getAddress()
);
expect(hasMinterRole).to.equal(true);
});
it('User with DEFAULT_ADMIN_ROLE should be able to assign MINTER_ROLE to himself and still have DEFAULT_ADMIN_ROLE', async () => {
const [owner] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const MINTER_ROLE = 'MINTER_ROLE';
const DEFAULT_ADMIN_ROLE = '';
await hardhatSitesNFTs.grantRole(
ethers.utils.formatBytes32String(MINTER_ROLE),
await owner.getAddress()
);
const hasMinterRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(MINTER_ROLE),
await owner.getAddress()
);
const hasAdminRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
await owner.getAddress()
);
expect(hasMinterRole).to.equal(true);
expect(hasAdminRole).to.equal(true);
});
it('User with DEFAULT_ADMIN_ROLE should be able to assign DEFAULT_ADMIN_ROLE to another user', async () => {
const [owner, address1] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const DEFAULT_ADMIN_ROLE = '';
await hardhatSitesNFTs.grantRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
await address1.getAddress()
);
const hasAdminRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
await address1.getAddress()
);
expect(hasAdminRole).to.equal(true);
});
it('User with DEFAULT_ADMIN_ROLE should be able to assign DEFAULT_ADMIN_ROLE to another user and still have DEFAULT_ADMIN_ROLE', async () => {
const [owner, address1] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const DEFAULT_ADMIN_ROLE = '';
await hardhatSitesNFTs.grantRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
await address1.getAddress()
);
let hasAdminRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
await address1.getAddress()
);
expect(hasAdminRole).to.equal(true);
hasAdminRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
await owner.getAddress()
);
expect(hasAdminRole).to.equal(true);
});
it('User without DEFAULT_ADMIN_ROLE shouldnt be able to assign DEFAULT_ADMIN_ROLE to another user', async () => {
const [owner, address1, address2] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const DEFAULT_ADMIN_ROLE = '';
try {
await hardhatSitesNFTs
.connect(address1)
.grantRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
await address2.getAddress()
);
} catch (e) {}
const hasAdminRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(DEFAULT_ADMIN_ROLE),
await address2.getAddress()
);
expect(hasAdminRole).to.equal(false);
});
it('User without DEFAULT_ADMIN_ROLE shouldnt be able to assign MINTER_ROLE to another user', async () => {
const [owner, address1, address2] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const MINTER_ROLE = 'MINTER_ROLE';
try {
await hardhatSitesNFTs
.connect(address1)
.grantRole(
ethers.utils.formatBytes32String(MINTER_ROLE),
await address2.getAddress()
);
} catch (e) {}
const hasMinterRole = await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(MINTER_ROLE),
await address2.getAddress()
);
expect(hasMinterRole).to.equal(false);
});
});
describe('Minting', () => {
it('User with DEFAULT_ADMIN_ROLE should be able to mint', async () => {
const [owner, address1] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const tokenURI = 'tokenURI';
await hardhatSitesNFTs.mint(tokenURI, await address1.getAddress());
const balance = await hardhatSitesNFTs.balanceOf(
await address1.getAddress()
);
expect(balance).to.equal(1);
});
it('User with MINTER_ROLE should be able to mint', async () => {
const [owner, address1, address2] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const MINTER_ROLE = 'MINTER_ROLE';
const tokenURI = 'tokenURI';
await hardhatSitesNFTs.grantRole(
ethers.utils.formatBytes32String(MINTER_ROLE),
await address1.getAddress()
);
await hardhatSitesNFTs.hasRole(
ethers.utils.formatBytes32String(MINTER_ROLE),
await address1.getAddress()
);
await hardhatSitesNFTs
.connect(address1)
.mint(tokenURI, await address2.getAddress());
const balance = await hardhatSitesNFTs.balanceOf(
await address2.getAddress()
);
expect(balance).to.equal(1);
});
it('User without MINTER_ROLE or DEFAULT_ADMIN_ROLE shouldnt be able to mint', async () => {
const [owner, address1, address2] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const tokenURI = 'tokenURI';
try {
await hardhatSitesNFTs
.connect(address1)
.mint(tokenURI, await address2.getAddress());
} catch (e) {}
const balance = await hardhatSitesNFTs.balanceOf(
await address2.getAddress()
);
expect(balance).to.equal(0);
});
it('Minted NFT should have data:application/json;base64, baseURI', async () => {
const [owner, address1] = await ethers.getSigners();
const SitesNFTs = await ethers.getContractFactory('SitesNFTs');
const hardhatSitesNFTs = await SitesNFTs.deploy('Sites NFTs', 'SNFT');
const tokenURI = 'tokenURI';
await hardhatSitesNFTs.mint(tokenURI, await address1.getAddress());
const mintedNFT = await hardhatSitesNFTs.tokenURI(0);
expect(mintedNFT.includes('data:application/json;base64,')).to.equal(
true
);
});
});
});

13915
yarn.lock

File diff suppressed because it is too large Load Diff