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:
parent
700293b94b
commit
ef8baad617
|
|
@ -16,8 +16,8 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
event NewTokenName(uint256 indexed token, string indexed name);
|
||||
event NewTokenDescription(uint256 indexed token, string indexed description);
|
||||
event NewTokenImage(uint256 indexed token, string indexed image);
|
||||
event NewTokenExternalURL(uint256 indexed token, bytes32 indexed external_url);
|
||||
event NewTokenENS(uint256 indexed token, bytes32 indexed ENS);
|
||||
event NewTokenExternalURL(uint256 indexed token, string indexed external_url);
|
||||
event NewTokenENS(uint256 indexed token, string indexed ENS);
|
||||
|
||||
struct Build {
|
||||
string commit_hash;
|
||||
|
|
@ -25,12 +25,17 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
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 {
|
||||
string name; // Name of the site
|
||||
string description; // Description about the site
|
||||
string image; // Preview Image IPFS Link
|
||||
bytes32 external_url; // Site URL
|
||||
bytes32 ENS; // ENS ID
|
||||
string external_url; // Site URL
|
||||
string ENS; // ENS ID
|
||||
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
|
||||
}
|
||||
|
|
@ -56,8 +61,8 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
string memory name,
|
||||
string memory description,
|
||||
string memory image,
|
||||
bytes32 external_url,
|
||||
bytes32 ENS,
|
||||
string memory external_url,
|
||||
string memory ENS,
|
||||
string memory commit_hash,
|
||||
string memory git_repository,
|
||||
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.
|
||||
app.current_build = 0;
|
||||
app.builds[0] = Build(commit_hash, git_repository, author);
|
||||
|
||||
|
||||
return tokenId;
|
||||
}
|
||||
|
||||
|
|
@ -97,12 +102,12 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
_requireMinted(tokenId);
|
||||
address owner = ownerOf(tokenId);
|
||||
App storage app = _apps[tokenId];
|
||||
|
||||
bytes memory dataURI = abi.encodePacked(
|
||||
'{',
|
||||
'"name":"', app.name, '",',
|
||||
'"description":"', app.description, '",',
|
||||
'"owner":"', abi.encodePacked(owner), '",',
|
||||
'"ENS":"', app.ENS, '",',
|
||||
'"owner":"', Strings.toHexString(uint160(owner), 20), '",',
|
||||
'"external_url":"', app.external_url, '",',
|
||||
'"image":"', app.image, '",',
|
||||
'"attributes": [',
|
||||
|
|
@ -110,12 +115,12 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
'{"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": "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(
|
||||
|
|
@ -146,8 +151,8 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
|
||||
function setTokenExternalURL(
|
||||
uint256 tokenId,
|
||||
bytes32 _tokenExternalURL
|
||||
) public virtual payable requireTokenController(tokenId) {
|
||||
string memory _tokenExternalURL
|
||||
) public virtual requireTokenController(tokenId) {
|
||||
_requireMinted(tokenId);
|
||||
_apps[tokenId].external_url = _tokenExternalURL;
|
||||
emit NewTokenExternalURL(tokenId, _tokenExternalURL);
|
||||
|
|
@ -155,8 +160,8 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
|
||||
function setTokenENS(
|
||||
uint256 tokenId,
|
||||
bytes32 _tokenENS
|
||||
) public virtual payable requireTokenController(tokenId) {
|
||||
string memory _tokenENS
|
||||
) public virtual requireTokenController(tokenId) {
|
||||
_requireMinted(tokenId);
|
||||
_apps[tokenId].ENS = _tokenENS;
|
||||
emit NewTokenENS(tokenId, _tokenENS);
|
||||
|
|
@ -165,7 +170,7 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
function setTokenName(
|
||||
uint256 tokenId,
|
||||
string memory _tokenName
|
||||
) public virtual payable requireTokenController(tokenId) {
|
||||
) public virtual requireTokenController(tokenId) {
|
||||
_requireMinted(tokenId);
|
||||
_apps[tokenId].name = _tokenName;
|
||||
emit NewTokenName(tokenId, _tokenName);
|
||||
|
|
@ -174,7 +179,7 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
function setTokenDescription(
|
||||
uint256 tokenId,
|
||||
string memory _tokenDescription
|
||||
) public virtual payable requireTokenController(tokenId) {
|
||||
) public virtual requireTokenController(tokenId) {
|
||||
_requireMinted(tokenId);
|
||||
_apps[tokenId].description = _tokenDescription;
|
||||
emit NewTokenDescription(tokenId, _tokenDescription);
|
||||
|
|
@ -183,7 +188,7 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
function setTokenImage(
|
||||
uint256 tokenId,
|
||||
string memory _tokenImage
|
||||
) public virtual payable requireTokenController(tokenId) {
|
||||
) public virtual requireTokenController(tokenId) {
|
||||
_requireMinted(tokenId);
|
||||
_apps[tokenId].image = _tokenImage;
|
||||
emit NewTokenImage(tokenId, _tokenImage);
|
||||
|
|
@ -194,20 +199,22 @@ contract FleekERC721 is ERC721, FleekAccessControl {
|
|||
string memory _commit_hash,
|
||||
string memory _git_repository,
|
||||
string memory _author
|
||||
) public virtual payable requireTokenController(tokenId) {
|
||||
) public virtual requireTokenController(tokenId) {
|
||||
_requireMinted(tokenId);
|
||||
_apps[tokenId].builds[++_apps[tokenId].current_build] = Build(_commit_hash, _git_repository, _author);
|
||||
emit NewBuild(tokenId, _commit_hash);
|
||||
}
|
||||
|
||||
function burn(uint256 tokenId) public virtual payable requireTokenController(tokenId) {
|
||||
function burn(
|
||||
uint256 tokenId
|
||||
) public virtual requireTokenController(tokenId) {
|
||||
require(
|
||||
ownerOf(tokenId) == msg.sender,
|
||||
"FleekERC721: must be token owner"
|
||||
);
|
||||
super._burn(tokenId);
|
||||
|
||||
if (_apps[tokenId].external_url.length != 0) {
|
||||
if (bytes(_apps[tokenId].external_url).length != 0) {
|
||||
delete _apps[tokenId];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
};
|
||||
|
|
@ -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;
|
||||
11
package.json
11
package.json
|
|
@ -23,14 +23,16 @@
|
|||
},
|
||||
"homepage": "https://github.com/FleekHQ/contracts#readme",
|
||||
"devDependencies": {
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.7",
|
||||
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
|
||||
"@nomiclabs/hardhat-ethers": "^2.2.1",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.1.0",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.3",
|
||||
"@nomiclabs/hardhat-web3": "^2.0.0",
|
||||
"@openzeppelin/contracts": "^4.7.3",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"chai": "^4.3.6",
|
||||
"dotenv": "^16.0.2",
|
||||
"ethereum-waffle": "^3.4.4",
|
||||
"ethers": "^5.7.2",
|
||||
"hardhat": "^2.11.2",
|
||||
"hardhat-contract-sizer": "^2.6.1",
|
||||
|
|
@ -39,6 +41,9 @@
|
|||
"minimist": "^1.2.7",
|
||||
"prettier": "^2.7.1",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue