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