Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial supply change #4

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 136 additions & 17 deletions test/ZToken.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,27 @@ import { expect } from "chai";
import { ZToken, ZToken__factory } from "../typechain/index.ts";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import { time } from "@nomicfoundation/hardhat-network-helpers";
import { AUTH_ERROR, INVALID_INFLATION_ARRAY_ERR, INVALID_TIME_ERR, ZERO_ADDRESS_ERR } from "./helpers/errors.ts";
import {
AUTH_ERROR,
INVALID_INFLATION_ARRAY_ERR,
INVALID_TIME_ERR,
ZERO_ADDRESS_ERR,
ZERO_INITIAL_SUPPLY_ERR,
} from "./helpers/errors.ts";
import {
FINAL_INFLATION_RATE_DEFAULT, getTokensPerPeriod, getYearlyMintableTokens,
INFLATION_RATES_DEFAULT,
MINTABLE_YEARLY_TOKENS_REF_DEFAULT,
INFLATION_RATES_DEFAULT, INITIAL_SUPPLY_DEFAULT,
getMintableTokensForYear,
YEAR_IN_SECONDS,
FINAL_MINTABLE_YEARLY_TOKENS_REF_DEFAULT,
} from "./helpers/inflation.ts";


const tokenName = "Z";
const tokenSymbol = "Z";


describe("MeowToken Test", () => {
describe("ZToken Test", () => {
let zToken : ZToken;
let admin : SignerWithAddress;
let beneficiary : SignerWithAddress;
Expand All @@ -26,6 +33,9 @@ describe("MeowToken Test", () => {
let deployTime : bigint;
let initialTotalSupply : bigint;

const a = getMintableTokensForYear(99999);
console.log(a);

before(async () => {
[admin, beneficiary, randomAcc] = await hre.ethers.getSigners();

Expand All @@ -36,15 +46,39 @@ describe("MeowToken Test", () => {
admin.address,
admin.address,
beneficiary.address,
INITIAL_SUPPLY_DEFAULT,
INFLATION_RATES_DEFAULT,
FINAL_INFLATION_RATE_DEFAULT,
);
initialTotalSupply = await zToken.baseSupply();

deployTime = await zToken.deployTime();
deployTime = await zToken.DEPLOY_TIME();
});

describe("Deployment", () => {
describe("Deployment and Access Control", () => {
it("should mint the provided initial supply to the beneficiary address upon deployment", async () => {
const beneficiaryBal = await zToken.balanceOf(beneficiary.address);
expect(beneficiaryBal).to.eq(hre.ethers.parseEther(INITIAL_SUPPLY_DEFAULT.toString()));
});

it("should revert if initial supply is passed as 0", async () => {
await expect(
ZTokenFactory.deploy(
tokenName,
tokenSymbol,
admin.address,
admin.address,
beneficiary.address,
0,
INFLATION_RATES_DEFAULT,
FINAL_INFLATION_RATE_DEFAULT,
)
).to.be.revertedWithCustomError(
zToken,
ZERO_INITIAL_SUPPLY_ERR
);
});

it("should revert when inflation rates array is empty", async () => {
const inflationRatesEmpty : Array<bigint> = [];

Expand All @@ -55,6 +89,7 @@ describe("MeowToken Test", () => {
admin.address,
admin.address,
beneficiary.address,
INITIAL_SUPPLY_DEFAULT,
inflationRatesEmpty,
FINAL_INFLATION_RATE_DEFAULT,
)
Expand All @@ -74,6 +109,7 @@ describe("MeowToken Test", () => {
admin.address,
admin.address,
beneficiary.address,
INITIAL_SUPPLY_DEFAULT,
inflationRatesInvalid,
FINAL_INFLATION_RATE_DEFAULT,
)
Expand All @@ -88,20 +124,21 @@ describe("MeowToken Test", () => {
0n, 1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 10n, 11n, 12n, 13n, 14n, 15n, 16n, 17n, 18n, 19n, 20n,
];

const meowToken2 = await ZTokenFactory.deploy(
const zToken2 = await ZTokenFactory.deploy(
tokenName,
tokenSymbol,
admin.address,
admin.address,
beneficiary.address,
INITIAL_SUPPLY_DEFAULT,
rates,
FINAL_INFLATION_RATE_DEFAULT,
);

const rateFromContract = await meowToken2.currentInflationRate(rates.length + 2);
const rateFromContract = await zToken2.currentInflationRate(rates.length + 2);
expect(rateFromContract).to.eq(FINAL_INFLATION_RATE_DEFAULT);

const rateFromRates = await meowToken2.YEARLY_INFLATION_RATES(3);
const rateFromRates = await zToken2.ANNUAL_INFLATION_RATES(3);
expect(rateFromRates).to.eq(rates[3]);
});

Expand All @@ -113,6 +150,7 @@ describe("MeowToken Test", () => {
hre.ethers.ZeroAddress,
admin.address,
beneficiary.address,
INITIAL_SUPPLY_DEFAULT,
INFLATION_RATES_DEFAULT,
FINAL_INFLATION_RATE_DEFAULT,
)
Expand All @@ -128,6 +166,7 @@ describe("MeowToken Test", () => {
admin.address,
hre.ethers.ZeroAddress,
beneficiary.address,
INITIAL_SUPPLY_DEFAULT,
INFLATION_RATES_DEFAULT,
FINAL_INFLATION_RATE_DEFAULT,
)
Expand All @@ -143,6 +182,7 @@ describe("MeowToken Test", () => {
admin.address,
admin.address,
hre.ethers.ZeroAddress,
INITIAL_SUPPLY_DEFAULT,
INFLATION_RATES_DEFAULT,
FINAL_INFLATION_RATE_DEFAULT,
)
Expand All @@ -165,12 +205,51 @@ describe("MeowToken Test", () => {
expect(await zToken.hasRole(await zToken.DEFAULT_ADMIN_ROLE(), randomAcc.address)).to.be.false;
});

it("Fails when an address that does not have the MINTER_ROLE tries to mint", async () => {
it("should fail when an address that does not have the MINTER_ROLE tries to mint", async () => {
await expect(
zToken.connect(beneficiary).mint()
).to.be.revertedWithCustomError(zToken, AUTH_ERROR)
.withArgs(beneficiary.address, hre.ethers.solidityPackedKeccak256(["string"], ["MINTER_ROLE"]));
});

it("should be able to reassign the minter role to another address", async () => {
const minterRole = await zToken.MINTER_ROLE();

expect(await zToken.hasRole(minterRole, beneficiary.address)).to.be.false;

await zToken.connect(admin).grantRole(minterRole, beneficiary.address);

expect(await zToken.hasRole(minterRole, admin.address)).to.be.true;
expect(await zToken.hasRole(minterRole, beneficiary.address)).to.be.true;

await zToken.connect(admin).revokeRole(minterRole, admin.address);

expect(await zToken.hasRole(minterRole, admin.address)).to.be.false;

// assign back
await zToken.connect(admin).grantRole(minterRole, admin.address);

expect(await zToken.hasRole(minterRole, admin.address)).to.be.true;
});

it("should be able to reassign DEFAULT_ADMIN_ROLE to another address", async () => {
const adminRole = await zToken.DEFAULT_ADMIN_ROLE();
expect(await zToken.hasRole(adminRole, beneficiary.address)).to.be.false;

await zToken.connect(admin).grantRole(adminRole, beneficiary.address);

expect(await zToken.hasRole(adminRole, admin.address)).to.be.true;
expect(await zToken.hasRole(adminRole, beneficiary.address)).to.be.true;

await zToken.connect(admin).revokeRole(adminRole, admin.address);

expect(await zToken.hasRole(adminRole, admin.address)).to.be.false;

// assign back
await zToken.connect(beneficiary).grantRole(adminRole, admin.address);

expect(await zToken.hasRole(adminRole, admin.address)).to.be.true;
});
});

describe("#calculateMintableTokens()", () => {
Expand Down Expand Up @@ -231,7 +310,7 @@ describe("MeowToken Test", () => {

expect(tokensPerYear).to.eq(tokensPerYearRef);

const fixedFinalRateAmtRef = 151515151515000000000000000n;
const fixedFinalRateAmtRef = FINAL_MINTABLE_YEARLY_TOKENS_REF_DEFAULT;

tokensPerYear = await zToken.tokensPerYear(INFLATION_RATES_DEFAULT.length + 1);
expect(tokensPerYear).to.eq(fixedFinalRateAmtRef);
Expand All @@ -253,7 +332,7 @@ describe("MeowToken Test", () => {
totalSupply = await zToken.totalSupply();

let timeOfMint1 = deployTime + YEAR_IN_SECONDS / 2n - 1n;
const inflationRate = await zToken.YEARLY_INFLATION_RATES(1);
const inflationRate = await zToken.ANNUAL_INFLATION_RATES(1);
const tokensPerYearRef = totalSupply * inflationRate / 10000n;
firstMintAmtRef = tokensPerYearRef / 2n;

Expand Down Expand Up @@ -314,7 +393,7 @@ describe("MeowToken Test", () => {
await zToken.connect(admin).mint();
const balanceAfter1 = await zToken.balanceOf(beneficiary.address);

const fullYear3 = MINTABLE_YEARLY_TOKENS_REF_DEFAULT[3];
const fullYear3 = getMintableTokensForYear(3);
const closeoutRefAmt = (YEAR_IN_SECONDS - year3Period - 1n) * fullYear3 / YEAR_IN_SECONDS;
expect(balanceAfter1 - balanceBefore1).to.eq(closeoutRefAmt);

Expand All @@ -337,7 +416,7 @@ describe("MeowToken Test", () => {
const balanceAfter = await zToken.balanceOf(beneficiary.address);
timeOfMint = BigInt(await time.latest());

const yearly = MINTABLE_YEARLY_TOKENS_REF_DEFAULT[4];
const yearly = getMintableTokensForYear(4);
const periodAmtRef = yearly * (period + 1n) / YEAR_IN_SECONDS;

expect(balanceAfter - balanceBefore).to.eq(periodAmtRef, idx.toString());
Expand All @@ -357,7 +436,7 @@ describe("MeowToken Test", () => {

let tokenAmountRef = getTokensPerPeriod(4, YEAR_IN_SECONDS - (year4Period + 3n));
for (let year = 5; year < 12; year++) {
tokenAmountRef += MINTABLE_YEARLY_TOKENS_REF_DEFAULT[year];
tokenAmountRef += getMintableTokensForYear(year);
}

tokenAmountRef += getTokensPerPeriod(12, year12Period);
Expand Down Expand Up @@ -472,11 +551,12 @@ describe("Minting scenarios on clean state.", () => {
admin.address,
admin.address,
beneficiary.address,
INITIAL_SUPPLY_DEFAULT,
INFLATION_RATES_DEFAULT,
FINAL_INFLATION_RATE_DEFAULT,
);

deployTime = await zToken.deployTime();
deployTime = await zToken.DEPLOY_TIME();
});

it("should mint the correct amount of tokens when minted every second", async () => {
Expand Down Expand Up @@ -537,4 +617,43 @@ describe("Minting scenarios on clean state.", () => {
);
}
});
});

// eslint-disable-next-line max-len
it("should correctly account totalSupply and mintable tokens when burning by transfer to token contract", async () => {
// mint some tokens after a year and a half
const firstMintTime = deployTime + YEAR_IN_SECONDS / 2n - 1n;
await time.increaseTo(firstMintTime);

const totalSupplyBefore = await zToken.totalSupply();

await zToken.connect(admin).mint();

const totalSupplyAfter = await zToken.totalSupply();

const mintedAmtRef = getTokensPerPeriod(1, YEAR_IN_SECONDS / 2n);

expect(totalSupplyAfter - totalSupplyBefore).to.eq(mintedAmtRef);

// burn some tokens by transferring to token contract
const transferAmt = mintedAmtRef / 3n;
await zToken.connect(beneficiary).transfer(zToken.target, transferAmt);

const totalSupplyAfterBurn = await zToken.totalSupply();

expect(totalSupplyAfterBurn).to.eq(totalSupplyAfter - transferAmt);

// mint more tokens
const newMintPeriod = 31545n;
// we do not do - 1n here because previous tx already moved it by 1 second
const secondMintTime = firstMintTime + newMintPeriod;
await time.increaseTo(secondMintTime);

const totalSupplyBefore2 = await zToken.totalSupply();
await zToken.connect(admin).mint();
const totalSupplyAfter2 = await zToken.totalSupply();

const mintedAmtRef2 = getTokensPerPeriod(1, newMintPeriod);

expect(totalSupplyAfter2 - totalSupplyBefore2).to.eq(mintedAmtRef2);
});
});
1 change: 1 addition & 0 deletions test/helpers/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export const INVALID_INFLATION_ARRAY_ERR = "InvalidInflationRatesArray";
export const INVALID_TIME_ERR = "InvalidTimeSupplied";
export const ZERO_ADDRESS_ERR = "ZeroAddressPassed";
export const AUTH_ERROR = "AccessControlUnauthorizedAccount";
export const ZERO_INITIAL_SUPPLY_ERR = "NoInitialSupplyProvided";
31 changes: 14 additions & 17 deletions test/helpers/inflation.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
export const YEAR_IN_SECONDS = 31536000n;
export const INITIAL_SUPPLY_DEFAULT = 369000000n;
export const INFLATION_RATES_DEFAULT = [0n, 900n, 765n, 650n, 552n, 469n, 398n, 338n, 287n, 243n, 206n, 175n];
export const FINAL_INFLATION_RATE_DEFAULT = 150n;
export const MINTABLE_YEARLY_TOKENS_REF_DEFAULT = [
0n, // so that index matches year number since deploy
909090909090000000000000000n,
772727272726500000000000000n,
656565656565000000000000000n,
557575757575200000000000000n,
473737373736900000000000000n,
402020202019800000000000000n,
341414141413800000000000000n,
289898989898700000000000000n,
245454545454300000000000000n,
208080808080600000000000000n,
176767676767500000000000000n,
];
export const FINAL_MINTABLE_YEARLY_TOKENS_REF_DEFAULT = 151515151515000000000000000n;

export const getMintableTokensForYear = (year : number) : bigint => {
const inflationRatesLength = INFLATION_RATES_DEFAULT.length;
const inflationRate = year < inflationRatesLength
? INFLATION_RATES_DEFAULT[year]
: FINAL_INFLATION_RATE_DEFAULT;

return INITIAL_SUPPLY_DEFAULT * 10n**18n * inflationRate / 10000n;
};

export const FINAL_MINTABLE_YEARLY_TOKENS_REF_DEFAULT = 5535000000000000000000000n;

export const getYearlyMintableTokens = (yearIndex : number) : bigint =>
MINTABLE_YEARLY_TOKENS_REF_DEFAULT[yearIndex] !== undefined
? MINTABLE_YEARLY_TOKENS_REF_DEFAULT[yearIndex]
getMintableTokensForYear(yearIndex) !== undefined
? getMintableTokensForYear(yearIndex)
: FINAL_MINTABLE_YEARLY_TOKENS_REF_DEFAULT;

export const getTokensPerPeriod = (yearIndex : number, periodLength : bigint) : bigint => {
Expand Down