Skip to content

Commit

Permalink
Introduce maximum supply
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelKim20 committed Sep 26, 2024
1 parent 4984e42 commit 89792e7
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 17 deletions.
6 changes: 5 additions & 1 deletion packages/contracts/contracts/LYT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ contract LYT is LoyaltyToken {
/*
* Public functions
*/
constructor(address account_, address feeAccount_) LoyaltyToken("Loyalty Coin", "LYT", account_, feeAccount_) {}
constructor(
address account_,
address feeAccount_,
uint256 maxSupply_
) LoyaltyToken("Loyalty Coin", "LYT", account_, feeAccount_, maxSupply_) {}
}
13 changes: 12 additions & 1 deletion packages/contracts/contracts/LoyaltyToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ contract LoyaltyToken is BIP20DelegatedTransfer {
/*
* Storage
*/
uint256 public _maxSupply;

/*
* Public functions
Expand All @@ -19,15 +20,25 @@ contract LoyaltyToken is BIP20DelegatedTransfer {
string memory name_,
string memory symbol_,
address account_,
address feeAccount_
address feeAccount_,
uint256 maxSupply_
) BIP20DelegatedTransfer(name_, symbol_, account_, feeAccount_) {
require(
IMultiSigWallet(owner).supportsInterface(type(IMultiSigWallet).interfaceId),
"LoyaltyToken: Invalid interface ID of multi sig wallet"
);
_maxSupply = maxSupply_;
}

function mint(uint256 amount) external onlyOwner {
require(
(_maxSupply == 0) || (_maxSupply > 0 && totalSupply() + amount <= _maxSupply),
"LoyaltyToken: The total supply exceeded maximum and rejected"
);
_mint(owner, amount);
}

function maxSupply() public view returns (uint256) {
return _maxSupply;
}
}
4 changes: 2 additions & 2 deletions packages/contracts/deploy/main_chain_devnet/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BOACoin } from "../../src/utils/Amount";
import { ContractUtils } from "../../src/utils/ContractUtils";
import { LYT, MultiSigWallet, MultiSigWalletFactory } from "../../typechain-types";

import { BaseContract, Contract, Wallet } from "ethers";
import { BaseContract, BigNumber, Contract, Wallet } from "ethers";

import fs from "fs";

Expand Down Expand Up @@ -198,7 +198,7 @@ async function deployToken(accounts: IAccount, deployment: Deployments) {
const factory = await ethers.getContractFactory("LYT");
const contract = (await factory
.connect(accounts.deployer)
.deploy(deployment.getContractAddress("MultiSigWallet"), deployment.accounts.feeAccount.address)) as LYT;
.deploy(deployment.getContractAddress("MultiSigWallet"), deployment.accounts.feeAccount.address, BigNumber.from(10).pow(BigNumber.from(28)))) as LYT;
await contract.deployed();
await contract.deployTransaction.wait();

Expand Down
4 changes: 2 additions & 2 deletions packages/contracts/deploy/side_chain_devnet/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BOACoin } from "../../src/utils/Amount";
import { ContractUtils } from "../../src/utils/ContractUtils";
import { LYT, MultiSigWallet, MultiSigWalletFactory } from "../../typechain-types";

import { BaseContract, Contract, Wallet } from "ethers";
import { BaseContract, BigNumber, Contract, Wallet } from "ethers";

import fs from "fs";

Expand Down Expand Up @@ -198,7 +198,7 @@ async function deployToken(accounts: IAccount, deployment: Deployments) {
const factory = await ethers.getContractFactory("LYT");
const contract = (await factory
.connect(accounts.deployer)
.deploy(deployment.getContractAddress("MultiSigWallet"), deployment.accounts.feeAccount.address)) as LYT;
.deploy(deployment.getContractAddress("MultiSigWallet"), deployment.accounts.feeAccount.address, BigNumber.from(10).pow(BigNumber.from(28)))) as LYT;
await contract.deployed();
await contract.deployTransaction.wait();

Expand Down
5 changes: 3 additions & 2 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "loyalty-tokens",
"version": "2.1.1",
"version": "2.3.0",
"description": "Smart contracts for the loyalty tokens",
"files": [
"**/*.sol"
Expand All @@ -16,7 +16,8 @@
"formatting:check": "prettier '**/*.{json,sol,ts,js,md}' -c",
"formatting:write": "prettier '**/*.{json,sol,ts,js,md}' --write",
"test:DelegatedTransfer": "hardhat test test/DelegatedTransfer.test.ts",
"test:MultiSigToken": "hardhat test test/MultiSigToken.test.ts"
"test:MultiSigToken": "hardhat test test/MultiSigToken.test.ts",
"test:MaxSupply": "hardhat test test/MaxSupply.test.ts"
},
"repository": {
"type": "git",
Expand Down
6 changes: 3 additions & 3 deletions packages/contracts/test/DelegatedTransfer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ async function deployMultiSigWallet(
: undefined;
}

async function deployToken(deployer: Wallet, owner: string, feeAccount: string): Promise<LYT> {
async function deployToken(deployer: Wallet, owner: string, feeAccount: string, maxSupply: BigNumber): Promise<LYT> {
const factory = await ethers.getContractFactory("LYT");
const contract = (await factory.connect(deployer).deploy(owner, feeAccount)) as LYT;
const contract = (await factory.connect(deployer).deploy(owner, feeAccount, maxSupply)) as LYT;
await contract.deployed();
await contract.deployTransaction.wait();
return contract;
Expand Down Expand Up @@ -88,7 +88,7 @@ describe("Test for LYT token", () => {
it("Create Token, Owner is MultiSigWallet", async () => {
assert.ok(multiSigWallet);

token = await deployToken(deployer, multiSigWallet.address, feeAccount.address);
token = await deployToken(deployer, multiSigWallet.address, feeAccount.address, BigNumber.from(0));
assert.deepStrictEqual(await token.getOwner(), multiSigWallet.address);
assert.deepStrictEqual(await token.balanceOf(multiSigWallet.address), BigNumber.from(0));
});
Expand Down
248 changes: 248 additions & 0 deletions packages/contracts/test/MaxSupply.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-waffle";
import { ethers } from "hardhat";

import { HardhatAccount } from "../src/HardhatAccount";
import { LYT, MultiSigWallet, MultiSigWalletFactory } from "../typechain-types";

import assert from "assert";
import { BigNumber, Wallet } from "ethers";
import { ContractUtils } from "../src/utils/ContractUtils";

import { expect } from "chai";

async function deployMultiSigWalletFactory(deployer: Wallet): Promise<MultiSigWalletFactory> {
const factory = await ethers.getContractFactory("MultiSigWalletFactory");
const contract = (await factory.connect(deployer).deploy()) as MultiSigWalletFactory;
await contract.deployed();
await contract.deployTransaction.wait();
return contract;
}

async function deployMultiSigWallet(
factoryAddress: string,
deployer: Wallet,
owners: string[],
required: number,
seed: BigNumber
): Promise<MultiSigWallet | undefined> {
const contractFactory = await ethers.getContractFactory("MultiSigWalletFactory");
const factoryContract = contractFactory.attach(factoryAddress) as MultiSigWalletFactory;

const address = await ContractUtils.getEventValueString(
await factoryContract.connect(deployer).create("", "", owners, required, seed),
factoryContract.interface,
"ContractInstantiation",
"wallet"
);

return address !== undefined
? ((await ethers.getContractFactory("MultiSigWallet")).attach(address) as MultiSigWallet)
: undefined;
}

async function deployToken(deployer: Wallet, owner: string, feeAccount: string, maxSupply: BigNumber): Promise<LYT> {
const factory = await ethers.getContractFactory("LYT");
const contract = (await factory.connect(deployer).deploy(owner, feeAccount, maxSupply)) as LYT;
await contract.deployed();
await contract.deployTransaction.wait();
return contract;
}

describe("Test for LYT token", () => {
const raws = HardhatAccount.keys.map((m) => new Wallet(m, ethers.provider));
const [deployer, feeAccount, account0, account1, account2, account3, account4] = raws;
const owners1 = [account0, account1, account2];

let multiSigFactory: MultiSigWalletFactory;
let multiSigWallet: MultiSigWallet | undefined;
let token: LYT;
const requiredConfirmations = 2;
let totalSupply = BigNumber.from(0);

before(async () => {
multiSigFactory = await deployMultiSigWalletFactory(deployer);
assert.ok(multiSigFactory);
});

it("Create Wallet by Factory", async () => {
multiSigWallet = await deployMultiSigWallet(
multiSigFactory.address,
deployer,
owners1.map((m) => m.address),
requiredConfirmations,
BigNumber.from(1)
);
assert.ok(multiSigWallet);

assert.deepStrictEqual(
await multiSigWallet.getMembers(),
owners1.map((m) => m.address)
);

assert.deepStrictEqual(await multiSigFactory.getNumberOfWalletsForMember(account0.address), BigNumber.from(1));
assert.deepStrictEqual(await multiSigFactory.getNumberOfWalletsForMember(account1.address), BigNumber.from(1));
assert.deepStrictEqual(await multiSigFactory.getNumberOfWalletsForMember(account2.address), BigNumber.from(1));
});

it("Create Token, Owner is wallet", async () => {
const factory = await ethers.getContractFactory("LYT");
await expect(
factory
.connect(deployer)
.deploy(account0.address, feeAccount.address, BigNumber.from(10).pow(BigNumber.from(28)))
).to.be.revertedWith("function call to a non-contract account");
});

it("Create Token, Owner is MultiSigWallet", async () => {
assert.ok(multiSigWallet);

token = await deployToken(
deployer,
multiSigWallet.address,
feeAccount.address,
BigNumber.from(10).pow(BigNumber.from(28))
);
assert.deepStrictEqual(await token.getOwner(), multiSigWallet.address);
assert.deepStrictEqual(await token.balanceOf(multiSigWallet.address), BigNumber.from(0));
assert.deepStrictEqual(await token.maxSupply(), BigNumber.from(10).pow(BigNumber.from(28)));
assert.deepStrictEqual(await token.totalSupply(), BigNumber.from(0));
});

it("Fail mint initial supply", async () => {
const amount = BigNumber.from(10).pow(BigNumber.from(18));
await expect(token.connect(account0).mint(amount)).to.be.revertedWith("Only the owner can execute");
});

it("mint 1", async () => {
assert.ok(multiSigWallet);
assert.ok(token);

const initialSupply = BigNumber.from(10).pow(BigNumber.from(27)).mul(5);
totalSupply = BigNumber.from(initialSupply);

const mintEncoded = token.interface.encodeFunctionData("mint", [initialSupply]);

const transactionId = await ContractUtils.getEventValueBigNumber(
await multiSigWallet
.connect(account0)
.submitTransaction("Mint", "Mint 1 token", token.address, 0, mintEncoded),
multiSigWallet.interface,
"Submission",
"transactionId"
);
assert.ok(transactionId !== undefined);

const executedTransactionId = await ContractUtils.getEventValueBigNumber(
await multiSigWallet.connect(account1).confirmTransaction(transactionId),
multiSigWallet.interface,
"Execution",
"transactionId"
);

// Check that transaction has been executed
assert.deepStrictEqual(transactionId, executedTransactionId);

// Check balance of target
assert.deepStrictEqual(await token.balanceOf(multiSigWallet.address), totalSupply);
});

it("mint 2", async () => {
assert.ok(multiSigWallet);
assert.ok(token);

const additionalSupply = BigNumber.from(10).pow(BigNumber.from(27)).mul(3);
totalSupply = totalSupply.add(additionalSupply);

const mintEncoded = token.interface.encodeFunctionData("mint", [additionalSupply]);

const transactionId = await ContractUtils.getEventValueBigNumber(
await multiSigWallet
.connect(account0)
.submitTransaction("Mint", "Mint 1 token", token.address, 0, mintEncoded),
multiSigWallet.interface,
"Submission",
"transactionId"
);
assert.ok(transactionId !== undefined);

const executedTransactionId = await ContractUtils.getEventValueBigNumber(
await multiSigWallet.connect(account1).confirmTransaction(transactionId),
multiSigWallet.interface,
"Execution",
"transactionId"
);

// Check that transaction has been executed
assert.deepStrictEqual(transactionId, executedTransactionId);

// Check balance of target
assert.deepStrictEqual(await token.balanceOf(multiSigWallet.address), totalSupply);
});

it("mint 3", async () => {
assert.ok(multiSigWallet);
assert.ok(token);

const additionalSupply = BigNumber.from(10).pow(BigNumber.from(27)).mul(2);
totalSupply = totalSupply.add(additionalSupply);

const mintEncoded = token.interface.encodeFunctionData("mint", [additionalSupply]);

const transactionId = await ContractUtils.getEventValueBigNumber(
await multiSigWallet
.connect(account0)
.submitTransaction("Mint", "Mint 1 token", token.address, 0, mintEncoded),
multiSigWallet.interface,
"Submission",
"transactionId"
);
assert.ok(transactionId !== undefined);

const executedTransactionId = await ContractUtils.getEventValueBigNumber(
await multiSigWallet.connect(account1).confirmTransaction(transactionId),
multiSigWallet.interface,
"Execution",
"transactionId"
);

// Check that transaction has been executed
assert.deepStrictEqual(transactionId, executedTransactionId);

// Check balance of target
assert.deepStrictEqual(await token.balanceOf(multiSigWallet.address), totalSupply);
});

it("mint 4", async () => {
assert.ok(multiSigWallet);
assert.ok(token);

const additionalSupply = BigNumber.from(1);
totalSupply = totalSupply.add(additionalSupply);

const mintEncoded = token.interface.encodeFunctionData("mint", [additionalSupply]);

const transactionId = await ContractUtils.getEventValueBigNumber(
await multiSigWallet
.connect(account0)
.submitTransaction("Mint", "Mint 1 token", token.address, 0, mintEncoded),
multiSigWallet.interface,
"Submission",
"transactionId"
);
assert.ok(transactionId !== undefined);

const executedTransactionId = await ContractUtils.getEventValueBigNumber(
await multiSigWallet.connect(account1).confirmTransaction(transactionId),
multiSigWallet.interface,
"Execution",
"transactionId"
);

// Check that transaction has been executed
assert.notDeepStrictEqual(transactionId, executedTransactionId);

// Check balance of target
assert.deepStrictEqual(await token.balanceOf(multiSigWallet.address), totalSupply.sub(1));
});
});
Loading

0 comments on commit 89792e7

Please sign in to comment.