Merge pull request #9 from doiim/dev

Merge dev branch updates to main.
This commit is contained in:
PedroCailleret 2023-02-14 20:26:54 -03:00 committed by GitHub
commit 4568cad8f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1539 additions and 592 deletions

View File

@ -19,3 +19,5 @@ coverage.json
npm-debug.log*
yarn-debug.log*
yarn-error.log*
contracts/p2pix.sol

View File

@ -32,8 +32,9 @@
## Current Deployment addresses
### V1
| Testnet | Token Address | P2pix Address |
|--------- |-------------------------------------------- |-------------------------------------------- |
| ------- | ------------------------------------------ | ------------------------------------------ |
| Goerli | 0x294003F602c321627152c6b7DED3EAb5bEa853Ee | 0x5f3EFA9A90532914545CEf527C530658af87e196 |
| Mumbai | 0x294003F602c321627152c6b7DED3EAb5bEa853Ee | 0x5f3EFA9A90532914545CEf527C530658af87e196 |
@ -48,19 +49,22 @@
<!-- https://mumbai.polygonscan.com/address/0x5f3EFA9A90532914545CEf527C530658af87e196#code -->
### V2
| Testnet | Token Address | P2pix Address | Reputation Address |
|--------- |-------------------------------------------- |-------------------------------------------- |-------------------------------------------- |
| Goerli | 0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00 | 0xefa5cE4351cda51192509cf8De7d8881ADAE95DD | 0x939d3c357dc7017cDbDE681BF8e552b54595318A |
| Mumbai | 0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29 | 0xA9258eBb157E4cf5e756b77FDD0DF09C2F73240b | 0x1fd30b94f20d2f73e9630261342ba68f244da92b |
| Testnet | Token Address | P2pix Address | Reputation Address | Multicall Address |
| ------- | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ |
| Goerli | 0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00 | 0x2414817FF64A114d91eCFA16a834d3fCf69103d4 | 0x2CFD9354Ec7614fEf036EFd6A730dA1d5fC2762A | 0x8FE009992d96A86c7f0Bccdaf1eC3471E302a8a6 |
| Mumbai | 0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29 | 0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00 | 0x570445E3eF413bCDb5De79ed27B1c3840683e385 | 0x718B2C4DE4F9654E1349F610ff561249bfe1c418 |
<!-- All contracts deployed by 0x8dC06F985C131166570825F52447E8c88d64aE20 -->
<!-- https://goerli.etherscan.io/address/0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00#code -->
<!-- https://goerli.etherscan.io/address/0xefa5cE4351cda51192509cf8De7d8881ADAE95DD#code -->
<!-- https://goerli.etherscan.io/address/0x939d3c357dc7017cDbDE681BF8e552b54595318A#code -->
<!-- https://goerli.etherscan.io/address/0x2414817FF64A114d91eCFA16a834d3fCf69103d4#code -->
<!-- https://goerli.etherscan.io/address/0x2CFD9354Ec7614fEf036EFd6A730dA1d5fC2762A#code -->
<!-- https://goerli.etherscan.io/address/0x8FE009992d96A86c7f0Bccdaf1eC3471E302a8a6#code -->
<!-- https://mumbai.polygonscan.com/address/0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29#code -->
<!-- https://mumbai.polygonscan.com/address/0xA9258eBb157E4cf5e756b77FDD0DF09C2F73240b#code -->
<!-- https://mumbai.polygonscan.com/address/0x1fd30b94f20d2f73e9630261342ba68f244da92b#code -->
<!-- https://mumbai.polygonscan.com/address/0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00#code -->
<!-- https://mumbai.polygonscan.com/address/0x570445e3ef413bcdb5de79ed27b1c3840683e385#code -->
<!-- https://mumbai.polygonscan.com/address/0x718B2C4DE4F9654E1349F610ff561249bfe1c418#code -->
## Usage
@ -136,7 +140,6 @@ yarn deploy2:localhost
**_NOTE_:** The second script transfers 2M tokens to the first wallet of the node.
To use the P2Pix smart contract first transfer some of the tokens to other wallets.
## Deploying to testnets
Deploy to Ethereum's Goerli testnet:

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -0,0 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -0,0 +1,116 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "Multicall",
"sourceName": "contracts/lib/utils/Multicall.sol",
"abi": [
{
"inputs": [],
"stateMutability": "payable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "string",
"name": "reason",
"type": "string"
}
],
"name": "CallFailed",
"type": "error"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "mtc1",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes[]",
"name": "",
"type": "bytes[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "mtc2",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
},
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall.Result[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x60806040526108ec806100136000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80631b57b72f1461003b57806386575ee914610066575b600080fd5b61004e610049366004610438565b610087565b60405161005d93929190610527565b60405180910390f35b610079610074366004610438565b610227565b60405161005d9291906105cb565b600080606043804085848167ffffffffffffffff8111156100aa576100aa610653565b6040519080825280602002602001820160405280156100f057816020015b6040805180820190915260008152606060208201528152602001906001900390816100c85790505b50905060005b82811015610217576000808b8b8481811061011357610113610682565b905060200281019061012591906106b1565b6101339060208101906106ef565b73ffffffffffffffffffffffffffffffffffffffff168c8c8581811061015b5761015b610682565b905060200281019061016d91906106b1565b61017b90602081019061072c565b604051610189929190610791565b6000604051808303816000865af19150503d80600081146101c6576040519150601f19603f3d011682016040523d82523d6000602084013e6101cb565b606091505b509150915060405180604001604052808315158152602001828152508484815181106101f9576101f9610682565b60200260200101819052508261020e906107a1565b925050506100f6565b5092989197509195509350505050565b600060604383838167ffffffffffffffff81111561024757610247610653565b60405190808252806020026020018201604052801561027a57816020015b60608152602001906001900390816102655790505b50905060005b828110156104285760008089898481811061029d5761029d610682565b90506020028101906102af91906106b1565b6102bd9060208101906106ef565b73ffffffffffffffffffffffffffffffffffffffff168a8a858181106102e5576102e5610682565b90506020028101906102f791906106b1565b61030590602081019061072c565b604051610313929190610791565b6000604051808303816000865af19150503d8060008114610350576040519150601f19603f3d011682016040523d82523d6000602084013e610355565b606091505b5091509150816103f7576044815110156103aa576040517fb5e1dc2d00000000000000000000000000000000000000000000000000000000815260206004820152600060248201526044015b60405180910390fd5b600481019050808060200190518101906103c49190610801565b6040517fb5e1dc2d0000000000000000000000000000000000000000000000000000000081526004016103a191906108cc565b8084848151811061040a5761040a610682565b60200260200101819052508261041f906107a1565b92505050610280565b50919350909150505b9250929050565b6000806020838503121561044b57600080fd5b823567ffffffffffffffff8082111561046357600080fd5b818501915085601f83011261047757600080fd5b81358181111561048657600080fd5b8660208260051b850101111561049b57600080fd5b60209290920196919550909350505050565b60005b838110156104c85781810151838201526020016104b0565b838111156104d7576000848401525b50505050565b600081518084526104f58160208601602086016104ad565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60006060820185835260208581850152604060608186015282865180855260808701915060808160051b880101945083880160005b828110156105bb578887037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001845281518051151588528601518688018690526105a8868901826104dd565b975050928501929085019060010161055c565b50949a9950505050505050505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610645577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08887030184526106338683516104dd565b955092840192908401906001016105f9565b509398975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126106e557600080fd5b9190910192915050565b60006020828403121561070157600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461072557600080fd5b9392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261076157600080fd5b83018035915067ffffffffffffffff82111561077c57600080fd5b60200191503681900382131561043157600080fd5b8183823760009101908152919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156107fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60006020828403121561081357600080fd5b815167ffffffffffffffff8082111561082b57600080fd5b818401915084601f83011261083f57600080fd5b81518181111561085157610851610653565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561089757610897610653565b816040528281528760208487010111156108b057600080fd5b6108c18360208301602088016104ad565b979650505050505050565b60208152600061072560208301846104dd56fea164736f6c6343000809000a",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c80631b57b72f1461003b57806386575ee914610066575b600080fd5b61004e610049366004610438565b610087565b60405161005d93929190610527565b60405180910390f35b610079610074366004610438565b610227565b60405161005d9291906105cb565b600080606043804085848167ffffffffffffffff8111156100aa576100aa610653565b6040519080825280602002602001820160405280156100f057816020015b6040805180820190915260008152606060208201528152602001906001900390816100c85790505b50905060005b82811015610217576000808b8b8481811061011357610113610682565b905060200281019061012591906106b1565b6101339060208101906106ef565b73ffffffffffffffffffffffffffffffffffffffff168c8c8581811061015b5761015b610682565b905060200281019061016d91906106b1565b61017b90602081019061072c565b604051610189929190610791565b6000604051808303816000865af19150503d80600081146101c6576040519150601f19603f3d011682016040523d82523d6000602084013e6101cb565b606091505b509150915060405180604001604052808315158152602001828152508484815181106101f9576101f9610682565b60200260200101819052508261020e906107a1565b925050506100f6565b5092989197509195509350505050565b600060604383838167ffffffffffffffff81111561024757610247610653565b60405190808252806020026020018201604052801561027a57816020015b60608152602001906001900390816102655790505b50905060005b828110156104285760008089898481811061029d5761029d610682565b90506020028101906102af91906106b1565b6102bd9060208101906106ef565b73ffffffffffffffffffffffffffffffffffffffff168a8a858181106102e5576102e5610682565b90506020028101906102f791906106b1565b61030590602081019061072c565b604051610313929190610791565b6000604051808303816000865af19150503d8060008114610350576040519150601f19603f3d011682016040523d82523d6000602084013e610355565b606091505b5091509150816103f7576044815110156103aa576040517fb5e1dc2d00000000000000000000000000000000000000000000000000000000815260206004820152600060248201526044015b60405180910390fd5b600481019050808060200190518101906103c49190610801565b6040517fb5e1dc2d0000000000000000000000000000000000000000000000000000000081526004016103a191906108cc565b8084848151811061040a5761040a610682565b60200260200101819052508261041f906107a1565b92505050610280565b50919350909150505b9250929050565b6000806020838503121561044b57600080fd5b823567ffffffffffffffff8082111561046357600080fd5b818501915085601f83011261047757600080fd5b81358181111561048657600080fd5b8660208260051b850101111561049b57600080fd5b60209290920196919550909350505050565b60005b838110156104c85781810151838201526020016104b0565b838111156104d7576000848401525b50505050565b600081518084526104f58160208601602086016104ad565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60006060820185835260208581850152604060608186015282865180855260808701915060808160051b880101945083880160005b828110156105bb578887037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001845281518051151588528601518688018690526105a8868901826104dd565b975050928501929085019060010161055c565b50949a9950505050505050505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610645577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08887030184526106338683516104dd565b955092840192908401906001016105f9565b509398975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126106e557600080fd5b9190910192915050565b60006020828403121561070157600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461072557600080fd5b9392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261076157600080fd5b83018035915067ffffffffffffffff82111561077c57600080fd5b60200191503681900382131561043157600080fd5b8183823760009101908152919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156107fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60006020828403121561081357600080fd5b815167ffffffffffffffff8082111561082b57600080fd5b818401915084601f83011261083f57600080fd5b81518181111561085157610851610653565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561089757610897610653565b816040528281528760208487010111156108b057600080fd5b6108c18360208301602088016104ad565b979650505050505050565b60208152600061072560208301846104dd56fea164736f6c6343000809000a",
"linkReferences": {},
"deployedLinkReferences": {}
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../../../build-info/901b067f80b6616939e558d795c555e5.json"
}

View File

@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/6e32e8b8339868a5a7b642cfedb2d144.json"
"buildInfo": "../../build-info/901b067f80b6616939e558d795c555e5.json"
}

File diff suppressed because one or more lines are too long

View File

@ -2,18 +2,6 @@
pragma solidity 0.8.9;
library DataTypes {
// struct Deposit {
// /// @dev Remaining tokens available.
// uint256 remaining;
// /// @dev The PIX account for the seller receive transactions.
// string pixTarget;
// address seller;
// /// @dev ERC20 stable token address.
// address token;
// /// @dev Could be invalidated by the seller.
// bool valid;
// }
struct Lock {
uint256 sellerKey;
uint256 counter;
@ -33,4 +21,12 @@ library DataTypes {
address relayerAddress;
address token;
}
// prettier-ignore
enum LockStatus {
Inexistent, // 0 := Uninitialized Lock.
Active, // 1 := Valid Lock.
Expired, // 2 := Expired Lock.
Released // 3 := Already released Lock.
}
}

View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/// @title Multicall.
/// @notice Contract that batches view function calls and aggregates their results.
/// @author Adapted from Makerdao's Multicall2 (https://github.com/makerdao/multicall/blob/master/src/Multicall2.sol).
contract Multicall {
/// @dev 0x
error CallFailed(string reason);
struct Call {
address target;
bytes callData;
}
struct Result {
bool success;
bytes returnData;
}
//prettier-ignore
constructor(/* */) payable {/* */}
function mtc1(Call[] calldata calls)
external
returns (uint256, bytes[] memory)
{
uint256 bn = block.number;
uint256 len = calls.length;
bytes[] memory res = new bytes[](len);
uint256 j;
while (j < len) {
(bool success, bytes memory ret) = calls[j]
.target
.call(calls[j].callData);
if (!success) {
if (ret.length < 0x44) revert CallFailed("");
assembly {
ret := add(ret, 0x04)
}
revert CallFailed({
reason: abi.decode(ret, (string))
});
}
res[j] = ret;
++j;
}
return (bn, res);
}
function mtc2(Call[] calldata calls)
external
returns (
uint256,
bytes32,
Result[] memory
)
{
uint256 bn = block.number;
// µ 0 s [0] P(IHp , µs [0], 0) P is the hash of a block of a particular number, up to a maximum age.
// 0 is left on the stack if the looked for `block.number` is >= to the current `block.number` or more than 256
// blocks behind the current block (Yellow Paper, p. 33, https://ethereum.github.io/yellowpaper/paper.pdf).
bytes32 bh = blockhash(
bn /* - 1 */
);
uint256 len = calls.length;
Result[] memory res = new Result[](len);
uint256 i;
for (i; i < len; ) {
(bool success, bytes memory ret) = calls[i]
.target
.call(calls[i].callData);
res[i] = Result(success, ret);
++i;
}
return (bn, bh, res);
}
}

View File

@ -114,46 +114,4 @@ library SafeTransferLib {
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
// We'll write our calldata to this slot below, but restore it later.
let memPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(
0,
0x095ea7b300000000000000000000000000000000000000000000000000000000
)
mstore(4, to) // Append the "to" argument.
mstore(36, amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(
and(
eq(mload(0), 1),
gt(returndatasize(), 31)
),
iszero(returndatasize())
),
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
// Counterintuitively, this call() must be positioned after the or() in the
// surrounding and() because and() evaluates its arguments from right to left.
call(gas(), token, 0, 0, 68, 0, 32)
)
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, memPointer) // Restore the memPointer.
}
require(success, "APPROVE_FAILED");
}
}

View File

@ -26,6 +26,7 @@ contract P2PIX is
// solhint-disable no-empty-blocks
using DT for DT.Lock;
using DT for DT.LockStatus;
/// Constants
@ -43,7 +44,6 @@ contract P2PIX is
/// Storage
IReputation public reputation;
// Counters.Counter public depositCount;
/// @dev Default blocks that lock will hold tokens.
uint256 public defaultLockBlocks;
@ -106,15 +106,11 @@ contract P2PIX is
ERC20 t = ERC20(_token);
uint256 k = _castAddrToKey(msg.sender);
if (_pixTarget == 0)
revert EmptyPixTarget();
if (!allowedERC20s[t])
revert TokenDenied();
uint256 _sellerBalance =
sellerBalance[k][t];
if (_pixTarget == 0) revert EmptyPixTarget();
if (!allowedERC20s[t]) revert TokenDenied();
uint256 _sellerBalance = sellerBalance[k][t];
uint256 currBal =
_sellerBalance & BITMASK_SB_ENTRY;
uint256 currBal = _sellerBalance & BITMASK_SB_ENTRY;
if ((currBal + _amount) > 1e8 ether)
revert MaxBalExceeded();
@ -131,11 +127,7 @@ contract P2PIX is
amountCasted,
pixTargetCasted,
validCasted
) = _castToUint(
_amount,
_pixTarget,
_valid
);
) = _castToUint(_amount, _pixTarget, _valid);
sellerBalance[k][t] =
(currBal + amountCasted) |
@ -151,11 +143,7 @@ contract P2PIX is
clearReentrancyGuard();
emit DepositAdded(
msg.sender,
_token,
_amount
);
emit DepositAdded(msg.sender, _token, _amount);
}
/// @notice Enables seller to invalidate future
@ -163,29 +151,23 @@ contract P2PIX is
/// @dev This function does not affect any ongoing active locks.
/// @dev Function sighash: 0x72fada5c.
function setValidState(ERC20 token, bool state) public {
uint256 key =
_castAddrToKey(msg.sender);
uint256 _sellerBalance =
sellerBalance[key][token];
uint256 key = _castAddrToKey(msg.sender);
uint256 _sellerBalance = sellerBalance[key][token];
if (_sellerBalance != 0) {
uint256 _valid;
assembly { _valid := state }
assembly {
_valid := state
}
_sellerBalance =
(_sellerBalance & BITMASK_VALID) |
(_valid << BITPOS_VALID);
sellerBalance[key][token] =
_sellerBalance;
sellerBalance[key][token] = _sellerBalance;
emit ValidSet(
msg.sender,
address(token),
state
);
} else
revert NotInitialized();
emit ValidSet(msg.sender, address(token), state);
} else revert NotInitialized();
}
/// @notice Public method designed to lock an remaining amount of
@ -220,33 +202,26 @@ contract P2PIX is
unlockExpired(expiredLocks);
ERC20 t = ERC20(_token);
if (!getValid(_seller, t))
revert InvalidDeposit();
if (!getValid(_seller, t)) revert InvalidDeposit();
uint256 bal =
getBalance(_seller, t);
if (bal < _amount)
revert NotEnoughTokens();
uint256 bal = getBalance(_seller, t);
if (bal < _amount) revert NotEnoughTokens();
uint256 k =
_castAddrToKey(_seller);
uint256 k = _castAddrToKey(_seller);
uint256 cCounter =
lockCounter + 1;
uint256 cCounter = lockCounter + 1;
if (mapLocks[cCounter].expirationBlock
>= block.number)
revert NotExpired();
if (
mapLocks[cCounter].expirationBlock >= block.number
) revert NotExpired();
DT.Lock memory l = DT.Lock(
k,
cCounter,
_relayerPremium,
_amount,
(block.number +
defaultLockBlocks),
uint160(sellerBalance[k][t]
>> BITPOS_PIXTARGET),
(block.number + defaultLockBlocks),
uint160(sellerBalance[k][t] >> BITPOS_PIXTARGET),
_buyerAddress,
_relayerTarget,
msg.sender,
@ -260,58 +235,34 @@ contract P2PIX is
msg.sender
);
_addLock(
bal,
_amount,
cCounter,
l,
t,
k
);
_addLock(bal, _amount, cCounter, l, t, k);
lockCounter++;
// Halt execution and output `lockID`.
return cCounter;
} else {
if (l.amount <= 1e2 ether) {
_addLock(
bal,
_amount,
cCounter,
l,
t,
k
);
_addLock(bal, _amount, cCounter, l, t, k);
lockCounter++;
// Halt execution and output `lockID`.
return cCounter;
} else {
uint256 userCredit = userRecord[
_castAddrToKey(msg.sender)
];
uint256 spendLimit;
(spendLimit) =
_limiter(userCredit / WAD);
(spendLimit) = _limiter(userCredit / WAD);
if (
l.amount > (spendLimit * WAD) ||
l.amount > 1e6 ether
) revert AmountNotAllowed();
_addLock(
bal,
_amount,
cCounter,
l,
t,
k
);
_addLock(bal, _amount, cCounter, l, t, k);
lockCounter++;
@ -375,25 +326,22 @@ contract P2PIX is
ERC20 t = ERC20(l.token);
// We cache values before zeroing them out.
uint256 lockAmount =
l.amount;
uint256 totalAmount =
(lockAmount - l.relayerPremium);
uint256 lockAmount = l.amount;
uint256 totalAmount = (lockAmount - l.relayerPremium);
l.amount = 0;
l.expirationBlock = 0;
usedTransactions[message] = true;
if (msg.sender != l.relayerAddress) {
userRecord[
_castAddrToKey(msg.sender)
] += l.relayerPremium;
userRecord[_castAddrToKey(msg.sender)] += l
.relayerPremium;
userRecord[
_castAddrToKey(l.relayerAddress)
] += lockAmount;
} else {
userRecord[_castAddrToKey(msg.sender)]
+= (l.relayerPremium + lockAmount);
userRecord[_castAddrToKey(msg.sender)] += (l
.relayerPremium + lockAmount);
}
SafeTransferLib.safeTransfer(
@ -424,11 +372,7 @@ contract P2PIX is
}
}
emit LockReleased(
l.buyerAddress,
lockID,
lockAmount
);
emit LockReleased(l.buyerAddress, lockID, lockAmount);
}
/// @notice Unlocks expired locks.
@ -444,42 +388,34 @@ contract P2PIX is
uint256 locksSize = lockIDs.length;
for (i; i < locksSize; ) {
DT.Lock storage l =
mapLocks[lockIDs[i]];
DT.Lock storage l = mapLocks[lockIDs[i]];
_notExpired(l);
uint256 _sellerBalance =
sellerBalance[
l.sellerKey][ERC20(l.token)
] & BITMASK_SB_ENTRY;
uint256 _sellerBalance = sellerBalance[
l.sellerKey
][ERC20(l.token)] & BITMASK_SB_ENTRY;
if (
(_sellerBalance + l.amount)
> 1e8 ether
)
if ((_sellerBalance + l.amount) > 1e8 ether)
revert MaxBalExceeded();
sellerBalance[
l.sellerKey][ERC20(l.token)
] += l.amount;
sellerBalance[l.sellerKey][ERC20(l.token)] += l
.amount;
l.amount = 0;
uint256 userKey =
_castAddrToKey(l.relayerAddress);
uint256 _newUserRecord =
(userRecord[userKey] >> 1);
uint256 userKey = _castAddrToKey(
l.relayerAddress
);
uint256 _newUserRecord = (userRecord[userKey] >>
1);
if (_newUserRecord <= 1e2 ether) {
userRecord[userKey] = 1e2 ether;
} else {
userRecord[userKey] = _newUserRecord;
}
emit LockReturned(
l.buyerAddress,
lockIDs[i]
);
emit LockReturned(l.buyerAddress, lockIDs[i]);
unchecked {
++i;
@ -503,23 +439,16 @@ contract P2PIX is
ERC20 token,
uint256 amount,
uint256[] calldata expiredLocks
)
public
nonReentrant
{
) public nonReentrant {
unlockExpired(expiredLocks);
if (getValid(msg.sender, token)
== true
) {
if (getValid(msg.sender, token) == true) {
setValidState(token, false);
}
uint256 key =
_castAddrToKey(msg.sender);
uint256 key = _castAddrToKey(msg.sender);
_decBal(
(sellerBalance[key][token]
& BITMASK_SB_ENTRY),
(sellerBalance[key][token] & BITMASK_SB_ENTRY),
amount,
token,
key
@ -785,7 +714,11 @@ contract P2PIX is
// sellerBalance[_castAddrToKey(seller)][token] &
// BITMASK_SB_ENTRY;
assembly {
for {/* */} iszero(0x0) {/* */} {
for {
/* */
} iszero(0x0) {
/* */
} {
mstore(0x00, shl(0xC, seller))
mstore(0x20, sellerBalance.slot)
let sbkslot := keccak256(0x00, 0x40)
@ -793,9 +726,11 @@ contract P2PIX is
mstore(0x20, sbkslot)
bal := and(
BITMASK_SB_ENTRY,
sload(keccak256(0x00,0x40)
)) break
}}
sload(keccak256(0x00, 0x40))
)
break
}
}
}
function getValid(address seller, ERC20 token)
@ -808,7 +743,11 @@ contract P2PIX is
// ][token];
// ] >> BITPOS_VALID) & BITMASK_SB_ENTRY;
assembly {
for {/* */} iszero(0x0) {/* */} {
for {
/* */
} iszero(0x0) {
/* */
} {
mstore(0x00, shl(0xC, seller))
mstore(0x20, sellerBalance.slot)
let sbkslot := keccak256(0x00, 0x40)
@ -818,9 +757,12 @@ contract P2PIX is
BITMASK_SB_ENTRY,
shr(
BITPOS_VALID,
sload(keccak256(0x00,0x40)
))) break
}}
sload(keccak256(0x00, 0x40))
)
)
break
}
}
}
function getPixTarget(address seller, ERC20 token)
@ -833,7 +775,11 @@ contract P2PIX is
// BITPOS_PIXTARGET
// );
assembly {
for {/* */} iszero(0) {/* */} {
for {
/* */
} iszero(0) {
/* */
} {
mstore(0, shl(12, seller))
mstore(32, sellerBalance.slot)
let sbkslot := keccak256(0, 64)
@ -841,37 +787,80 @@ contract P2PIX is
mstore(32, sbkslot)
pixTarget := shr(
BITPOS_PIXTARGET,
sload(keccak256(0,64)
)) break
}}
sload(keccak256(0, 64))
)
break
}
}
}
function getBalances(
address[] memory sellers,
ERC20 token
)
external
view
returns(uint256[] memory)
{
) external view returns (uint256[] memory) {
uint256 j;
uint256 len =
sellers.length;
uint256[] memory balances =
new uint256[](len);
uint256 len = sellers.length;
uint256[] memory balances = new uint256[](len);
while (j < len) {
uint256 bal =
getBalance(
sellers[j],
token
);
uint256 bal = getBalance(sellers[j], token);
balances[j] = bal;
unchecked { ++j; }
unchecked {
++j;
}
}
return balances;
}
/// @notice External getter that returns the status of a lockIDs array.
/// @dev Call will not revert if provided with an empty array as parameter.
/// @dev Function sighash: 0x49ef8448
function getLocksStatus(uint256[] memory ids)
external
view
returns (uint256[] memory, DT.LockStatus[] memory)
{
if (ids.length == 0) {
uint256[] memory null1 = new uint256[](0);
DT.LockStatus[]
memory null2 = new DT.LockStatus[](0);
return (null1, null2);
}
uint256 c;
uint256 len = ids.length;
uint256[] memory sortedIDs = new uint256[](len);
DT.LockStatus[] memory status = new DT.LockStatus[](
len
);
unchecked {
for (c; c < len; ) {
if (mapLocks[ids[c]].sellerKey == 0x0) {
sortedIDs[c] = ids[c];
status[c] = type(DT.LockStatus).min;
++c;
} else if (mapLocks[ids[c]].amount == 0x0) {
sortedIDs[c] = ids[c];
status[c] = type(DT.LockStatus).max;
++c;
} else if (
mapLocks[ids[c]].expirationBlock <
block.number
) {
sortedIDs[c] = ids[c];
status[c] = DT.LockStatus.Expired;
++c;
} else {
sortedIDs[c] = ids[c];
status[c] = DT.LockStatus.Active;
++c;
}
}
}
return (sortedIDs, status);
}
/// @notice Public method that handles `address`
/// to `uint256` safe type casting.
/// @dev Function sighash: 0x4b2ae980.

View File

@ -3,6 +3,6 @@
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
],
"p2pix": "0xefa5cE4351cda51192509cf8De7d8881ADAE95DD",
"p2pix": "0x2414817FF64A114d91eCFA16a834d3fCf69103d4",
"token": "0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00"
}

View File

@ -3,6 +3,6 @@
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
],
"p2pix": "0xA9258eBb157E4cf5e756b77FDD0DF09C2F73240b",
"p2pix": "0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00",
"token": "0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29"
}

View File

@ -44,7 +44,9 @@ function getChainConfig(
let jsonRpcUrl: string;
switch (chain) {
case "polygon-mumbai":
jsonRpcUrl = "https://polygon-mumbai.g.alchemy.com/v2/" + alchemyApiKey;
jsonRpcUrl =
"https://polygon-mumbai.g.alchemy.com/v2/" +
alchemyApiKey;
break;
default:
jsonRpcUrl =

View File

@ -64,7 +64,7 @@
"fs-extra": "^10.1.0",
"hardhat": "^2.12.2",
"hardhat-gas-reporter": "^1.0.9",
"hardhat-tracer": "^1.2.0",
"hardhat-tracer": "beta",
"husky": "^8.0.1",
"keccak256": "^1.0.6",
"lint-staged": "^13.0.3",

View File

@ -3,11 +3,10 @@ import "@nomiclabs/hardhat-etherscan";
import { BigNumber } from "ethers";
import * as fs from "fs";
import { ethers, network } from "hardhat";
import hre from "hardhat";
import { Deploys } from "../test/utils/fixtures";
import hre from "hardhat";
let deploysJson: Deploys;
const supply: BigNumber = ethers.utils.parseEther("20000000");

View File

@ -2,11 +2,10 @@ import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-etherscan";
import * as fs from "fs";
import { ethers, network } from "hardhat";
import hre from "hardhat";
import { Deploys } from "../test/utils/fixtures";
import hre from "hardhat";
let deploysJson: Deploys;
const main = async () => {
@ -27,8 +26,13 @@ const main = async () => {
const Reputation = await ethers.getContractFactory(
"Reputation",
);
const Multicall = await ethers.getContractFactory(
"Multicall",
);
const reputation = await Reputation.deploy();
await reputation.deployed();
const mutlicall = await Multicall.deploy();
await mutlicall.deployed();
const P2PIX = await ethers.getContractFactory("P2PIX");
const p2pix = await P2PIX.deploy(
@ -42,6 +46,8 @@ const main = async () => {
deploysJson.p2pix = p2pix.address;
console.log("🚀 P2PIX Deployed:", p2pix.address);
console.log("🌠 Reputation Deployed:", reputation.address);
console.log("🛰 Multicall Deployed:", mutlicall.address);
await p2pix.deployTransaction.wait(6);
fs.writeFileSync(
@ -53,8 +59,7 @@ const main = async () => {
//verify
await hre.run("verify:verify", {
address: p2pix.address,
constructorArguments:
[
constructorArguments: [
10,
deploysJson.signers,
reputation.address,
@ -66,6 +71,10 @@ const main = async () => {
address: reputation.address,
constructorArguments: [],
});
await hre.run("verify:verify", {
address: mutlicall.address,
constructorArguments: [],
});
};
main()

View File

@ -263,12 +263,6 @@ const _abi = [
name: "amount",
type: "uint256",
},
{
indexed: false,
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "LockReleased",
type: "event",

View File

@ -0,0 +1,174 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import {
Signer,
utils,
Contract,
ContractFactory,
PayableOverrides,
} from "ethers";
import type { Provider, TransactionRequest } from "@ethersproject/providers";
import type { PromiseOrValue } from "../../../common";
import type {
Multicall,
MulticallInterface,
} from "../../../lib/utils/Multicall";
const _abi = [
{
inputs: [],
stateMutability: "payable",
type: "constructor",
},
{
inputs: [
{
internalType: "string",
name: "reason",
type: "string",
},
],
name: "CallFailed",
type: "error",
},
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bytes",
name: "callData",
type: "bytes",
},
],
internalType: "struct Multicall.Call[]",
name: "calls",
type: "tuple[]",
},
],
name: "mtc1",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
{
internalType: "bytes[]",
name: "",
type: "bytes[]",
},
],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bytes",
name: "callData",
type: "bytes",
},
],
internalType: "struct Multicall.Call[]",
name: "calls",
type: "tuple[]",
},
],
name: "mtc2",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
{
internalType: "bytes32",
name: "",
type: "bytes32",
},
{
components: [
{
internalType: "bool",
name: "success",
type: "bool",
},
{
internalType: "bytes",
name: "returnData",
type: "bytes",
},
],
internalType: "struct Multicall.Result[]",
name: "",
type: "tuple[]",
},
],
stateMutability: "nonpayable",
type: "function",
},
];
const _bytecode =
"0x60806040526108ec806100136000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80631b57b72f1461003b57806386575ee914610066575b600080fd5b61004e610049366004610438565b610087565b60405161005d93929190610527565b60405180910390f35b610079610074366004610438565b610227565b60405161005d9291906105cb565b600080606043804085848167ffffffffffffffff8111156100aa576100aa610653565b6040519080825280602002602001820160405280156100f057816020015b6040805180820190915260008152606060208201528152602001906001900390816100c85790505b50905060005b82811015610217576000808b8b8481811061011357610113610682565b905060200281019061012591906106b1565b6101339060208101906106ef565b73ffffffffffffffffffffffffffffffffffffffff168c8c8581811061015b5761015b610682565b905060200281019061016d91906106b1565b61017b90602081019061072c565b604051610189929190610791565b6000604051808303816000865af19150503d80600081146101c6576040519150601f19603f3d011682016040523d82523d6000602084013e6101cb565b606091505b509150915060405180604001604052808315158152602001828152508484815181106101f9576101f9610682565b60200260200101819052508261020e906107a1565b925050506100f6565b5092989197509195509350505050565b600060604383838167ffffffffffffffff81111561024757610247610653565b60405190808252806020026020018201604052801561027a57816020015b60608152602001906001900390816102655790505b50905060005b828110156104285760008089898481811061029d5761029d610682565b90506020028101906102af91906106b1565b6102bd9060208101906106ef565b73ffffffffffffffffffffffffffffffffffffffff168a8a858181106102e5576102e5610682565b90506020028101906102f791906106b1565b61030590602081019061072c565b604051610313929190610791565b6000604051808303816000865af19150503d8060008114610350576040519150601f19603f3d011682016040523d82523d6000602084013e610355565b606091505b5091509150816103f7576044815110156103aa576040517fb5e1dc2d00000000000000000000000000000000000000000000000000000000815260206004820152600060248201526044015b60405180910390fd5b600481019050808060200190518101906103c49190610801565b6040517fb5e1dc2d0000000000000000000000000000000000000000000000000000000081526004016103a191906108cc565b8084848151811061040a5761040a610682565b60200260200101819052508261041f906107a1565b92505050610280565b50919350909150505b9250929050565b6000806020838503121561044b57600080fd5b823567ffffffffffffffff8082111561046357600080fd5b818501915085601f83011261047757600080fd5b81358181111561048657600080fd5b8660208260051b850101111561049b57600080fd5b60209290920196919550909350505050565b60005b838110156104c85781810151838201526020016104b0565b838111156104d7576000848401525b50505050565b600081518084526104f58160208601602086016104ad565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60006060820185835260208581850152604060608186015282865180855260808701915060808160051b880101945083880160005b828110156105bb578887037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8001845281518051151588528601518688018690526105a8868901826104dd565b975050928501929085019060010161055c565b50949a9950505050505050505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610645577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08887030184526106338683516104dd565b955092840192908401906001016105f9565b509398975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc18336030181126106e557600080fd5b9190910192915050565b60006020828403121561070157600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461072557600080fd5b9392505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261076157600080fd5b83018035915067ffffffffffffffff82111561077c57600080fd5b60200191503681900382131561043157600080fd5b8183823760009101908152919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156107fa577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b60006020828403121561081357600080fd5b815167ffffffffffffffff8082111561082b57600080fd5b818401915084601f83011261083f57600080fd5b81518181111561085157610851610653565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561089757610897610653565b816040528281528760208487010111156108b057600080fd5b6108c18360208301602088016104ad565b979650505050505050565b60208152600061072560208301846104dd56fea164736f6c6343000809000a";
type MulticallConstructorParams =
| [signer?: Signer]
| ConstructorParameters<typeof ContractFactory>;
const isSuperArgs = (
xs: MulticallConstructorParams
): xs is ConstructorParameters<typeof ContractFactory> => xs.length > 1;
export class Multicall__factory extends ContractFactory {
constructor(...args: MulticallConstructorParams) {
if (isSuperArgs(args)) {
super(...args);
} else {
super(_abi, _bytecode, args[0]);
}
}
override deploy(
overrides?: PayableOverrides & { from?: PromiseOrValue<string> }
): Promise<Multicall> {
return super.deploy(overrides || {}) as Promise<Multicall>;
}
override getDeployTransaction(
overrides?: PayableOverrides & { from?: PromiseOrValue<string> }
): TransactionRequest {
return super.getDeployTransaction(overrides || {});
}
override attach(address: string): Multicall {
return super.attach(address) as Multicall;
}
override connect(signer: Signer): Multicall__factory {
return super.connect(signer) as Multicall__factory;
}
static readonly bytecode = _bytecode;
static readonly abi = _abi;
static createInterface(): MulticallInterface {
return new utils.Interface(_abi) as MulticallInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): Multicall {
return new Contract(address, _abi, signerOrProvider) as Multicall;
}
}

View File

@ -1,4 +1,5 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export { Multicall__factory } from "./Multicall__factory";
export { ReentrancyGuard__factory } from "./ReentrancyGuard__factory";

File diff suppressed because one or more lines are too long

View File

@ -32,6 +32,10 @@ declare module "hardhat/types/runtime" {
name: "ERC20",
signerOrOptions?: ethers.Signer | FactoryOptions
): Promise<Contracts.ERC20__factory>;
getContractFactory(
name: "Multicall",
signerOrOptions?: ethers.Signer | FactoryOptions
): Promise<Contracts.Multicall__factory>;
getContractFactory(
name: "ReentrancyGuard",
signerOrOptions?: ethers.Signer | FactoryOptions
@ -70,6 +74,11 @@ declare module "hardhat/types/runtime" {
address: string,
signer?: ethers.Signer
): Promise<Contracts.ERC20>;
getContractAt(
name: "Multicall",
address: string,
signer?: ethers.Signer
): Promise<Contracts.Multicall>;
getContractAt(
name: "ReentrancyGuard",
address: string,

View File

@ -17,6 +17,8 @@ export type { MockToken } from "./lib/mock/mockToken.sol/MockToken";
export { MockToken__factory } from "./factories/lib/mock/mockToken.sol/MockToken__factory";
export type { ERC20 } from "./lib/tokens/ERC20";
export { ERC20__factory } from "./factories/lib/tokens/ERC20__factory";
export type { Multicall } from "./lib/utils/Multicall";
export { Multicall__factory } from "./factories/lib/utils/Multicall__factory";
export type { ReentrancyGuard } from "./lib/utils/ReentrancyGuard";
export { ReentrancyGuard__factory } from "./factories/lib/utils/ReentrancyGuard__factory";
export type { P2PIX } from "./p2pix.sol/P2PIX";

View File

@ -0,0 +1,155 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type { FunctionFragment, Result } from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
PromiseOrValue,
} from "../../common";
export declare namespace Multicall {
export type CallStruct = {
target: PromiseOrValue<string>;
callData: PromiseOrValue<BytesLike>;
};
export type CallStructOutput = [string, string] & {
target: string;
callData: string;
};
export type ResultStruct = {
success: PromiseOrValue<boolean>;
returnData: PromiseOrValue<BytesLike>;
};
export type ResultStructOutput = [boolean, string] & {
success: boolean;
returnData: string;
};
}
export interface MulticallInterface extends utils.Interface {
functions: {
"mtc1((address,bytes)[])": FunctionFragment;
"mtc2((address,bytes)[])": FunctionFragment;
};
getFunction(nameOrSignatureOrTopic: "mtc1" | "mtc2"): FunctionFragment;
encodeFunctionData(
functionFragment: "mtc1",
values: [Multicall.CallStruct[]]
): string;
encodeFunctionData(
functionFragment: "mtc2",
values: [Multicall.CallStruct[]]
): string;
decodeFunctionResult(functionFragment: "mtc1", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "mtc2", data: BytesLike): Result;
events: {};
}
export interface Multicall extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: MulticallInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
mtc1(
calls: Multicall.CallStruct[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
mtc2(
calls: Multicall.CallStruct[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
};
mtc1(
calls: Multicall.CallStruct[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
mtc2(
calls: Multicall.CallStruct[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;
callStatic: {
mtc1(
calls: Multicall.CallStruct[],
overrides?: CallOverrides
): Promise<[BigNumber, string[]]>;
mtc2(
calls: Multicall.CallStruct[],
overrides?: CallOverrides
): Promise<[BigNumber, string, Multicall.ResultStructOutput[]]>;
};
filters: {};
estimateGas: {
mtc1(
calls: Multicall.CallStruct[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;
mtc2(
calls: Multicall.CallStruct[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;
};
populateTransaction: {
mtc1(
calls: Multicall.CallStruct[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;
mtc2(
calls: Multicall.CallStruct[],
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;
};
}

View File

@ -1,4 +1,5 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export type { Multicall } from "./Multicall";
export type { ReentrancyGuard } from "./ReentrancyGuard";

View File

@ -37,6 +37,7 @@ export interface P2PIXInterface extends utils.Interface {
"deposit(address,uint96,uint160,bool,bytes32)": FunctionFragment;
"getBalance(address,address)": FunctionFragment;
"getBalances(address[],address)": FunctionFragment;
"getLocksStatus(uint256[])": FunctionFragment;
"getPixTarget(address,address)": FunctionFragment;
"getValid(address,address)": FunctionFragment;
"lock(address,address,address,address,uint256,uint256,bytes32[],uint256[])": FunctionFragment;
@ -72,6 +73,7 @@ export interface P2PIXInterface extends utils.Interface {
| "deposit"
| "getBalance"
| "getBalances"
| "getLocksStatus"
| "getPixTarget"
| "getValid"
| "lock"
@ -132,6 +134,10 @@ export interface P2PIXInterface extends utils.Interface {
functionFragment: "getBalances",
values: [PromiseOrValue<string>[], PromiseOrValue<string>]
): string;
encodeFunctionData(
functionFragment: "getLocksStatus",
values: [PromiseOrValue<BigNumberish>[]]
): string;
encodeFunctionData(
functionFragment: "getPixTarget",
values: [PromiseOrValue<string>, PromiseOrValue<string>]
@ -265,6 +271,10 @@ export interface P2PIXInterface extends utils.Interface {
functionFragment: "getBalances",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getLocksStatus",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getPixTarget",
data: BytesLike
@ -577,6 +587,11 @@ export interface P2PIX extends BaseContract {
overrides?: CallOverrides
): Promise<[BigNumber[]]>;
getLocksStatus(
ids: PromiseOrValue<BigNumberish>[],
overrides?: CallOverrides
): Promise<[BigNumber[], number[]]>;
getPixTarget(
seller: PromiseOrValue<string>,
token: PromiseOrValue<string>,
@ -767,6 +782,11 @@ export interface P2PIX extends BaseContract {
overrides?: CallOverrides
): Promise<BigNumber[]>;
getLocksStatus(
ids: PromiseOrValue<BigNumberish>[],
overrides?: CallOverrides
): Promise<[BigNumber[], number[]]>;
getPixTarget(
seller: PromiseOrValue<string>,
token: PromiseOrValue<string>,
@ -957,6 +977,11 @@ export interface P2PIX extends BaseContract {
overrides?: CallOverrides
): Promise<BigNumber[]>;
getLocksStatus(
ids: PromiseOrValue<BigNumberish>[],
overrides?: CallOverrides
): Promise<[BigNumber[], number[]]>;
getPixTarget(
seller: PromiseOrValue<string>,
token: PromiseOrValue<string>,
@ -1257,6 +1282,11 @@ export interface P2PIX extends BaseContract {
overrides?: CallOverrides
): Promise<BigNumber>;
getLocksStatus(
ids: PromiseOrValue<BigNumberish>[],
overrides?: CallOverrides
): Promise<BigNumber>;
getPixTarget(
seller: PromiseOrValue<string>,
token: PromiseOrValue<string>,
@ -1424,6 +1454,11 @@ export interface P2PIX extends BaseContract {
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
getLocksStatus(
ids: PromiseOrValue<BigNumberish>[],
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
getPixTarget(
seller: PromiseOrValue<string>,
token: PromiseOrValue<string>,

View File

@ -23,19 +23,73 @@ describe("Reputation", () => {
({ reputation } = await loadFixture(repFixture));
});
// describe("Limiter", async () => {
// it("Curve reliability", async () => {
// const tx1 = await reputation.connect(owner).limiter(0);
// const tx2 = await reputation.limiter(500);
// const tx3 = await reputation
// .connect(owner)
// .limiter(444444);
// const tx4 = await reputation.limiter(988700);
// expect(tx1).to.eq(curve(0));
// expect(tx2).to.eq(curve(500));
// expect(tx3).to.eq(curve(444444));
// expect(tx4).to.eq(curve(988700));
// });
// });
describe("Limiter", async () => {
it("Curve reliability", async () => {
const tx1 = await reputation.connect(owner).limiter(0);
const tx2 = await reputation.limiter(500);
const tx3 = await reputation
.connect(owner)
.limiter(444444);
const tx4 = await reputation.limiter(988700);
const testCases = [
{
x: 0,
expected: curve(0),
},
{
x: 500,
expected: curve(500),
},
{
x: 444444,
expected: curve(444444),
},
{
x: 988700,
expected: curve(988700),
},
{
x: Number.MAX_SAFE_INTEGER,
shouldRevert: "overflow",
},
{
x: Number.POSITIVE_INFINITY,
shouldRevert: "overflow",
},
{
x: Number.NEGATIVE_INFINITY,
shouldRevert: "overflow",
},
{
x: -1,
shouldRevert: "value out-of-bounds",
},
{
x: Number.NaN,
shouldRevert: "invalid BigNumber string",
},
];
expect(tx1).to.eq(curve(0));
expect(tx2).to.eq(curve(500));
expect(tx3).to.eq(curve(444444));
expect(tx4).to.eq(curve(988700));
for (const testCase of testCases) {
if (testCase.shouldRevert != undefined) {
await expect(reputation.limiter(testCase.x)).to.be
.rejected;
} else {
const result = await reputation.limiter(testCase.x);
expect(result).to.eq(testCase.expected).and.to.be
.ok;
}
}
});
});
});

View File

@ -7,6 +7,7 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai";
import {
BigNumber,
BytesLike,
ContractReceipt,
ContractTransaction,
Wallet,
@ -17,12 +18,19 @@ import {
/* tracer */
} from "hardhat";
// import keccak256 from "keccak256";
import { MockToken, P2PIX, Reputation } from "../src/types";
import {
MockToken,
Multicall,
P2PIX,
Reputation,
} from "../src/types";
import { P2PixErrors } from "./utils/errors";
import {
// Deposit,
Call,
Lock,
Result,
getBnFrom,
getLockData,
getSignerAddrs,
p2pixFixture,
randomSigners,
@ -45,6 +53,7 @@ describe("P2PIX", () => {
let p2pix: P2PIX; // Contract instance
let erc20: MockToken; // Token instance
let reputation: Reputation; // Reputation Interface instance
let multicall: Multicall; // Multicall contract instance
let merkleRoot: string; // MerkleRoot from seller's allowlist
let proof: string[]; // Owner's proof as whitelisted address
@ -62,8 +71,14 @@ describe("P2PIX", () => {
await network.provider.send("hardhat_reset");
});
beforeEach("Load deployment fixtures", async () => {
({ erc20, p2pix, reputation, merkleRoot, proof } =
await loadFixture(p2pixFixture));
({
erc20,
p2pix,
reputation,
multicall,
merkleRoot,
proof,
} = await loadFixture(p2pixFixture));
});
describe("Init", async () => {
@ -234,11 +249,22 @@ describe("P2PIX", () => {
await p2pix.callStatic.allowedERC20s(erc20.address);
const newTokenState2 =
await p2pix.callStatic.allowedERC20s(owner.address);
const funcSig = "0xd6e8b973";
const args = ethers.utils.defaultAbiCoder.encode(
["address[]", "bool[]"],
[[acc01.address], [false]],
);
const cd = funcSig + args.substring(2);
const callStruct: Call = {
target: p2pix.address,
callData: cd,
};
const fail = p2pix
.connect(acc01)
.tokenSettings([acc01.address], [false]);
const fail2 = p2pix.tokenSettings([], [true, false]);
const fail3 = p2pix.tokenSettings([zero], [true, true]);
const mtcFail = multicall.mtc1([callStruct]);
expect(tx).to.be.ok;
await expect(tx)
@ -262,6 +288,12 @@ describe("P2PIX", () => {
p2pix,
P2PixErrors.LengthMismatch,
);
await expect(mtcFail)
.to.be.revertedWithCustomError(
multicall,
P2PixErrors.CallFailed,
)
.withArgs(P2PixErrors.UNAUTHORIZED);
});
});
describe("Deposit", async () => {
@ -330,9 +362,18 @@ describe("P2PIX", () => {
true,
root,
);
const storage = await p2pix.callStatic.getBalance(owner.address, erc20.address);
const pixTarget = await p2pix.callStatic.getPixTarget(owner.address,erc20.address);
const valid = await p2pix.callStatic.getValid(owner.address,erc20.address);
const storage = await p2pix.callStatic.getBalance(
owner.address,
erc20.address,
);
const pixTarget = await p2pix.callStatic.getPixTarget(
owner.address,
erc20.address,
);
const valid = await p2pix.callStatic.getValid(
owner.address,
erc20.address,
);
const ownerKey = await p2pix.callStatic._castAddrToKey(
owner.address,
);
@ -385,7 +426,12 @@ describe("P2PIX", () => {
const price2 = price.mul(ethers.BigNumber.from(2));
const price3 = price.mul(ethers.BigNumber.from(3));
const price4 = price.mul(ethers.BigNumber.from(4));
const prices:BigNumber[] = [price, price2, price3, price4];
const prices: BigNumber[] = [
price,
price2,
price3,
price4,
];
await erc20.mint(
getSignerAddrs(4, await ethers.getSigners()),
price4,
@ -408,31 +454,86 @@ describe("P2PIX", () => {
.deposit(erc20.address, price, pTarget, true, root);
const tx2 = await p2pix
.connect(acc01)
.deposit(erc20.address, price2, pTarget2, false, nullRoot);
.deposit(
erc20.address,
price2,
pTarget2,
false,
nullRoot,
);
const tx3 = await p2pix
.connect(acc02)
.deposit(erc20.address, price3, pTarget3, true, root);
const tx4 = await p2pix
.connect(acc03)
.deposit(erc20.address, price4, pTarget, false, nullRoot);
.deposit(
erc20.address,
price4,
pTarget,
false,
nullRoot,
);
const balances = await p2pix.callStatic.getBalances(
[owner.address, acc01.address, acc02.address, acc03.address], erc20.address);
[
owner.address,
acc01.address,
acc02.address,
acc03.address,
],
erc20.address,
);
const storage1 = await p2pix.callStatic.getBalance(owner.address, erc20.address);
const storage2 = await p2pix.callStatic.getBalance(acc01.address, erc20.address);
const storage3 = await p2pix.callStatic.getBalance(acc02.address, erc20.address);
const storage4 = await p2pix.callStatic.getBalance(acc03.address, erc20.address);
const storage1 = await p2pix.callStatic.getBalance(
owner.address,
erc20.address,
);
const storage2 = await p2pix.callStatic.getBalance(
acc01.address,
erc20.address,
);
const storage3 = await p2pix.callStatic.getBalance(
acc02.address,
erc20.address,
);
const storage4 = await p2pix.callStatic.getBalance(
acc03.address,
erc20.address,
);
const pixTarget1 = await p2pix.callStatic.getPixTarget(owner.address, erc20.address);
const pixTarget2 = await p2pix.callStatic.getPixTarget(acc01.address, erc20.address);
const pixTarget3 = await p2pix.callStatic.getPixTarget(acc02.address, erc20.address);
const pixTarget4 = await p2pix.callStatic.getPixTarget(acc03.address, erc20.address);
const pixTarget1 = await p2pix.callStatic.getPixTarget(
owner.address,
erc20.address,
);
const pixTarget2 = await p2pix.callStatic.getPixTarget(
acc01.address,
erc20.address,
);
const pixTarget3 = await p2pix.callStatic.getPixTarget(
acc02.address,
erc20.address,
);
const pixTarget4 = await p2pix.callStatic.getPixTarget(
acc03.address,
erc20.address,
);
const valid1 = await p2pix.callStatic.getValid(owner.address, erc20.address);
const valid2 = await p2pix.callStatic.getValid(acc01.address, erc20.address);
const valid3 = await p2pix.callStatic.getValid(acc02.address, erc20.address);
const valid4 = await p2pix.callStatic.getValid(acc03.address, erc20.address);
const valid1 = await p2pix.callStatic.getValid(
owner.address,
erc20.address,
);
const valid2 = await p2pix.callStatic.getValid(
acc01.address,
erc20.address,
);
const valid3 = await p2pix.callStatic.getValid(
acc02.address,
erc20.address,
);
const valid4 = await p2pix.callStatic.getValid(
acc03.address,
erc20.address,
);
const allowList1 = await p2pix.sellerAllowList(
ownerKey,
@ -452,41 +553,30 @@ describe("P2PIX", () => {
expect(tx3).to.be.ok;
expect(tx4).to.be.ok;
const transactions = [tx, tx2, tx3, tx4];
const addresses = [
owner.address,
acc01.address,
acc02.address,
acc03.address,
];
const depositPrices = [price, price2, price3, price4];
for (let i = 0; i < transactions.length; i++) {
const tx = transactions[i];
const addr = addresses[i];
const depositPrice = depositPrices[i];
const amount = `-${(i + 1) * 100000000000000000000}`;
await expect(tx)
.to.emit(p2pix, "DepositAdded")
.withArgs(owner.address, erc20.address, price);
.withArgs(addr, erc20.address, depositPrice);
await expect(tx).to.changeTokenBalances(
erc20,
[owner.address, p2pix.address],
["-100000000000000000000", price],
);
await expect(tx2)
.to.emit(p2pix, "DepositAdded")
.withArgs(acc01.address, erc20.address, price2);
await expect(tx2).to.changeTokenBalances(
erc20,
[acc01.address, p2pix.address],
["-200000000000000000000", price2],
);
await expect(tx3)
.to.emit(p2pix, "DepositAdded")
.withArgs(acc02.address, erc20.address, price3);
await expect(tx3).to.changeTokenBalances(
erc20,
[acc02.address, p2pix.address],
["-300000000000000000000", price3],
);
await expect(tx4)
.to.emit(p2pix, "DepositAdded")
.withArgs(acc03.address, erc20.address, price4);
await expect(tx4).to.changeTokenBalances(
erc20,
[acc03.address, p2pix.address],
["-400000000000000000000", price4],
[addr, p2pix.address],
[amount, depositPrice],
);
}
expect(prices[0]).to.eq(balances[0]);
expect(prices[1]).to.eq(balances[1]);
@ -586,36 +676,7 @@ describe("P2PIX", () => {
P2PixErrors.NotEnoughTokens,
);
});
// test invalid since lockID has been replaced by a counter.
// it.only("should revert if a non expired lock has the same ID encoded", async () => {
// const pTarget = ethers.BigNumber.from(1337);
// await erc20.approve(p2pix.address, price);
// await p2pix.deposit(
// erc20.address,
// price,
// pTarget,
// true,
// ethers.constants.HashZero,
// );
// await p2pix
// .connect(acc03)
// .lock(
// owner.address,
// erc20.address,
// acc02.address,
// acc03.address,
// 0, 1, [], []);
// console.log(await p2pix.callStatic.getValid(owner.address,erc20.address))
// const fail = p2pix
// .connect(acc03)
// .lock(owner.address, erc20.address, acc02.address, acc03.address, 0, 1, [], []);
// await expect(fail).to.be.revertedWithCustomError(
// p2pix,
// P2PixErrors.NotExpired,
// );
// });
it("should revert if an invalid allowlist merkleproof is provided", async () => {
await erc20.approve(p2pix.address, price);
await p2pix.deposit(
@ -699,11 +760,15 @@ describe("P2PIX", () => {
proof,
[],
);
const storage: Lock = await p2pix.callStatic.mapLocks(1);
const storage: Lock = await p2pix.callStatic.mapLocks(
1,
);
const rc: ContractReceipt = await tx.wait();
const expiration = rc.blockNumber + 10;
const key = await p2pix.callStatic._castAddrToKey(owner.address);
const key = await p2pix.callStatic._castAddrToKey(
owner.address,
);
await expect(tx)
.to.emit(p2pix, "LockAdded")
@ -750,12 +815,18 @@ describe("P2PIX", () => {
[],
[],
);
const storage: Lock = await p2pix.callStatic.mapLocks(1);
const storage: Lock = await p2pix.callStatic.mapLocks(
1,
);
const rc: ContractReceipt = await tx.wait();
const expiration = rc.blockNumber + 10;
const key = await p2pix.callStatic._castAddrToKey(owner.address);
const castBack = await p2pix.callStatic._castKeyToAddr(key);
const key = await p2pix.callStatic._castAddrToKey(
owner.address,
);
const castBack = await p2pix.callStatic._castKeyToAddr(
key,
);
expect(tx).to.be.ok;
expect(castBack).to.eq(owner.address);
@ -774,16 +845,13 @@ describe("P2PIX", () => {
await expect(tx)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc02.address,
1,
key,
storage.amount,
);
.withArgs(acc02.address, 1, key, storage.amount);
});
it("should create a lock, update storage and emit events via the reputation path 2", async () => {
const root = ethers.constants.HashZero;
const newPrice = price.mul(ethers.constants.Two).add(ethers.constants.One);
const newPrice = price
.mul(ethers.constants.Two)
.add(ethers.constants.One);
const endtoendID = ethers.constants.HashZero;
const target = ethers.BigNumber.from(101);
const messageToSign = ethers.utils.solidityKeccak256(
@ -838,12 +906,18 @@ describe("P2PIX", () => {
[],
[],
);
const storage: Lock = await p2pix.callStatic.mapLocks(2);
const storage: Lock = await p2pix.callStatic.mapLocks(
2,
);
const rc: ContractReceipt = await tx.wait();
const expiration = rc.blockNumber + 10;
const key = await p2pix.callStatic._castAddrToKey(owner.address);
const castBack = await p2pix.callStatic._castKeyToAddr(key);
const key = await p2pix.callStatic._castAddrToKey(
owner.address,
);
const castBack = await p2pix.callStatic._castKeyToAddr(
key,
);
expect(tx).to.be.ok;
expect(castBack).to.eq(owner.address);
@ -852,7 +926,9 @@ describe("P2PIX", () => {
expect(storage.relayerPremium).to.eq(
ethers.constants.Zero,
);
expect(storage.amount).to.eq(price.add(ethers.constants.One));
expect(storage.amount).to.eq(
price.add(ethers.constants.One),
);
expect(storage.expirationBlock).to.eq(expiration);
expect(storage.pixTarget).to.eq(target);
expect(storage.buyerAddress).to.eq(acc02.address);
@ -862,12 +938,7 @@ describe("P2PIX", () => {
await expect(tx)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc02.address,
2,
key,
storage.amount,
);
.withArgs(acc02.address, 2, key, storage.amount);
});
// edge case test
it("should create multiple locks", async () => {
@ -912,7 +983,9 @@ describe("P2PIX", () => {
[],
[],
);
const storage2: Lock = await p2pix.callStatic.mapLocks(2);
const storage2: Lock = await p2pix.callStatic.mapLocks(
2,
);
const rc2: ContractReceipt = await tx2.wait();
const expiration2 = rc2.blockNumber + 10;
@ -929,17 +1002,81 @@ describe("P2PIX", () => {
[],
[],
);
const storage3: Lock = await p2pix.callStatic.mapLocks(3);
const storage3: Lock = await p2pix.callStatic.mapLocks(
3,
);
const rc3: ContractReceipt = await tx3.wait();
const expiration3 = rc3.blockNumber + 10;
const key = await p2pix.callStatic._castAddrToKey(owner.address);
const key = await p2pix.callStatic._castAddrToKey(
owner.address,
);
// const lockStatus1 = await p2pix.callStatic.getLocksStatus([1,7,7,2,3,4,5,5,2,3]);
// const lockStatus2 = await p2pix.callStatic.getLocksStatus([0,1,2,3]);
// const lockStatus3 = await p2pix.callStatic.getLocksStatus([7,7,333,14,777]);
// const lockStatus4 = await p2pix.callStatic.getLocksStatus([]);
// All getLocksStatus calls were batched via the Multicall contract.
const ls1: [BigNumber[], BigNumber[]] = [
getBnFrom([1, 7, 7, 2, 3, 4, 5, 5, 2, 3]),
getBnFrom([1, 0, 0, 1, 1, 0, 0, 0, 1, 1]),
];
const ls2: [BigNumber[], BigNumber[]] = [
getBnFrom([0, 1, 2, 3]),
getBnFrom([0, 1, 1, 1]),
];
const ls3: [BigNumber[], BigNumber[]] = [
getBnFrom([7, 7, 333, 14, 777]),
getBnFrom([0, 0, 0, 0, 0]),
];
const ls4 = [[], []];
const batchedLocks: Array<BigNumber[]> = [
ls1,
ls2,
ls3,
ls4,
].map(arr => arr[0]);
const cData: Call[] = getLockData(
p2pix.address,
batchedLocks,
);
const batchCall = await multicall.callStatic.mtc1(
cData,
);
const blockNumber = batchCall[0];
const result: Array<BytesLike> = batchCall[1].slice(
0,
4,
);
const decodedData = result.map(r =>
ethers.utils.defaultAbiCoder.decode(
["uint256[]", "uint8[]"],
r,
),
);
const [ls1Res, ls2Res, ls3Res, ls4Res] = decodedData;
expect(tx1).to.be.ok;
expect(tx2).to.be.ok;
expect(tx3).to.be.ok;
expect(blockNumber).to.eq(9);
expect(ls1Res).to.deep.equal(ls1);
expect(ls2Res).to.deep.equal(ls2);
expect(ls3Res).to.deep.equal(ls3);
expect(ls4Res).to.deep.equal(ls4);
expect(key)
.to.eq(storage1.sellerKey)
.and.to.eq(storage2.sellerKey)
@ -990,28 +1127,13 @@ describe("P2PIX", () => {
await expect(tx1)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc02.address,
1,
key,
storage1.amount,
);
.withArgs(acc02.address, 1, key, storage1.amount);
await expect(tx2)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc02.address,
2,
key,
storage2.amount,
);
.withArgs(acc02.address, 2, key, storage2.amount);
await expect(tx3)
.to.emit(p2pix, "LockAdded")
.withArgs(
acc03.address,
3,
key,
storage3.amount,
);
.withArgs(acc03.address, 3, key, storage3.amount);
});
});
describe("Set sellerBalance Valid State", async () => {
@ -1032,11 +1154,18 @@ describe("P2PIX", () => {
true,
merkleRoot,
);
const state1 =
await p2pix.callStatic.getValid(owner.address,erc20.address);
const tx = await p2pix.setValidState(erc20.address,false);
const state2 =
await p2pix.callStatic.getValid(owner.address,erc20.address);
const state1 = await p2pix.callStatic.getValid(
owner.address,
erc20.address,
);
const tx = await p2pix.setValidState(
erc20.address,
false,
);
const state2 = await p2pix.callStatic.getValid(
owner.address,
erc20.address,
);
expect(tx).to.be.ok;
await expect(tx)
@ -1057,37 +1186,64 @@ describe("P2PIX", () => {
true,
hashZero,
);
await erc20.connect(acc01).approve(p2pix.address, price);
await p2pix.connect(acc01).deposit(
await erc20
.connect(acc01)
.approve(p2pix.address, price);
await p2pix
.connect(acc01)
.deposit(
erc20.address,
price,
target,
false,
hashZero,
);
await erc20.connect(acc02).approve(p2pix.address, price);
await p2pix.connect(acc02).deposit(
await erc20
.connect(acc02)
.approve(p2pix.address, price);
await p2pix
.connect(acc02)
.deposit(
erc20.address,
price,
target,
true,
hashZero,
);
const oldState1 =
await p2pix.callStatic.getValid(owner.address,erc20.address);
const oldState2 =
await p2pix.callStatic.getValid(acc01.address,erc20.address);
const oldState3 =
await p2pix.callStatic.getValid(acc02.address,erc20.address);
const tx1 = await p2pix.setValidState(erc20.address,false);
const tx2 = await p2pix.connect(acc01).setValidState(erc20.address,true);
const tx3 = await p2pix.connect(acc02).setValidState(erc20.address,true);
const newState1 =
await p2pix.callStatic.getValid(owner.address,erc20.address);
const newState2 =
await p2pix.callStatic.getValid(acc01.address,erc20.address);
const newState3 =
await p2pix.callStatic.getValid(acc02.address,erc20.address);
const oldState1 = await p2pix.callStatic.getValid(
owner.address,
erc20.address,
);
const oldState2 = await p2pix.callStatic.getValid(
acc01.address,
erc20.address,
);
const oldState3 = await p2pix.callStatic.getValid(
acc02.address,
erc20.address,
);
const tx1 = await p2pix.setValidState(
erc20.address,
false,
);
const tx2 = await p2pix
.connect(acc01)
.setValidState(erc20.address, true);
const tx3 = await p2pix
.connect(acc02)
.setValidState(erc20.address, true);
const newState1 = await p2pix.callStatic.getValid(
owner.address,
erc20.address,
);
const newState2 = await p2pix.callStatic.getValid(
acc01.address,
erc20.address,
);
const newState3 = await p2pix.callStatic.getValid(
acc02.address,
erc20.address,
);
expect(tx1).to.be.ok;
expect(tx2).to.be.ok;
@ -1384,7 +1540,9 @@ describe("P2PIX", () => {
const userRecord1 = await p2pix.callStatic.userRecord(
acc03Key,
);
const storage1: Lock = await p2pix.callStatic.mapLocks(1);
const storage1: Lock = await p2pix.callStatic.mapLocks(
1,
);
const tx = await p2pix
.connect(acc01)
.release(
@ -1395,7 +1553,41 @@ describe("P2PIX", () => {
sig.s,
sig.v,
);
const storage2: Lock = await p2pix.callStatic.mapLocks(1);
const lockStatus1 =
await p2pix.callStatic.getLocksStatus([1]);
const ls1: [BigNumber[], number[]] = [
[ethers.constants.One],
[3],
];
const funcSig = "0xd6e8b973";
const args = ethers.utils.defaultAbiCoder.encode(
["address[]", "bool[]"],
[[acc01.address], [false]],
);
const cd1 = funcSig + args.substring(2);
const cd2: Call[] = getLockData(p2pix.address, [
ls1[0],
]);
const mtcCalls = [
{ target: p2pix.address, callData: cd1 },
];
mtcCalls.push(cd2[0]);
const mtc2 = await multicall.callStatic.mtc2(mtcCalls);
const blockNumber: BigNumber = mtc2[0];
const blockhash: BytesLike = mtc2[1];
const result = mtc2.slice(2).flat(1) as Result[];
const res1: BytesLike[] = [result[1].returnData];
const decodedLockData = res1.map(r =>
ethers.utils.defaultAbiCoder.decode(
["uint256[]", "uint8[]"],
r,
),
);
const storage2: Lock = await p2pix.callStatic.mapLocks(
1,
);
const userRecordB = await p2pix.callStatic.userRecord(
acc01Key,
);
@ -1408,13 +1600,30 @@ describe("P2PIX", () => {
expect(tx).to.be.ok;
await expect(tx)
.to.emit(p2pix, "LockReleased")
.withArgs(acc02.address, ethers.constants.One, storage1.amount);
.withArgs(
acc02.address,
ethers.constants.One,
storage1.amount,
);
expect(storage1.expirationBlock).to.eq(
ethers.BigNumber.from(16),
ethers.BigNumber.from(17),
);
expect(storage1.amount).to.eq(
ethers.BigNumber.from(100),
);
expect(lockStatus1[0].toString()).to.equal(
ls1[0].toString(),
);
expect(lockStatus1[1].toString()).to.equal(
ls1[1].toString(),
);
expect(blockNumber).to.eq(8);
expect(blockhash).to.deep.equal(
ethers.constants.HashZero,
);
expect(result[0].success).to.eq(false);
expect(result[1].success).to.eq(true);
expect(decodedLockData.flat(1)).to.deep.eq(ls1);
expect(storage2.expirationBlock).to.eq(zero);
expect(storage2.amount).to.eq(zero);
expect(used).to.eq(true);
@ -1501,7 +1710,7 @@ describe("P2PIX", () => {
6,
50,
[],
[]
[],
);
await p2pix
.connect(acc03)
@ -1515,12 +1724,31 @@ describe("P2PIX", () => {
[],
[],
);
const lockStatus1 =
await p2pix.callStatic.getLocksStatus([1, 2, 3, 44]);
const ls1: [BigNumber[], BigNumber[]] = [
[
ethers.constants.One,
ethers.constants.Two,
ethers.BigNumber.from(3),
ethers.BigNumber.from(44),
],
getBnFrom([1, 1, 1, 0]),
];
const lockID = ethers.constants.One;
const lockID2 = ethers.constants.Two;
const lockID3 = ethers.BigNumber.from(3);
const storage1: Lock = await p2pix.callStatic.mapLocks(lockID);
const storage2: Lock = await p2pix.callStatic.mapLocks(lockID2);
const storage3: Lock = await p2pix.callStatic.mapLocks(lockID3);
const storage1: Lock = await p2pix.callStatic.mapLocks(
lockID,
);
const storage2: Lock = await p2pix.callStatic.mapLocks(
lockID2,
);
const storage3: Lock = await p2pix.callStatic.mapLocks(
lockID3,
);
// relayerPremium == 0
const tx = await p2pix
.connect(acc01)
@ -1572,6 +1800,27 @@ describe("P2PIX", () => {
acc03Key,
);
const lockStatus2 =
await p2pix.callStatic.getLocksStatus([1, 2, 3, 44]);
const ls2: [BigNumber[], BigNumber[]] = [
[
ethers.constants.One,
ethers.constants.Two,
ethers.BigNumber.from(3),
ethers.BigNumber.from(44),
],
getBnFrom([3, 3, 3, 0]),
];
const batchedLocks: Array<BigNumber[]> = [
ls1.slice(0, 1)[0],
ls2.slice(0, 1)[0],
];
const cData: Call[] = getLockData(
erc20.address,
batchedLocks,
);
expect(tx).to.be.ok;
expect(tx1).to.be.ok;
expect(tx2).to.be.ok;
@ -1590,35 +1839,46 @@ describe("P2PIX", () => {
expect(0).to.eq(acc01Record1).and.to.eq(acc03Record1);
expect(acc01Record2).to.eq(6); // 0 + 6
expect(acc03Record2).to.eq(185); // 100 + 50 + 25 + 10
await expect(tx).to.changeTokenBalances(
erc20,
[
const addresses = [
acc01.address,
acc02.address,
acc03.address,
p2pix.address,
],
];
const balances = [
[0, 100, 0, "-100"],
);
await expect(tx1).to.changeTokenBalances(
erc20,
[
acc01.address,
acc02.address,
acc03.address,
p2pix.address,
],
[0, 47, 3, "-50"],
);
await expect(tx2).to.changeTokenBalances(
erc20,
[
acc01.address,
acc02.address,
acc03.address,
p2pix.address,
],
[0, 20, 5, "-25"],
];
for (let i = 0; i < 3; i++) {
const txs = [tx, tx1, tx2][i];
await expect(txs).to.changeTokenBalances(
erc20,
addresses,
balances[i],
);
}
expect(lockStatus1[0].toString()).to.equal(
ls1[0].toString(),
);
expect(lockStatus1[1].toString()).to.equal(
ls1[1].toString(),
);
expect(lockStatus2[0].toString()).to.equal(
ls2[0].toString(),
);
expect(lockStatus2[1].toString()).to.equal(
ls2[1].toString(),
);
await expect(
multicall.callStatic.mtc1(cData),
).to.be.revertedWithCustomError(
multicall,
P2PixErrors.CallFailed,
);
});
});
@ -1643,7 +1903,7 @@ describe("P2PIX", () => {
0,
1,
[],
[]
[],
);
const lockID = ethers.constants.One;
const fail = p2pix.unlockExpired([lockID]);
@ -1684,7 +1944,7 @@ describe("P2PIX", () => {
0,
1,
[],
[]
[],
);
const lockID = ethers.constants.One;
// await mine(10);
@ -1723,10 +1983,22 @@ describe("P2PIX", () => {
0,
1,
[],
[]
[],
);
const lockID = ethers.constants.One;
await mine(11);
const lockStatus1 =
await p2pix.callStatic.getLocksStatus([11, 1, 777]);
const ls1: [BigNumber[], BigNumber[]] = [
[
ethers.BigNumber.from(11),
ethers.constants.One,
ethers.BigNumber.from(777),
],
getBnFrom([0, 2, 0]),
];
const storage: Lock = await p2pix.callStatic.mapLocks(
lockID,
);
@ -1752,6 +2024,9 @@ describe("P2PIX", () => {
expect(storage2.amount).to.eq(ethers.constants.Zero);
expect(record1).to.eq(0);
expect(record2).to.eq(price);
expect(lockStatus1[0].toString()).to.equal(
ls1[0].toString(),
);
});
it("should unlock expired through lock function", async () => {
const target = ethers.BigNumber.from(101);
@ -1818,7 +2093,10 @@ describe("P2PIX", () => {
[],
[lockID],
);
const remaining = await p2pix.callStatic.getBalance(owner.address,erc20.address);
const remaining = await p2pix.callStatic.getBalance(
owner.address,
erc20.address,
);
expect(tx1).to.be.ok;
await expect(tx1)
@ -1854,8 +2132,13 @@ describe("P2PIX", () => {
const lockID = ethers.constants.One;
// mine blocks to expire lock
await mine(11);
const tx = await p2pix.withdraw(erc20.address, price, [lockID]);
const remaining = await p2pix.callStatic.getBalance(owner.address, erc20.address);
const tx = await p2pix.withdraw(erc20.address, price, [
lockID,
]);
const remaining = await p2pix.callStatic.getBalance(
owner.address,
erc20.address,
);
expect(tx).to.be.ok;
await expect(tx)
@ -1876,13 +2159,18 @@ describe("P2PIX", () => {
true,
merkleRoot,
);
const fail = p2pix.connect(acc02).withdraw(erc20.address, price.mul(ethers.constants.Two),[]);
const fail = p2pix
.connect(acc02)
.withdraw(
erc20.address,
price.mul(ethers.constants.Two),
[],
);
await expect(fail).to.be.revertedWithCustomError(
p2pix,
P2PixErrors.DecOverflow,
);
});
it("should withdraw remaining funds from deposit, update storage and emit event", async () => {
const newPrice = price.div(ethers.constants.Two);
@ -1897,7 +2185,7 @@ describe("P2PIX", () => {
const tx = await p2pix.withdraw(
erc20.address,
price.div(ethers.constants.Two),
[]
[],
);
expect(tx).to.be.ok;
@ -1925,6 +2213,7 @@ describe("P2PIX", () => {
.withArgs(owner.address, erc20.address, newPrice);
});
});
describe("Allowlist Settings", async () => {
it("should revert if the msg.sender differs from deposit's seller", async () => {
const root = ethers.utils.keccak256(

View File

@ -19,4 +19,5 @@ export enum P2PixErrors {
MaxBalExceeded = "MaxBalExceeded",
NotInitialized = "NotInitialized",
DecOverflow = "DecOverflow",
CallFailed = "CallFailed",
}

View File

@ -6,7 +6,9 @@ import { MerkleTree } from "merkletreejs";
import {
MockToken,
Multicall,
P2PIX,
P2PIX__factory,
Reputation,
} from "../../src/types";
@ -17,14 +19,6 @@ export interface Deploys {
token: string;
}
// export interface Deposit {
// remaining: BigNumber;
// pixTarget: string;
// seller: string;
// token: string;
// valid: boolean;
// }
export interface Lock {
sellerKey: BigNumber;
counter: BigNumber;
@ -38,6 +32,16 @@ export interface Lock {
token: string;
}
export interface Call {
target: string;
callData: string;
}
export interface Result {
success: boolean;
returnData: string;
}
export interface P2pixFixture {
p2pix: P2PIX;
erc20: MockToken;
@ -49,19 +53,40 @@ export interface RepFixture {
reputation: Reputation;
}
type P2PixAndReputation = P2pixFixture & RepFixture;
export interface MtcFixture {
multicall: Multicall;
}
type P2PixAndReputation = P2pixFixture &
RepFixture &
MtcFixture;
// exported constants
export const getSignerAddrs = (
amount: number,
addrs: SignerWithAddress[],
): string[] => {
const signers: string[] = [];
const buffr = addrs.slice(0, amount);
for (let i = 0; i < amount; i++) {
signers.push(buffr[i].address);
}
return signers;
return addrs.slice(0, amount).map(({ address }) => address);
};
export const getBnFrom = (nums: number[]): BigNumber[] => {
const bns = nums.map(num => ethers.BigNumber.from(num));
return bns;
};
export const getLockData = (
addr: string,
locks: BigNumber[][],
): Call[] => {
const iface = new ethers.utils.Interface(
P2PIX__factory.abi,
);
return locks.map(lock => ({
target: addr,
callData: iface.encodeFunctionData("getLocksStatus", [
lock,
]),
}));
};
export const randomSigners = (amount: number): Signer[] => {
@ -128,6 +153,11 @@ export async function p2pixFixture(): Promise<P2PixAndReputation> {
[true],
)) as P2PIX;
const Multicall = await ethers.getContractFactory(
"Multicall",
);
const multicall = (await Multicall.deploy()) as Multicall;
const signers = await ethers.getSigners();
const whitelisted = signers.slice(0, 2);
const leaves = whitelisted.map(account =>
@ -142,6 +172,7 @@ export async function p2pixFixture(): Promise<P2PixAndReputation> {
);
return {
multicall,
reputation,
erc20,
p2pix,

View File

@ -6049,16 +6049,17 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"hardhat-tracer@npm:^1.2.0":
version: 1.2.0
resolution: "hardhat-tracer@npm:1.2.0"
"hardhat-tracer@npm:beta":
version: 2.0.0-beta.6
resolution: "hardhat-tracer@npm:2.0.0-beta.6"
dependencies:
ethers: ^5.6.1
peerDependencies:
chai: 4.x
chalk: 4.x
ethers: 5.x
hardhat: 2.x
checksum: 1d348fb3ed60cbde2287329730ccd37c73af80cc2cf4ccfb045f26af26c7efebd4a6fdf611035f34ca7fb75d0eb95985e8f5ed4f0537d2615fb6e126d1d035f0
checksum: b14795adf3eecd487b874ef06799ab43c342a4908bcf58bbd0bc7274caa7976456a0c75a702aaf3a937fe7c8afbfe2e2a9f263b6a0c7e2fc56e6a54144288369
languageName: node
linkType: hard
@ -8459,7 +8460,7 @@ fsevents@~2.1.1:
fs-extra: ^10.1.0
hardhat: ^2.12.2
hardhat-gas-reporter: ^1.0.9
hardhat-tracer: ^1.2.0
hardhat-tracer: beta
husky: ^8.0.1
keccak256: ^1.0.6
lint-staged: ^13.0.3