diff --git a/test/Passport.ts b/test/Passport.ts index 90053ea..c9616b9 100644 --- a/test/Passport.ts +++ b/test/Passport.ts @@ -1,19 +1,21 @@ +import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import BigNumber from 'bignumber.js'; import { expect } from 'chai'; -import { Signer } from 'ethers'; +import { Contract, Signer } from 'ethers'; import { ethers, upgrades } from 'hardhat'; -import { expectBalances } from './utils'; +import { expectBalances, expectEmittedEventWithArgs, toHex } from './utils'; const CALL_NETWORK = ethers.keccak256(ethers.toUtf8Bytes('development')); const USERNAME_KEY = ethers.keccak256(ethers.toUtf8Bytes('username')); const USERNAME_VALUE = ethers.toUtf8Bytes('chris'); +const EMPTY_BYTES = ethers.toUtf8Bytes(''); -describe('Passport', () => { +describe('Passport', function () { async function deployPriceFeed(signer: Signer, decimals: number, rounds: { roundId: string, answer: string; timestamp: string | number }[]) { const PriceFeed = await ethers.getContractFactory('PriceFeedMock', signer); - + const priceFeed = await PriceFeed.deploy( decimals, rounds.map((round) => ([ @@ -72,18 +74,27 @@ describe('Passport', () => { return { signers, baseV1, passportV1Call, crowdfundV1 }; } - describe('PassportV1', () => { - it('should have price', async () => { - const { passportV1Call } = await loadFixture(deployFixture); + let signers: HardhatEthersSigner[]; + let passportV1Call: Contract; + let baseV1: Contract; + let crowdfundV1: Contract; + + this.beforeAll(async function () { + const fixture = await loadFixture(deployFixture); + signers = fixture.signers; + passportV1Call = fixture.passportV1Call; + baseV1 = fixture.baseV1; + crowdfundV1 = fixture.crowdfundV1; + }); + describe('PassportV1', function () { + it('should have price', async () => { const [_, price] = await passportV1Call.price(); expect(ethers.formatEther(price)).to.equal('3117.507763975155279503'); }); it('should have price at', async () => { - const { passportV1Call } = await loadFixture(deployFixture); - const [roundIds, price] = await passportV1Call.price(); expect(await passportV1Call.priceAt(roundIds)).to.equal(price); @@ -91,8 +102,6 @@ describe('Passport', () => { describe('signer 1', () => { it('should not withdraw without admin', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const balance = await ethers.provider.getBalance(passportV1Call.target); await expect(passportV1Call.connect(signers[1]).withdraw(signers[1].address, balance)) @@ -100,114 +109,93 @@ describe('Passport', () => { }); it('should not assign without reservation', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - await expect(passportV1Call.connect(signers[1]).assign([[signers[2].address, 1]])) - .to.be.revertedWith('Forbidden'); + .to.be.revertedWithCustomError(passportV1Call, 'Forbidden'); }); it('should not claim without reservation', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - await expect(passportV1Call.connect(signers[1]).claim()) - .to.be.revertedWith('Forbidden'); + .to.be.revertedWithCustomError(passportV1Call, 'Forbidden'); }); it('should not purchase with smaller value', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const [roundIds, value] = await passportV1Call.price(); - await expect(passportV1Call.connect(signers[1]).purchase(roundIds, ethers.ZeroAddress, [], { value: value.sub(1) })) - .to.be.revertedWith('Forbidden'); + await expect(passportV1Call.connect(signers[1]) + .purchase( + roundIds, + ethers.ZeroAddress, + EMPTY_BYTES, + { value: (value as bigint) - 1n }, + )) + .to.be.revertedWithCustomError(passportV1Call, 'Forbidden'); }); it('should purchase', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const [roundIds, value] = await passportV1Call.price(); - await expect(passportV1Call.connect(signers[1]).purchase(roundIds, ethers.ZeroAddress, [], { value })) + await expect(passportV1Call.connect(signers[1]).purchase(roundIds, ethers.ZeroAddress, EMPTY_BYTES, { value })) .to.emit(passportV1Call, 'Transfer').withArgs(ethers.ZeroAddress, signers[1].address, 1); }); it('should have balance of', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - expect(await passportV1Call.balanceOf(signers[1].address)).to.eq(1); }); it('should have owner of', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - expect(await passportV1Call.ownerOf(1)).to.eq(signers[1].address); }); it('should have token', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - expect(await passportV1Call.token(signers[1].address)).to.eq(1); }); it('should not purchase again', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const [roundIds, value] = await passportV1Call.price(); - await expect(passportV1Call.connect(signers[1]).purchase(roundIds, ethers.ZeroAddress, [], { value })) - .to.be.revertedWith('Forbidden'); + await expect(passportV1Call.connect(signers[1]).purchase(roundIds, ethers.ZeroAddress, EMPTY_BYTES, { value })) + .to.be.revertedWithCustomError(passportV1Call, 'Forbidden'); }); }); - describe('signer 2', () => { + describe('signer 2', function () { it('should reserve', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const [roundIds, value] = await passportV1Call.price(); - await expect(passportV1Call.connect(signers[2]).reserve(roundIds, [[signers[2].address, 2]], [], { value: value.mul(2) })) - .to.emit(passportV1Call, 'ExecuteReserve'); + await expect( + passportV1Call + .connect(signers[2]) + .reserve(roundIds, [[signers[2].address, 2]], EMPTY_BYTES, { value: (value as bigint) * 2n }) + ).to.emit(passportV1Call, 'ExecuteReserve'); }); it('should claim', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - await expect(passportV1Call.connect(signers[2]).claim()) .to.emit(passportV1Call, 'Transfer').withArgs(ethers.ZeroAddress, signers[2].address, 2); }); it('should not claim again', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - await expect(passportV1Call.connect(signers[2]).claim()) - .to.be.revertedWith('Forbidden'); + .to.be.revertedWithCustomError(passportV1Call, 'Forbidden'); }); it('should not purchase again', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const [roundIds, value] = await passportV1Call.price(); - await expect(passportV1Call.connect(signers[2]).purchase(roundIds, ethers.ZeroAddress, [], { value })) - .to.be.revertedWith('Forbidden'); + await expect(passportV1Call.connect(signers[2]).purchase(roundIds, ethers.ZeroAddress, EMPTY_BYTES, { value })) + .to.be.revertedWithCustomError(passportV1Call, 'Forbidden'); }); it('should set property', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - await expect(passportV1Call.connect(signers[2]).setProperty(USERNAME_KEY, USERNAME_VALUE)) - .to.emit(passportV1Call, 'SetProperty').withArgs(2, USERNAME_KEY, ethers.utils.hexlify(USERNAME_VALUE)); + .to.emit(passportV1Call, 'SetProperty').withArgs(2, USERNAME_KEY, ethers.hexlify(USERNAME_VALUE)); }); it('should have property', async () => { - const { passportV1Call } = await loadFixture(deployFixture); - - expect(await passportV1Call.property(2, USERNAME_KEY)).to.eq(ethers.utils.hexlify(USERNAME_VALUE)); + expect(await passportV1Call.property(2, USERNAME_KEY)).to.eq(ethers.hexlify(USERNAME_VALUE)); }); it('should assign to signer 3', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - await expect(passportV1Call.connect(signers[2]).assign([[signers[3].address, 1]])) .to.emit(passportV1Call, 'Assign'); }); @@ -215,42 +203,38 @@ describe('Passport', () => { describe('signer 3', () => { it('should not set property without passport', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - await expect(passportV1Call.connect(signers[3]).setProperty(USERNAME_KEY, USERNAME_VALUE)) - .to.be.revertedWith('Forbidden'); + .to.be.revertedWithCustomError(passportV1Call, 'Forbidden'); }); it('should claim', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - await expect(passportV1Call.connect(signers[3]).claim()) .to.emit(passportV1Call, 'Transfer').withArgs(ethers.ZeroAddress, signers[3].address, 3); }); it('should set property batch', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); + const tx = await passportV1Call.connect(signers[3]).setPropertyBatch([[USERNAME_KEY, USERNAME_VALUE]]); - await expect(passportV1Call.connect(signers[3]).setPropertyBatch([[USERNAME_KEY, USERNAME_VALUE]])) - .to.emit(passportV1Call, 'SetPropertyBatch').withNamedArgs({ tokenId: 3 }); + await expectEmittedEventWithArgs( + passportV1Call, + tx, + 'SetPropertyBatch', + { tokenId: 3 }, + ); }); it('should have property', async () => { - const { passportV1Call } = await loadFixture(deployFixture); - - expect(await passportV1Call.property(3, USERNAME_KEY)).to.eq(ethers.utils.hexlify(USERNAME_VALUE)); + expect(await passportV1Call.property(3, USERNAME_KEY)).to.eq(ethers.hexlify(USERNAME_VALUE)); }); }); describe('signer 4', () => { it('should purchase with invalid referral without cashback', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const verify = await expectBalances([signers[0].address, signers[4].address, signers[5].address], passportV1Call); const [roundIds, value] = await passportV1Call.price(); - const tx = await passportV1Call.connect(signers[4]).purchase(roundIds, signers[5].address, [], { value }); + const tx = await passportV1Call.connect(signers[4]).purchase(roundIds, signers[5].address, EMPTY_BYTES, { value }); await expect(tx) .to.emit(passportV1Call, 'Transfer').withArgs(ethers.ZeroAddress, signers[4].address, 4); @@ -258,8 +242,8 @@ describe('Passport', () => { const receipt = await tx.wait(); await verify([ - (n) => n.add(value), - (n) => n.sub(value).sub(receipt.gasUsed.mul(receipt.effectiveGasPrice)), + (n) => n.plus(value), + (n) => n.minus(value).minus(new BigNumber(receipt.gasUsed).multipliedBy(receipt.gasPrice)), (n) => n, ]); }); @@ -267,13 +251,11 @@ describe('Passport', () => { describe('signer 5', () => { it('should reserve without passport without cashback', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const verify = await expectBalances([signers[0].address, signers[5].address], passportV1Call); const [roundIds, value] = await passportV1Call.price(); - const tx = await passportV1Call.connect(signers[5]).reserve(roundIds, [[signers[5].address, 1]], [], { value }); + const tx = await passportV1Call.connect(signers[5]).reserve(roundIds, [[signers[5].address, 1]], EMPTY_BYTES, { value }); await expect(tx) .to.emit(passportV1Call, 'ExecuteReserve'); @@ -281,68 +263,62 @@ describe('Passport', () => { const receipt = await tx.wait(); await verify([ - (n) => n.add(value), - (n) => n.sub(value).sub(receipt.gasUsed.mul(receipt.effectiveGasPrice)), + (n) => n.plus(value), + (n) => n.minus(value).minus(new BigNumber(receipt.gasUsed).multipliedBy(receipt.gasPrice)), ]); }); it('should purchase with referral with cashback', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const verify = await expectBalances([signers[0].address, signers[4].address, signers[5].address], passportV1Call); const [roundIds, value] = await passportV1Call.price(); - const tx = await passportV1Call.connect(signers[5]).purchase(roundIds, signers[4].address, [], { value }); + const tx = await passportV1Call.connect(signers[5]).purchase(roundIds, signers[4].address, EMPTY_BYTES, { value }); await expect(tx) .to.emit(passportV1Call, 'Transfer').withArgs(ethers.ZeroAddress, signers[5].address, 5); const receipt = await tx.wait(); - const cashback = value.mul(5).div(100); + const cashback = new BigNumber(value).multipliedBy(5).div(100).toFixed(0); await verify([ - (n) => n.add(value).sub(cashback).sub(cashback), - (n) => n.add(cashback), - (n) => n.sub(value).sub(receipt.gasUsed.mul(receipt.effectiveGasPrice)).add(cashback), + (n) => n.plus(value).minus(cashback).minus(cashback), + (n) => n.plus(cashback), + (n) => n.minus(value).minus(new BigNumber(receipt.gasUsed).multipliedBy(receipt.gasPrice)).plus(cashback), ]); }); it('should reserve with cashback with passport', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - const verify = await expectBalances([signers[0].address, signers[5].address], passportV1Call); const [roundIds, value] = await passportV1Call.price(); - const tx = await passportV1Call.connect(signers[5]).reserve(roundIds, [[signers[5].address, 1]], [], { value }); + const tx = await passportV1Call.connect(signers[5]).reserve(roundIds, [[signers[5].address, 1]], EMPTY_BYTES, { value }); await expect(tx) .to.emit(passportV1Call, 'ExecuteReserve'); const receipt = await tx.wait(); - const cashback = value.mul(5).div(100); + const cashback = new BigNumber(value).multipliedBy(5).div(100).toFixed(0); await verify([ - (n) => n.add(value).sub(cashback), - (n) => n.sub(value).sub(receipt.gasUsed.mul(receipt.effectiveGasPrice)).add(cashback), + (n) => n.plus(value).minus(cashback), + (n) => n.minus(value).minus(new BigNumber(receipt.gasUsed).multipliedBy(receipt.gasPrice)).plus(cashback), ]); }); }); describe('signer 0', () => { it('should withdraw', async () => { - const { signers, passportV1Call } = await loadFixture(deployFixture); - - const verify = await expectBalances([passportV1Call.address, signers[1].address], passportV1Call); + const verify = await expectBalances([passportV1Call.target, signers[1].address], passportV1Call); - const balance = await ethers.provider.getBalance(passportV1Call.address); + const balance = await ethers.provider.getBalance(passportV1Call.target); await passportV1Call.connect(signers[0]).withdraw(signers[1].address, balance); await verify([ - (n) => n.sub(balance), - (n) => n.add(balance), + (n) => n.minus(balance.toString()), + (n) => n.plus(balance.toString()), ]); }); }); @@ -350,114 +326,80 @@ describe('Passport', () => { describe('BaseV1', () => { it('should have total supply', async () => { - const { baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.totalSupply()).to.eq(5); }); it('should have total reservations', async () => { - const { baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.totalReservations()).to.eq(2); }); describe('signer 1', () => { it('should not grant without admin', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - await expect(baseV1.connect(signers[1]).grant([[signers[1].address, 1]])) .to.be.revertedWith(/^AccessControl: account .* is missing role/); }); it('should have balance of', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.balanceOf(signers[1].address)).to.eq(1); }); it('should have network of', async () => { - const { baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.networkOf(1)).to.eq(CALL_NETWORK); }); it('should have owner of', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.ownerOf(1)).to.eq(signers[1].address); }); it('should have token', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.token(signers[1].address)).to.eq(1); }); }); describe('signer 5', () => { it('should assign to signer 6', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - await expect(baseV1.connect(signers[5]).assign([[signers[6].address, 2]])) .to.emit(baseV1, 'Assign'); }); it('should have no reservations', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.reservations(signers[5].address)).to.eq(0); }); it('should have referrals', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.referrals(signers[5].address)).to.eq(2); }); }); describe('signer 6', () => { it('should have reservations', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.reservations(signers[6].address)).to.eq(2); }); it('should claim', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - await expect(baseV1.connect(signers[6]).claim(CALL_NETWORK)) .to.emit(baseV1, 'Mint').withArgs(CALL_NETWORK, signers[6].address, 6); }); it('should have balance of', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.balanceOf(signers[6].address)).to.eq(1); }); it('should have network of', async () => { - const { baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.networkOf(6)).to.eq(CALL_NETWORK); }); it('should have owner of', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.ownerOf(6)).to.eq(signers[6].address); }); it('should have token', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - expect(await baseV1.token(signers[6].address)).to.eq(6); }); }); describe('signer 0', () => { it('should grant signer 7', async () => { - const { signers, baseV1 } = await loadFixture(deployFixture); - await baseV1.connect(signers[0]).grant([[signers[7].address, 1]]); expect(await baseV1.reservations(signers[7].address)).to.eq(1); @@ -468,8 +410,6 @@ describe('Passport', () => { describe('Combined', () => { describe('signer 7', () => { it('should claim', async () => { - const { signers, baseV1, passportV1Call } = await loadFixture(deployFixture); - await expect(baseV1.connect(signers[7]).claim(CALL_NETWORK)) .to.emit(baseV1, 'Mint').withArgs(CALL_NETWORK, signers[7].address, 7) .to.emit(passportV1Call, 'Transfer').withArgs(ethers.ZeroAddress, signers[7].address, 7); @@ -481,41 +421,31 @@ describe('Passport', () => { describe('executable', () => { describe('signer 8', () => { it('should create', async () => { - const { signers, crowdfundV1 } = await loadFixture(deployFixture); - - await expect(crowdfundV1.connect(signers[8]).create('title', 'description', [[signers[10].address, 1]])) - .to.emit(crowdfundV1, 'Create').withNamedArgs({ campaignId: 0 }); + const tx = await crowdfundV1.connect(signers[8]).create('title', 'description', [[signers[10].address, 1]]); + await expectEmittedEventWithArgs(crowdfundV1, tx, 'Create', { campaignId: 0 }); }); it('should have state', async () => { - const { crowdfundV1 } = await loadFixture(deployFixture); - expect(await crowdfundV1.state(0)).to.equal(0); }); it('should not execute without fund', async () => { - const { signers, crowdfundV1 } = await loadFixture(deployFixture); - const [roundIds] = await crowdfundV1.goal(0); await expect(crowdfundV1.connect(signers[8]).execute(0, roundIds)) - .to.be.revertedWith('Forbidden'); + .to.be.revertedWithCustomError(crowdfundV1, 'Forbidden'); }); it('should fund', async () => { - const { signers, passportV1Call, crowdfundV1 } = await loadFixture(deployFixture); - const [_, price] = await passportV1Call.price(); - await expect(crowdfundV1.connect(signers[8]).fund(0, { value: price.add(100) })) + await expect(crowdfundV1.connect(signers[8]).fund(0, { value: toHex(new BigNumber(price).plus(100)) })) .to.emit(crowdfundV1, 'Fund'); }); }); describe('signer 9', () => { it('should execute and refund additional', async () => { - const { signers, passportV1Call, crowdfundV1 } = await loadFixture(deployFixture); - const verify = await expectBalances([signers[0].address, signers[8].address], crowdfundV1); const [roundIds, amount] = await crowdfundV1.goal(0); @@ -525,14 +455,12 @@ describe('Passport', () => { .to.emit(crowdfundV1, 'StateChanged').withArgs(0, 2); await verify([ - (n) => n.add(amount), - (n) => n.add(100), + (n) => n.plus(amount), + (n) => n.plus(100), ]); }); it('should have state', async () => { - const { crowdfundV1 } = await loadFixture(deployFixture); - expect(await crowdfundV1.state(0)).to.equal(2); }); }); @@ -541,17 +469,13 @@ describe('Passport', () => { describe('refundable', async () => { describe('signer 1', () => { it('should create', async () => { - const { signers, crowdfundV1 } = await loadFixture(deployFixture); - - await expect(crowdfundV1.connect(signers[1]).create('title', 'description', [[signers[10].address, 1]])) - .to.emit(crowdfundV1, 'Create').withNamedArgs({ campaignId: 1 }); + const tx = await crowdfundV1.connect(signers[1]).create('title', 'description', [[signers[10].address, 1]]); + await expectEmittedEventWithArgs(crowdfundV1, tx, 'Create', { campaignId: 1 }); }); }); describe('signer 2', () => { it('should fund', async () => { - const { signers, passportV1Call, crowdfundV1 } = await loadFixture(deployFixture); - const [_, price] = await passportV1Call.price(); await expect(crowdfundV1.connect(signers[2]).fund(1, { value: price })) @@ -561,8 +485,6 @@ describe('Passport', () => { describe('signer 3', () => { it('should fund', async () => { - const { signers, passportV1Call, crowdfundV1 } = await loadFixture(deployFixture); - const [_, price] = await passportV1Call.price(); await expect(crowdfundV1.connect(signers[3]).fund(1, { value: price })) @@ -570,17 +492,13 @@ describe('Passport', () => { }); it('should not refund without creator', async () => { - const { signers, crowdfundV1 } = await loadFixture(deployFixture); - await expect(crowdfundV1.connect(signers[3]).refund(1)) - .to.be.revertedWith('Forbidden'); + .to.be.revertedWithCustomError(crowdfundV1, 'Forbidden'); }); }); describe('signer 1', async () => { it('should refund', async () => { - const { signers, passportV1Call, crowdfundV1 } = await loadFixture(deployFixture); - const verify = await expectBalances([signers[2].address, signers[3].address], crowdfundV1); await crowdfundV1.connect(signers[1]).refund(1); @@ -588,22 +506,18 @@ describe('Passport', () => { const [_, price] = await passportV1Call.price(); await verify([ - (n) => n.add(price), - (n) => n.add(price), + (n) => n.plus(price), + (n) => n.plus(price), ]); }); it('should have state', async () => { - const { crowdfundV1 } = await loadFixture(deployFixture); - expect(await crowdfundV1.state(1)).to.equal(3); }); it('should not refund again', async () => { - const { signers, crowdfundV1 } = await loadFixture(deployFixture); - await expect(crowdfundV1.connect(signers[1]).refund(1)) - .to.be.revertedWith('Forbidden'); + .to.be.revertedWithCustomError(crowdfundV1, 'Forbidden'); }); }); }); diff --git a/test/utils.ts b/test/utils.ts index c6e2bb5..e0b9c9d 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -20,7 +20,8 @@ export async function expectBalances(addresses: string[], pullPayment: Contract) const before: BigNumber[] = []; for (const address of addresses) { - before.push(await getBalance(address, pullPayment)); + const balance = await getBalance(address, pullPayment); + before.push(balance); } return async (expected: ((before: BigNumber) => BigNumber)[]) => {