Copy import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import { createLocalConfig } from "@repo/config/contracts/envs/local";
import {
getOrDeployContracts,
} from "../../helpers";
import {
Stargate,
StargateNFT,
TokenAuctionMock,
TokenAuctionMock__factory,
} from "../../../typechain-types";
// PoC: LevelCirculatingSupplyUpdated is not emitted when supply actually changes (mint/burn/migrate)
// but is emitted on addLevel
describe("poc: LevelCirculatingSupplyUpdated emission behavior", () => {
let deployer: HardhatEthersSigner;
let user: HardhatEthersSigner;
let otherAccounts: HardhatEthersSigner[];
let stargateNFTContract: StargateNFT;
let stargateContract: Stargate;
beforeEach(async () => {
[deployer, user, ...otherAccounts] = await ethers.getSigners();
});
describe("Mint/Burn do not emit LevelCirculatingSupplyUpdated", () => {
beforeEach(async () => {
const config = createLocalConfig();
// Allow calling mint/burn directly by setting stargate to deployer for tests
config.STARGATE_CONTRACT_ADDRESS = deployer.address;
const contracts = await getOrDeployContracts({ forceDeploy: true, config });
stargateNFTContract = contracts.stargateNFTContract;
stargateContract = contracts.stargateContract;
});
it("mint does not emit LevelCirculatingSupplyUpdated", async () => {
const level = await stargateNFTContract.getLevel(1);
const txMint = stargateNFTContract
.connect(deployer)
.mint(level.id, user.address);
await expect(txMint)
.to.emit(stargateNFTContract, "TokenMinted")
.withArgs(
user.address,
level.id,
false,
(await stargateNFTContract.getCurrentTokenId()) + 1n,
level.vetAmountRequiredToStake
);
await expect(txMint).to.not.emit(
stargateNFTContract,
"LevelCirculatingSupplyUpdated"
);
// sanity: circulating supply increased
const supplyAfter = await stargateNFTContract.getLevelSupply(level.id);
expect(supplyAfter.circulating).to.equal(1);
});
it("burn does not emit LevelCirculatingSupplyUpdated", async () => {
const level = await stargateNFTContract.getLevel(1);
const tokenId = (await stargateNFTContract.getCurrentTokenId()) + 1n;
await (await stargateNFTContract.connect(deployer).mint(level.id, user.address)).wait();
const txBurn = stargateNFTContract.connect(deployer).burn(tokenId);
await expect(txBurn)
.to.emit(stargateNFTContract, "TokenBurned")
.withArgs(user.address, level.id, tokenId, level.vetAmountRequiredToStake);
await expect(txBurn).to.not.emit(
stargateNFTContract,
"LevelCirculatingSupplyUpdated"
);
// sanity: circulating supply decreased
const supplyAfter = await stargateNFTContract.getLevelSupply(level.id);
expect(supplyAfter.circulating).to.equal(0);
});
});
describe("Migrate does not emit LevelCirculatingSupplyUpdated", () => {
let legacyNodesMock: TokenAuctionMock;
beforeEach(async () => {
const legacyNodesMockFactory = new TokenAuctionMock__factory(deployer);
legacyNodesMock = await legacyNodesMockFactory.deploy();
await legacyNodesMock.waitForDeployment();
const config = createLocalConfig();
config.STARGATE_CONTRACT_ADDRESS = deployer.address;
// Override legacy nodes address with the mock so we can control metadata
config.TOKEN_AUCTION_CONTRACT_ADDRESS = await legacyNodesMock.getAddress();
const contracts = await getOrDeployContracts({ forceDeploy: true, config });
stargateNFTContract = contracts.stargateNFTContract;
stargateContract = contracts.stargateContract;
});
it("migrate does not emit LevelCirculatingSupplyUpdated", async () => {
const LEVEL_ID = 4;
const TOKEN_ID = 500; // <= LEGACY_LAST_TOKEN_ID so it can be preserved
// Prepare legacy token metadata so migrate can succeed
await (await legacyNodesMock.helper__setMetadata(TOKEN_ID, {
owner: user.address,
strengthLevel: LEVEL_ID,
onUpgrade: false,
isOnAuction: false,
lastTransferTime: 0,
createdAt: 0,
updatedAt: 0,
})).wait();
const level = await stargateNFTContract.getLevel(LEVEL_ID);
const txMigrate = stargateNFTContract.connect(deployer).migrate(TOKEN_ID);
await expect(txMigrate)
.to.emit(stargateNFTContract, "TokenMinted")
.withArgs(user.address, LEVEL_ID, true, TOKEN_ID, level.vetAmountRequiredToStake);
await expect(txMigrate).to.not.emit(
stargateNFTContract,
"LevelCirculatingSupplyUpdated"
);
const supplyAfter = await stargateNFTContract.getLevelSupply(LEVEL_ID);
expect(supplyAfter.circulating).to.equal(1);
});
});
describe("Sanity: addLevel does emit LevelCirculatingSupplyUpdated", () => {
let levelOperator: HardhatEthersSigner;
beforeEach(async () => {
const config = createLocalConfig();
// Any address can be level operator if granted; deployer will grant to user
const contracts = await getOrDeployContracts({ forceDeploy: true, config });
stargateNFTContract = contracts.stargateNFTContract;
stargateContract = contracts.stargateContract;
levelOperator = otherAccounts[0];
await (await stargateNFTContract.grantRole(
await stargateNFTContract.LEVEL_OPERATOR_ROLE(),
levelOperator.address
)).wait();
});
it("addLevel emits LevelCirculatingSupplyUpdated once", async () => {
const currentIds = await stargateNFTContract.getLevelIds();
const expectedNewId = currentIds[currentIds.length - 1] + 1n;
const tx = stargateNFTContract.connect(levelOperator).addLevel({
level: {
id: 123, // ignored
name: "My New Level",
isX: false,
vetAmountRequiredToStake: ethers.parseEther("42"),
scaledRewardFactor: 150,
maturityBlocks: 10,
},
cap: 5,
circulatingSupply: 2,
});
await expect(tx)
.to.emit(stargateNFTContract, "LevelCirculatingSupplyUpdated")
.withArgs(expectedNewId, 0, 2);
});
});
});