@vechain/vebetterdao-contracts
Version:
Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.
486 lines (485 loc) • 30.3 kB
JavaScript
import { ethers } from "hardhat";
import { expect } from "chai";
import { describe, it, beforeEach } from "mocha";
import { createLocalConfig } from "@repo/config/contracts/envs/local";
import { getOrDeployContractInstances } from "../helpers/deploy";
import { getVot3Tokens, bootstrapAndStartEmissions, moveBlocks } from "../helpers/common";
describe("NavigatorRegistry - @shard19a", function () {
let navigatorRegistry;
let xAllocationVoting;
let b3tr;
let vot3;
let treasury;
let owner;
let otherAccount;
let minterAccount;
let otherAccounts;
const config = createLocalConfig();
const STAKE_AMOUNT = ethers.parseEther("50000");
const METADATA_URI = "ipfs://navigator-metadata";
// Ensure VOT3 supply exists so max stake calculation is non-zero
const ensureVot3Supply = async () => {
const supply = await vot3.totalSupply();
if (supply === 0n) {
// Mint and convert enough B3TR -> VOT3 to create supply
await getVot3Tokens(owner, "10000000");
}
};
// Helper: mint B3TR for account and approve NavigatorRegistry
const fundAndApprove = async (account, amount) => {
await b3tr.connect(minterAccount).mint(account.address, amount);
const registryAddress = await navigatorRegistry.getAddress();
await b3tr.connect(account).approve(registryAddress, amount);
};
// Helper: advance blocks past the exit effective deadline
const advancePastExitDeadline = async (navigatorAddr) => {
const filter = navigatorRegistry.filters.ExitAnnounced(navigatorAddr);
const events = await navigatorRegistry.queryFilter(filter);
const effectiveDeadline = events[events.length - 1].args.effectiveDeadline;
const currentBlock = await xAllocationVoting.clock();
if (currentBlock < effectiveDeadline) {
await moveBlocks(Number(effectiveDeadline - currentBlock) + 1);
}
};
// Helper: register a navigator with default stake (ensures VOT3 supply first)
const registerNavigator = async (account, amount = STAKE_AMOUNT, uri = METADATA_URI) => {
await ensureVot3Supply();
await fundAndApprove(account, amount);
await navigatorRegistry.connect(account).register(amount, uri);
};
beforeEach(async function () {
const deployment = await getOrDeployContractInstances({ forceDeploy: true });
if (!deployment)
throw new Error("Failed to deploy contracts");
navigatorRegistry = deployment.navigatorRegistry;
xAllocationVoting = deployment.xAllocationVoting;
b3tr = deployment.b3tr;
vot3 = deployment.vot3;
treasury = deployment.treasury;
owner = deployment.owner;
otherAccount = deployment.otherAccount;
minterAccount = deployment.minterAccount;
otherAccounts = deployment.otherAccounts;
});
// ======================== 1. Initialization ======================== //
describe("Initialization", function () {
it("version() returns '1'", async function () {
expect(await navigatorRegistry.version()).to.equal("1");
});
it("BASIS_POINTS returns 10000", async function () {
expect(await navigatorRegistry.BASIS_POINTS()).to.equal(10000n);
});
it("getMinStake returns config value", async function () {
expect(await navigatorRegistry.getMinStake()).to.equal(config.NAVIGATOR_MIN_STAKE);
});
it("getFeeLockPeriod returns config value", async function () {
expect(await navigatorRegistry.getFeeLockPeriod()).to.equal(config.NAVIGATOR_FEE_LOCK_PERIOD);
});
it("getFeePercentage returns config value", async function () {
expect(await navigatorRegistry.getFeePercentage()).to.equal(config.NAVIGATOR_FEE_PERCENTAGE);
});
it("getReportInterval returns config value", async function () {
expect(await navigatorRegistry.getReportInterval()).to.equal(config.NAVIGATOR_REPORT_INTERVAL);
});
it("getMinorSlashPercentage returns config value", async function () {
expect(await navigatorRegistry.getMinorSlashPercentage()).to.equal(config.NAVIGATOR_MINOR_SLASH_PERCENTAGE);
});
it("getPreferenceCutoffPeriod returns config value", async function () {
expect(await navigatorRegistry.getPreferenceCutoffPeriod()).to.equal(config.NAVIGATOR_PREFERENCE_CUTOFF_PERIOD);
});
it("should not allow reinitialization", async function () {
await expect(navigatorRegistry.initialize({
admin: owner.address,
upgrader: owner.address,
governance: owner.address,
b3trToken: await b3tr.getAddress(),
vot3Token: await vot3.getAddress(),
treasury: await treasury.getAddress(),
minStake: STAKE_AMOUNT,
maxStakePercentage: 100,
feeLockPeriod: 4,
feePercentage: 2000,
exitNoticePeriod: 1,
reportInterval: 2,
minorSlashPercentage: 1000,
preferenceCutoffPeriod: 8640,
voterRewards: owner.address,
xAllocationVoting: owner.address,
relayerRewardsPool: owner.address,
})).to.be.reverted;
});
});
// ======================== 2. Registration & Staking ======================== //
describe("Registration & Staking", function () {
describe("register()", function () {
it("should register a navigator with correct stake, metadata, and emit event", async function () {
const navigator = otherAccounts[10];
await ensureVot3Supply();
await fundAndApprove(navigator, STAKE_AMOUNT);
const tx = await navigatorRegistry.connect(navigator).register(STAKE_AMOUNT, METADATA_URI);
expect(await navigatorRegistry.isNavigator(navigator.address)).to.be.true;
expect(await navigatorRegistry.getStake(navigator.address)).to.equal(STAKE_AMOUNT);
expect(await navigatorRegistry.getMetadataURI(navigator.address)).to.equal(METADATA_URI);
await expect(tx)
.to.emit(navigatorRegistry, "NavigatorRegistered")
.withArgs(navigator.address, STAKE_AMOUNT, METADATA_URI);
});
it("should revert with StakeBelowMinimum when stake is below minimum", async function () {
const navigator = otherAccounts[10];
const lowStake = ethers.parseEther("100");
await fundAndApprove(navigator, lowStake);
await expect(navigatorRegistry.connect(navigator).register(lowStake, METADATA_URI)).to.be.revertedWithCustomError(navigatorRegistry, "StakeBelowMinimum");
});
it("should revert with AlreadyRegistered when registering twice", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
// Fund again and try to register a second time
await fundAndApprove(navigator, STAKE_AMOUNT);
await expect(navigatorRegistry.connect(navigator).register(STAKE_AMOUNT, METADATA_URI)).to.be.revertedWithCustomError(navigatorRegistry, "AlreadyRegistered");
});
it("should revert with DelegatorCannotRegister when caller is currently delegating", async function () {
const existingNavigator = otherAccounts[10];
const delegator = otherAccounts[11];
await registerNavigator(existingNavigator);
// delegator gets VOT3 and delegates to the existing navigator
await getVot3Tokens(delegator, "1000");
await navigatorRegistry.connect(delegator).delegate(existingNavigator.address, ethers.parseEther("500"));
// delegator tries to register as a navigator while still delegating
await fundAndApprove(delegator, STAKE_AMOUNT);
await expect(navigatorRegistry.connect(delegator).register(STAKE_AMOUNT, METADATA_URI)).to.be.revertedWithCustomError(navigatorRegistry, "DelegatorCannotRegister");
});
it("should allow registration after delegator undelegates", async function () {
const existingNavigator = otherAccounts[10];
const delegator = otherAccounts[11];
await registerNavigator(existingNavigator);
// delegator delegates then undelegates
await getVot3Tokens(delegator, "1000");
await navigatorRegistry.connect(delegator).delegate(existingNavigator.address, ethers.parseEther("500"));
await navigatorRegistry.connect(delegator).undelegate();
// Now registration should succeed
await fundAndApprove(delegator, STAKE_AMOUNT);
await navigatorRegistry.connect(delegator).register(STAKE_AMOUNT, METADATA_URI);
expect(await navigatorRegistry.isNavigator(delegator.address)).to.be.true;
});
it("should allow registration when previous navigator was deactivated (stale delegation)", async function () {
const existingNavigator = otherAccounts[10];
const delegator = otherAccounts[11];
await registerNavigator(existingNavigator);
// delegator delegates
await getVot3Tokens(delegator, "1000");
await navigatorRegistry.connect(delegator).delegate(existingNavigator.address, ethers.parseEther("500"));
// Governance deactivates the navigator — delegation becomes stale
await navigatorRegistry.connect(owner).deactivateNavigator(existingNavigator.address, 0, false);
// Delegator should be able to register since their navigator is dead
await fundAndApprove(delegator, STAKE_AMOUNT);
await navigatorRegistry.connect(delegator).register(STAKE_AMOUNT, METADATA_URI);
expect(await navigatorRegistry.isNavigator(delegator.address)).to.be.true;
});
});
describe("addStake()", function () {
it("should increase stake and emit event", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
const additionalStake = ethers.parseEther("10000");
await fundAndApprove(navigator, additionalStake);
const tx = await navigatorRegistry.connect(navigator).addStake(additionalStake);
const expectedTotal = STAKE_AMOUNT + additionalStake;
expect(await navigatorRegistry.getStake(navigator.address)).to.equal(expectedTotal);
await expect(tx)
.to.emit(navigatorRegistry, "StakeAdded")
.withArgs(navigator.address, additionalStake, expectedTotal);
});
it("should revert when caller is not a navigator", async function () {
const nonNavigator = otherAccounts[11];
const amount = ethers.parseEther("10000");
await fundAndApprove(nonNavigator, amount);
await expect(navigatorRegistry.connect(nonNavigator).addStake(amount)).to.be.revertedWithCustomError(navigatorRegistry, "NotRegistered");
});
});
describe("withdrawStake()", function () {
it("should allow withdrawal after exit notice period elapses", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
await bootstrapAndStartEmissions();
await navigatorRegistry.connect(navigator).announceExit();
await advancePastExitDeadline(navigator.address);
const tx = await navigatorRegistry.connect(navigator).withdrawStake(STAKE_AMOUNT);
expect(await navigatorRegistry.getStake(navigator.address)).to.equal(0n);
await expect(tx).to.emit(navigatorRegistry, "StakeWithdrawn").withArgs(navigator.address, STAKE_AMOUNT, 0n);
});
it("should revert with NavigatorStillActive when navigator has not announced exit", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
await expect(navigatorRegistry.connect(navigator).withdrawStake(STAKE_AMOUNT)).to.be.revertedWithCustomError(navigatorRegistry, "NavigatorStillActive");
});
it("should revert with InsufficientStake when withdrawing more than staked", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
await bootstrapAndStartEmissions();
await navigatorRegistry.connect(navigator).announceExit();
await advancePastExitDeadline(navigator.address);
const tooMuch = STAKE_AMOUNT + ethers.parseEther("1");
await expect(navigatorRegistry.connect(navigator).withdrawStake(tooMuch)).to.be.revertedWithCustomError(navigatorRegistry, "InsufficientStake");
});
});
describe("reduceStake()", function () {
it("should allow active navigator to reduce stake and emit StakeWithdrawn", async function () {
const navigator = otherAccounts[10];
const extraStake = ethers.parseEther("100000");
await registerNavigator(navigator, extraStake);
const reduceAmount = ethers.parseEther("10000");
const remaining = extraStake - reduceAmount;
const tx = await navigatorRegistry.connect(navigator).reduceStake(reduceAmount);
expect(await navigatorRegistry.getStake(navigator.address)).to.equal(remaining);
await expect(tx)
.to.emit(navigatorRegistry, "StakeWithdrawn")
.withArgs(navigator.address, reduceAmount, remaining);
});
it("should revert with StakeBelowMinimum when reducing below min stake", async function () {
const navigator = otherAccounts[10];
const minStake = await navigatorRegistry.getMinStake();
// Register with exactly min stake
await registerNavigator(navigator, minStake);
// Reducing by any amount should revert since we're at the minimum
await expect(navigatorRegistry.connect(navigator).reduceStake(1n)).to.be.revertedWithCustomError(navigatorRegistry, "StakeBelowMinimum");
});
it("should revert when reducing would break delegation capacity", async function () {
const navigator = otherAccounts[10];
// Register with extra stake so we can reduce
const extraStake = ethers.parseEther("100000");
await registerNavigator(navigator, extraStake);
// Delegate close to capacity: 100K stake * 10 = 1M capacity, delegate 600K
const citizen = otherAccounts[11];
await getVot3Tokens(citizen, "600000");
await navigatorRegistry.connect(citizen).delegate(navigator.address, ethers.parseEther("600000"));
// Reducing 50K would leave 50K stake, capacity = 500K < 600K delegated
await expect(navigatorRegistry.connect(navigator).reduceStake(ethers.parseEther("50000"))).to.be.revertedWithCustomError(navigatorRegistry, "StakeBelowMinimum");
});
it("should revert with InsufficientStake when reducing more than staked", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
const tooMuch = STAKE_AMOUNT + ethers.parseEther("1");
await expect(navigatorRegistry.connect(navigator).reduceStake(tooMuch)).to.be.revertedWithCustomError(navigatorRegistry, "InsufficientStake");
});
it("should revert when caller is not a navigator", async function () {
const nonNavigator = otherAccounts[11];
await expect(navigatorRegistry.connect(nonNavigator).reduceStake(ethers.parseEther("1000"))).to.be.revertedWithCustomError(navigatorRegistry, "NotRegistered");
});
});
describe("Delegation Capacity", function () {
it("getDelegationCapacity should be stake * 10", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
const capacity = await navigatorRegistry.getDelegationCapacity(navigator.address);
expect(capacity).to.equal(STAKE_AMOUNT * 10n);
});
it("getRemainingCapacity should decrease after delegation", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
const citizen = otherAccounts[12];
const delegateAmount = ethers.parseEther("100000"); // 100k VOT3
// Give citizen VOT3 tokens
await getVot3Tokens(citizen, "100000");
// Delegate
await navigatorRegistry.connect(citizen).delegate(navigator.address, delegateAmount);
const remaining = await navigatorRegistry.getRemainingCapacity(navigator.address);
const expectedRemaining = STAKE_AMOUNT * 10n - delegateAmount;
expect(remaining).to.equal(expectedRemaining);
});
});
});
// ======================== 3. Governance Setters ======================== //
describe("Governance Setters", function () {
// ---- Numeric setters with GOVERNANCE_ROLE ---- //
describe("setMinStake", function () {
it("should update minStake", async function () {
const newValue = ethers.parseEther("100000");
await navigatorRegistry.connect(owner).setMinStake(newValue);
expect(await navigatorRegistry.getMinStake()).to.equal(newValue);
});
it("should revert with InvalidParameter when value is 0", async function () {
await expect(navigatorRegistry.connect(owner).setMinStake(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert when caller lacks GOVERNANCE_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setMinStake(1)).to.be.reverted;
});
});
describe("setMaxStakePercentage", function () {
it("should update maxStakePercentage", async function () {
// Create some VOT3 supply so getMaxStake returns non-zero
await getVot3Tokens(owner, "1000000");
await navigatorRegistry.connect(owner).setMaxStakePercentage(500);
const maxStake = await navigatorRegistry.getMaxStake();
expect(maxStake).to.be.gt(0n);
});
it("should revert with InvalidParameter when value is 0", async function () {
await expect(navigatorRegistry.connect(owner).setMaxStakePercentage(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert with InvalidParameter when value exceeds BASIS_POINTS", async function () {
await expect(navigatorRegistry.connect(owner).setMaxStakePercentage(10001)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert when caller lacks GOVERNANCE_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setMaxStakePercentage(500)).to.be.reverted;
});
});
describe("setFeeLockPeriod", function () {
it("should update feeLockPeriod", async function () {
await navigatorRegistry.connect(owner).setFeeLockPeriod(8);
expect(await navigatorRegistry.getFeeLockPeriod()).to.equal(8);
});
it("should revert with InvalidParameter when value is 0", async function () {
await expect(navigatorRegistry.connect(owner).setFeeLockPeriod(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert when caller lacks GOVERNANCE_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setFeeLockPeriod(8)).to.be.reverted;
});
});
describe("setFeePercentage", function () {
it("should update feePercentage", async function () {
await navigatorRegistry.connect(owner).setFeePercentage(3000);
expect(await navigatorRegistry.getFeePercentage()).to.equal(3000);
});
it("should revert with InvalidParameter when value exceeds BASIS_POINTS", async function () {
await expect(navigatorRegistry.connect(owner).setFeePercentage(10001)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert when caller lacks GOVERNANCE_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setFeePercentage(3000)).to.be.reverted;
});
});
describe("setReportInterval", function () {
it("should update reportInterval", async function () {
await navigatorRegistry.connect(owner).setReportInterval(5);
expect(await navigatorRegistry.getReportInterval()).to.equal(5);
});
it("should revert with InvalidParameter when value is 0", async function () {
await expect(navigatorRegistry.connect(owner).setReportInterval(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert when caller lacks GOVERNANCE_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setReportInterval(5)).to.be.reverted;
});
});
describe("setMinorSlashPercentage", function () {
it("should update minorSlashPercentage", async function () {
await navigatorRegistry.connect(owner).setMinorSlashPercentage(2000);
expect(await navigatorRegistry.getMinorSlashPercentage()).to.equal(2000);
});
it("should revert with InvalidParameter when value is 0", async function () {
await expect(navigatorRegistry.connect(owner).setMinorSlashPercentage(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert with InvalidParameter when value exceeds BASIS_POINTS", async function () {
await expect(navigatorRegistry.connect(owner).setMinorSlashPercentage(10001)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert when caller lacks GOVERNANCE_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setMinorSlashPercentage(2000)).to.be.reverted;
});
});
describe("setPreferenceCutoffPeriod", function () {
it("should update preferenceCutoffPeriod", async function () {
await navigatorRegistry.connect(owner).setPreferenceCutoffPeriod(4320);
expect(await navigatorRegistry.getPreferenceCutoffPeriod()).to.equal(4320);
});
it("should revert with InvalidParameter when value is 0", async function () {
await expect(navigatorRegistry.connect(owner).setPreferenceCutoffPeriod(0)).to.be.revertedWithCustomError(navigatorRegistry, "InvalidParameter");
});
it("should revert when caller lacks GOVERNANCE_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setPreferenceCutoffPeriod(4320)).to.be.reverted;
});
});
// ---- Address setters with DEFAULT_ADMIN_ROLE ---- //
describe("setXAllocationVoting", function () {
it("should update xAllocationVoting address", async function () {
const newAddress = otherAccounts[13].address;
await navigatorRegistry.connect(owner).setXAllocationVoting(newAddress);
});
it("should revert with ZeroAddress when address is zero", async function () {
await expect(navigatorRegistry.connect(owner).setXAllocationVoting(ethers.ZeroAddress)).to.be.revertedWithCustomError(navigatorRegistry, "ZeroAddress");
});
it("should revert when caller lacks DEFAULT_ADMIN_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setXAllocationVoting(otherAccounts[13].address)).to.be
.reverted;
});
});
describe("setRelayerRewardsPool", function () {
it("should update relayerRewardsPool address", async function () {
const newAddress = otherAccounts[14].address;
await navigatorRegistry.connect(owner).setRelayerRewardsPool(newAddress);
});
it("should revert with ZeroAddress when address is zero", async function () {
await expect(navigatorRegistry.connect(owner).setRelayerRewardsPool(ethers.ZeroAddress)).to.be.revertedWithCustomError(navigatorRegistry, "ZeroAddress");
});
it("should revert when caller lacks DEFAULT_ADMIN_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setRelayerRewardsPool(otherAccounts[14].address)).to.be
.reverted;
});
});
describe("setVoterRewards", function () {
it("should update voterRewards address", async function () {
const newAddress = otherAccounts[15].address;
await navigatorRegistry.connect(owner).setVoterRewards(newAddress);
});
it("should revert with ZeroAddress when address is zero", async function () {
await expect(navigatorRegistry.connect(owner).setVoterRewards(ethers.ZeroAddress)).to.be.revertedWithCustomError(navigatorRegistry, "ZeroAddress");
});
it("should revert when caller lacks DEFAULT_ADMIN_ROLE", async function () {
await expect(navigatorRegistry.connect(otherAccount).setVoterRewards(otherAccounts[15].address)).to.be.reverted;
});
});
});
describe("getStatus()", function () {
it("should return NONE (0) for unregistered address", async function () {
const status = await navigatorRegistry.getStatus(otherAccounts[10].address);
expect(status).to.equal(0n);
});
it("should return ACTIVE (1) after registration", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
const status = await navigatorRegistry.getStatus(navigator.address);
expect(status).to.equal(1n);
});
it("should return EXITING (2) after announceExit while notice period is active", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
await bootstrapAndStartEmissions();
await navigatorRegistry.connect(navigator).announceExit();
const status = await navigatorRegistry.getStatus(navigator.address);
expect(status).to.equal(2n);
});
it("should return DEACTIVATED (3) after notice period elapses", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
await bootstrapAndStartEmissions();
await navigatorRegistry.connect(navigator).announceExit();
expect(await navigatorRegistry.getStatus(navigator.address)).to.equal(2n);
await advancePastExitDeadline(navigator.address);
const status = await navigatorRegistry.getStatus(navigator.address);
expect(status).to.equal(3n);
});
it("should return DEACTIVATED (3) after governance deactivation", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
await navigatorRegistry.connect(owner).deactivateNavigator(navigator.address, 0, false);
const status = await navigatorRegistry.getStatus(navigator.address);
expect(status).to.equal(3n);
});
it("should return DEACTIVATED (3) for governance-deactivated even if exit was also announced", async function () {
const navigator = otherAccounts[10];
await registerNavigator(navigator);
await bootstrapAndStartEmissions();
await navigatorRegistry.connect(navigator).announceExit();
expect(await navigatorRegistry.getStatus(navigator.address)).to.equal(2n);
// Governance deactivates while exiting
await navigatorRegistry.connect(owner).deactivateNavigator(navigator.address, 0, false);
const status = await navigatorRegistry.getStatus(navigator.address);
expect(status).to.equal(3n);
});
it("should still return ACTIVE (1) for an active navigator after another is deactivated", async function () {
const nav1 = otherAccounts[10];
const nav2 = otherAccounts[11];
await registerNavigator(nav1);
await registerNavigator(nav2);
await navigatorRegistry.connect(owner).deactivateNavigator(nav1.address, 0, false);
expect(await navigatorRegistry.getStatus(nav1.address)).to.equal(3n);
expect(await navigatorRegistry.getStatus(nav2.address)).to.equal(1n);
});
});
});