test: added unexpire locks unit tests

This commit is contained in:
PedroCailleret 2022-12-08 20:38:58 -03:00
parent d541e7b70c
commit 932b2a03b4
18 changed files with 332 additions and 88 deletions

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/ce60783a904758b510bc61bc47947601.json" "buildInfo": "../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/4a0f142c7439e90ed9431fa5c0d6c0c6.json" "buildInfo": "../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/ba59382ab04b2124c4bb550173fdda55.json" "buildInfo": "../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ce60783a904758b510bc61bc47947601.json" "buildInfo": "../../../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ce60783a904758b510bc61bc47947601.json" "buildInfo": "../../../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/e5d5971d8e129f0e49fa041ef3572fed.json" "buildInfo": "../../../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ce60783a904758b510bc61bc47947601.json" "buildInfo": "../../../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ce60783a904758b510bc61bc47947601.json" "buildInfo": "../../../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ce60783a904758b510bc61bc47947601.json" "buildInfo": "../../../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ce60783a904758b510bc61bc47947601.json" "buildInfo": "../../../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ce60783a904758b510bc61bc47947601.json" "buildInfo": "../../../../build-info/8b1c16bb48b706a49fa1987390ca36fd.json"
} }

View File

@ -1,4 +1,4 @@
{ {
"_format": "hh-sol-dbg-1", "_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/4a0f142c7439e90ed9431fa5c0d6c0c6.json" "buildInfo": "../../build-info/43c90b4df62900d52ce9b2809e9d7089.json"
} }

File diff suppressed because one or more lines are too long

View File

@ -348,8 +348,8 @@ contract P2PIX is
uint256 _newUserRecord = (userRecord[userKey] >> uint256 _newUserRecord = (userRecord[userKey] >>
1); 1);
if (_newUserRecord <= 100) { if (_newUserRecord <= 1e2) {
userRecord[userKey] = 100; userRecord[userKey] = 1e2;
} else { } else {
userRecord[userKey] = _newUserRecord; userRecord[userKey] = _newUserRecord;
} }
@ -406,10 +406,7 @@ contract P2PIX is
sellerAllowList[ sellerAllowList[
_castAddrToKey(addr) _castAddrToKey(addr)
] = merkleroot; ] = merkleroot;
emit RootUpdated( emit RootUpdated(addr, merkleroot);
addr,
merkleroot
);
} else revert OnlySeller(); } else revert OnlySeller();
} }
@ -536,11 +533,11 @@ contract P2PIX is
/// @dev Called exclusively by the `unlockExpired` method. /// @dev Called exclusively by the `unlockExpired` method.
/// @dev Function sighash: 0x74e2a0bb. /// @dev Function sighash: 0x74e2a0bb.
function _notExpired(DT.Lock storage _l) private view { function _notExpired(DT.Lock storage _l) private view {
// not expired or released
// Custom Error Solidity Impl // Custom Error Solidity Impl
if ( if (_l.expirationBlock > block.number)
_l.expirationBlock >= block.number || revert NotExpired();
_l.amount <= 0 if (_l.amount == 0) revert AlreadyReleased();
) revert NotExpired();
/* /*
// Custom Error Yul Impl // Custom Error Yul Impl
assembly { assembly {
@ -558,13 +555,13 @@ contract P2PIX is
} }
} }
// Require Error Solidity Impl
require(
_l.expirationBlock < block.number &&
_l.amount > 0,
"P2PIX: Lock not expired or already released"
);
*/ */
// Require Error Solidity Impl
// require(
// _l.expirationBlock > block.number &&
// _l.amount > 0,
// "P2PIX: Lock not expired or already released"
// );
} }
/// @notice Internal view auxiliar logic that returns a new valid `_depositID`. /// @notice Internal view auxiliar logic that returns a new valid `_depositID`.
@ -576,8 +573,9 @@ contract P2PIX is
returns (uint256 _depositID) returns (uint256 _depositID)
{ {
(_depositID) = depositCount.current(); (_depositID) = depositCount.current();
if (mapDeposits[_depositID].valid == true) /// @todo remove this for good
revert DepositAlreadyExists(); // if (mapDeposits[_depositID].valid == true)
// revert DepositAlreadyExists();
} }
function _addLock( function _addLock(
@ -648,7 +646,7 @@ contract P2PIX is
assembly { assembly {
success := staticcall( success := staticcall(
// gas // gas
30000, 0x7530,
// address // address
sload(reputation.slot), sload(reputation.slot),
// argsOffset // argsOffset

View File

@ -4,13 +4,9 @@ import { BigNumber } from "ethers";
import * as fs from "fs"; import * as fs from "fs";
import { ethers, network } from "hardhat"; import { ethers, network } from "hardhat";
// import hre from "hardhat"; import { Deploys } from "../test/utils/fixtures";
interface Deploys { // import hre from "hardhat";
signers: string[];
p2pix: string;
token: string;
}
let deploysJson: Deploys; let deploysJson: Deploys;
const supply: BigNumber = ethers.utils.parseEther("20000000"); const supply: BigNumber = ethers.utils.parseEther("20000000");

View File

@ -2,8 +2,8 @@ import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-etherscan"; import "@nomiclabs/hardhat-etherscan";
import * as fs from "fs"; import * as fs from "fs";
import { ethers, network } from "hardhat"; import { ethers, network } from "hardhat";
import { Deploys } from "../test/utils/fixtures";
import { Deploys } from "../test/utils/fixtures";
let deploysJson: Deploys; let deploysJson: Deploys;
@ -22,17 +22,19 @@ const main = async () => {
const [deployer] = await ethers.getSigners(); const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with ${deployer.address}`); console.log(`Deploying contracts with ${deployer.address}`);
const Reputation = await ethers.getContractFactory("Reputation"); const Reputation = await ethers.getContractFactory(
"Reputation",
);
const reputation = await Reputation.deploy(); const reputation = await Reputation.deploy();
await reputation.deployed(); await reputation.deployed();
const P2PIX = await ethers.getContractFactory("P2PIX"); const P2PIX = await ethers.getContractFactory("P2PIX");
const p2pix = await P2PIX.deploy( const p2pix = await P2PIX.deploy(
10, 10,
deploysJson.signers, deploysJson.signers,
reputation.address, reputation.address,
[deploysJson.token], [deploysJson.token],
[true] [true],
); );
await p2pix.deployed(); await p2pix.deployed();

File diff suppressed because one or more lines are too long

View File

@ -1,15 +1,23 @@
import "@nomicfoundation/hardhat-chai-matchers"; import "@nomicfoundation/hardhat-chai-matchers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import {
loadFixture,
mine,
} from "@nomicfoundation/hardhat-network-helpers";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai"; import { expect } from "chai";
import { BigNumber, ContractReceipt, Wallet } from "ethers"; import {
BigNumber,
ContractReceipt,
ContractTransaction,
Wallet,
} from "ethers";
import { import {
ethers, ethers,
network, network,
/* , tracer */ /* , tracer */
} from "hardhat"; } from "hardhat";
// import keccak256 from "keccak256";
// import keccak256 from "keccak256";
import { MockToken, P2PIX, Reputation } from "../src/types"; import { MockToken, P2PIX, Reputation } from "../src/types";
import { P2PixErrors } from "./utils/errors"; import { P2PixErrors } from "./utils/errors";
import { import {
@ -276,25 +284,6 @@ describe("P2PIX", () => {
P2PixErrors.TokenDenied, P2PixErrors.TokenDenied,
); );
}); });
/// @todo DepositAlreadyExists() seems to be unreacheable
// it("should revert if deposit already exists", async () => {
// const pTarget = ethers.utils.keccak256(
// ethers.utils.toUtf8Bytes("_pixTarget"),
// );
// const root = ethers.utils.keccak256(
// ethers.utils.toUtf8Bytes("0"),
// );
// await erc20.approve(p2pix.address, 1);
// const tx = await p2pix.deposit(
// erc20.address,
// 1,
// pTarget,
// root,
// );
// const info: Deposit = await p2pix.mapDeposits(0);
// // console.log(info)
// // console.log(info.valid);
// });
it("should create deposit, update storage and emit event", async () => { it("should create deposit, update storage and emit event", async () => {
const pTarget = ethers.utils.keccak256( const pTarget = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes("_pixTarget"), ethers.utils.toUtf8Bytes("_pixTarget"),
@ -955,18 +944,69 @@ describe("P2PIX", () => {
expect(newState3.valid).to.be.false; expect(newState3.valid).to.be.false;
}); });
}); });
describe("Release", async () => { // describe("Release", async () => {
// it("should revert if lock has expired or has already been released") // // 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 signed message has already been used")
// it("should revert if ecrecovered signer is invalid") // // it("should revert if ecrecovered signer is invalid")
// it("should release lock, update storage and emit events") // // // @todo Finish storage and event checks
// it("should release multiple locks") - EDGE CASE TEST { // // it("should release lock, update storage and emit events", async () => {
// TEST 3 CASES ( // // const endtoendID = "124";
// EMPTY PREMIUM, // // const pixTarget = "pixTarget";
// LOCK RELAYER != RELEASE RELAYER, (check userRecord storage update) // // const messageToSign = ethers.utils.solidityKeccak256(
// LOCK RELAYER == RELEASE RELAYER (check userRecord storage update) // // ["string", "uint256", "uint256"],
// )} // // [pixTarget, 100, endtoendID],
}); // // );
// // const messageHashBytes =
// // ethers.utils.arrayify(messageToSign);
// // const flatSig = await acc01.signMessage(
// // messageHashBytes,
// // );
// // const sig = ethers.utils.splitSignature(flatSig);
// // const root = ethers.constants.HashZero;
// // await erc20.approve(p2pix.address, price);
// // await p2pix.deposit(
// // erc20.address,
// // price,
// // pixTarget,
// // root,
// // );
// // await p2pix
// // .connect(acc01)
// // .lock(
// // 0,
// // acc02.address,
// // acc03.address,
// // 0,
// // 100,
// // [],
// // [],
// // );
// // const lockID = ethers.utils.solidityKeccak256(
// // ["uint256", "uint256", "address"],
// // [0, 100, acc02.address],
// // );
// // const storage1: Lock = await p2pix.callStatic.mapLocks(
// // lockID,
// // );
// // const tx = await p2pix
// // .connect(acc01)
// // .release(
// // lockID,
// // acc03.address,
// // endtoendID,
// // sig.r,
// // sig.s,
// // sig.v,
// // );
// // });
// // 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("Unexpire Locks", async () => { describe("Unexpire Locks", async () => {
it("should revert if lock isn't expired", async () => { it("should revert if lock isn't expired", async () => {
await erc20.approve(p2pix.address, price); await erc20.approve(p2pix.address, price);
@ -990,10 +1030,203 @@ describe("P2PIX", () => {
P2PixErrors.NotExpired, P2PixErrors.NotExpired,
); );
}); });
it("should revert if lock has already been released", async () => {
const endtoendID = "124";
const pixTarget = "pixTarget";
const messageToSign = ethers.utils.solidityKeccak256(
["string", "uint256", "uint256"],
[pixTarget, 1, endtoendID],
);
const messageHashBytes =
ethers.utils.arrayify(messageToSign);
const flatSig = await acc01.signMessage(
messageHashBytes,
);
const sig = ethers.utils.splitSignature(flatSig);
await erc20.approve(p2pix.address, price);
await p2pix.deposit(
erc20.address,
price,
pixTarget,
merkleRoot,
);
await p2pix
.connect(acc02)
.lock(0, acc02.address, acc03.address, 0, 1, [], []);
const lockID = ethers.utils.solidityKeccak256(
["uint256", "uint256", "address"],
[0, 1, acc02.address],
);
// await mine(10);
await p2pix.release(
lockID,
acc03.address,
endtoendID,
sig.r,
sig.s,
sig.v,
);
const fail = p2pix.unlockExpired([lockID]);
await expect(fail).to.be.revertedWithCustomError(
p2pix,
P2PixErrors.AlreadyReleased,
);
});
it("should unlock expired locks, update storage and emit events", async () => { it("should unlock expired locks, update storage and emit events", async () => {
// CHECK FOR userRecord STORAGE UPDATE await erc20.approve(p2pix.address, price);
await p2pix.deposit(
erc20.address,
price,
"pixTarget",
merkleRoot,
);
await p2pix
.connect(acc02)
.lock(0, acc02.address, acc03.address, 0, 1, [], []);
const lockID = ethers.utils.solidityKeccak256(
["uint256", "uint256", "address"],
[0, 1, acc02.address],
);
await mine(11);
const storage: Lock = await p2pix.callStatic.mapLocks(
lockID,
);
const userKey = await p2pix.callStatic._castAddrToKey(
acc02.address,
);
const record1 = await p2pix.callStatic.userRecord(
userKey,
);
const tx = await p2pix.unlockExpired([lockID]);
const storage2: Lock = await p2pix.callStatic.mapLocks(
lockID,
);
const record2 = await p2pix.callStatic.userRecord(
userKey,
);
expect(tx).to.be.ok;
await expect(tx)
.to.emit(p2pix, "LockReturned")
.withArgs(acc02.address, lockID);
expect(storage.amount).to.eq(ethers.constants.One);
expect(storage2.amount).to.eq(ethers.constants.Zero);
expect(record1).to.eq(0);
expect(record2).to.eq(100);
});
it("should unlock expired through lock function", async () => {
// test method through lock fx // test method through lock fx
await erc20.approve(p2pix.address, price);
await p2pix.deposit(
erc20.address,
price,
"pixTarget",
merkleRoot,
);
const lock1: ContractTransaction = await p2pix
.connect(acc01)
.lock(
0,
acc02.address,
acc03.address,
0,
price,
proof,
[],
);
// as return values of non view functions can't be accessed
// outside the evm, we fetch the lockID from the emitted event.
const rc: ContractReceipt = await lock1.wait();
const event = rc.events?.find(
event => event.event === "LockAdded",
);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const emittedLockID = event?.args!["lockID"];
const lockID = ethers.utils.solidityKeccak256(
["uint256", "uint256", "address"],
[0, price, acc02.address],
);
// mine blocks to expire lock
await mine(12);
// const blocknum = await p2pix.provider.getBlockNumber();
// console.log("bn = 6 + 12 ( i.e., %s )", blocknum);
// console.log(
// "\n",
// " 2 blocks past the expiration block",
// );
// const struct2: Lock = await p2pix.callStatic.mapLocks(
// emittedLockID,
// );
// console.log(
// "\n",
// "Current state of the lock:",
// "\n",
// struct2,
// );
expect(emittedLockID).to.eq(lockID);
// create another lock by freeing the price value
// back to `l.remamining` and lock 100 again.
const tx1 = await p2pix.lock(
0,
acc02.address,
acc03.address,
0,
100,
[],
[lockID],
);
const dep: Deposit = await p2pix.callStatic.mapDeposits(
0,
);
expect(tx1).to.be.ok;
await expect(tx1)
.to.emit(p2pix, "LockReturned")
.withArgs(acc02.address, lockID);
expect(dep.remaining).to.eq(
price.sub(ethers.BigNumber.from(100)),
);
});
it("should unlock expired through withdraw function", async () => {
// test method through withdraw fx // test method through withdraw fx
await erc20.approve(p2pix.address, price);
await p2pix.deposit(
erc20.address,
price,
"pixTarget",
merkleRoot,
);
await p2pix
.connect(acc01)
.lock(
0,
acc02.address,
acc03.address,
0,
price,
proof,
[],
);
const lockID = ethers.utils.solidityKeccak256(
["uint256", "uint256", "address"],
[0, price, acc02.address],
);
// mine blocks to expire lock
await mine(11);
const tx = await p2pix.withdraw(0, [lockID]);
const dep: Deposit = await p2pix.callStatic.mapDeposits(
0,
);
expect(tx).to.be.ok;
await expect(tx)
.to.emit(p2pix, "LockReturned")
.withArgs(acc02.address, lockID);
expect(dep.remaining).to.eq(0);
}); });
}); });
describe("Seller Withdraw", async () => { describe("Seller Withdraw", async () => {
@ -1051,19 +1284,34 @@ describe("P2PIX", () => {
it(" should revert if the msg.sender differs from deposit's seller", async () => { it(" should revert if the msg.sender differs from deposit's seller", async () => {
const root = ethers.utils.keccak256( const root = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes("root"), ethers.utils.toUtf8Bytes("root"),
); );
const fail = p2pix.connect(acc02).setRoot(owner.address, root); const fail = p2pix
.connect(acc02)
.setRoot(owner.address, root);
await expect(fail).to.be.revertedWithCustomError(p2pix, P2PixErrors.OnlySeller); await expect(fail).to.be.revertedWithCustomError(
p2pix,
P2PixErrors.OnlySeller,
);
}); });
it("should set root of seller's allowlist, update storage and emit event", async () => { it("should set root of seller's allowlist, update storage and emit event", async () => {
const ownerKey = await p2pix.callStatic._castAddrToKey(owner.address); const ownerKey = await p2pix.callStatic._castAddrToKey(
const oldState = await p2pix.callStatic.sellerAllowList(ownerKey); owner.address,
const tx = await p2pix.connect(owner).setRoot(owner.address, merkleRoot); );
const newState = await p2pix.callStatic.sellerAllowList(ownerKey); const oldState = await p2pix.callStatic.sellerAllowList(
ownerKey,
);
const tx = await p2pix
.connect(owner)
.setRoot(owner.address, merkleRoot);
const newState = await p2pix.callStatic.sellerAllowList(
ownerKey,
);
expect(tx).to.be.ok; expect(tx).to.be.ok;
await expect(tx).to.emit(p2pix, "RootUpdated").withArgs(owner.address, merkleRoot); await expect(tx)
.to.emit(p2pix, "RootUpdated")
.withArgs(owner.address, merkleRoot);
expect(oldState).to.eq(ethers.constants.HashZero); expect(oldState).to.eq(ethers.constants.HashZero);
expect(newState).to.eq(merkleRoot); expect(newState).to.eq(merkleRoot);
}); });