UNPKG

@vechain/vebetterdao-contracts

Version:

Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.

686 lines (685 loc) 172 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const hardhat_network_helpers_1 = require("@nomicfoundation/hardhat-network-helpers"); const upgrades_core_1 = require("@openzeppelin/upgrades-core"); const local_1 = require("@repo/config/contracts/envs/local"); const chai_1 = require("chai"); const hardhat_1 = require("hardhat"); const mocha_1 = require("mocha"); const helpers_1 = require("../scripts/helpers"); const helpers_2 = require("./helpers"); const config_1 = require("./helpers/config"); const xnodes_1 = require("./helpers/xnodes"); (0, mocha_1.describe)("Galaxy Member - @shard3a", () => { (0, mocha_1.describe)("Contract parameters", () => { (0, mocha_1.it)("Should have correct parameters set on deployment", async () => { const { galaxyMember, owner } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true }); (0, chai_1.expect)(await galaxyMember.name()).to.equal("GalaxyMember"); (0, chai_1.expect)(await galaxyMember.symbol()).to.equal("GM"); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.DEFAULT_ADMIN_ROLE(), await owner.getAddress())).to.equal(true); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.PAUSER_ROLE(), await owner.getAddress())).to.equal(true); (0, chai_1.expect)(await galaxyMember.MAX_LEVEL()).to.equal(1); }); (0, mocha_1.it)("Admin should be able to set x-allocation voting contract address", async () => { const { galaxyMember, owner, xAllocationVoting, otherAccount } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.CONTRACTS_ADDRESS_MANAGER_ROLE(), owner.address)).to.equal(true); const tx = await galaxyMember.connect(owner).setXAllocationsGovernorAddress(await xAllocationVoting.getAddress()); const receipt = await tx.wait(); const name = (0, helpers_2.getEventName)(receipt, galaxyMember); (0, chai_1.expect)(name).to.equal("XAllocationsGovernorAddressUpdated"); (0, chai_1.expect)(await galaxyMember.xAllocationsGovernor()).to.equal(await xAllocationVoting.getAddress()); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.CONTRACTS_ADDRESS_MANAGER_ROLE(), otherAccount.address)).to.equal(false); await (0, chai_1.expect)(galaxyMember.connect(otherAccount).setXAllocationsGovernorAddress(otherAccount.address)).to.be .reverted; // Only admin should be able to set x-allocation voting contract address await (0, chai_1.expect)(galaxyMember.connect(owner).setXAllocationsGovernorAddress(helpers_2.ZERO_ADDRESS)).to.be.reverted; // Cannot set x-allocation voting contract address to zero address }); (0, mocha_1.it)("Admin should be able to set B3TR Governor contract address", async () => { const { galaxyMember, owner, xAllocationVoting, otherAccount } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.CONTRACTS_ADDRESS_MANAGER_ROLE(), owner.address)).to.equal(true); const tx = await galaxyMember.connect(owner).setB3trGovernorAddress(await xAllocationVoting.getAddress()); const receipt = await tx.wait(); const name = (0, helpers_2.getEventName)(receipt, galaxyMember); (0, chai_1.expect)(name).to.equal("B3trGovernorAddressUpdated"); (0, chai_1.expect)(await galaxyMember.b3trGovernor()).to.equal(await xAllocationVoting.getAddress()); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.CONTRACTS_ADDRESS_MANAGER_ROLE(), otherAccount.address)).to.equal(false); await (0, chai_1.expect)(galaxyMember.connect(otherAccount).setB3trGovernorAddress(await otherAccount.getAddress())).to.be .reverted; // Only admin should be able to set B3TR Governor contract address await (0, chai_1.expect)(galaxyMember.connect(owner).setB3trGovernorAddress(helpers_2.ZERO_ADDRESS)).to.be.reverted; }); (0, mocha_1.it)("Only admin should be able to set B3TR Governor contract address", async () => { const { galaxyMember, otherAccount, xAllocationVoting } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); const initialAddress = await galaxyMember.b3trGovernor(); await (0, helpers_2.catchRevert)(galaxyMember.connect(otherAccount).setB3trGovernorAddress(await xAllocationVoting.getAddress())); (0, chai_1.expect)(await galaxyMember.b3trGovernor()).to.equal(initialAddress); }); (0, mocha_1.it)("Only admin should be able to set x-allocation voting contract address", async () => { const { galaxyMember, otherAccount, xAllocationVoting } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); const initialAddress = await galaxyMember.xAllocationsGovernor(); await (0, helpers_2.catchRevert)(galaxyMember.connect(otherAccount).setXAllocationsGovernorAddress(await xAllocationVoting.getAddress())); (0, chai_1.expect)(await galaxyMember.xAllocationsGovernor()).to.equal(initialAddress); }); (0, mocha_1.it)("Should have base URI set correctly", async () => { const config = (0, local_1.createLocalConfig)(); const { galaxyMember, owner } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, config }); (0, chai_1.expect)(await galaxyMember.baseURI()).to.equal(config.GM_NFT_BASE_URI); const tx = await galaxyMember.connect(owner).setBaseURI("https://newbaseuri.com/"); const receipt = await tx.wait(); const name = (0, helpers_2.getEventName)(receipt, galaxyMember); (0, chai_1.expect)(name).to.equal("BaseURIUpdated"); }); (0, mocha_1.it)("Only pauser role should be able to pause and unpause the contract", async () => { const { galaxyMember, otherAccount, owner } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true }); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.PAUSER_ROLE(), otherAccount.address)).to.eql(false); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.PAUSER_ROLE(), owner.address)).to.eql(true); await (0, helpers_2.catchRevert)(galaxyMember.connect(otherAccount).pause()); await galaxyMember.connect(owner).pause(); (0, chai_1.expect)(await galaxyMember.paused()).to.equal(true); await galaxyMember.connect(owner).unpause(); (0, chai_1.expect)(await galaxyMember.paused()).to.equal(false); await (0, helpers_2.catchRevert)(galaxyMember.connect(otherAccount).unpause()); }); (0, mocha_1.it)("Should have b3tr required to upgrade set on deployment", async () => { const { galaxyMember } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true }); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(2)).to.equal(10000000000000000000000n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(3)).to.equal(25000000000000000000000n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(4)).to.equal(50000000000000000000000n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(5)).to.equal(100000000000000000000000n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(6)).to.equal(250000000000000000000000n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(7)).to.equal(500000000000000000000000n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(8)).to.equal(2500000000000000000000000n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(9)).to.equal(5000000000000000000000000n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(10)).to.equal(25000000000000000000000000n); }); (0, mocha_1.it)("Should be able to update b3tr required to upgrade if admin", async () => { const { galaxyMember, owner } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: false }); const tx = await galaxyMember .connect(owner) .setB3TRtoUpgradeToLevel([ 10000000000000000000001n, 25000000000000000000001n, 50000000000000000000001n, 100000000000000000000001n, 250000000000000000000001n, 500000000000000000000001n, 2500000000000000000000001n, 5000000000000000000000001n, 25000000000000000000000001n, ]); const receipt = await tx.wait(); const name = (0, helpers_2.getEventName)(receipt, galaxyMember); (0, chai_1.expect)(name).to.equal("B3TRtoUpgradeToLevelUpdated"); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(2)).to.equal(10000000000000000000001n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(3)).to.equal(25000000000000000000001n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(4)).to.equal(50000000000000000000001n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(5)).to.equal(100000000000000000000001n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(6)).to.equal(250000000000000000000001n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(7)).to.equal(500000000000000000000001n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(8)).to.equal(2500000000000000000000001n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(9)).to.equal(5000000000000000000000001n); (0, chai_1.expect)(await galaxyMember.getB3TRtoUpgradeToLevel(10)).to.equal(25000000000000000000000001n); }); (0, mocha_1.it)("Admin should be able to set new base uri", async () => { const { galaxyMember, owner, otherAccount } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true }); const newBaseURI = "https://newbaseuri.com/"; await galaxyMember.connect(owner).setBaseURI(newBaseURI); (0, chai_1.expect)(await galaxyMember.baseURI()).to.equal(newBaseURI); await (0, chai_1.expect)(galaxyMember.connect(otherAccount).setBaseURI(newBaseURI + "2")).to.be.reverted; await (0, chai_1.expect)(galaxyMember.connect(owner).setBaseURI("")).to.be.reverted; // base uri cannot be empty }); (0, mocha_1.it)("Should have b3tr and treasury addresses set correctly", async () => { const { galaxyMember, b3tr, treasury } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true }); (0, chai_1.expect)(await galaxyMember.b3tr()).to.equal(await b3tr.getAddress()); (0, chai_1.expect)(await galaxyMember.treasury()).to.equal(await treasury.getAddress()); }); (0, mocha_1.it)("Should support ERC 165 interface", async () => { const { galaxyMember } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true }); (0, chai_1.expect)(await galaxyMember.supportsInterface("0x01ffc9a7")).to.equal(true); // ERC165 }); (0, mocha_1.it)("Should have Vechain Nodes Manager role correctly set", async () => { const { galaxyMember, owner } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true }); (0, chai_1.expect)(await galaxyMember.hasRole(await galaxyMember.NODES_MANAGER_ROLE(), owner.address)).to.eql(true); }); (0, mocha_1.it)("Should have correct node to free level mapping", async () => { const { galaxyMember } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true }); (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(0)).to.equal(1); // Level 1 Free Upgrade for None (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(1)).to.equal(2); // Level 2 Free Upgrade for Strength (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(2)).to.equal(4); // Level 4 Free Upgrade for Thunder (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(3)).to.equal(6); // Level 6 Free Upgrade for Mjolnir (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(4)).to.equal(2); // Level 2 Free Upgrade for VeThorX (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(5)).to.equal(4); // Level 4 Free Upgrade for StrengthX (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(6)).to.equal(6); // Level 6 Free Upgrade for ThunderX (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(7)).to.equal(7); // Level 7 Free Upgrade for MjolnirX (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(8)).to.equal(0); // No free upgrade for Dawn (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(9)).to.equal(0); // No free upgrade for Lightning (0, chai_1.expect)(await galaxyMember.getNodeToFreeLevel(10)).to.equal(0); // No free upgrade for Flash }); }); (0, mocha_1.describe)("ERC721 Compliance", () => { let galaxyMember; let owner; let approved; let operator; let other; let tokenId; (0, mocha_1.beforeEach)(async () => { const contracts = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, deployMocks: true, }); galaxyMember = contracts.galaxyMember; owner = contracts.owner; approved = contracts.otherAccount; operator = contracts.otherAccounts[0]; other = contracts.otherAccounts[1]; // Mint a token for testing await (0, helpers_2.participateInAllocationVoting)(owner); await galaxyMember.connect(owner).freeMint(); tokenId = 1; }); //After each unstake, so the VET is recovered, otherwise some tests will fail because of insufficient VET afterEach(async () => { const { stargateMock, stargateNftMock } = await (0, helpers_2.getOrDeployContractInstances)({}); const nodeId = (await stargateNftMock.connect(owner).idsOwnedBy(owner.address))?.[0]; if (nodeId) { await stargateMock.connect(owner).unstake(nodeId); } }); (0, mocha_1.describe)("ERC721 Metadata", () => { (0, mocha_1.it)("should implement supportsInterface for ERC721Metadata", async () => { const ERC721MetadataInterfaceId = "0x5b5e139f"; (0, chai_1.expect)(await galaxyMember.supportsInterface(ERC721MetadataInterfaceId)).to.be.true; }); (0, mocha_1.it)("should return correct name and symbol", async () => { (0, chai_1.expect)(await galaxyMember.name()).to.equal("GalaxyMember"); (0, chai_1.expect)(await galaxyMember.symbol()).to.equal("GM"); }); }); (0, mocha_1.describe)("ERC721 Enumerable", () => { (0, mocha_1.it)("should implement supportsInterface for ERC721Enumerable", async () => { const ERC721EnumerableInterfaceId = "0x780e9d63"; (0, chai_1.expect)(await galaxyMember.supportsInterface(ERC721EnumerableInterfaceId)).to.be.true; }); (0, mocha_1.it)("should correctly implement totalSupply and tokenByIndex", async () => { (0, chai_1.expect)(await galaxyMember.totalSupply()).to.equal(1); (0, chai_1.expect)(await galaxyMember.tokenByIndex(0)).to.equal(tokenId); }); (0, mocha_1.it)("should correctly implement tokenOfOwnerByIndex", async () => { (0, chai_1.expect)(await galaxyMember.tokenOfOwnerByIndex(owner.address, 0)).to.equal(tokenId); }); }); (0, mocha_1.describe)("ERC721 Core Functions", () => { (0, mocha_1.it)("should implement approve correctly", async () => { await galaxyMember.connect(owner).approve(approved.address, tokenId); (0, chai_1.expect)(await galaxyMember.getApproved(tokenId)).to.equal(approved.address); }); (0, mocha_1.it)("should implement setApprovalForAll correctly", async () => { await galaxyMember.connect(owner).setApprovalForAll(operator.address, true); (0, chai_1.expect)(await galaxyMember.isApprovedForAll(owner.address, operator.address)).to.be.true; }); (0, mocha_1.it)("should emit Approval event on approve", async () => { await (0, chai_1.expect)(galaxyMember.connect(owner).approve(approved.address, tokenId)) .to.emit(galaxyMember, "Approval") .withArgs(owner.address, approved.address, tokenId); }); (0, mocha_1.it)("should emit ApprovalForAll event on setApprovalForAll", async () => { await (0, chai_1.expect)(galaxyMember.connect(owner).setApprovalForAll(operator.address, true)) .to.emit(galaxyMember, "ApprovalForAll") .withArgs(owner.address, operator.address, true); }); }); (0, mocha_1.describe)("ERC721 Transfer Mechanics", () => { (0, mocha_1.it)("should correctly transfer tokens using transferFrom", async () => { await galaxyMember.connect(owner).approve(approved.address, tokenId); await galaxyMember.connect(approved).transferFrom(owner.address, other.address, tokenId); (0, chai_1.expect)(await galaxyMember.ownerOf(tokenId)).to.equal(other.address); }); (0, mocha_1.it)("should correctly transfer tokens using safeTransferFrom", async () => { await galaxyMember .connect(owner)["safeTransferFrom(address,address,uint256)"](owner.address, other.address, tokenId); (0, chai_1.expect)(await galaxyMember.ownerOf(tokenId)).to.equal(other.address); }); (0, mocha_1.it)("should clear approvals after transfer", async () => { await galaxyMember.connect(owner).approve(approved.address, tokenId); await galaxyMember.connect(owner).transferFrom(owner.address, other.address, tokenId); (0, chai_1.expect)(await galaxyMember.getApproved(tokenId)).to.equal(hardhat_1.ethers.ZeroAddress); }); }); (0, mocha_1.describe)("ERC721 Safety Checks", () => { (0, mocha_1.it)("should revert when transferring to zero address", async () => { await (0, chai_1.expect)(galaxyMember.connect(owner).transferFrom(owner.address, hardhat_1.ethers.ZeroAddress, tokenId)).to.be .reverted; }); (0, mocha_1.it)("should revert when caller is not owner or approved", async () => { await (0, chai_1.expect)(galaxyMember.connect(other).transferFrom(owner.address, other.address, tokenId)).to.be.reverted; }); (0, mocha_1.it)("should revert when querying non-existent token", async () => { const nonExistentTokenId = 999; await (0, chai_1.expect)(galaxyMember.ownerOf(nonExistentTokenId)).to.be.reverted; }); }); (0, mocha_1.describe)("ERC721 Receiver Compliance", () => { let receiverContract; (0, mocha_1.beforeEach)(async () => { const MockERC721Receiver = await hardhat_1.ethers.getContractFactory("MockERC721Receiver"); receiverContract = await MockERC721Receiver.deploy(); }); (0, mocha_1.it)("should transfer to ERC721Receiver implementer", async () => { await galaxyMember .connect(owner)["safeTransferFrom(address,address,uint256)"](owner.address, await receiverContract.getAddress(), tokenId); (0, chai_1.expect)(await galaxyMember.ownerOf(tokenId)).to.equal(await receiverContract.getAddress()); }); (0, mocha_1.it)("should revert when transferring to non-receiver contract", async () => { // Try to transfer to the GalaxyMember contract itself (which doesn't implement ERC721Receiver) await (0, chai_1.expect)(galaxyMember .connect(owner)["safeTransferFrom(address,address,uint256)"](owner.address, await galaxyMember.getAddress(), tokenId)).to.be.reverted; }); }); }); (0, mocha_1.describe)("Contract upgradeablity", () => { (0, mocha_1.it)("Admin should be able to upgrade the contract", async function () { const { galaxyMember, owner } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory("GalaxyMember"); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await galaxyMember.getAddress()); const UPGRADER_ROLE = await galaxyMember.UPGRADER_ROLE(); (0, chai_1.expect)(await galaxyMember.hasRole(UPGRADER_ROLE, owner.address)).to.eql(true); await (0, chai_1.expect)(galaxyMember.connect(owner).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not.be .reverted; const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await galaxyMember.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)("Only admin should be able to upgrade the contract", async function () { const { galaxyMember, otherAccount } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory("GalaxyMember"); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await galaxyMember.getAddress()); const UPGRADER_ROLE = await galaxyMember.UPGRADER_ROLE(); (0, chai_1.expect)(await galaxyMember.hasRole(UPGRADER_ROLE, otherAccount.address)).to.eql(false); await (0, chai_1.expect)(galaxyMember.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to.be .reverted; const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await galaxyMember.getAddress()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.eql(currentImplAddress.toUpperCase()); (0, chai_1.expect)(newImplAddress.toUpperCase()).to.not.eql((await implementation.getAddress()).toUpperCase()); }); (0, mocha_1.it)("Admin can change UPGRADER_ROLE", async function () { const { galaxyMember, owner, otherAccount } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); // Deploy the implementation contract const Contract = await hardhat_1.ethers.getContractFactory("GalaxyMember"); const implementation = await Contract.deploy(); await implementation.waitForDeployment(); const currentImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await galaxyMember.getAddress()); const UPGRADER_ROLE = await galaxyMember.UPGRADER_ROLE(); (0, chai_1.expect)(await galaxyMember.hasRole(UPGRADER_ROLE, otherAccount.address)).to.eql(false); await (0, chai_1.expect)(galaxyMember.connect(owner).grantRole(UPGRADER_ROLE, otherAccount.address)).to.not.be.reverted; await (0, chai_1.expect)(galaxyMember.connect(owner).revokeRole(UPGRADER_ROLE, owner.address)).to.not.be.reverted; await (0, chai_1.expect)(galaxyMember.connect(otherAccount).upgradeToAndCall(await implementation.getAddress(), "0x")).to.not .be.reverted; const newImplAddress = await (0, upgrades_core_1.getImplementationAddress)(hardhat_1.ethers.provider, await galaxyMember.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 not be able to deploy contract with max level less than 1", async function () { const { owner, b3tr, treasury } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); const config = (0, local_1.createLocalConfig)(); await (0, chai_1.expect)((0, helpers_1.deployProxy)("GalaxyMemberV1", [ { name: helpers_2.NFT_NAME, symbol: helpers_2.NFT_SYMBOL, admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 0, baseTokenURI: config.GM_NFT_BASE_URI, b3trToUpgradeToLevel: config.GM_NFT_B3TR_REQUIRED_TO_UPGRADE_TO_LEVEL, b3tr: await b3tr.getAddress(), treasury: await treasury.getAddress(), }, ])).to.be.reverted; }); (0, mocha_1.it)("Should not be able to increase max level if b3tr required to upgrade is not set", async function () { const { owner, b3tr, treasury, minterAccount, otherAccount, xAllocationVoting, governor } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); const config = (0, local_1.createLocalConfig)(); await (0, chai_1.expect)((0, helpers_1.deployProxy)("GalaxyMemberV1", [ { name: helpers_2.NFT_NAME, symbol: helpers_2.NFT_SYMBOL, admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 2, baseTokenURI: config.GM_NFT_BASE_URI, b3trToUpgradeToLevel: [], b3tr: await b3tr.getAddress(), treasury: await treasury.getAddress(), }, ])).to.be.reverted; // Deploy with correct b3tr required to upgrade const galaxyMember = (await (0, helpers_1.deployProxy)("GalaxyMemberV1", [ { name: helpers_2.NFT_NAME, symbol: helpers_2.NFT_SYMBOL, admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 2, baseTokenURI: config.GM_NFT_BASE_URI, b3trToUpgradeToLevel: [10000000000000000000000n], b3tr: await b3tr.getAddress(), treasury: await treasury.getAddress(), }, ])); await galaxyMember.waitForDeployment(); await galaxyMember.connect(owner).setXAllocationsGovernorAddress(await xAllocationVoting.getAddress()); await galaxyMember.connect(owner).setB3trGovernorAddress(await governor.getAddress()); // Bootstrap emissions await (0, helpers_2.bootstrapEmissions)(); // participation in governance is a requirement for minting await (0, helpers_2.participateInAllocationVoting)(otherAccount); await galaxyMember.connect(otherAccount).freeMint(); await galaxyMember.connect(otherAccount).burn(0); await galaxyMember.connect(otherAccount).freeMint(); // Upgrade to level 2 await (0, helpers_2.upgradeNFTtoLevel)(1, 2, galaxyMember, b3tr, otherAccount, minterAccount); await (0, chai_1.expect)((0, helpers_2.upgradeNFTtoLevel)(1, 3, galaxyMember, b3tr, otherAccount, minterAccount)).to.be.reverted; // Should not be able to upgrade to level 3 // Set max level to 3 await (0, chai_1.expect)(galaxyMember.connect(owner).setMaxLevel(3)).to.be.reverted; // Should not be able to set max level to 3 as b3tr required to upgrade to level 3 is not set await galaxyMember.setB3TRtoUpgradeToLevel([10000000000000000000000n, 25000000000000000000000n]); // Set b3tr required to upgrade to level 3 too await galaxyMember.connect(owner).setMaxLevel(3); // Should be able to set max level to 3 now }); (0, mocha_1.it)("Should not be able to deploy contract if base uri is empty", async function () { const { owner, b3tr, treasury } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); const config = (0, local_1.createLocalConfig)(); await (0, chai_1.expect)((0, helpers_1.deployProxy)("GalaxyMemberV1", [ { name: helpers_2.NFT_NAME, symbol: helpers_2.NFT_SYMBOL, admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 1, baseTokenURI: "", b3trToUpgradeToLevel: config.GM_NFT_B3TR_REQUIRED_TO_UPGRADE_TO_LEVEL, b3tr: await b3tr.getAddress(), treasury: await treasury.getAddress(), }, ])).to.be.reverted; }); (0, mocha_1.it)("Should not be able to deploy contract if b3tr address is not set", async function () { const { owner, treasury } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); const config = (0, local_1.createLocalConfig)(); await (0, chai_1.expect)((0, helpers_1.deployProxy)("GalaxyMemberV1", [ { name: helpers_2.NFT_NAME, symbol: helpers_2.NFT_SYMBOL, admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 1, baseTokenURI: config.GM_NFT_BASE_URI, b3trToUpgradeToLevel: config.GM_NFT_B3TR_REQUIRED_TO_UPGRADE_TO_LEVEL, b3tr: helpers_2.ZERO_ADDRESS, treasury: await treasury.getAddress(), }, ])).to.be.reverted; }); (0, mocha_1.it)("Should not be able to deploy contract if treasury address is not set", async function () { const { owner, b3tr } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); const config = (0, local_1.createLocalConfig)(); await (0, chai_1.expect)((0, helpers_1.deployProxy)("GalaxyMemberV1", [ { name: helpers_2.NFT_NAME, symbol: helpers_2.NFT_SYMBOL, admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 1, baseTokenURI: config.GM_NFT_BASE_URI, b3trToUpgradeToLevel: config.GM_NFT_B3TR_REQUIRED_TO_UPGRADE_TO_LEVEL, b3tr: await b3tr.getAddress(), treasury: helpers_2.ZERO_ADDRESS, }, ])).to.be.reverted; }); (0, mocha_1.it)("Should return correct version of the contract", async () => { const { galaxyMember } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, }); (0, chai_1.expect)(await galaxyMember.version()).to.equal("6"); }); (0, mocha_1.it)("Should not have state conflict after upgrading", async () => { const config = (0, local_1.createLocalConfig)(); const { owner, b3tr, treasury, governor, xAllocationVoting, otherAccount, otherAccounts, minterAccount, vechainNodesMock, nodeManagement, stargateNftMock, } = await (0, helpers_2.getOrDeployContractInstances)({ forceDeploy: true, config, deployMocks: true, }); if (!vechainNodesMock) throw new Error("VechainNodesMock not deployed"); // Bootstrap emissions await (0, helpers_2.bootstrapEmissions)(); // Should be able to free mint after participating in allocation voting await (0, helpers_2.participateInAllocationVoting)(owner, false, otherAccounts[10]); const galaxyMember = (await (0, helpers_1.deployProxy)("GalaxyMemberV1", [ { name: helpers_2.NFT_NAME, symbol: helpers_2.NFT_SYMBOL, admin: owner.address, upgrader: owner.address, pauser: owner.address, minter: owner.address, contractsAddressManager: owner.address, maxLevel: 5, baseTokenURI: config.GM_NFT_BASE_URI, b3trToUpgradeToLevel: config.GM_NFT_B3TR_REQUIRED_TO_UPGRADE_TO_LEVEL, b3tr: await b3tr.getAddress(), treasury: await treasury.getAddress(), }, ])); await galaxyMember.waitForDeployment(); (0, chai_1.expect)(await galaxyMember.MAX_LEVEL()).to.equal(5); // Contract setup await galaxyMember.connect(owner).setB3trGovernorAddress(await governor.getAddress()); await galaxyMember.connect(owner).setXAllocationsGovernorAddress(await xAllocationVoting.getAddress()); // Participation in governance is a requirement for minting const participated = await galaxyMember.connect(owner).participatedInGovernance(owner); (0, chai_1.expect)(participated).to.equal(true); // Mint 4 tokens to owner await galaxyMember.connect(owner).freeMint(); // gmId 0 await galaxyMember.connect(owner).burn(0); // gmId 0 await galaxyMember.connect(owner).freeMint(); // gmId 1 await galaxyMember.connect(owner).freeMint(); // gmId 2 await galaxyMember.connect(owner).freeMint(); // gmId 3 await galaxyMember.connect(owner).freeMint(); // gmId 4 (0, chai_1.expect)(await galaxyMember.balanceOf(await owner.getAddress())).to.equal(4); // Transfer GM NFTs to other accounts: otherAccount, otherAccounts[0], otherAccounts[1] await galaxyMember.connect(owner)["safeTransferFrom(address,address,uint256)"](owner, otherAccount, 1); await galaxyMember.connect(owner).transferFrom(owner.address, otherAccounts[0].address, 2); await galaxyMember.connect(owner).transferFrom(owner.address, otherAccounts[1].address, 3); // All 4 accounts hold 1 GM NFT each (0, chai_1.expect)(await galaxyMember.balanceOf(await owner.getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMember.balanceOf(await otherAccount.getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMember.balanceOf(await otherAccounts[0].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMember.balanceOf(await otherAccounts[1].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMember.ownerOf(1)).to.equal(await otherAccount.getAddress()); (0, chai_1.expect)(await galaxyMember.ownerOf(2)).to.equal(await otherAccounts[0].getAddress()); (0, chai_1.expect)(await galaxyMember.ownerOf(3)).to.equal(await otherAccounts[1].getAddress()); (0, chai_1.expect)(await galaxyMember.ownerOf(4)).to.equal(await owner.getAddress()); // All GMs are of level 1 (0, chai_1.expect)(await galaxyMember.levelOf(1)).to.equal(1); // Earth (0, chai_1.expect)(await galaxyMember.levelOf(2)).to.equal(1); (0, chai_1.expect)(await galaxyMember.levelOf(3)).to.equal(1); (0, chai_1.expect)(await galaxyMember.levelOf(4)).to.equal(1); let storageSlots = []; const initialSlot = BigInt("0x7a79e46844ed04411e4579c7bc49d053e59b0854fa4e9a8df3d5a0597ce45200"); // Slot 0 of GalaxyMember for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlots.push(await hardhat_1.ethers.provider.getStorage(await galaxyMember.getAddress(), i)); } storageSlots = storageSlots.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); // removing empty slots const galaxyMemberV2 = (await (0, helpers_1.upgradeProxy)("GalaxyMemberV1", "GalaxyMemberV2", await galaxyMember.getAddress(), [owner.address, await nodeManagement.getAddress(), owner.address, config.GM_NFT_NODE_TO_FREE_LEVEL], { version: 2 })); let storageSlotsAfter = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlotsAfter.push(await hardhat_1.ethers.provider.getStorage(await galaxyMemberV2.getAddress(), i)); } storageSlotsAfter = storageSlotsAfter.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); // removing empty slots // Check if storage slots are the same after upgrade for (let i = 0; i < storageSlots.length; i++) { // console.log("*** storageSlots v1", storageSlots[i], "vs v2", storageSlotsAfter[i]) (0, chai_1.expect)(storageSlots[i]).to.equal(storageSlotsAfter[i]); } // Contract setup await galaxyMemberV2.setVechainNodes(await vechainNodesMock.getAddress()); (0, chai_1.expect)(await galaxyMemberV2.MAX_LEVEL()).to.equal(5); await galaxyMemberV2.setMaxLevel(10); (0, chai_1.expect)(await galaxyMemberV2.MAX_LEVEL()).to.equal(10); // Check if all GM NFTs are still owned by the original owners (0, chai_1.expect)(await galaxyMemberV2.balanceOf(await owner.getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV2.balanceOf(await otherAccount.getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV2.balanceOf(await otherAccounts[0].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV2.balanceOf(await otherAccounts[1].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV2.ownerOf(1)).to.equal(await otherAccount.getAddress()); (0, chai_1.expect)(await galaxyMemberV2.ownerOf(2)).to.equal(await otherAccounts[0].getAddress()); (0, chai_1.expect)(await galaxyMemberV2.ownerOf(3)).to.equal(await otherAccounts[1].getAddress()); (0, chai_1.expect)(await galaxyMemberV2.ownerOf(4)).to.equal(await owner.getAddress()); // All GMs are still of level 1 (0, chai_1.expect)(await galaxyMemberV2.levelOf(1)).to.equal(1); (0, chai_1.expect)(await galaxyMemberV2.levelOf(2)).to.equal(1); (0, chai_1.expect)(await galaxyMemberV2.levelOf(3)).to.equal(1); (0, chai_1.expect)(await galaxyMemberV2.levelOf(4)).to.equal(1); // Mint a new GM NFT to owner await galaxyMemberV2.connect(owner).freeMint(); (0, chai_1.expect)(await galaxyMemberV2.balanceOf(await owner.getAddress())).to.equal(2); (0, chai_1.expect)(await galaxyMemberV2.ownerOf(5)).to.equal(await owner.getAddress()); (0, chai_1.expect)(await galaxyMemberV2.levelOf(5)).to.equal(1); // Earth // Let's upgrade gmId 1, owned by otherAccount, to level 2 await (0, helpers_2.upgradeNFTtoLevel)(1, 2, galaxyMemberV2, b3tr, otherAccount, minterAccount); (0, chai_1.expect)(await galaxyMemberV2.levelOf(1)).to.equal(2); // Moon // Mint Mjolnir X Node to otherAccount await (0, helpers_2.mintLegacyNode)(7, otherAccount); const nodeId = 1; //Should be the first minted (0, chai_1.expect)(await vechainNodesMock.idToOwner(nodeId)).to.equal(await otherAccount.getAddress()); // Expect a free upgrade to level 7 when attaching a Mjolnir X Node to a GM NFT of a lower level (0, chai_1.expect)(await galaxyMemberV2.getLevelAfterAttachingNode(1, nodeId)).to.equal(7); // Attach nodeId 2 to gmId 1 await galaxyMemberV2.connect(otherAccount).attachNode(nodeId, 1); (0, chai_1.expect)(await galaxyMemberV2.levelOf(1)).to.equal(7); // Saturn (0, chai_1.expect)(await galaxyMemberV2.tokenURI(1)).to.equal(config.GM_NFT_BASE_URI + "7.json"); storageSlots = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlots.push(await hardhat_1.ethers.provider.getStorage(await galaxyMemberV2.getAddress(), i)); } storageSlots = storageSlots.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); const galaxyMemberV3 = (await (0, helpers_1.upgradeProxy)("GalaxyMemberV2", "GalaxyMemberV3", await galaxyMember.getAddress(), [], { version: 3 })); storageSlotsAfter = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlotsAfter.push(await hardhat_1.ethers.provider.getStorage(await galaxyMemberV3.getAddress(), i)); } storageSlotsAfter = storageSlotsAfter.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); // Check if storage slots are the same after upgrade for (let i = 0; i < storageSlots.length; i++) { // console.log("*** storageSlots v2", storageSlots[i], "vs v3", storageSlotsAfter[i]) (0, chai_1.expect)(storageSlots[i]).to.equal(storageSlotsAfter[i]); } // Check if all GM NFTs are still owned by the original owners (0, chai_1.expect)(await galaxyMemberV3.balanceOf(await owner.getAddress())).to.equal(2); (0, chai_1.expect)(await galaxyMemberV3.balanceOf(await otherAccount.getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV3.balanceOf(await otherAccounts[0].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV3.balanceOf(await otherAccounts[1].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(1)).to.equal(await otherAccount.getAddress()); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(2)).to.equal(await otherAccounts[0].getAddress()); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(3)).to.equal(await otherAccounts[1].getAddress()); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(4)).to.equal(await owner.getAddress()); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(5)).to.equal(await owner.getAddress()); // Check that existing GM NFTs have the same level (0, chai_1.expect)(await galaxyMemberV3.levelOf(1)).to.equal(7); (0, chai_1.expect)(await galaxyMemberV3.levelOf(2)).to.equal(1); (0, chai_1.expect)(await galaxyMemberV3.levelOf(3)).to.equal(1); (0, chai_1.expect)(await galaxyMemberV3.levelOf(4)).to.equal(1); (0, chai_1.expect)(await galaxyMemberV3.levelOf(5)).to.equal(1); // Mint a new GM NFT to owner await galaxyMemberV3.connect(owner).freeMint(); (0, chai_1.expect)(await galaxyMemberV3.balanceOf(await owner.getAddress())).to.equal(3); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(6)).to.equal(await owner.getAddress()); (0, chai_1.expect)(await galaxyMemberV3.levelOf(6)).to.equal(1); // Earth // Get selected token Id at block number - since you can hold +1 GM NFT, select one to boost rewards (0, chai_1.expect)(await galaxyMemberV3.getSelectedTokenIdAtBlock(owner.address, await hardhat_1.ethers.provider.getBlockNumber())).to.equal(0n); // admin selects gmId 4 as part of upgrade await galaxyMemberV3.connect(owner).selectFor(owner.getAddress(), 4); (0, chai_1.expect)(await galaxyMemberV3.getSelectedTokenId(owner.getAddress())).to.equal(4); // Get selected gmId at block number (0, chai_1.expect)(await galaxyMemberV3.getSelectedTokenIdAtBlock(owner.address, await hardhat_1.ethers.provider.getBlockNumber())).to.equal(4n); // Transfer gmId 4 to otherAccounts[6] - upon transfer, if the `from` address had that id selected, // and if they own other GM NFTs, the "first" one is selected await galaxyMemberV3.connect(owner).transferFrom(owner.address, otherAccounts[6].address, 4); (0, chai_1.expect)(await galaxyMemberV3.getSelectedTokenId(owner.getAddress())).to.equal(6n); // otherAccounts[6] (0, chai_1.expect)(await galaxyMemberV3.getSelectedTokenIdAtBlock(owner.address, await hardhat_1.ethers.provider.getBlockNumber())).to.equal(6n); // Check if the token is transferred (0, chai_1.expect)(await galaxyMemberV3.ownerOf(4)).to.equal(await otherAccounts[6].getAddress()); // Get selected token Id at block number for the transfer recipient (0, chai_1.expect)(await galaxyMemberV3.getSelectedTokenIdAtBlock(otherAccounts[6].address, await hardhat_1.ethers.provider.getBlockNumber())).to.equal(4n); storageSlots = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlots.push(await hardhat_1.ethers.provider.getStorage(await galaxyMemberV3.getAddress(), i)); } storageSlots = storageSlots.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); const galaxyMemberV4 = (await (0, helpers_1.upgradeProxy)("GalaxyMemberV3", "GalaxyMemberV4", await galaxyMember.getAddress(), [], { version: 4 })); storageSlotsAfter = []; for (let i = initialSlot; i < initialSlot + BigInt(100); i++) { storageSlotsAfter.push(await hardhat_1.ethers.provider.getStorage(await galaxyMemberV4.getAddress(), i)); } storageSlotsAfter = storageSlotsAfter.filter(slot => slot !== "0x0000000000000000000000000000000000000000000000000000000000000000"); // Check if storage slots are the same after upgrade for (let i = 0; i < storageSlots.length; i++) { // console.log("*** storageSlots v3", storageSlots[i], "vs v4", storageSlotsAfter[i]) (0, chai_1.expect)(storageSlots[i]).to.equal(storageSlotsAfter[i]); } // Check if all GM NFTs are still owned by the original owners (0, chai_1.expect)(await galaxyMemberV4.balanceOf(await owner.getAddress())).to.equal(2); (0, chai_1.expect)(await galaxyMemberV4.balanceOf(await otherAccount.getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV4.balanceOf(await otherAccounts[0].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV4.balanceOf(await otherAccounts[1].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV4.balanceOf(await otherAccounts[6].getAddress())).to.equal(1); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(1)).to.equal(await otherAccount.getAddress()); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(2)).to.equal(await otherAccounts[0].getAddress()); (0, chai_1.expect)(await galaxyMemberV3.ownerOf(3)).to.equal(await otherAccounts[1].getAddress()); (0, chai_1.expect)(await galaxyMemberV4.ownerOf(4)).to.equal(await otherAccounts[6].getAddress());