p2pix-smart-contracts/test/p2pix.test.ts
2022-12-03 04:38:30 -03:00

286 lines
9.9 KiB
TypeScript

import "@nomicfoundation/hardhat-chai-matchers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai";
import { BigNumber, Wallet } from "ethers";
import { ethers, network } from "hardhat";
import { MockToken, P2PIX, Reputation } from "../src/types";
import { P2PixErrors } from "./utils/errors";
import {
// getSignerAddrs,
p2pixFixture,
randomSigners,
} from "./utils/fixtures";
describe("P2PIX", () => {
type WalletWithAddress = Wallet & SignerWithAddress;
// contract deployer/admin
let owner: WalletWithAddress;
// extra EOAs
let acc01: WalletWithAddress;
let acc02: WalletWithAddress;
let acc03: WalletWithAddress;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let res: any;
let p2pix: P2PIX; // Contract instance
let erc20: MockToken; // Token instance
let reputation: Reputation; // Reputation Interface instance;
const fundAmount: BigNumber =
ethers.utils.parseEther("10000");
const price: BigNumber = ethers.utils.parseEther("100");
const zero = ethers.constants.AddressZero;
before("Set signers and reset network", async () => {
[owner, acc01, acc02, acc03] =
await // eslint-disable-next-line @typescript-eslint/no-explicit-any
(ethers as any).getSigners();
await network.provider.send("hardhat_reset");
});
beforeEach("Load deployment fixtures", async () => {
({ erc20, p2pix, reputation } = await loadFixture(
p2pixFixture,
));
});
describe("Init", async () => {
it("P2PIX, Reputation and ERC20 should initialize", async () => {
await p2pix.deployed();
await erc20.deployed();
await reputation.deployed();
expect(p2pix).to.be.ok;
expect(erc20).to.be.ok;
expect(reputation).to.be.ok;
const ownerKey = await p2pix._castAddrToKey(
owner.address,
);
const acc01Key = await p2pix._castAddrToKey(
acc01.address,
);
// storage checks
expect(
await p2pix.callStatic.defaultLockBlocks(),
).to.eq(4);
expect(await p2pix.callStatic.reputation()).to.eq(
reputation.address,
);
expect(await p2pix.callStatic.depositCount()).to.eq(0);
expect(
await p2pix.callStatic.validBacenSigners(ownerKey),
).to.eq(true);
expect(
await p2pix.callStatic.validBacenSigners(acc01Key),
).to.eq(true);
expect(
await p2pix.callStatic.allowedERC20s(erc20.address),
).to.eq(true);
// event emission
await expect(await p2pix.deployTransaction)
.to.emit(p2pix, "OwnerUpdated")
.withArgs(zero, owner.address)
.and.to.emit(p2pix, "LockBlocksUpdated")
.withArgs(4)
.and.to.emit(p2pix, "ReputationUpdated")
.withArgs(reputation.address)
.and.to.emit(p2pix, "ValidSignersUpdated")
.withArgs([owner.address, acc01.address])
.and.to.emit(p2pix, "AllowedERC20Updated")
.withArgs(erc20.address, true);
});
it("accounts have been funded", async () => {
// can't be eq to fundAmount due to contract deployment cost
res = await ethers.provider.getBalance(owner.address);
expect(res.toString()).to.have.lengthOf(22);
// console.log(res); // lengthOf = 22
// console.log(fundAmount); // lengthOf = 23
// those should eq to hardhat prefunded account's value
expect(
await ethers.provider.getBalance(acc01.address),
).to.eq(fundAmount);
expect(
await ethers.provider.getBalance(acc02.address),
).to.eq(fundAmount);
expect(
await ethers.provider.getBalance(acc03.address),
).to.eq(fundAmount);
});
});
// each describe tests a set of functionalities of the contract's behavior
describe("Owner Functions", async () => {
it("should allow owner to withdraw contract's balance", async () => {
const oldBal = await p2pix.provider.getBalance(
p2pix.address,
);
// this call also tests p2pix's receive() fallback mechanism.
const tx1 = await acc01.sendTransaction({
to: p2pix.address,
value: price,
});
const newBal = await p2pix.provider.getBalance(
p2pix.address,
);
expect(tx1).to.be.ok;
expect(oldBal).to.eq(0);
expect(newBal).to.eq(price);
await expect(p2pix.withdrawBalance())
.to.changeEtherBalances(
[owner.address, p2pix.address],
[price, "-100000000000000000000"],
)
.and.to.emit(p2pix, "FundsWithdrawn")
.withArgs(owner.address, price);
await expect(
p2pix.connect(acc01).withdrawBalance(),
).to.be.revertedWith(P2PixErrors.UNAUTHORIZED);
});
it("should allow owner to change reputation instance", async () => {
const tx = await p2pix.setReputation(acc03.address);
const newRep = await p2pix.callStatic.reputation();
const fail = p2pix
.connect(acc02)
.setReputation(owner.address);
expect(tx).to.be.ok;
await expect(tx)
.to.emit(p2pix, "ReputationUpdated")
.withArgs(acc03.address);
expect(newRep).to.eq(acc03.address);
await expect(fail).to.be.revertedWith(
P2PixErrors.UNAUTHORIZED,
);
});
it("should allow owner to change defaultLockBlocks ", async () => {
const magicVal = 1337;
const tx = await p2pix.setDefaultLockBlocks(magicVal);
const newVal =
await p2pix.callStatic.defaultLockBlocks();
const fail = p2pix
.connect(acc02)
.setDefaultLockBlocks(0);
expect(tx).to.be.ok;
await expect(tx)
.to.emit(p2pix, "LockBlocksUpdated")
.withArgs(magicVal);
expect(newVal).to.eq(magicVal);
await expect(fail).to.be.revertedWith(
P2PixErrors.UNAUTHORIZED,
);
});
it("should allow owner to add valid Bacen signers", async () => {
const newSigners = randomSigners(2);
const bob = await newSigners[0].getAddress();
const alice = await newSigners[1].getAddress();
const bobCasted = await p2pix._castAddrToKey(bob);
const aliceCasted = await p2pix._castAddrToKey(alice);
const tx = await p2pix.setValidSigners([bob, alice]);
const newSigner1 =
await p2pix.callStatic.validBacenSigners(bobCasted);
const newSigner2 =
await p2pix.callStatic.validBacenSigners(aliceCasted);
const fail = p2pix
.connect(acc03)
.setValidSigners([owner.address, acc02.address]);
expect(tx).to.be.ok;
expect(newSigner1).to.eq(true);
expect(newSigner2).to.eq(true);
await expect(tx)
.to.emit(p2pix, "ValidSignersUpdated")
.withArgs([bob, alice]);
await expect(fail).to.be.revertedWith(
P2PixErrors.UNAUTHORIZED,
);
});
it("should allow owner to adjust tokenSettings", async () => {
const tx = await p2pix.tokenSettings(
[erc20.address, owner.address],
[false, true],
);
const newTokenState1 =
await p2pix.callStatic.allowedERC20s(erc20.address);
const newTokenState2 =
await p2pix.callStatic.allowedERC20s(owner.address);
const fail = p2pix
.connect(acc01)
.tokenSettings([acc01.address], [false]);
expect(tx).to.be.ok;
await expect(tx)
.to.emit(p2pix, "AllowedERC20Updated")
.withArgs(erc20.address, false)
.and.to.emit(p2pix, "AllowedERC20Updated")
.withArgs(owner.address, true);
expect(newTokenState1).to.eq(false);
expect(newTokenState2).to.eq(true);
await expect(fail).to.be.revertedWith(
P2PixErrors.UNAUTHORIZED,
);
});
});
describe("Deposit", async () => {
// it ("should revert if ERC20 is not alloed")
// it ("should revert if deposit already exists")
// it ("should create deposit, update storage and emit event")
// it ("should create multiple deposits") - EDGE CASE TEST
});
describe("Lock", async () => {
// it ("should revert if deposit is invalid")
// it ("should revert if wished amount is greater than deposit's remaining amount")
// it ("should revert if a non expired lock has the same ID encoded")
// it ("should revert if an invalid allowlist merkleproof is provided")
// it ("should revert if msg.sender does not have enough credit in his spend limit")
// it ("should create a lock, update storage and emit events via the allowlist path")
// it ("should create a lock, update storage and emit events via the reputation path")
// it ("should create multiple locks") - EDGE CASE TEST
// CHECK UNEXPIRE LOCK
});
describe("Cancel Deposit", async () => {
// it("should revert if the msg.sender isn't the deposit's seller")
// it("should cancel deposit, update storage and emit events")
// it("should cancel multiple deposits")
});
describe("Release", async () => {
// it("should revert if lock has expired or has already been released")
// it("should revert if signed message has already been used")
// it("should revert if ecrecovered signer is invalid")
// it("should release lock, update storage and emit events")
// it("should release multiple locks") - EDGE CASE TEST {
// TEST 3 CASES (
// EMPTY PREMIUM,
// LOCK RELAYER != RELEASE RELAYER, (check userRecord storage update)
// LOCK RELAYER == RELEASE RELAYER (check userRecord storage update)
// )}
});
describe("Unlock Expired Locks", async () => {
// it("should revert if lock isn't expired")
// it("should unlock expired locks, update storage and emit events")
// CHECK FOR userRecord STORAGE UPDATE
});
describe("Seller withdraw", async () => {
// it("should revert if the msg.sender isn't the deposit's seller")
// it -> withdraw remaining funds from deposit
// CHECK UNEXPIRE LOCKS
});
describe("Seller Allowlist Settings", async () => {
// it -> set root of seller's allowlist
// (test msg.sender != seller error)
});
});