@vechain/vebetterdao-contracts
Version:
Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.
282 lines (281 loc) • 21.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const mocha_1 = require("mocha");
const helpers_1 = require("./helpers");
const chai_1 = require("chai");
const hardhat_1 = require("hardhat");
const local_1 = require("@repo/config/contracts/envs/local");
const upgrades_core_1 = require("@openzeppelin/upgrades-core");
const helpers_2 = require("../scripts/helpers");
(0, mocha_1.describe)("X2EarnCreator - @shard11", () => {
(0, mocha_1.describe)("Contract parameters", () => {
(0, mocha_1.it)("Should have correct parameters set on deployment", async () => {
const { x2EarnCreator, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
(0, chai_1.expect)(await x2EarnCreator.name()).to.equal("X2EarnCreator");
(0, chai_1.expect)(await x2EarnCreator.symbol()).to.equal("X2C");
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.DEFAULT_ADMIN_ROLE(), await owner.getAddress())).to.equal(true);
});
(0, mocha_1.it)("Should support ERC 165 interface", async () => {
const { x2EarnCreator } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
(0, chai_1.expect)(await x2EarnCreator.supportsInterface("0x01ffc9a7")).to.equal(true); // ERC165
});
});
(0, mocha_1.describe)("Pausing", () => {
(0, mocha_1.it)("Only PAUSER_ROLE or DEFAULT_ADMIN_ROLE should be able to pause and unpause the contract", async () => {
const { x2EarnCreator, otherAccount, otherAccounts, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const pauser = otherAccounts[0];
await x2EarnCreator.grantRole(await x2EarnCreator.PAUSER_ROLE(), pauser.address);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.PAUSER_ROLE(), otherAccount.address)).to.eql(false);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.PAUSER_ROLE(), pauser.address)).to.eql(true);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.DEFAULT_ADMIN_ROLE(), owner.address)).to.eql(true);
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(otherAccount).pause());
// DEFAULT_ADMIN_ROLE
await (0, chai_1.expect)(x2EarnCreator.connect(owner).pause()).to.emit(x2EarnCreator, "Paused");
(0, chai_1.expect)(await x2EarnCreator.paused()).to.equal(true);
await (0, chai_1.expect)(x2EarnCreator.connect(owner).unpause()).to.emit(x2EarnCreator, "Unpaused");
(0, chai_1.expect)(await x2EarnCreator.paused()).to.equal(false);
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(otherAccount).unpause());
// PAUSER_ROLE
await x2EarnCreator.connect(pauser).pause();
(0, chai_1.expect)(await x2EarnCreator.paused()).to.equal(true);
await x2EarnCreator.connect(pauser).unpause();
(0, chai_1.expect)(await x2EarnCreator.paused()).to.equal(false);
});
(0, mocha_1.it)("Should not allow tokens to be minted when paused", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).pause();
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(owner).safeMint(otherAccount.address));
});
});
(0, mocha_1.describe)("Base URI", () => {
(0, mocha_1.it)("Should be able to set the base URI", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// normal user
await (0, chai_1.expect)(x2EarnCreator.connect(otherAccount).setBaseURI("ipfs://BASE_URI")).to.be.reverted;
// default admin
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.DEFAULT_ADMIN_ROLE(), owner.address)).to.equal(true);
await (0, chai_1.expect)(x2EarnCreator.connect(owner).setBaseURI("ipfs://BASE_URI2")).to.not.be.reverted;
(0, chai_1.expect)(await x2EarnCreator.baseURI()).to.equal("ipfs://BASE_URI2");
});
});
(0, mocha_1.describe)("Upgrading", () => {
(0, mocha_1.it)("Admin should be able to upgrade the contract", async function () {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
// Deploy the implementation contract
const Contract = await hardhat_1.ethers.getContractFactory("X2EarnCreator");
const implementation = await Contract.deploy();
await implementation.waitForDeployment();
const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await x2EarnCreator.getAddress());
await x2EarnCreator.grantRole(await x2EarnCreator.UPGRADER_ROLE(), owner.address);
const UPGRADER_ROLE = await x2EarnCreator.UPGRADER_ROLE();
(0, chai_1.expect)(await x2EarnCreator.hasRole(UPGRADER_ROLE, owner.address)).to.eql(true);
// If non-admin tries to upgrade, it should fail
await (0, chai_1.expect)(x2EarnCreator.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to.be
.reverted;
await (0, chai_1.expect)(x2EarnCreator.connect(owner).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not.be
.reverted;
const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await x2EarnCreator.getAddress());
(0, chai_1.expect)(newImplAddress.toUpperCase()).to.not.eql(currentImplAddress.toUpperCase());
(0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql((await implementation.getAddress()).toUpperCase());
});
(0, mocha_1.it)("should return the correct version", async function () {
const { x2EarnCreator } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const version = await x2EarnCreator.version();
(0, chai_1.expect)(version).to.equal("2");
});
(0, mocha_1.it)("Should not be able to initialize the contract after it has already been initialized", async function () {
const config = (0, local_1.createLocalConfig)();
const { x2EarnCreator, owner } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
await (0, chai_1.expect)(x2EarnCreator.initialize(config.CREATOR_NFT_URI, owner.address)).to.be.reverted; // already initialized
});
(0, mocha_1.it)("V1 to V2 upgrade should preserve storage and enable selfMint", async function () {
const config = (0, local_1.createLocalConfig)();
const [owner, user] = await hardhat_1.ethers.getSigners();
const v1 = (await (0, helpers_2.deployProxy)("X2EarnCreatorV1", [config.CREATOR_NFT_URI, owner.address]));
await v1.connect(owner).safeMint(user.address);
(0, chai_1.expect)(await v1.balanceOf(user.address)).to.equal(1);
(0, chai_1.expect)(await v1.version()).to.equal("1");
const v2 = (await (0, helpers_2.upgradeProxy)("X2EarnCreatorV1", "X2EarnCreator", await v1.getAddress(), [true], {
version: 2,
}));
(0, chai_1.expect)(await v2.version()).to.equal("2");
(0, chai_1.expect)(await v2.balanceOf(user.address)).to.equal(1);
(0, chai_1.expect)(await v2.baseURI()).to.equal(config.CREATOR_NFT_URI);
(0, chai_1.expect)(await v2.selfMintEnabled()).to.equal(true);
const [, , newUser] = await hardhat_1.ethers.getSigners();
await (0, chai_1.expect)(v2.connect(newUser).selfMint()).to.emit(v2, "Transfer");
(0, chai_1.expect)(await v2.balanceOf(newUser.address)).to.equal(1);
});
});
(0, mocha_1.describe)("Minting", () => {
(0, mocha_1.it)("Should mint a token to the caller", async () => {
const { x2EarnCreator, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await (0, chai_1.expect)(x2EarnCreator.connect(owner).safeMint(otherAccounts[14].address)).to.emit(x2EarnCreator, "Transfer");
(0, chai_1.expect)(await x2EarnCreator.ownerOf(10)).to.equal(otherAccounts[14].address);
(0, chai_1.expect)(await x2EarnCreator.tokenURI(10)).to.equal("ipfs://bafybeie2onvzl3xsod5becuswpdmi63gtq7wgjqhqjecehytt7wdeg4py4/metadata/1.json");
});
(0, mocha_1.it)("Should not allow minting when paused", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).pause();
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(owner).safeMint(otherAccount.address));
});
(0, mocha_1.it)("Should not allow minting to the zero address", async () => {
const { x2EarnCreator, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(owner).safeMint(helpers_1.ZERO_ADDRESS));
});
(0, mocha_1.it)("Only user with MINTER_ROLE or DEFAULT_ADMIN_ROLE should be able to mint tokens", async () => {
const { x2EarnCreator, owner, otherAccount, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const minter = otherAccounts[0];
await x2EarnCreator.grantRole(await x2EarnCreator.MINTER_ROLE(), minter.address);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.MINTER_ROLE(), otherAccount.address)).to.eql(false);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.MINTER_ROLE(), minter.address)).to.eql(true);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.DEFAULT_ADMIN_ROLE(), owner.address)).to.eql(true);
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(otherAccount).safeMint(otherAccount.address));
// DEFAULT_ADMIN_ROLE
await (0, chai_1.expect)(x2EarnCreator.connect(owner).safeMint(otherAccount.address)).to.not.be.reverted;
// MINTER_ROLE
await (0, chai_1.expect)(x2EarnCreator.connect(minter).safeMint(otherAccounts[10].address)).to.not.be.reverted;
});
(0, mocha_1.it)("Should not be able to get the token URI if token not minted", async () => {
const { x2EarnCreator } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// x2earnApp v5: Token [2...5] are reserved for the creator NFTs
await (0, chai_1.expect)(x2EarnCreator.tokenURI(10)).to.be.reverted;
});
(0, mocha_1.it)("Should not be able to mint token to a user that already has a token", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
await (0, chai_1.expect)(x2EarnCreator.connect(owner).safeMint(otherAccount.address)).to.be.revertedWithCustomError(x2EarnCreator, "AlreadyOwnsNFT");
});
});
(0, mocha_1.describe)("Transferring", () => {
(0, mocha_1.it)("Should not be able to tranfet a token using transferFrom", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
await (0, chai_1.expect)(x2EarnCreator.connect(owner).transferFrom(owner.address, otherAccount.address, 0)).to.be.revertedWithCustomError(x2EarnCreator, "TransfersDisabled");
});
(0, mocha_1.it)("Should not be able to transfer a token using safeTransferFrom without data", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
// Testing the first overload without data parameter
await (0, chai_1.expect)(x2EarnCreator["safeTransferFrom(address,address,uint256)"](owner.address, otherAccount.address, 1)).to.be.revertedWithCustomError(x2EarnCreator, "TransfersDisabled");
});
(0, mocha_1.it)("Should not be able to transfer a token using safeTransferFrom with data", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
// Testing the second overload with the data parameter
await (0, chai_1.expect)(x2EarnCreator["safeTransferFrom(address,address,uint256,bytes)"](owner.address, otherAccount.address, 1, "0x")).to.be.revertedWithCustomError(x2EarnCreator, "TransfersDisabled");
});
(0, mocha_1.it)("Should not be able to approve a token for transfer", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
await (0, chai_1.expect)(x2EarnCreator.connect(owner).approve(otherAccount.address, 0)).to.be.revertedWithCustomError(x2EarnCreator, "TransfersDisabled");
});
(0, mocha_1.it)("Should not be able to setApprovalForAll", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
await (0, chai_1.expect)(x2EarnCreator.connect(owner).setApprovalForAll(otherAccount.address, true)).to.be.revertedWithCustomError(x2EarnCreator, "TransfersDisabled");
});
});
(0, mocha_1.describe)("Burning", () => {
(0, mocha_1.it)("Only user with BURNER_ROLE or DEFAULT_ADMIN_ROLE should be able to burn NFT", async () => {
const { x2EarnCreator, owner, otherAccount, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({
forceDeploy: true,
});
const burner = otherAccounts[0];
await x2EarnCreator.grantRole(await x2EarnCreator.BURNER_ROLE(), burner.address);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.BURNER_ROLE(), otherAccount.address)).to.eql(false);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.BURNER_ROLE(), burner.address)).to.eql(true);
(0, chai_1.expect)(await x2EarnCreator.hasRole(await x2EarnCreator.DEFAULT_ADMIN_ROLE(), owner.address)).to.eql(true);
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
// normal user
await (0, chai_1.expect)(x2EarnCreator.connect(otherAccount).burn(1)).to.be.reverted;
// default admin
await (0, chai_1.expect)(x2EarnCreator.connect(owner).burn(1)).to.emit(x2EarnCreator, "Transfer");
// burner
await (0, chai_1.expect)(x2EarnCreator.connect(burner).burn(2)).to.emit(x2EarnCreator, "Transfer");
});
(0, mocha_1.it)("Should not be able to burn a token when paused", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
await x2EarnCreator.connect(owner).pause();
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(owner).burn(0));
});
(0, mocha_1.it)("Should not be able to burn a token that does not exist", async () => {
const { x2EarnCreator, owner } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(owner).burn(10));
});
(0, mocha_1.it)("Should not be able to get the token URI after burning", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
await x2EarnCreator.connect(owner).burn(1);
await (0, chai_1.expect)(x2EarnCreator.tokenURI(1)).to.be.reverted;
});
});
(0, mocha_1.describe)("Token Info", () => {
(0, mocha_1.it)("Should return the correct token URI", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
(0, chai_1.expect)(await x2EarnCreator.tokenURI(1)).to.equal("ipfs://bafybeie2onvzl3xsod5becuswpdmi63gtq7wgjqhqjecehytt7wdeg4py4/metadata/1.json");
});
(0, mocha_1.it)("Should return the correct token owner", async () => {
const { x2EarnCreator, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
let creator10 = otherAccounts[10];
await x2EarnCreator.connect(owner).safeMint(creator10.address);
(0, chai_1.expect)(await x2EarnCreator.ownerOf(1)).to.equal(owner.address);
(0, chai_1.expect)(await x2EarnCreator.ownerOf(10)).to.equal(creator10.address);
});
(0, mocha_1.it)("Should return the correct token balance", async () => {
const { x2EarnCreator, owner, otherAccount } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccount.address);
(0, chai_1.expect)(await x2EarnCreator.balanceOf(owner.address)).to.equal(1);
(0, chai_1.expect)(await x2EarnCreator.balanceOf(otherAccount.address)).to.equal(1);
});
(0, mocha_1.it)("Should return the correct token total supply", async () => {
const { x2EarnCreator, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
// Already minted 9 tokens to otherAccounts[0..7] and owner -> see deploy.ts
await x2EarnCreator.connect(owner).safeMint(otherAccounts[10].address);
await x2EarnCreator.connect(owner).safeMint(otherAccounts[11].address);
await x2EarnCreator.connect(owner).burn(1);
(0, chai_1.expect)(await x2EarnCreator.totalSupply()).to.equal(10);
});
(0, mocha_1.it)("Should return the correct token owner by index", async () => {
const { x2EarnCreator, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
await x2EarnCreator.connect(owner).safeMint(otherAccounts[10].address);
await x2EarnCreator.connect(owner).safeMint(otherAccounts[11].address);
(0, chai_1.expect)(await x2EarnCreator.tokenOfOwnerByIndex(owner.address, 0)).to.equal(1);
(0, chai_1.expect)(await x2EarnCreator.tokenOfOwnerByIndex(otherAccounts[10].address, 0)).to.equal(10);
(0, chai_1.expect)(await x2EarnCreator.tokenOfOwnerByIndex(otherAccounts[11].address, 0)).to.equal(11);
});
});
(0, mocha_1.describe)("Self Minting", () => {
(0, mocha_1.it)("Should allow self-minting after initializeV2", async () => {
const { x2EarnCreator, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
const user = otherAccounts[15];
(0, chai_1.expect)(await x2EarnCreator.selfMintEnabled()).to.equal(true);
await (0, chai_1.expect)(x2EarnCreator.connect(user).selfMint()).to.emit(x2EarnCreator, "Transfer");
(0, chai_1.expect)(await x2EarnCreator.balanceOf(user.address)).to.equal(1);
});
(0, mocha_1.it)("Should revert selfMint when user already owns NFT", async () => {
const { x2EarnCreator, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
const user = otherAccounts[15];
await x2EarnCreator.connect(user).selfMint();
await (0, chai_1.expect)(x2EarnCreator.connect(user).selfMint()).to.be.revertedWithCustomError(x2EarnCreator, "AlreadyOwnsNFT");
});
(0, mocha_1.it)("Should revert selfMint when paused", async () => {
const { x2EarnCreator, owner, otherAccounts } = await (0, helpers_1.getOrDeployContractInstances)({ forceDeploy: true });
const user = otherAccounts[15];
await x2EarnCreator.connect(owner).pause();
await (0, helpers_1.catchRevert)(x2EarnCreator.connect(user).selfMint());
});
});
});