UNPKG

@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
"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()); }); }); });