feat: deployment scripts fixed and lock fx unit tests added

This commit is contained in:
PedroCailleret 2022-12-07 12:43:57 -03:00
parent 1a4b4973d4
commit d541e7b70c
17 changed files with 585 additions and 96 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/4a0f142c7439e90ed9431fa5c0d6c0c6.json"
} }

View File

@ -286,6 +286,25 @@
"name": "ReputationUpdated", "name": "ReputationUpdated",
"type": "event" "type": "event"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "seller",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "merkleRoot",
"type": "bytes32"
}
],
"name": "RootUpdated",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [

View File

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

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.9; pragma solidity 0.8.9;
// prettier-ignore
interface EventAndErrors { interface EventAndErrors {
/// Events /// Events
@ -25,16 +26,29 @@ interface EventAndErrors {
uint256 depositID, uint256 depositID,
uint256 amount uint256 amount
); );
event LockReleased(address indexed buyer, bytes32 lockId); event LockReleased(
event LockReturned(address indexed buyer, bytes32 lockId); address indexed buyer,
event FundsWithdrawn(address owner, uint256 amount); bytes32 lockId
event ReputationUpdated(address reputation); );
event LockBlocksUpdated(uint256 blocks); event LockReturned(
event ValidSignersUpdated(address[] signers); address indexed buyer,
bytes32 lockId
);
event FundsWithdrawn(
address owner,
uint256 amount
);
event RootUpdated(
address seller,
bytes32 merkleRoot
);
event AllowedERC20Updated( event AllowedERC20Updated(
address indexed token, address indexed token,
bool indexed state bool indexed state
); );
event ReputationUpdated(address reputation);
event LockBlocksUpdated(uint256 blocks);
event ValidSignersUpdated(address[] signers);
/// Errors /// Errors

View File

@ -194,40 +194,29 @@ contract P2PIX is
msg.sender msg.sender
); );
mapLocks[lockID] = l; _addLock(lockID, l, d);
d.remaining -= _amount;
emit LockAdded(
_buyerAddress,
lockID,
l.depositID,
_amount
);
// Halt execution and output `lockID`. // Halt execution and output `lockID`.
return lockID; return lockID;
} else { } else {
uint256 userCredit = userRecord[ if (l.amount <= 1e2) {
_castAddrToKey(msg.sender) _addLock(lockID, l, d);
]; // Halt execution and output `lockID`.
uint256 spendLimit; return lockID;
(spendLimit) = _limiter(userCredit); } else {
uint256 userCredit = userRecord[
_castAddrToKey(msg.sender)
];
uint256 spendLimit;
(spendLimit) = _limiter(userCredit);
if (l.amount > spendLimit || l.amount > 1e6) if (l.amount > spendLimit || l.amount > 1e6)
revert AmountNotAllowed(); revert AmountNotAllowed();
mapLocks[lockID] = l; _addLock(lockID, l, d);
d.remaining -= _amount; // Halt execution and output `lockID`.
return lockID;
emit LockAdded( }
_buyerAddress,
lockID,
l.depositID,
_amount
);
// Halt execution and output `lockID`.
return lockID;
} }
} }
@ -417,6 +406,10 @@ contract P2PIX is
sellerAllowList[ sellerAllowList[
_castAddrToKey(addr) _castAddrToKey(addr)
] = merkleroot; ] = merkleroot;
emit RootUpdated(
addr,
merkleroot
);
} else revert OnlySeller(); } else revert OnlySeller();
} }
@ -587,6 +580,22 @@ contract P2PIX is
revert DepositAlreadyExists(); revert DepositAlreadyExists();
} }
function _addLock(
bytes32 _lockID,
DT.Lock memory _l,
DT.Deposit storage _d
) internal {
mapLocks[_lockID] = _l;
_d.remaining -= _l.amount;
emit LockAdded(
_l.buyerAddress,
_lockID,
_l.depositID,
_l.amount
);
}
/// @notice Private view auxiliar logic that encodes/returns /// @notice Private view auxiliar logic that encodes/returns
/// the `bytes32` identifier of an lock. /// the `bytes32` identifier of an lock.
/// @dev reverts on a not expired lock with the same ID passed /// @dev reverts on a not expired lock with the same ID passed

View File

@ -3,6 +3,6 @@
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8" "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
], ],
"p2pix": "0x294003F602c321627152c6b7DED3EAb5bEa853Ee", "p2pix": "0x37c856F4d5bC2597da60f607b1335738468453F3",
"token": "0x5BdEa33E2D14E3D4C66f9A7bbbc38Cd785D0C592" "token": "0x294003F602c321627152c6b7DED3EAb5bEa853Ee"
} }

View File

@ -14,8 +14,8 @@
"compile": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat compile", "compile": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat compile",
"typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain", "typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain",
"test": "hardhat test", "test": "hardhat test",
"deploy1:localhost": "hardhat run scripts/1-deploy-p2pix.ts --network localhost", "deploy1:localhost": "hardhat run scripts/1-deploy-mockToken.ts --network localhost",
"deploy2:localhost": "hardhat run scripts/2-deploy-mockToken.ts --network localhost", "deploy2:localhost": "hardhat run scripts/2-deploy-p2pix.ts --network localhost",
"coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"test/**/*.ts\" && yarn typechain", "coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"test/**/*.ts\" && yarn typechain",
"lint": "yarn lint:sol && yarn lint:ts && yarn prettier:check", "lint": "yarn lint:sol && yarn lint:ts && yarn prettier:check",
"lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"", "lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"",
@ -49,6 +49,7 @@
"@typescript-eslint/eslint-plugin": "^5.42.0", "@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0", "@typescript-eslint/parser": "^5.42.0",
"chai": "^4.3.6", "chai": "^4.3.6",
"chalk": "4.x",
"commitizen": "^4.2.5", "commitizen": "^4.2.5",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",

View File

@ -2,14 +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 hre from "hardhat";
interface Deploys {
signers: string[];
p2pix: string;
token: string;
}
let deploysJson: Deploys; let deploysJson: Deploys;
@ -28,8 +22,18 @@ 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 Reputation.deploy();
await reputation.deployed();
const P2PIX = await ethers.getContractFactory("P2PIX"); const P2PIX = await ethers.getContractFactory("P2PIX");
const p2pix = await P2PIX.deploy(2, deploysJson.signers); const p2pix = await P2PIX.deploy(
10,
deploysJson.signers,
reputation.address,
[deploysJson.token],
[true]
);
await p2pix.deployed(); await p2pix.deployed();
deploysJson.p2pix = p2pix.address; deploysJson.p2pix = p2pix.address;

View File

@ -26,6 +26,7 @@ export interface EventAndErrorsInterface extends utils.Interface {
"LockReleased(address,bytes32)": EventFragment; "LockReleased(address,bytes32)": EventFragment;
"LockReturned(address,bytes32)": EventFragment; "LockReturned(address,bytes32)": EventFragment;
"ReputationUpdated(address)": EventFragment; "ReputationUpdated(address)": EventFragment;
"RootUpdated(address,bytes32)": EventFragment;
"ValidSignersUpdated(address[])": EventFragment; "ValidSignersUpdated(address[])": EventFragment;
}; };
@ -39,6 +40,7 @@ export interface EventAndErrorsInterface extends utils.Interface {
getEvent(nameOrSignatureOrTopic: "LockReleased"): EventFragment; getEvent(nameOrSignatureOrTopic: "LockReleased"): EventFragment;
getEvent(nameOrSignatureOrTopic: "LockReturned"): EventFragment; getEvent(nameOrSignatureOrTopic: "LockReturned"): EventFragment;
getEvent(nameOrSignatureOrTopic: "ReputationUpdated"): EventFragment; getEvent(nameOrSignatureOrTopic: "ReputationUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "RootUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "ValidSignersUpdated"): EventFragment; getEvent(nameOrSignatureOrTopic: "ValidSignersUpdated"): EventFragment;
} }
@ -159,6 +161,17 @@ export type ReputationUpdatedEvent = TypedEvent<
export type ReputationUpdatedEventFilter = export type ReputationUpdatedEventFilter =
TypedEventFilter<ReputationUpdatedEvent>; TypedEventFilter<ReputationUpdatedEvent>;
export interface RootUpdatedEventObject {
seller: string;
merkleRoot: string;
}
export type RootUpdatedEvent = TypedEvent<
[string, string],
RootUpdatedEventObject
>;
export type RootUpdatedEventFilter = TypedEventFilter<RootUpdatedEvent>;
export interface ValidSignersUpdatedEventObject { export interface ValidSignersUpdatedEventObject {
signers: string[]; signers: string[];
} }
@ -288,6 +301,12 @@ export interface EventAndErrors extends BaseContract {
): ReputationUpdatedEventFilter; ): ReputationUpdatedEventFilter;
ReputationUpdated(reputation?: null): ReputationUpdatedEventFilter; ReputationUpdated(reputation?: null): ReputationUpdatedEventFilter;
"RootUpdated(address,bytes32)"(
seller?: null,
merkleRoot?: null
): RootUpdatedEventFilter;
RootUpdated(seller?: null, merkleRoot?: null): RootUpdatedEventFilter;
"ValidSignersUpdated(address[])"( "ValidSignersUpdated(address[])"(
signers?: null signers?: null
): ValidSignersUpdatedEventFilter; ): ValidSignersUpdatedEventFilter;

View File

@ -293,6 +293,25 @@ const _abi = [
name: "ReputationUpdated", name: "ReputationUpdated",
type: "event", type: "event",
}, },
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "seller",
type: "address",
},
{
indexed: false,
internalType: "bytes32",
name: "merkleRoot",
type: "bytes32",
},
],
name: "RootUpdated",
type: "event",
},
{ {
anonymous: false, anonymous: false,
inputs: [ inputs: [

File diff suppressed because one or more lines are too long

View File

@ -276,6 +276,7 @@ export interface P2PIXInterface extends utils.Interface {
"LockReturned(address,bytes32)": EventFragment; "LockReturned(address,bytes32)": EventFragment;
"OwnerUpdated(address,address)": EventFragment; "OwnerUpdated(address,address)": EventFragment;
"ReputationUpdated(address)": EventFragment; "ReputationUpdated(address)": EventFragment;
"RootUpdated(address,bytes32)": EventFragment;
"ValidSignersUpdated(address[])": EventFragment; "ValidSignersUpdated(address[])": EventFragment;
}; };
@ -290,6 +291,7 @@ export interface P2PIXInterface extends utils.Interface {
getEvent(nameOrSignatureOrTopic: "LockReturned"): EventFragment; getEvent(nameOrSignatureOrTopic: "LockReturned"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OwnerUpdated"): EventFragment; getEvent(nameOrSignatureOrTopic: "OwnerUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "ReputationUpdated"): EventFragment; getEvent(nameOrSignatureOrTopic: "ReputationUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "RootUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "ValidSignersUpdated"): EventFragment; getEvent(nameOrSignatureOrTopic: "ValidSignersUpdated"): EventFragment;
} }
@ -421,6 +423,17 @@ export type ReputationUpdatedEvent = TypedEvent<
export type ReputationUpdatedEventFilter = export type ReputationUpdatedEventFilter =
TypedEventFilter<ReputationUpdatedEvent>; TypedEventFilter<ReputationUpdatedEvent>;
export interface RootUpdatedEventObject {
seller: string;
merkleRoot: string;
}
export type RootUpdatedEvent = TypedEvent<
[string, string],
RootUpdatedEventObject
>;
export type RootUpdatedEventFilter = TypedEventFilter<RootUpdatedEvent>;
export interface ValidSignersUpdatedEventObject { export interface ValidSignersUpdatedEventObject {
signers: string[]; signers: string[];
} }
@ -985,6 +998,12 @@ export interface P2PIX extends BaseContract {
): ReputationUpdatedEventFilter; ): ReputationUpdatedEventFilter;
ReputationUpdated(reputation?: null): ReputationUpdatedEventFilter; ReputationUpdated(reputation?: null): ReputationUpdatedEventFilter;
"RootUpdated(address,bytes32)"(
seller?: null,
merkleRoot?: null
): RootUpdatedEventFilter;
RootUpdated(seller?: null, merkleRoot?: null): RootUpdatedEventFilter;
"ValidSignersUpdated(address[])"( "ValidSignersUpdated(address[])"(
signers?: null signers?: null
): ValidSignersUpdatedEventFilter; ): ValidSignersUpdatedEventFilter;

View File

@ -2,13 +2,13 @@ import "@nomicfoundation/hardhat-chai-matchers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { loadFixture } 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, Wallet } from "ethers"; import { BigNumber, ContractReceipt, 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";
@ -595,7 +595,7 @@ describe("P2PIX", () => {
P2PixErrors.AddressDenied, P2PixErrors.AddressDenied,
); );
}); });
it ("should revert if msg.sender does not have enough credit in his spend limit", async () => { it("should revert if msg.sender does not have enough credit in his spend limit", async () => {
await erc20.approve(p2pix.address, price); await erc20.approve(p2pix.address, price);
await p2pix.deposit( await p2pix.deposit(
erc20.address, erc20.address,
@ -620,9 +620,246 @@ describe("P2PIX", () => {
P2PixErrors.AmountNotAllowed, P2PixErrors.AmountNotAllowed,
); );
}); });
// 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 allowlist path", async () => {
// it ("should create a lock, update storage and emit events via the reputation path") await erc20.approve(p2pix.address, price);
// it ("should create multiple locks") - EDGE CASE TEST await p2pix.deposit(
erc20.address,
price,
"pixTarget",
merkleRoot,
);
const tx = 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],
);
const storage: Lock = await p2pix.callStatic.mapLocks(
lockID,
);
const rc: ContractReceipt = await tx.wait();
const expiration = rc.blockNumber + 10;
expect(tx).to.be.ok;
expect(storage.depositID).to.eq(0);
expect(storage.relayerPremium).to.eq(
ethers.constants.Zero,
);
expect(storage.amount).to.eq(price);
expect(storage.expirationBlock).to.eq(expiration);
expect(storage.buyerAddress).to.eq(acc02.address);
expect(storage.relayerTarget).to.eq(acc03.address);
expect(storage.relayerAddress).to.eq(acc01.address);
await expect(tx)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc02.address,
lockID,
storage.depositID,
storage.amount,
);
});
it("should create a lock, update storage and emit events via the reputation path", async () => {
const root = ethers.constants.HashZero;
await erc20.approve(p2pix.address, price);
await p2pix.deposit(
erc20.address,
price,
"pixTarget",
root,
);
const tx = 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 storage: Lock = await p2pix.callStatic.mapLocks(
lockID,
);
const rc: ContractReceipt = await tx.wait();
const expiration = rc.blockNumber + 10;
expect(tx).to.be.ok;
expect(storage.depositID).to.eq(0);
expect(storage.relayerPremium).to.eq(
ethers.constants.Zero,
);
expect(storage.amount).to.eq(
ethers.BigNumber.from(100),
);
expect(storage.expirationBlock).to.eq(expiration);
expect(storage.buyerAddress).to.eq(acc02.address);
expect(storage.relayerTarget).to.eq(acc03.address);
expect(storage.relayerAddress).to.eq(acc01.address);
await expect(tx)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc02.address,
lockID,
storage.depositID,
storage.amount,
);
});
// edge case test
it("should create multiple locks", async () => {
const newPrice = price.div(ethers.BigNumber.from(2));
await erc20.approve(p2pix.address, price);
await p2pix.deposit(
erc20.address,
price,
"pixTarget",
merkleRoot,
);
const tx1 = await p2pix
.connect(acc01)
.lock(
0,
acc02.address,
acc03.address,
0,
newPrice,
proof,
[],
);
const lockID1 = ethers.utils.solidityKeccak256(
["uint256", "uint256", "address"],
[0, newPrice, acc02.address],
);
const storage1: Lock = await p2pix.callStatic.mapLocks(
lockID1,
);
const rc1: ContractReceipt = await tx1.wait();
const expiration1 = rc1.blockNumber + 10;
const tx2 = await p2pix
.connect(acc01)
.lock(
0,
acc02.address,
acc03.address,
0,
100,
[],
[],
);
const lockID2 = ethers.utils.solidityKeccak256(
["uint256", "uint256", "address"],
[0, 100, acc02.address],
);
const storage2: Lock = await p2pix.callStatic.mapLocks(
lockID2,
);
const rc2: ContractReceipt = await tx2.wait();
const expiration2 = rc2.blockNumber + 10;
const tx3 = await p2pix
.connect(acc03)
.lock(
0,
acc03.address,
acc03.address,
0,
100,
[],
[],
);
const lockID3 = ethers.utils.solidityKeccak256(
["uint256", "uint256", "address"],
[0, 100, acc03.address],
);
const storage3: Lock = await p2pix.callStatic.mapLocks(
lockID3,
);
const rc3: ContractReceipt = await tx3.wait();
const expiration3 = rc3.blockNumber + 10;
expect(tx1).to.be.ok;
expect(tx2).to.be.ok;
expect(tx3).to.be.ok;
expect(0)
.to.eq(storage1.depositID)
.and.to.eq(storage2.depositID)
.and.to.eq(storage3.depositID);
expect(ethers.constants.Zero)
.to.eq(storage1.relayerPremium)
.and.to.eq(storage2.relayerPremium)
.and.to.eq(storage3.relayerPremium);
expect(storage1.amount).to.eq(newPrice);
expect(ethers.BigNumber.from(100))
.to.eq(storage2.amount)
.and.to.eq(storage3.amount);
expect(storage1.expirationBlock).to.eq(expiration1);
expect(storage2.expirationBlock).to.eq(expiration2);
expect(storage3.expirationBlock).to.eq(expiration3);
expect(acc02.address)
.to.eq(storage1.buyerAddress)
.and.to.eq(storage2.buyerAddress);
expect(storage3.buyerAddress).to.eq(acc03.address);
expect(acc03.address)
.to.eq(storage1.relayerTarget)
.and.to.eq(storage2.relayerTarget)
.and.to.eq(storage3.relayerTarget);
expect(acc01.address)
.to.eq(storage1.relayerAddress)
.and.to.eq(storage2.relayerAddress);
expect(storage3.relayerAddress).to.eq(acc03.address);
await expect(tx1)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc02.address,
lockID1,
storage1.depositID,
storage1.amount,
);
await expect(tx2)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc02.address,
lockID2,
storage2.depositID,
storage2.amount,
);
await expect(tx3)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc03.address,
lockID3,
storage3.depositID,
storage3.amount,
);
});
}); });
describe("Cancel Deposit", async () => { describe("Cancel Deposit", async () => {
it("should revert if the msg.sender isn't the deposit's seller", async () => { it("should revert if the msg.sender isn't the deposit's seller", async () => {
@ -634,7 +871,10 @@ describe("P2PIX", () => {
merkleRoot, merkleRoot,
); );
const fail = p2pix.connect(acc01).cancelDeposit(0); const fail = p2pix.connect(acc01).cancelDeposit(0);
await expect(fail).to.be.revertedWithCustomError(p2pix, P2PixErrors.OnlySeller); await expect(fail).to.be.revertedWithCustomError(
p2pix,
P2PixErrors.OnlySeller,
);
}); });
it("should cancel deposit, update storage and emit events", async () => { it("should cancel deposit, update storage and emit events", async () => {
await erc20.approve(p2pix.address, price); await erc20.approve(p2pix.address, price);
@ -644,12 +884,16 @@ describe("P2PIX", () => {
"pixTarget", "pixTarget",
merkleRoot, merkleRoot,
); );
const state1:Deposit = await p2pix.callStatic.mapDeposits(0); const state1: Deposit =
await p2pix.callStatic.mapDeposits(0);
const tx = await p2pix.cancelDeposit(0); const tx = await p2pix.cancelDeposit(0);
const state2:Deposit = await p2pix.callStatic.mapDeposits(0); const state2: Deposit =
await p2pix.callStatic.mapDeposits(0);
expect(tx).to.be.ok; expect(tx).to.be.ok;
await expect(tx).to.emit(p2pix, "DepositClosed").withArgs(owner.address, 0); await expect(tx)
.to.emit(p2pix, "DepositClosed")
.withArgs(owner.address, 0);
expect(state1.valid).to.be.true; expect(state1.valid).to.be.true;
expect(state2.valid).to.be.false; expect(state2.valid).to.be.false;
}); });
@ -675,22 +919,34 @@ describe("P2PIX", () => {
"pixTarget", "pixTarget",
ethers.constants.HashZero, ethers.constants.HashZero,
); );
const oldState1:Deposit = await p2pix.callStatic.mapDeposits(0); const oldState1: Deposit =
const oldState2:Deposit = await p2pix.callStatic.mapDeposits(1); await p2pix.callStatic.mapDeposits(0);
const oldState3:Deposit = await p2pix.callStatic.mapDeposits(2); const oldState2: Deposit =
await p2pix.callStatic.mapDeposits(1);
const oldState3: Deposit =
await p2pix.callStatic.mapDeposits(2);
const tx1 = await p2pix.cancelDeposit(0); const tx1 = await p2pix.cancelDeposit(0);
const tx2 = await p2pix.cancelDeposit(1); const tx2 = await p2pix.cancelDeposit(1);
const tx3 = await p2pix.cancelDeposit(2); const tx3 = await p2pix.cancelDeposit(2);
const newState1:Deposit = await p2pix.callStatic.mapDeposits(0); const newState1: Deposit =
const newState2:Deposit = await p2pix.callStatic.mapDeposits(1); await p2pix.callStatic.mapDeposits(0);
const newState3:Deposit = await p2pix.callStatic.mapDeposits(2); const newState2: Deposit =
await p2pix.callStatic.mapDeposits(1);
const newState3: Deposit =
await p2pix.callStatic.mapDeposits(2);
expect(tx1).to.be.ok; expect(tx1).to.be.ok;
expect(tx2).to.be.ok; expect(tx2).to.be.ok;
expect(tx3).to.be.ok; expect(tx3).to.be.ok;
await expect(tx1).to.emit(p2pix, "DepositClosed").withArgs(owner.address, 0); await expect(tx1)
await expect(tx2).to.emit(p2pix, "DepositClosed").withArgs(owner.address, 1); .to.emit(p2pix, "DepositClosed")
await expect(tx3).to.emit(p2pix, "DepositClosed").withArgs(owner.address, 2); .withArgs(owner.address, 0);
await expect(tx2)
.to.emit(p2pix, "DepositClosed")
.withArgs(owner.address, 1);
await expect(tx3)
.to.emit(p2pix, "DepositClosed")
.withArgs(owner.address, 2);
expect(oldState1.valid).to.be.true; expect(oldState1.valid).to.be.true;
expect(oldState2.valid).to.be.true; expect(oldState2.valid).to.be.true;
expect(oldState3.valid).to.be.true; expect(oldState3.valid).to.be.true;
@ -712,19 +968,104 @@ describe("P2PIX", () => {
// )} // )}
}); });
describe("Unexpire Locks", async () => { describe("Unexpire Locks", async () => {
// it("should revert if lock isn't expired") it("should revert if lock isn't expired", async () => {
// it("should unlock expired locks, update storage and emit events") await erc20.approve(p2pix.address, price);
// CHECK FOR userRecord STORAGE UPDATE await p2pix.deposit(
// test method through lock fx erc20.address,
// test method through withdraw fx 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],
);
const fail = p2pix.unlockExpired([lockID]);
await expect(fail).to.be.revertedWithCustomError(
p2pix,
P2PixErrors.NotExpired,
);
});
it("should unlock expired locks, update storage and emit events", async () => {
// CHECK FOR userRecord STORAGE UPDATE
// test method through lock fx
// test method through withdraw fx
});
}); });
describe("Seller Withdraw", async () => { describe("Seller Withdraw", async () => {
// it("should revert if the msg.sender isn't the deposit's seller") it("should revert if the msg.sender isn't the deposit's seller", async () => {
// it -> withdraw remaining funds from deposit await erc20.approve(p2pix.address, price);
await p2pix.deposit(
erc20.address,
price,
"pixTarget",
merkleRoot,
);
const fail = p2pix.connect(acc02).withdraw(0, []);
await expect(fail).to.be.revertedWithCustomError(
p2pix,
P2PixErrors.OnlySeller,
);
});
it("should withdraw remaining funds from deposit, update storage and emit event", async () => {
await erc20.approve(p2pix.address, price);
const dep = await p2pix.deposit(
erc20.address,
price,
"pixTarget",
merkleRoot,
);
const tx = await p2pix.withdraw(0, []);
expect(tx).to.be.ok;
await expect(dep)
.to.changeTokenBalance(
erc20,
owner.address,
"-100000000000000000000",
)
.and.to.changeTokenBalance(
erc20,
p2pix.address,
price,
);
await expect(tx)
.to.changeTokenBalance(erc20, owner.address, price)
.and.to.changeTokenBalance(
erc20,
p2pix.address,
"-100000000000000000000",
);
await expect(tx)
.to.emit(p2pix, "DepositWithdrawn")
.withArgs(owner.address, 0, price);
});
}); });
describe("Allowlist Settings", async () => { describe("Allowlist Settings", async () => {
// it -> set root of seller's allowlist it(" should revert if the msg.sender differs from deposit's seller", async () => {
// (test msg.sender != seller error) const root = ethers.utils.keccak256(
// i.e., set it in the fixture ethers.utils.toUtf8Bytes("root"),
);
const fail = p2pix.connect(acc02).setRoot(owner.address, root);
await expect(fail).to.be.revertedWithCustomError(p2pix, P2PixErrors.OnlySeller);
});
it("should set root of seller's allowlist, update storage and emit event", async () => {
const ownerKey = await p2pix.callStatic._castAddrToKey(owner.address);
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;
await expect(tx).to.emit(p2pix, "RootUpdated").withArgs(owner.address, merkleRoot);
expect(oldState).to.eq(ethers.constants.HashZero);
expect(newState).to.eq(merkleRoot);
});
}); });
}); });

View File

@ -11,6 +11,11 @@ import {
} from "../../src/types"; } from "../../src/types";
// exported interfaces // exported interfaces
export interface Deploys {
signers: string[];
p2pix: string;
token: string;
}
export interface Deposit { export interface Deposit {
remaining: BigNumber; remaining: BigNumber;
@ -21,10 +26,10 @@ export interface Deposit {
} }
export interface Lock { export interface Lock {
depositID: string; depositID: BigNumber;
relayerPremium: string; relayerPremium: BigNumber;
amount: string; amount: BigNumber;
expirationBlock: string; expirationBlock: BigNumber;
buyerAddress: string; buyerAddress: string;
relayerTarget: string; relayerTarget: string;
relayerAddress: string; relayerAddress: string;
@ -130,7 +135,7 @@ export async function p2pixFixture(): Promise<P2PixAndReputation> {
}); });
const merkleRoot: string = tree.getHexRoot(); const merkleRoot: string = tree.getHexRoot();
const proof: string[] = tree.getHexProof( const proof: string[] = tree.getHexProof(
padBuffer(whitelisted[0].address), padBuffer(whitelisted[1].address),
); );
return { return {

View File

@ -3495,6 +3495,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chalk@npm:4.x, chalk@npm:^4.1.1":
version: 4.1.2
resolution: "chalk@npm:4.1.2"
dependencies:
ansi-styles: ^4.1.0
supports-color: ^7.1.0
checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc
languageName: node
linkType: hard
"chalk@npm:^2.0.0, chalk@npm:^2.1.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": "chalk@npm:^2.0.0, chalk@npm:^2.1.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2":
version: 2.4.2 version: 2.4.2
resolution: "chalk@npm:2.4.2" resolution: "chalk@npm:2.4.2"
@ -3516,16 +3526,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"chalk@npm:^4.1.1":
version: 4.1.2
resolution: "chalk@npm:4.1.2"
dependencies:
ansi-styles: ^4.1.0
supports-color: ^7.1.0
checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc
languageName: node
linkType: hard
"chardet@npm:^0.7.0": "chardet@npm:^0.7.0":
version: 0.7.0 version: 0.7.0
resolution: "chardet@npm:0.7.0" resolution: "chardet@npm:0.7.0"
@ -8448,6 +8448,7 @@ fsevents@~2.1.1:
"@typescript-eslint/eslint-plugin": ^5.42.0 "@typescript-eslint/eslint-plugin": ^5.42.0
"@typescript-eslint/parser": ^5.42.0 "@typescript-eslint/parser": ^5.42.0
chai: ^4.3.6 chai: ^4.3.6
chalk: 4.x
commitizen: ^4.2.5 commitizen: ^4.2.5
cross-env: ^7.0.3 cross-env: ^7.0.3
cz-conventional-changelog: ^3.3.0 cz-conventional-changelog: ^3.3.0