Compare commits

..

3 Commits

Author SHA1 Message Date
Mateus
31139b0236 Remove conflicting extension 2022-11-22 19:00:01 +00:00
Mateus
9e671662a8 Add Pedro extension suggestions 2022-11-22 18:11:27 +00:00
Mateus
5cc60954a8 Add initial Codespaces settings file 2022-11-11 17:10:42 +00:00
90 changed files with 31138 additions and 18554 deletions

View File

@ -1,2 +0,0 @@
extends:
- "@commitlint/config-conventional"

3
.czrc
View File

@ -1,3 +0,0 @@
{
"path": "cz-conventional-changelog"
}

View File

@ -0,0 +1,32 @@
{
"name": "Node.js",
"image": "mcr.microsoft.com/devcontainers/javascript-node:16-bullseye",
// Features to add to the dev container. More info: https://containers.dev/implementors/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "npm install",
// Configure tool-specific properties.
"customizations": {
"vscode": {
"settings": {
"extensions.ignoreRecommendations": true
},
"extensions": [
"NomicFoundation.hardhat-solidity",
"tintinweb.vscode-inline-bookmarks",
"tintinweb.solidity-visual-auditor",
"tintinweb.solidity-metrics",
"tintinweb.graphviz-interactive-preview"
]
}
},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

View File

@ -1,16 +0,0 @@
# EditorConfig http://EditorConfig.org
# top-most EditorConfig file
root = true
# All files
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.sol]
indent_size = 4

View File

@ -1,12 +0,0 @@
MNEMONIC="{INSERT_12_WORD_MNEMONIC}"
INFURA_API_KEY="{INSERT_API_KEY}"
ALCHEMY_API_KEY="{INSERT_API_KEY}"
# Block explorer API keys
ETHERSCAN_API_KEY="{INSERT_API_KEY}"
POLYGONSCAN_API_KEY="{INSERT_API_KEY}"
GASPRICE_API_ENDPOINT="https://api.etherscan.io/api?module=proxy&action=eth_gasPrice"
COINMARKETCAP_API_KEY="{INSERT_API_KEY}"
REPORT_GAS="true"

View File

@ -1,21 +0,0 @@
# directories
.yarn/
**/.coverage_artifacts
**/.coverage_cache
**/.coverage_contracts
**/artifacts
**/build
**/cache
**/coverage
**/dist
**/node_modules
**/types
# files
*.env
*.log
.pnp.*
coverage.json
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -1,21 +0,0 @@
extends:
- "eslint:recommended"
- "plugin:@typescript-eslint/eslint-recommended"
- "plugin:@typescript-eslint/recommended"
- "prettier"
parser: "@typescript-eslint/parser"
parserOptions:
project: "./tsconfig.json"
plugins:
- "@typescript-eslint"
root: true
rules:
"@typescript-eslint/no-floating-promises":
- error
- ignoreIIFE: true
ignoreVoid: true
"@typescript-eslint/no-inferrable-types": "off"
"@typescript-eslint/no-unused-vars":
- error
- argsIgnorePattern: "_"
varsIgnorePattern: "_"

37
.gitignore vendored
View File

@ -1,30 +1,9 @@
# directories
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
artifacts
**/artifacts
artifacts/build-info
**/build
**/ref
**/cache
**/coverage
**/.coverage_artifacts
**/.coverage_cache
**/.coverage_contracts
**/dist
**/node_modules
.deps
src/types
# files
*.env
*.log
.pnp.*
node_modules
.env
coverage
coverage.json
npm-debug.log*
yarn-debug.log*
yarn-error.log*
#Hardhat files
cache
artifacts/build-info
artifacts/@openzeppelin

1
.husky/.gitignore vendored
View File

@ -1 +0,0 @@
_

View File

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn dlx commitlint --edit $1

View File

@ -1,4 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn dlx lint-staged

View File

@ -1,5 +0,0 @@
{
"*.{js,json,md,sol,ts,yaml,yml}": [
"prettier --config ./.prettierrc.yaml --write"
]
}

View File

@ -1,23 +0,0 @@
# directories
.yarn/
**/.coverage_artifacts
**/.coverage_cache
**/.coverage_contracts
**/artifacts
**/build
**/cache
**/coverage
**/dist
**/node_modules
**/types
# files
*.env
*.log
.pnp.*
coverage.json
npm-debug.log*
yarn-debug.log*
yarn-error.log*
contracts/p2pix.sol

View File

@ -1,16 +0,0 @@
arrowParens: avoid
bracketSpacing: true
endOfLine: auto
importOrder: ["<THIRD_PARTY_MODULES>", "^[./]"]
importOrderParserPlugins: ["typescript"]
importOrderSeparation: true
importOrderSortSpecifiers: true
printWidth: 62
singleQuote: false
tabWidth: 2
trailingComma: all
overrides:
- files: "*.sol"
options:
tabWidth: 4

View File

@ -1,18 +0,0 @@
module.exports = {
configureYulOptimizer: true,
solcOptimizerDetails: {
deduplicate: true,
cse: true,
constantOptimizer: true,
peephole: true,
jumpdestRemover: true,
yul: true,
// inliner: false,
// orderLiterals: true,
},
istanbulReporter: ["html", "lcov"],
providerOptions: {
mnemonic: process.env.MNEMONIC,
},
skipFiles: ["test", 'core/BaseUtils.sol', 'core/OwnerSettings.sol'],
};

View File

@ -1,23 +0,0 @@
{
"extends": "solhint:recommended",
"plugins": ["prettier"],
"rules": {
"code-complexity": ["error", 8],
"compiler-version": ["error", ">=0.8.4"],
"const-name-snakecase": "off",
"constructor-syntax": "error",
"func-visibility": [
"error",
{ "ignoreConstructors": true }
],
"max-line-length": ["error", 120],
"not-rely-on-time": "off",
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
],
"reason-string": ["warn", { "maxLength": 64 }]
}
}

View File

@ -1,3 +0,0 @@
# directories
**/artifacts
**/node_modules

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +0,0 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.2.1.cjs

174
README.md
View File

@ -1,160 +1,68 @@
# p2pix-smart-contracts
**Repository for P2Pix EVM contracts to be imported by the project.**
Repository for P2Pix EVM contracts to be imported by the project.
## SM Dependency Tree
## Installation
Import the repository on your app to allow use the artifacts for Ethers.
```rs
./contracts/
├── Constants.sol
├── DataTypes.sol
├── EventAndErrors.sol
├── lib
│ ├── auth
│ │ └── Owned.sol
│ ├── interfaces
│ │ └── IReputation.sol
│ ├── mock
│ │ └── mockToken.sol
│ ├── tokens
│ │ └── ERC20.sol
│ └── utils
│ ├── ECDSA.sol
│ ├── MerkleProofLib.sol
│ ├── Multicall.sol
│ ├── ReentrancyGuard.sol
│ └── SafeTransferLib.sol
├── p2pix.sol
└── Reputation.sol
```
## Callgraph
![Callgraph](docs/callgraph.svg)
## Current Deployment addresses
### V1
| Testnet | Token Address | P2pix Address |
| ------- | ------------------------------------------ | ------------------------------------------ |
| Goerli | 0x294003F602c321627152c6b7DED3EAb5bEa853Ee | 0x5f3EFA9A90532914545CEf527C530658af87e196 |
| Mumbai | 0x294003F602c321627152c6b7DED3EAb5bEa853Ee | 0x5f3EFA9A90532914545CEf527C530658af87e196 |
<!-- All contracts deployed by 0x8dC06F985C131166570825F52447E8c88d64aE20 -->
<!-- https://goerli.etherscan.io/address/0x294003F602c321627152c6b7DED3EAb5bEa853Ee#code -->
<!-- https://goerli.etherscan.io/address/0x5f3EFA9A90532914545CEf527C530658af87e196#code -->
<!-- https://mumbai.polygonscan.com/address/0x294003F602c321627152c6b7DED3EAb5bEa853Ee#code -->
<!-- https://mumbai.polygonscan.com/address/0x5f3EFA9A90532914545CEf527C530658af87e196#code -->
### V2
| 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/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/0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00#code -->
<!-- https://mumbai.polygonscan.com/address/0x570445e3ef413bcdb5de79ed27b1c3840683e385#code -->
<!-- https://mumbai.polygonscan.com/address/0x718B2C4DE4F9654E1349F610ff561249bfe1c418#code -->
## Usage
### Pre Requisites
Before installing, create a `.env` file and set a BIP-39 compatible mnemonic and other env criteria as in `.env.example`.
### Install
```sh
$ yarn install
npm install --save git+https://github.com/doiim/p2pix-smart-contracts.git
```
### Compile
```sh
$ yarn compile
```
**_NOTE:_** TypeChain artifacts generated at compile time.
### Test
```sh
$ yarn test
```
### Report Gas
```sh
$ REPORT_GAS=true yarn test
```
**_NOTE_:** Gas usage per unit test and average gas per method call.
### Clean
Delete the smart contract artifacts and cache:
```sh
$ yarn clean
```
## Importing artifacts
To import artifacts on the project use the following:
```ts
import P2PIXArtifact from "p2pix-smart-contracts/artifacts/contracts/p2pix.sol/P2PIX.json";
```
import P2PIXArtifact from 'p2pix-smart-contracts/artifacts/contracts/p2pix.sol/P2PIX.json'
```
To grab deployment addresses you can just grab from deploys folder:
```ts
import localhostDeploys from "p2pix-smart-contracts/deploys/localhost.json";
```
import localhostDeploys from 'p2pix-smart-contracts/deploys/localhost.json'
```
## Deploying to local environment
The default deploy addresses for localhost is the following:
| Contract | Address |
|-|-|
|p2pix|`0x5FbDB2315678afecb367f032d93F642f64180aa3`|
|token|`0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512`|
On the first teminal, use the following command and import some wallets to your Metamask, then connect to the network pointed:
Then use a Contract instance to interact directly with it:
```sh
yarn hardhat node
```
const p2pixContract = new ethers.Contract(address, P2PIXArtifact.abi, signer);
```
On the second teminal, run the following commands:
## Deploying local environment
```sh
yarn deploy1:localhost
yarn deploy2:localhost
Clone the repo and install dependencies:
```
git clone https://github.com/doiim/p2pix-smart-contracts.git
cd p2pix-smart-contract
npm install
```
**_NOTE_:** The second script transfers 2M tokens to the first wallet of the node.
On the first teminal use the following command and import some wallets to your Metamask and connect to the network pointed:
```
npx hardhat node
```
On the second teminal run following commands:
```
npx hardhat run --network localhost scripts/1-deploy-p2pix.js
npx hardhat run --network localhost scripts/2-deploy-mockToken.js
```
The second script transfer 2M tokens to the firrs wallet of the node.
To use the P2Pix smart contract first transfer some of the tokens to other wallets.
## Deploying to testnets
## Testing
Deploy to Ethereum's Goerli testnet:
To run tests, clone this repo, install dependencies and run Hardhat tests.
```sh
yarn deploy1:goerli
yarn deploy2:goerli
```
Deploy to Polygon's Mumbai testnet:
```sh
yarn deploy1:mumbai
yarn deploy2:mumbai
git clone https://github.com/doiim/p2pix-smart-contracts.git
cd p2pix-smart-contract
npm install
npx hardhat test
```

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

View File

@ -1,79 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import { IReputation } from "./lib/interfaces/IReputation.sol";
contract Reputation is IReputation {
/// @dev Asymptote numerator constant value for the `limiter` fx.
uint256 public constant maxLimit = 1e6;
/// @dev Denominator's constant operand for the `limiter` fx.
uint256 public constant magicValue = 2.5e11;
// prettier-ignore
// solhint-disable no-inline-assembly
// solhint-disable-next-line no-empty-blocks
constructor(/* */) payable {/* */}
function limiter(
uint256 _userCredit
)
external
pure
override(IReputation)
returns (uint256 _spendLimit)
{
_spendLimit = (1 +
((maxLimit * _userCredit) /
sqrt(
magicValue + (_userCredit * _userCredit)
)));
}
/// @notice Taken from Solmate's FixedPointMathLib.
/// (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
function sqrt(
uint256 x
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(
lt(y, 0x10000000000000000000000000000000000)
) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := sub(z, lt(div(x, z), z))
}
}
}

View File

@ -1,181 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import { ERC20, OwnerSettings } from "contracts/core/OwnerSettings.sol";
import { ECDSA } from "contracts/lib/utils/ECDSA.sol";
import { MerkleProofLib as Merkle } from "contracts/lib/utils/MerkleProofLib.sol";
import { ReentrancyGuard } from "contracts/lib/utils/ReentrancyGuard.sol";
abstract contract BaseUtils is
OwnerSettings,
ReentrancyGuard
{
/// Storage
/// @dev List of Pix transactions already signed.
/// mapping(bytes32 => bool) public usedTransactions;
/// @dev Value in custom storage slot given by:
/// let value := sload(bytes32).
/// Helper FX
function _setUsedTransactions(bytes32 message) internal {
assembly ("memory-safe") {
sstore(message, true)
}
}
function usedTransactions(
bytes32 message
) public view returns (bool used) {
assembly ("memory-safe") {
used := sload(message)
}
}
function _signerCheck(
bytes32 _message,
bytes calldata _signature
) internal view {
if (usedTransactions(_message))
revert TxAlreadyUsed();
if (
!validBacenSigners(
_castAddrToKey(
ECDSA.recoverCalldata(
ECDSA.toEthSignedMessageHash(
_message
),
_signature
)
)
)
) revert InvalidSigner();
}
function _merkleVerify(
bytes32[] calldata _merkleProof,
bytes32 _root,
address _addr
) internal pure {
if (
!Merkle.verify(
_merkleProof,
_root,
bytes32(uint256(uint160(_addr)))
)
) revert AddressDenied();
}
function _castBool(
bool _valid
) internal pure returns (uint256 _validCasted) {
assembly ("memory-safe") {
_validCasted := _valid
}
}
function getStr(
string memory str
) public pure returns (bytes32 strEnc) {
bytes memory enc = bytes(abi.encodePacked(str));
assembly ("memory-safe") {
if lt(0x20, mload(enc)) {
invalid()
}
strEnc := mload(add(enc, 0x20))
}
}
function _setSellerBalance(
address _sellerKey,
ERC20 _erc20,
uint256 _packed,
bytes32 _pixTarget
) internal {
assembly ("memory-safe") {
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, _sellerKey)
let _loc := keccak256(0x0c, 0x34)
sstore(add(_loc, 0x01), _packed)
sstore(_loc, _pixTarget)
}
}
function _setValidState(
address _sellerKey,
ERC20 _erc20,
uint256 _packed
) internal {
assembly ("memory-safe") {
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, _sellerKey)
let _loc := keccak256(0x0c, 0x34)
sstore(add(_loc, 0x01), _packed)
}
}
function _addSellerBalance(
address _sellerKey,
ERC20 _erc20,
uint256 _amount
) internal {
assembly ("memory-safe") {
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, _sellerKey)
let _loc := add(keccak256(0x0c, 0x34), 0x01)
sstore(_loc, add(sload(_loc), _amount))
}
}
function _decSellerBalance(
address _sellerKey,
ERC20 _erc20,
uint256 _amount
) internal {
assembly ("memory-safe") {
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, _sellerKey)
let _loc := add(keccak256(0x0c, 0x34), 0x01)
sstore(_loc, sub(sload(_loc), _amount))
}
}
function __sellerBalance(
address _sellerKey,
ERC20 _erc20
) internal view returns (uint256 _packed) {
assembly ("memory-safe") {
mstore(0x20, _erc20)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, _sellerKey)
_packed := sload(add(keccak256(0x0c, 0x34), 0x01))
}
}
/// @notice Public method that handles `address`
/// to `uint256` safe type casting.
/// @dev Function sighash: 0x4b2ae980.
function _castAddrToKey(
address _addr
) public pure returns (uint256 _key) {
// _key = uint256(uint160(address(_addr))) << 12;
assembly ("memory-safe") {
_key := shl(0xc, _addr)
}
}
function _castKeyToAddr(
uint256 _key
) public pure returns (address _addr) {
// _addr = address(uint160(uint256(_key >> 12)));
assembly ("memory-safe") {
_addr := shr(0xc, _key)
}
}
}

View File

@ -1,47 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
abstract contract Constants {
/// Constants
uint256 constant _ROOT_UPDATED_EVENT_SIGNATURE =
0x0b294da292f26e55fd442b5c0164fbb9013036ff00c5cfdde0efd01c1baaf632;
uint256 constant _ALLOWED_ERC20_UPDATED_EVENT_SIGNATURE =
0x5d6e86e5341d57a92c49934296c51542a25015c9b1782a1c2722a940131c3d9a;
uint256 constant _TRUSTED_FORWARDER_UPDATED_EVENT_SIGNATURE =
0xbee55516e29d3969d3cb8eb01351eb3c52d06f9e2435bd5a8bfe3647e185df92;
/// @dev Seller casted to key => Seller's allowlist merkleroot.
/// mapping(uint256 => bytes32) public sellerAllowList;
uint256 constant _SELLER_ALLOWLIST_SLOT_SEED = 0x74dfee70;
/// @dev Tokens allowed to serve as the underlying amount of a deposit.
/// mapping(ERC20 => bool) public allowedERC20s;
uint256 constant _ALLOWED_ERC20_SLOT_SEED = 0xcbc9d1c4;
/// @dev `balance` max. value = 10**26.
/// @dev `pixTarget` keys are restricted to 160 bits.
/// mapping(uint256 => mapping(ERC20 => { `uint256`, `uint96` } )) public sellerBalance;
/// @dev Bits layout:
/// `bytes32` [0...255] := pixTarget
/// `uint96` [0...94] := balance
/// `bool` [95] := valid
/// @dev Value in custom storage slot given by:
/// mstore(0x20, token)
/// mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
/// mstore(0x00, seller)
/// let value := sload(keccak256(0x0c, 0x34)).
uint256 constant _SELLER_BALANCE_SLOT_SEED = 0x739094b1;
/// @dev The bitmask of `sellerBalance` entry.
uint256 constant BITMASK_SB_ENTRY = (1 << 94) - 1;
/// @dev The bit position of `valid` in `sellerBalance`.
uint256 constant BITPOS_VALID = 95;
/// @dev The scalar of BRZ token.
uint256 constant WAD = 1e18;
uint256 constant MAXBALANCE_UPPERBOUND = 1e8 ether;
uint256 constant REPUTATION_LOWERBOUND = 1e2 ether;
uint256 constant LOCKAMOUNT_UPPERBOUND = 1e6 ether;
}

View File

@ -1,25 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import { ERC20 } from "contracts/lib/tokens/ERC20.sol";
library DataTypes {
struct Lock {
uint256 counter;
uint256 expirationBlock;
bytes32 pixTarget;
uint80 amount;
ERC20 token;
address buyerAddress;
address seller;
}
// prettier-ignore
enum LockStatus {
Inexistent, // 0 := Uninitialized Lock.
Active, // 1 := Valid Lock.
Expired, // 2 := Expired Lock.
Released // 3 := Already released Lock.
}
}

View File

@ -1,133 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import { ERC20 } from "contracts/lib/tokens/ERC20.sol";
// prettier-ignore
interface EventAndErrors {
/// Events
/// @dev 0x63d8d7d5e63e9840ec91a12a160d27b7cfab294f6ba070b7359692acfe6b03bf
event DepositAdded(
address indexed seller,
// uint256 depositID,
ERC20 token,
uint256 amount
);
/// @dev 0xca585721b6b442dc9183932f7c84dc2880efb67c4da52cc06873e78971105d49
event ValidSet(
address indexed seller,
ERC20 token,
bool state
);
/// @dev 0x2cd6435b1b961c13f55202979edd0765a809f69a539d8a477436c94c1211e43e
event DepositWithdrawn(
address indexed seller,
ERC20 token,
uint256 amount
);
/// @dev 0x8fb3989f70bd172a37d15b41b015e48ea09d59329638377304a4198cd0c4ea65
event LockAdded(
address indexed buyer,
uint256 indexed lockID,
address seller,
uint256 amount
);
/// @dev 0x364537f14276f2a0ce9905588413f96454cbb8fb2e4f5308389307c1098bede8
event LockReleased(
address indexed buyer,
uint256 lockId,
uint256 amount
);
/// @dev 0x830501e61b8b075e170b22a430e39454bdb12ed3e9620e586430b6ac00079da5
event LockReturned(
address indexed buyer,
uint256 lockId
);
/// @dev 0xeaff4b37086828766ad3268786972c0cd24259d4c87a80f9d3963a3c3d999b0d
event FundsWithdrawn(
address owner,
uint256 amount
);
/// @dev 0x0b294da292f26e55fd442b5c0164fbb9013036ff00c5cfdde0efd01c1baaf632
event RootUpdated(
address indexed seller,
bytes32 indexed merkleRoot
);
/// @dev 0x5d6e86e5341d57a92c49934296c51542a25015c9b1782a1c2722a940131c3d9a
event AllowedERC20Updated(
address indexed token,
bool indexed state
);
/// @dev 0xbee55516e29d3969d3cb8eb01351eb3c52d06f9e2435bd5a8bfe3647e185df92
event TrustedForwarderUpdated(
address indexed forwarder,
bool indexed state
);
/// @dev 0xe127cf589a3879da0156d4a24f43b44f65cfa3570de594806b0bfa2fcf06884f
event ReputationUpdated(address reputation);
/// @dev 0x70fa43ca70216ad905ade86b9e650a691b2ce5a01980d0a81bdd8324141b8511
event LockBlocksUpdated(uint256 blocks);
/// @dev 0x14a422d2412784a5749d03da98921fe468c98577b767851389a9f58ea5a363d7
event ValidSignersUpdated(address[] signers);
/// Errors
/// @dev Only seller could call this function.
/// @dev `msg.sender` and the seller differ.
/// @dev 0x85d1f726
error OnlySeller();
/// @dev Lock not expired or already released.
/// @dev Another lock with same ID is not expired yet.
/// @dev 0xd0404f85
error NotExpired();
/// @dev Loop bounds have overflowed.
/// @dev 0xdfb035c9
error LoopOverflow();
/// @dev Deposit not valid anymore.
/// @dev 0xb2e532de
error InvalidDeposit();
/// @dev Not enough token remaining on deposit.
/// @dev 0x22bbb43c
error NotEnoughTokens();
/// @dev Lock already released or returned.
/// @dev 0x63b4904e
error AlreadyReleased();
/// @dev Transaction already used to unlock payment.
/// @dev 0xf490a6ea
error TxAlreadyUsed();
/// @dev Signer is not a valid signer.
/// @dev 0x815e1d64
error InvalidSigner();
/// @dev Address doesn't exist in a MerkleTree.
/// @dev Address not allowed as relayer.
/// @dev 0x3b8474be
error AddressDenied();
/// @dev Arrays' length don't match.
/// @dev 0xff633a38
error LengthMismatch();
/// @dev No tokens array provided as argument.
/// @dev 0xdf957883
error NoTokens();
/// @dev Token address not allowed to be deposited.
/// @dev 0x1578328e
error TokenDenied();
/// @dev Wished amount to be locked exceeds the limit allowed.
/// @dev 0x1c18f846
error AmountNotAllowed();
/// @dev Reverts when success return value returns false.
/// @dev 0xe10bf1cc
error StaticCallFailed();
/// @dev Reverts on an expired lock.
/// @dev 0xf6fafba0
error LockExpired();
/// @dev 0xce3a3d37
error DecOverflow();
/// @dev 0xf3fb0eb9
error MaxBalExceeded();
/// @dev 0x6a3bc53e
error EmptyPixTarget();
/// @dev 0x87138d5c
error NotInitialized();
}

View File

@ -1,241 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import { ERC2771Context as ERC2771 } from "contracts/lib/metatx/ERC2771Context.sol";
import { ERC20, SafeTransferLib } from "contracts/lib/utils/SafeTransferLib.sol";
import { IReputation } from "contracts/lib/interfaces/IReputation.sol";
import { EventAndErrors } from "contracts/core/EventAndErrors.sol";
import { Constants } from "contracts/core/Constants.sol";
import { Owned } from "contracts/lib/auth/Owned.sol";
abstract contract OwnerSettings is
Constants,
EventAndErrors,
Owned(msg.sender),
ERC2771
{
/// Storage
/// @dev List of valid Bacen signature addresses
/// mapping(uint256 => bool) public validBacenSigners;
/// @dev Value in custom storage slot given by:
/// let value := sload(shl(12, address)).
IReputation public reputation;
/// @dev Default blocks that lock will hold tokens.
uint256 public defaultLockBlocks;
/// Constructor
constructor(
uint256 defaultBlocks,
address[] memory validSigners,
address _reputation,
ERC20[] memory tokens,
bool[] memory tokenStates
) {
setDefaultLockBlocks(defaultBlocks);
setValidSigners(validSigners);
setReputation(IReputation(_reputation));
tokenSettings(tokens, tokenStates);
}
/// Owner Only
function setTrustedFowarders(
address[] memory forwarders,
bool[] memory states
) external onlyOwner {
assembly ("memory-safe") {
// first 32 bytes eq to array's length
let fLen := mload(forwarders)
// halts execution if forwarders.length eq 0
if iszero(fLen) {
invalid()
}
// revert with `LengthMismatch()`
if iszero(eq(fLen, mload(states))) {
mstore(0x00, 0xff633a38)
revert(0x1c, 0x04)
}
let fLoc := add(forwarders, 0x20)
let sLoc := add(states, 0x20)
for {
let end := add(fLoc, shl(5, fLen))
} iszero(eq(fLoc, end)) {
fLoc := add(fLoc, 0x20)
sLoc := add(sLoc, 0x20)
} {
// cache hashmap entry in scratch space
mstore(0x20, trustedForwarders.slot)
mstore(0x00, mload(fLoc))
// let mapSlot := keccak256(0x00, 0x40)
sstore(keccak256(0x00, 0x40), mload(sLoc))
// emit TrustedForwarderUpdated(address, bool)
log3(
0,
0,
_TRUSTED_FORWARDER_UPDATED_EVENT_SIGNATURE,
mload(fLoc),
mload(sLoc)
)
}
}
}
/// @dev Contract's underlying balance withdraw method.
/// @dev Function sighash: 0x5fd8c710.
function withdrawBalance() external onlyOwner {
uint256 balance = address(this).balance;
SafeTransferLib.safeTransferETH(msg.sender, balance);
emit FundsWithdrawn(msg.sender, balance);
}
function setReputation(
IReputation _reputation
) public onlyOwner {
assembly ("memory-safe") {
sstore(reputation.slot, _reputation)
}
emit ReputationUpdated(address(_reputation));
}
function setDefaultLockBlocks(
uint256 _blocks
) public onlyOwner {
assembly ("memory-safe") {
sstore(defaultLockBlocks.slot, _blocks)
}
emit LockBlocksUpdated(_blocks);
}
function setValidSigners(
address[] memory _validSigners
) public onlyOwner {
assembly ("memory-safe") {
let i := add(_validSigners, 0x20)
let end := add(i, shl(0x05, mload(_validSigners)))
for {
/* */
} iszero(returndatasize()) {
/* */
} {
sstore(shl(0xc, mload(i)), true)
i := add(i, 0x20)
if iszero(lt(i, end)) {
break
}
}
}
emit ValidSignersUpdated(_validSigners);
}
function tokenSettings(
ERC20[] memory _tokens,
bool[] memory _states
) public onlyOwner {
/* Yul Impl */
assembly ("memory-safe") {
// first 32 bytes eq to array's length
let tLen := mload(_tokens)
// NoTokens()
if iszero(tLen) {
mstore(0x00, 0xdf957883)
revert(0x1c, 0x04)
}
// LengthMismatch()
if iszero(eq(tLen, mload(_states))) {
mstore(0x00, 0xff633a38)
revert(0x1c, 0x04)
}
let tLoc := add(_tokens, 0x20)
let sLoc := add(_states, 0x20)
for {
let end := add(tLoc, shl(5, tLen))
} iszero(eq(tLoc, end)) {
tLoc := add(tLoc, 0x20)
sLoc := add(sLoc, 0x20)
} {
// cache hashmap entry in scratch space
mstore(0x0c, _ALLOWED_ERC20_SLOT_SEED)
mstore(0x00, mload(tLoc))
// let mapSlot := keccak256(0x0c, 0x20)
sstore(keccak256(0x0c, 0x20), mload(sLoc))
// emit AllowedERC20Updated(address, bool)
log3(
0,
0,
_ALLOWED_ERC20_UPDATED_EVENT_SIGNATURE,
mload(tLoc),
mload(sLoc)
)
}
}
}
/// View FX
function validBacenSigners(
uint256 signer
) public view returns (bool valid) {
assembly ("memory-safe") {
valid := sload(signer)
}
}
function sellerAllowList(
address sellerKey
) public view returns (bytes32 root) {
assembly ("memory-safe") {
mstore(0x0c, _SELLER_ALLOWLIST_SLOT_SEED)
mstore(0x00, sellerKey)
root := sload(keccak256(0x00, 0x20))
}
}
function allowedERC20s(
ERC20 erc20
) public view returns (bool state) {
assembly ("memory-safe") {
mstore(0x0c, _ALLOWED_ERC20_SLOT_SEED)
mstore(0x00, erc20)
state := sload(keccak256(0x0c, 0x20))
}
}
function _limiter(
uint256 _userCredit
) internal view returns (uint256 _spendLimit) {
bytes memory encodedParams = abi.encodeWithSelector(
// IReputation.limiter.selector,
0x4d2b1791,
_userCredit
);
bool success;
assembly ("memory-safe") {
success := staticcall(
// gas
gas(),
// address
sload(reputation.slot),
// argsOffset
add(encodedParams, 0x20),
// argsSize
mload(encodedParams),
// retOffset
0x00,
// retSize
0x20
)
_spendLimit := mload(0x00)
if iszero(success) {
// StaticCallFailed()
mstore(0x00, 0xe10bf1cc)
revert(0x1c, 0x04)
}
}
}
}

View File

@ -1,51 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
error Unauthorized();
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnerUpdated(
address indexed user,
address indexed newOwner
);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
if (msg.sender != owner) revert Unauthorized();
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnerUpdated(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function setOwner(
address newOwner
) public virtual onlyOwner {
owner = newOwner;
emit OwnerUpdated(msg.sender, newOwner);
}
}

View File

@ -1,8 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
interface IReputation {
function limiter(
uint256 _userCredit
) external pure returns (uint256 _spendLimit);
}

View File

@ -1,87 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/// @author OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Context.sol)
/// @dev Provides information about the current execution context, including the
/// sender of the transaction and its data. While these are generally available
/// via msg.sender and msg.data, they should not be accessed in such a direct
/// manner, since when dealing with meta-transactions the account sending and
/// paying for execution may not be the actual sender (as far as an application
/// is concerned).
///
/// This contract is only required for intermediate, library-like contracts.
abstract contract Context {
function _msgSender()
internal
view
virtual
returns (address)
{
return msg.sender;
}
function _msgData()
internal
view
virtual
returns (bytes calldata)
{
return msg.data;
}
}
/// @author Modified from OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/metatx/ERC2771Context.sol
/// @dev Context variant with ERC2771 support.
abstract contract ERC2771Context is Context {
// address private immutable _trustedForwarder;
mapping(address => bool) public trustedForwarders;
/// @custom:oz-upgrades-unsafe-allow constructor
// constructor(address trustedForwarder) {
// _trustedForwarder = trustedForwarder;
// }
function _msgSender()
internal
view
virtual
override
returns (address sender)
{
if (trustedForwarders[msg.sender]) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
/// @solidity memory-safe-assembly
assembly {
sender := shr(
96,
calldataload(sub(calldatasize(), 20))
)
}
} else {
return super._msgSender();
}
}
function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
return trustedForwarders[forwarder];
}
function _msgData()
internal
view
virtual
override
returns (bytes calldata)
{
if (isTrustedForwarder(msg.sender)) {
return msg.data[:msg.data.length - 20];
} else {
return super._msgData();
}
}
}

View File

@ -1,22 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import { ERC20 } from "../tokens/ERC20.sol";
contract MockToken is ERC20 {
constructor(uint256 supply) ERC20("MockBRL", "MBRL", 18) {
_mint(msg.sender, supply);
}
function mint(
address[] memory to,
uint256 value
) public virtual {
uint256 len = to.length;
uint256 j;
while (j < len) {
_mint(to[j], value);
++j;
}
}
}

View File

@ -1,250 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(
address indexed from,
address indexed to,
uint256 amount
);
event Approval(
address indexed owner,
address indexed spender,
uint256 amount
);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256))
public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(
address spender,
uint256 amount
) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(
address to,
uint256 amount
) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max)
allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(
deadline >= block.timestamp,
"PERMIT_DEADLINE_EXPIRED"
);
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(
recoveredAddress != address(0) &&
recoveredAddress == owner,
"INVALID_SIGNER"
);
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR()
public
view
virtual
returns (bytes32)
{
return
block.chainid == INITIAL_CHAIN_ID
? INITIAL_DOMAIN_SEPARATOR
: computeDomainSeparator();
}
function computeDomainSeparator()
internal
view
virtual
returns (bytes32)
{
return
keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(
address to,
uint256 amount
) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(
address from,
uint256 amount
) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}

View File

@ -1,95 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
library ECDSA {
/// @dev The signature is invalid.
error InvalidSignature();
/// @dev The number which `s` must not exceed in order for
/// the signature to be non-malleable.
bytes32 private constant _MALLEABILITY_THRESHOLD =
0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the `signature`.
///
/// This function does NOT accept EIP-2098 short form signatures.
/// Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
/// short form signatures instead.
function recoverCalldata(
bytes32 hash,
bytes calldata signature
) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
// Copy the free memory pointer so that we can restore it later.
let m := mload(0x40)
// Directly copy `r` and `s` from the calldata.
calldatacopy(0x40, signature.offset, 0x40)
// Store the `hash` in the scratch space.
mstore(0x00, hash)
// Compute `v` and store it in the scratch space.
mstore(
0x20,
byte(
0,
calldataload(add(signature.offset, 0x40))
)
)
pop(
staticcall(
gas(), // Amount of gas left for the transaction.
and(
// If the signature is exactly 65 bytes in length.
eq(signature.length, 65),
// If `s` in lower half order, such that the signature is not malleable.
lt(
mload(0x60),
add(_MALLEABILITY_THRESHOLD, 1)
)
), // Address of `ecrecover`.
0x00, // Start of input.
0x80, // Size of input.
0x00, // Start of output.
0x20 // Size of output.
)
)
result := mload(0x00)
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
// Store the function selector of `InvalidSignature()`.
mstore(0x00, 0x8baa579f)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the zero slot.
mstore(0x60, 0)
// Restore the free memory pointer.
mstore(0x40, m)
}
}
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(
bytes32 hash
) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// Store into scratch space for keccak256.
mstore(0x20, hash)
mstore(
0x00,
"\x00\x00\x00\x00\x19Ethereum Signed Message:\n32"
)
// 0x40 - 0x04 = 0x3c
result := keccak256(0x04, 0x3c)
}
}
}

View File

@ -1,58 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady
/// (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate
/// (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verify(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
if proof.length {
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(
proof.offset,
shl(5, proof.length)
)
// Initialize `offset` to the offset of `proof` in the calldata.
let offset := proof.offset
// Iterate over proof elements to compute root hash.
for {
} 1 {
} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(
5,
gt(leaf, calldataload(offset))
)
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(
xor(scratch, 0x20),
calldataload(offset)
)
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) {
break
}
}
}
isValid := eq(leaf, root)
}
}
}

View File

@ -1,80 +0,0 @@
// 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

@ -1,34 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
/// @notice Reentrancy protection for smart contracts.
/// @author z0r0z.eth
/// @author Modified from Seaport
/// (https://github.com/ProjectOpenSea/seaport/blob/main/contracts/lib/ReentrancyGuard.sol)
/// @author Modified from Solmate
/// (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
error Reentrancy();
uint256 private guard = 1;
modifier nonReentrant() virtual {
setReentrancyGuard();
_;
clearReentrancyGuard();
}
/// @dev Check guard sentinel value and set it.
function setReentrancyGuard() internal virtual {
if (guard == 2) revert Reentrancy();
guard = 2;
}
/// @dev Unset sentinel value.
function clearReentrancyGuard() internal virtual {
guard = 1;
}
}

View File

@ -1,143 +0,0 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
import { ERC20 } from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
/// @dev Sends `amount` (in wei) ETH to `to`.
/// Reverts upon failure.
function safeTransferETH(
address to,
uint256 amount
) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and check if it succeeded or not.
if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
// Store the function selector of `transferFrom(address,address,uint256)`.
mstore(0x0c, 0x23b872dd000000000000000000000000)
if iszero(
and(
// The arguments of `and` are evaluated from right to left.
// 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(
eq(mload(0x00), 1),
iszero(returndatasize())
),
call(
gas(),
token,
0,
0x1c,
0x64,
0x00,
0x20
)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
// Store the function selector of `transfer(address,uint256)`.
mstore(0x00, 0xa9059cbb000000000000000000000000)
if iszero(
and(
// The arguments of `and` are evaluated from right to left.
// 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(
eq(mload(0x00), 1),
iszero(returndatasize())
),
call(
gas(),
token,
0,
0x10,
0x44,
0x00,
0x20
)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten.
mstore(0x34, 0)
}
}
}

11
contracts/mockToken.sol Normal file
View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockToken is ERC20 {
constructor(uint256 supply) ERC20("MockBRL", "MBRL") {
_mint(msg.sender, supply);
}
}

View File

@ -1,554 +1,183 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
pragma solidity ^0.8.9;
/// ______ __
/// .-----.|__ |.-----.|__|.--.--.
/// | _ || __|| _ || ||_ _|
/// | __||______|| __||__||__.__|
/// |__| |__|
///
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import { OwnerSettings, ERC20, SafeTransferLib } from "contracts/core/OwnerSettings.sol";
import { BaseUtils } from "contracts/core/BaseUtils.sol";
import { DataTypes as DT } from "contracts/core/DataTypes.sol";
contract P2PIX is Ownable {
event DepositAdded(address indexed seller, bytes32 depositID, address token, uint256 premium, uint256 amount);
event DepositClosed(address indexed seller, bytes32 depositID);
event DepositWithdrawn(address indexed seller, bytes32 depositID, uint256 amount);
event LockAdded(address indexed buyer, bytes32 indexed lockID, bytes32 depositID, uint256 amount);
event LockReleased(address indexed buyer, bytes32 lockId);
event LockReturned(address indexed buyer, bytes32 lockId);
// Events
event PremiumsWithdrawn(address owner, uint256 amount);
contract P2PIX is BaseUtils {
// solhint-disable use-forbidden-name
// solhint-disable no-inline-assembly
// solhint-disable no-empty-blocks
struct Deposit {
address seller;
address token; // ERC20 stable token address
uint256 remaining; // Remaining tokens available
uint256 premium; // Premium paid in ETH for priority
bool valid; // Could be invalidated by the seller
string pixTarget; // The PIX account for the seller receive transactions
}
using DT for DT.Lock;
using DT for DT.LockStatus;
struct Lock {
bytes32 depositID;
address targetAddress; // Where goes the tokens when validated
address relayerAddress; // Relayer address that facilitated this transaction
uint256 relayerPremium; // Amount to be paid for relayer
uint256 amount; // Amount to be tranfered via PIX
uint256 expirationBlock; // If not paid at this block will be expired
}
uint256 public lockCounter;
// Default blocks that lock will hold tokens
uint256 public defaultLockBlocks;
// List of valid Bacen signature addresses
mapping(address => bool) public validBacenSigners;
/// @dev List of Locks.
mapping(uint256 => DT.Lock) public mapLocks;
/// @dev Stores an relayer's last computed credit.
mapping(uint256 => uint256) public userRecord;
// Seller list of deposits
mapping(bytes32 => Deposit) mapDeposits;
// List of Locks
mapping(bytes32 => Lock) mapLocks;
// List of Pix transactions already signed
mapping(bytes32 => bool) usedTransactions;
constructor(
uint256 defaultBlocks,
address[] memory validSigners,
address _reputation,
ERC20[] memory tokens,
bool[] memory tokenStates
)
OwnerSettings(
defaultBlocks,
validSigners,
_reputation,
tokens,
tokenStates
)
payable {/* */}
modifier onlySeller(bytes32 depositID) {
require(mapDeposits[depositID].seller == msg.sender, "P2PIX: Only seller could call this function.");
_;
}
/// @notice Creates a deposit order based on a seller's
/// offer of an amount of ERC20 tokens.
/// @notice Seller needs to send his tokens to the P2PIX smart contract.
/// @param pixTarget Pix key destination provided by the offer's seller.
/// @param allowlistRoot Optional allow list merkleRoot update `bytes32` value.
/// as the deposit identifier.
/// @dev Function sighash: 0x5e918943
constructor (uint256 defaultBlocks, address[] memory validSigners) Ownable() {
defaultLockBlocks = defaultBlocks;
for (uint8 i = 0; i < validSigners.length; i++){
validBacenSigners[validSigners[i]] = true;
}
}
// Vendedor precisa mandar token para o smart contract + chave PIX destino. Retorna um DepositID.
function deposit(
string calldata pixTarget,
bytes32 allowlistRoot,
ERC20 token,
uint96 amount,
bool valid
) public nonReentrant {
if (bytes(pixTarget).length == 0) revert EmptyPixTarget();
if (!allowedERC20s(token)) revert TokenDenied();
uint256 _sellerBalance = __sellerBalance(msg.sender, token);
uint256 currBal = _sellerBalance & BITMASK_SB_ENTRY;
uint256 _newBal = uint256(currBal + amount);
if (_newBal > MAXBALANCE_UPPERBOUND)
revert MaxBalExceeded();
if (allowlistRoot != 0) {
setRoot(msg.sender, allowlistRoot);
address token,
uint256 amount,
string calldata pixTarget
) public payable returns (bytes32 depositID){
depositID = keccak256(abi.encodePacked(pixTarget, amount));
require(!mapDeposits[depositID].valid, 'P2PIX: Deposit already exist and it is still valid');
IERC20 t = IERC20(token);
t.transferFrom(msg.sender, address(this), amount);
Deposit memory d = Deposit(msg.sender, token, amount, msg.value, true, pixTarget);
mapDeposits[depositID] = d;
emit DepositAdded(msg.sender, depositID, token, msg.value, amount);
}
bytes32 pixTargetCasted = getStr(pixTarget);
uint256 validCasted = _castBool(valid);
_setSellerBalance(
msg.sender,
token,
(_newBal | (validCasted << BITPOS_VALID)),
pixTargetCasted
);
SafeTransferLib.safeTransferFrom(
token,
msg.sender,
address(this),
amount
);
emit DepositAdded(msg.sender, token, amount);
// Vendedor pode invalidar da ordem de venda impedindo novos locks na mesma (isso não afeta nenhum lock que esteja ativo).
function cancelDeposit(bytes32 depositID) public onlySeller(depositID) {
mapDeposits[depositID].valid = false;
emit DepositClosed(mapDeposits[depositID].seller, depositID);
}
/// @notice Enables seller to invalidate future
/// locks made to his/her token offering order.
/// @notice This function does not affect any ongoing active locks.
/// @dev Function sighash: 0x6d82d9e0
function setValidState(ERC20 token, bool state) public {
uint256 _sellerBalance = __sellerBalance(msg.sender, token);
if (_sellerBalance != 0) {
uint256 _valid = _castBool(state);
_sellerBalance =
(_sellerBalance & BITMASK_SB_ENTRY) |
(_valid << BITPOS_VALID);
_setValidState(msg.sender, token, _sellerBalance);
emit ValidSet(msg.sender, token, state);
} else revert NotInitialized();
}
/// @notice Public method designed to lock an remaining amount of
/// the deposit order of a seller.
/// @notice Transaction forwarding must leave `merkleProof` empty;
/// otherwise, the trustedForwarder must be previously added
/// to a seller whitelist.
/// @notice This method can be performed either by:
/// - An user allowed via the seller's allowlist;
/// - An user with enough userRecord to lock the wished amount;
/// @notice There can only exist a lock per each `amount` partitioned
/// from the total `remaining` value.
/// @notice Locks can only be performed in valid orders.
/// @param amount The deposit's remaining amount wished to be locked.
/// @param merkleProof Provided as a pass if the `msg.sender` is in the
/// seller's allowlist; Left empty otherwise;
/// @param expiredLocks An array of identifiers to be
/// provided so to unexpire locks using this transaction gas push.
/// @return lockID The lock identifier.
/// @dev Function sighash: 0xdc43221c
// Relayer interaje adicionando um lock na ordem de venda.
// O lock precisa incluir address do comprador + address do relayer + reembolso/premio relayer + valor.
// ** poder ter um lock em aberto para cada (ordem de venda, valor)**.
// pode fazer lock de ordens que não estão invalidadas(Passo 5).
// Essa etapa pode ser feita pelo vendedor conjuntamente com a parte 1.
// Retorna um LockID.
function lock(
address seller,
ERC20 token,
uint80 amount,
bytes32[] calldata merkleProof,
uint256[] calldata expiredLocks
) public nonReentrant returns (uint256 lockID) {
bytes32 depositID,
address targetAddress,
address relayerAddress,
uint256 relayerPremium,
uint256 amount,
bytes32[] calldata expiredLocks
) public returns (bytes32 lockID){
unlockExpired(expiredLocks);
if (!getValid(seller, token)) revert InvalidDeposit();
uint256 bal = getBalance(seller, token);
if (bal < amount) revert NotEnoughTokens();
unchecked {
lockID = ++lockCounter;
}
if (
mapLocks[lockID].expirationBlock >= block.number
) revert NotExpired();
bytes32 _pixTarget = getPixTarget(seller, token);
// transaction forwarding must leave `merkleProof` empty;
// otherwise, the trustedForwarder must be previously added
// to a seller whitelist.
if (merkleProof.length != 0) {
_merkleVerify( merkleProof, sellerAllowList(seller), _msgSender());
} else if ( amount > REPUTATION_LOWERBOUND && msg.sender == _msgSender() ) {
uint256 spendLimit; uint256 userCredit =
userRecord[_castAddrToKey(_msgSender())];
(spendLimit) = _limiter(userCredit / WAD);
if (
amount > (spendLimit * WAD) ||
amount > LOCKAMOUNT_UPPERBOUND
) revert AmountNotAllowed();
}
DT.Lock memory l = DT.Lock(
lockID,
(block.number + defaultLockBlocks),
_pixTarget,
amount,
token,
_msgSender(),
seller
Deposit storage d = mapDeposits[depositID];
require(d.valid, "P2PIX: Deposit not valid anymore");
require(d.remaining >= amount, "P2PIX: Not enough token remaining on deposit");
lockID = keccak256(abi.encodePacked(depositID, amount, targetAddress));
require(
mapLocks[lockID].expirationBlock < block.number,
"P2PIX: Another lock with same ID is not expired yet"
);
_addLock(bal, l);
Lock memory l = Lock(
depositID,
targetAddress,
relayerAddress,
relayerPremium,
amount,
block.number+defaultLockBlocks
);
mapLocks[lockID] = l;
d.remaining -= amount;
emit LockAdded(targetAddress, lockID, depositID, amount);
}
/// @notice Lock release method that liquidate lock
/// orders and distributes relayer fees.
/// @notice This method can be called by any public actor
/// as long the signature provided is valid.
/// @notice `relayerPremium` gets splitted equaly
/// if relayer addresses differ.
/// @notice If the `msg.sender` of this method and `l.relayerAddress` are the same,
/// `msg.sender` accrues both l.amount and l.relayerPremium as userRecord credit.
/// In case of they differing:
/// - `lock` caller gets accrued with `l.amount` as userRecord credit;
/// - `release` caller gets accrued with `l.relayerPremium` as userRecord credit;
/// @dev Function sighash: 0x11fc7f9a
// Relayer interage com o smart contract, colocando no calldata o comprovante do PIX realizado.
// Smart contract valida o comprovante, manda os tokens para o endereço do pagador, e reembolsa o custo do gás para o endereço do relayer especificado na parte (2).
function release(
uint256 lockID,
bytes32 pixTimestamp,
bytes calldata signature
) public nonReentrant {
DT.Lock storage l = mapLocks[lockID];
if (l.amount == 0) revert AlreadyReleased();
if (l.expirationBlock < block.number)
revert LockExpired();
bytes32 message = keccak256(
abi.encodePacked(
l.pixTarget,
bytes32 lockID,
uint256 pixTimestamp,
bytes32 r,
bytes32 s,
uint8 v
) public {
// TODO **Prevenir que um Pix não relacionado ao APP seja usado pois tem o mesmo destino
Lock storage l = mapLocks[lockID];
require(l.expirationBlock > block.number && l.amount > 0, "P2PIX: Lock already released or returned");
Deposit storage d = mapDeposits[l.depositID];
bytes32 message = keccak256(abi.encodePacked(
mapDeposits[l.depositID].pixTarget,
l.amount,
pixTimestamp
)
);
_signerCheck(message, signature);
ERC20 t = ERC20(l.token);
// We cache lockAmount value before zeroing it out.
uint256 lockAmount = l.amount;
));
bytes32 messageDigest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", message));
require(!usedTransactions[message], "P2PIX: Transaction already used to unlock payment");
address signer = ecrecover(messageDigest, v, r, s);
require(validBacenSigners[signer], "P2PIX: Signer is not a valid signer");
IERC20 t = IERC20(d.token);
t.transfer(l.targetAddress, l.amount-l.relayerPremium);
if (l.relayerPremium > 0) t.transfer(l.relayerAddress, l.relayerPremium);
l.amount = 0;
l.expirationBlock = 0;
_setUsedTransactions(message);
if (_msgSender() == msg.sender) {
if (msg.sender != l.buyerAddress) {
userRecord[_castAddrToKey(msg.sender)] += (lockAmount >> 1);
userRecord[_castAddrToKey(l.buyerAddress)] += (lockAmount >> 1);
} else {
userRecord[_castAddrToKey(msg.sender)] += lockAmount;
}}
SafeTransferLib.safeTransfer(
t,
l.buyerAddress,
lockAmount
);
emit LockReleased(l.buyerAddress, lockID, lockAmount);
usedTransactions[message] = true;
emit LockReleased(l.targetAddress, lockID);
}
/// @notice Unlocks expired locks.
/// @notice Triggered in the callgraph by both `lock` and `withdraw` functions.
/// @notice This method can also have any public actor as its `tx.origin`.
/// @notice For each successfull unexpired lock recovered,
/// `userRecord[_castAddrToKey(l.relayerAddress)]` is decreased by half of its value.
/// @dev Function sighash: 0xb0983d39
function unlockExpired(uint256[] calldata lockIDs)
public
{
uint256 i;
// Unlock expired locks
function unlockExpired(bytes32[] calldata lockIDs) public {
uint256 locksSize = lockIDs.length;
for (i; i < locksSize; ) {
DT.Lock storage l = mapLocks[lockIDs[i]];
_notExpired(l);
uint256 _sellerBalance =
__sellerBalance(l.seller, l.token) & BITMASK_SB_ENTRY;
if ((_sellerBalance + l.amount) > MAXBALANCE_UPPERBOUND)
revert MaxBalExceeded();
_addSellerBalance(l.seller, l.token, l.amount);
for (uint16 i = 0; i < locksSize; i++){
Lock storage l = mapLocks[lockIDs[i]];
require(l.expirationBlock < block.number && l.amount > 0, "P2PIX: Lock not expired or already released");
mapDeposits[l.depositID].remaining += l.amount;
l.amount = 0;
uint256 userKey = _castAddrToKey(
l.buyerAddress
);
uint256 _newUserRecord = (userRecord[userKey] >>
1);
if (_newUserRecord <= REPUTATION_LOWERBOUND) {
userRecord[userKey] = REPUTATION_LOWERBOUND;
} else {
userRecord[userKey] = _newUserRecord;
}
emit LockReturned(l.buyerAddress, lockIDs[i]);
unchecked {
++i;
emit LockReturned(l.targetAddress, lockIDs[i]);
}
}
assembly ("memory-safe") {
if lt(i, locksSize) {
// LoopOverflow()
mstore(0x00, 0xdfb035c9)
revert(0x1c, 0x04)
}
}
}
/// @notice Seller's expired deposit fund sweeper.
/// @notice A seller may use this method to recover
/// tokens from expired deposits.
/// @dev Function sighash: 0xfb8c5ef0
// Após os locks expirarem, vendedor pode interagir c/ o contrato e recuperar os tokens de um depósito específico.
function withdraw(
ERC20 token,
uint256 amount,
uint256[] calldata expiredLocks
) public nonReentrant {
bytes32 depositID,
bytes32[] calldata expiredLocks
) public onlySeller(depositID) {
unlockExpired(expiredLocks);
if (getValid(msg.sender, token))
setValidState(token, false);
_decBal(
(__sellerBalance(msg.sender, token) & BITMASK_SB_ENTRY),
amount,
token,
msg.sender
);
// safeTransfer tokens to seller
SafeTransferLib.safeTransfer(
token,
msg.sender,
amount
);
emit DepositWithdrawn(
msg.sender,
token,
amount
);
Deposit storage d = mapDeposits[depositID];
if (d.valid) cancelDeposit(depositID);
IERC20 token = IERC20(d.token);
// Withdraw remaining tokens from mapDeposit[depositID]
token.transfer(d.seller, d.remaining);
uint256 amount = d.remaining;
d.remaining = 0;
emit DepositWithdrawn(msg.sender, depositID, amount);
}
function setRoot(address addr, bytes32 merkleroot)
public
{
assembly ("memory-safe") {
// if (addr != msg.sender)
if iszero(eq(addr, caller())) {
// revert OnlySeller()
mstore(0x00, 0x85d1f726)
revert(0x1c, 0x04)
}
// sets root to SellerAllowlist's storage slot
mstore(0x0c, _SELLER_ALLOWLIST_SLOT_SEED)
mstore(0x00, addr)
sstore(keccak256(0x00, 0x20), merkleroot)
// emit RootUpdated(addr, merkleroot);
log3(
0,
0,
_ROOT_UPDATED_EVENT_SIGNATURE,
addr,
merkleroot
)
}
// O dono do contrato pode sacar os premiums pagos
function withdrawPremiums() external onlyOwner {
uint256 balance = address(this).balance;
payable(msg.sender).transfer(balance);
emit PremiumsWithdrawn(msg.sender, balance);
}
// solhint-disable-next-line no-empty-blocks
receive() external payable {}
/// @notice Private view auxiliar logic that reverts
/// on a not expired lock passed as argument of the function.
/// @dev Called exclusively by the `unlockExpired` method.
function _notExpired(DT.Lock storage _l) private view {
if (_l.expirationBlock > block.number)
revert NotExpired();
if (_l.amount == 0) revert AlreadyReleased();
}
function _addLock(
uint256 _bal,
DT.Lock memory _l
) internal {
mapLocks[_l.counter] = _l;
_decBal(_bal, _l.amount, ERC20(_l.token), _l.seller);
emit LockAdded(
_l.buyerAddress,
_l.counter,
_l.seller,
_l.amount
);
}
function _decBal(
uint256 _bal,
uint256 _amount,
ERC20 _t,
address _k
) private {
assembly ("memory-safe") {
if iszero(
iszero(
or(
iszero(_bal),
gt(sub(_bal, _amount), _bal)
)
)
) {
// DecOverflow()
mstore(0x00, 0xce3a3d37)
revert(0x1c, 0x04)
}
}
// we can directly dec from packed uint entry value
_decSellerBalance(_k,_t, _amount);
}
function getBalance(address seller, ERC20 token)
public
view
returns (uint256 bal)
{
assembly ("memory-safe") {
for {
/* */
} iszero(returndatasize()) {
/* */
} {
mstore(0x20, token)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, seller)
bal := and(
BITMASK_SB_ENTRY,
sload(add(keccak256(0x0c, 0x34), 0x01))
)
break
}
}
}
function getValid(address seller, ERC20 token)
public
view
returns (bool valid)
{
assembly ("memory-safe") {
for {
/* */
} iszero(returndatasize()) {
/* */
} {
mstore(0x20, token)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, seller)
valid := and(
BITMASK_SB_ENTRY,
shr(
BITPOS_VALID,
sload(add(keccak256(0x0c, 0x34), 0x01))
)
)
break
}
}
}
function getPixTarget(address seller, ERC20 token)
public
view
returns (bytes32 pixTarget)
{
assembly ("memory-safe") {
for {
/* */
} iszero(returndatasize()) {
/* */
} {
mstore(0x20, token)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, seller)
pixTarget := sload(keccak256(0x0c, 0x34))
break
}
}
}
function getPixTargetString(address seller, ERC20 token) public view returns (string memory pixTarget) {
bytes32 _pixEnc = getPixTarget(seller, token);
pixTarget = string(abi.encodePacked(_pixEnc));
}
function getBalances(
address[] memory sellers,
ERC20 token
) external view returns (uint256[] memory) {
uint256 j;
uint256 len = sellers.length;
uint256[] memory balances = new uint256[](len);
while (j < len) {
uint256 bal = getBalance(sellers[j], token);
balances[j] = bal;
unchecked {
++j;
}
}
return balances;
}
/// @notice External getter that returns the status of a lockIDs array.
/// @notice 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]].seller == address(0)) {
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);
}
}

View File

@ -1,8 +0,0 @@
{
"signers": [
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
],
"p2pix": "0x2414817FF64A114d91eCFA16a834d3fCf69103d4",
"token": "0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00"
}

View File

@ -3,6 +3,6 @@
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
],
"p2pix": "0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29",
"token": "0xD38D6367f452D097ccBfDe4490b7de570B6A72Db"
"p2pix": "0x5FbDB2315678afecb367f032d93F642f64180aa3",
"token": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"
}

View File

@ -1,8 +0,0 @@
{
"signers": [
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
],
"p2pix": "0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00",
"token": "0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29"
}

View File

@ -1,8 +0,0 @@
{
"signers": [
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
],
"token": "0xfE841c74250e57640390f46d914C88d22C51e82e",
"p2pix": "0x98ba35eb14b38D6Aa709338283af3e922476dE34"
}

View File

@ -1,8 +0,0 @@
{
"signers": [
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
],
"p2pix": "0xb7cD135F5eFD9760981e02E2a898790b688939fe",
"token": "0x3eBE67A2C7bdB2081CBd34ba3281E90377462289"
}

View File

@ -1,41 +0,0 @@
# Solidity API
## Reputation
### maxLimit
```solidity
uint256 maxLimit
```
_Asymptote numerator constant value for the `limiter` fx._
### magicValue
```solidity
uint256 magicValue
```
_Denominator's constant operand for the `limiter` fx._
### constructor
```solidity
constructor() public payable
```
### limiter
```solidity
function limiter(uint256 _userCredit) external pure returns (uint256 _spendLimit)
```
### sqrt
```solidity
function sqrt(uint256 x) internal pure returns (uint256 z)
```
Taken from Solmate's FixedPointMathLib.
(https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)

View File

@ -1,957 +0,0 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="841pt" height="1998pt" viewBox="0.00 0.00 841.22 1998.00">
<g id="graph0" class="graph" transform="translate(4,1994) scale(1)" data-name="G">
<polygon fill="#2e3e56" stroke="none" points="-4,4 -4,-1994 837.22,-1994 837.22,4 -4,4" style=""/>
<g id="clust1" class="cluster" data-name="clusterP2PIX">
<path fill="#445773" stroke="#445773" d="M20,-286C20,-286 813.22,-286 813.22,-286 819.22,-286 825.22,-292 825.22,-298 825.22,-298 825.22,-1833 825.22,-1833 825.22,-1839 819.22,-1845 813.22,-1845 813.22,-1845 20,-1845 20,-1845 14,-1845 8,-1839 8,-1833 8,-1833 8,-298 8,-298 8,-292 14,-286 20,-286" style=""/>
<text text-anchor="middle" x="416.61" y="-1828.4" font-family="Times,serif" font-size="14.00" fill="#f0f0f0" style="">P2PIX</text>
</g>
<g id="clust2" class="cluster" data-name="clusterSafeTransferLib">
<path fill="#3b4b63" stroke="#e8726d" stroke-dasharray="5,2" d="M456.84,-8C456.84,-8 609.27,-8 609.27,-8 615.27,-8 621.27,-14 621.27,-20 621.27,-20 621.27,-181 621.27,-181 621.27,-187 615.27,-193 609.27,-193 609.27,-193 456.84,-193 456.84,-193 450.84,-193 444.84,-187 444.84,-181 444.84,-181 444.84,-20 444.84,-20 444.84,-14 450.84,-8 456.84,-8" style=""/>
<text text-anchor="middle" x="533.05" y="-176.4" font-family="Times,serif" font-size="14.00" fill="#f0f0f0" style="">SafeTransferLib</text>
</g>
<g id="clust3" class="cluster" data-name="clusterDT">
<path fill="#3b4b63" stroke="#e8726d" stroke-dasharray="5,2" d="M278.1,-171C278.1,-171 331.86,-171 331.86,-171 337.86,-171 343.86,-177 343.86,-183 343.86,-183 343.86,-236 343.86,-236 343.86,-242 337.86,-248 331.86,-248 331.86,-248 278.1,-248 278.1,-248 272.1,-248 266.1,-242 266.1,-236 266.1,-236 266.1,-183 266.1,-183 266.1,-177 272.1,-171 278.1,-171" style=""/>
<text text-anchor="middle" x="304.98" y="-231.4" font-family="Times,serif" font-size="14.00" fill="#f0f0f0" style="">DT</text>
</g>
<g id="clust4" class="cluster" data-name="clusterMerkle">
<path fill="#3b4b63" stroke="#e8726d" stroke-dasharray="5,2" d="M502.96,-201C502.96,-201 563.15,-201 563.15,-201 569.15,-201 575.15,-207 575.15,-213 575.15,-213 575.15,-266 575.15,-266 575.15,-272 569.15,-278 563.15,-278 563.15,-278 502.96,-278 502.96,-278 496.96,-278 490.96,-272 490.96,-266 490.96,-266 490.96,-213 490.96,-213 490.96,-207 496.96,-201 502.96,-201" style=""/>
<text text-anchor="middle" x="533.05" y="-261.4" font-family="Times,serif" font-size="14.00" fill="#f0f0f0" style="">Merkle</text>
</g>
<g id="clust5" class="cluster" data-name="cluster_01">
<polygon fill="#2e3e56" stroke="black" points="20.54,-1853 20.54,-1982 339.98,-1982 339.98,-1853 20.54,-1853" style=""/>
<text text-anchor="middle" x="180.26" y="-1965.4" font-family="Times,serif" font-size="14.00" style="">Legend</text>
</g>
<!-- P2PIX.&lt;Constructor&gt; -->
<g id="node1" class="node" pointer-events="visible" data-name="P2PIX.&lt;Constructor&gt;">
<ellipse fill="#ff9797" stroke="brown" stroke-width="3" cx="93.54" cy="-1713" rx="67.55" ry="18" style=""/>
<text text-anchor="middle" x="93.54" y="-1708.8" font-family="Times,serif" font-size="14.00" style="">&lt;Constructor&gt;</text>
</g>
<!-- P2PIX.setReputation -->
<g id="node10" class="node" pointer-events="visible" data-name="P2PIX.setReputation">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1578" rx="64.28" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1573.8" font-family="Times,serif" font-size="14.00" style="">setReputation</text>
</g>
<!-- P2PIX.&lt;Constructor&gt;&#45;&gt;P2PIX.setReputation -->
<g id="edge2" class="edge" data-name="P2PIX.&lt;Constructor&gt;-&gt;P2PIX.setReputation">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M109.19,-1694.04C128.87,-1669.85 166.15,-1628.34 207.08,-1605 216.35,-1599.71 226.71,-1595.41 237.12,-1591.93" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="237.86,-1595.36 246.4,-1589.09 235.81,-1588.67 237.86,-1595.36" style=""/>
</g>
<!-- P2PIX.setDefaultLockBlocks -->
<g id="node11" class="node" pointer-events="visible" data-name="P2PIX.setDefaultLockBlocks">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1740" rx="97.28" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1735.8" font-family="Times,serif" font-size="14.00" style="">setDefaultLockBlocks</text>
</g>
<!-- P2PIX.&lt;Constructor&gt;&#45;&gt;P2PIX.setDefaultLockBlocks -->
<g id="edge1" class="edge" data-name="P2PIX.&lt;Constructor&gt;-&gt;P2PIX.setDefaultLockBlocks">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M156.25,-1720.95C172.93,-1723.11 191.38,-1725.48 209.42,-1727.81" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="208.77,-1731.25 219.13,-1729.06 209.66,-1724.31 208.77,-1731.25" style=""/>
</g>
<!-- P2PIX.setValidSigners -->
<g id="node12" class="node" pointer-events="visible" data-name="P2PIX.setValidSigners">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1686" rx="72.38" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1681.8" font-family="Times,serif" font-size="14.00" style="">setValidSigners</text>
</g>
<!-- P2PIX.&lt;Constructor&gt;&#45;&gt;P2PIX.setValidSigners -->
<g id="edge3" class="edge" data-name="P2PIX.&lt;Constructor&gt;-&gt;P2PIX.setValidSigners">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M156.25,-1705.05C178.03,-1702.24 202.83,-1699.04 225.8,-1696.08" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="226.24,-1699.55 235.71,-1694.8 225.34,-1692.61 226.24,-1699.55" style=""/>
</g>
<!-- P2PIX.tokenSettings -->
<g id="node13" class="node" pointer-events="visible" data-name="P2PIX.tokenSettings">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1794" rx="63.72" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1789.8" font-family="Times,serif" font-size="14.00" style="">tokenSettings</text>
</g>
<!-- P2PIX.&lt;Constructor&gt;&#45;&gt;P2PIX.tokenSettings -->
<g id="edge4" class="edge" data-name="P2PIX.&lt;Constructor&gt;-&gt;P2PIX.tokenSettings">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M126.39,-1730.14C148.57,-1741.61 179.09,-1756.48 207.08,-1767 218.13,-1771.15 230.09,-1774.99 241.72,-1778.4" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="240.66,-1781.73 251.23,-1781.09 242.57,-1775 240.66,-1781.73" style=""/>
</g>
<!-- P2PIX.deposit -->
<g id="node2" class="node" pointer-events="visible" data-name="P2PIX.deposit">
<ellipse fill="#ff9797" stroke="#ff9797" stroke-width="3" cx="93.54" cy="-1315" rx="38.86" ry="18" style=""/>
<text text-anchor="middle" x="93.54" y="-1310.8" font-family="Times,serif" font-size="14.00" style="">deposit</text>
</g>
<!-- P2PIX.setRoot -->
<g id="node8" class="node" pointer-events="visible" data-name="P2PIX.setRoot">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1632" rx="40.59" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1627.8" font-family="Times,serif" font-size="14.00" style="">setRoot</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.setRoot -->
<g id="edge11" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.setRoot">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M96.19,-1334.18C101.68,-1385.96 124.04,-1529.75 207.08,-1605 219.23,-1616.01 235.51,-1622.58 251.24,-1626.48" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="250.19,-1629.83 260.7,-1628.45 251.62,-1622.98 250.19,-1629.83" style=""/>
</g>
<!-- P2PIX._castToUint -->
<g id="node19" class="node" pointer-events="visible" data-name="P2PIX._castToUint">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1524" rx="58.55" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1519.8" font-family="Times,serif" font-size="14.00" style="">_castToUint</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX._castToUint -->
<g id="edge12" class="edge" data-name="P2PIX.deposit-&gt;P2PIX._castToUint">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M100.9,-1333.95C114.36,-1371.09 149.31,-1453.6 207.08,-1497 216.11,-1503.78 226.76,-1508.86 237.64,-1512.67" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="236.46,-1515.96 247.04,-1515.55 238.51,-1509.27 236.46,-1515.96" style=""/>
</g>
<!-- P2PIX._castAddrToKey -->
<g id="node25" class="node" pointer-events="visible" data-name="P2PIX._castAddrToKey">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-980" rx="77.58" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-975.8" font-family="Times,serif" font-size="14.00" style="">_castAddrToKey</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge6" class="edge" data-name="P2PIX.deposit-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M104.52,-1296.43C121.42,-1266.95 158.6,-1209.94 207.08,-1182 283.68,-1137.87 338.08,-1207.13 402.88,-1147 449.97,-1103.3 394.14,-1053.11 438.88,-1007 443.38,-1002.36 448.63,-998.5 454.29,-995.3" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="455.36,-998.67 462.89,-991.21 452.36,-992.35 455.36,-998.67" style=""/>
</g>
<!-- P2PIX.ERC20 -->
<g id="node27" class="node" pointer-events="visible" data-name="P2PIX.ERC20">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1142" rx="39.45" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1137.8" font-family="Times,serif" font-size="14.00" style="">ERC20</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.ERC20 -->
<g id="edge5" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.ERC20">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M107.41,-1296.82C126.17,-1271.88 163.59,-1227.79 207.08,-1207 286.23,-1169.17 317.6,-1202.58 402.88,-1182 419.41,-1178.01 422.7,-1174.26 438.88,-1169 453.74,-1164.17 470.08,-1159.29 484.86,-1155.04" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="485.8,-1158.41 494.47,-1152.3 483.89,-1151.67 485.8,-1158.41" style=""/>
</g>
<!-- P2PIX.EmptyPixTarget -->
<g id="node28" class="node" pointer-events="visible" data-name="P2PIX.EmptyPixTarget">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1234" rx="74.74" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1229.8" font-family="Times,serif" font-size="14.00" style="">EmptyPixTarget</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.EmptyPixTarget -->
<g id="edge7" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.EmptyPixTarget">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M120.8,-1300.78C143.25,-1289.01 176.65,-1272.44 207.08,-1261 216.73,-1257.37 227.08,-1253.99 237.29,-1250.92" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="238.09,-1254.33 246.72,-1248.18 236.14,-1247.61 238.09,-1254.33" style=""/>
</g>
<!-- P2PIX.TokenDenied -->
<g id="node29" class="node" pointer-events="visible" data-name="P2PIX.TokenDenied">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1288" rx="63.69" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1283.8" font-family="Times,serif" font-size="14.00" style="">TokenDenied</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.TokenDenied -->
<g id="edge8" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.TokenDenied">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M132.47,-1310.11C160.27,-1306.53 198.86,-1301.55 232.39,-1297.23" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="232.49,-1300.75 241.96,-1296 231.59,-1293.8 232.49,-1300.75" style=""/>
</g>
<!-- P2PIX.MaxBalExceeded -->
<g id="node30" class="node" pointer-events="visible" data-name="P2PIX.MaxBalExceeded">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1352" rx="79.85" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1347.8" font-family="Times,serif" font-size="14.00" style="">MaxBalExceeded</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.MaxBalExceeded -->
<g id="edge9" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.MaxBalExceeded">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M118.4,-1330.36C140.42,-1343.56 174.54,-1361.6 207.08,-1369 292.04,-1388.33 392.57,-1378.2 459.08,-1367.09" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="459.68,-1370.54 468.93,-1365.38 458.48,-1363.64 459.68,-1370.54" style=""/>
</g>
<!-- P2PIX.setReentrancyGuard -->
<g id="node31" class="node" pointer-events="visible" data-name="P2PIX.setReentrancyGuard">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1342" rx="89.73" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1337.8" font-family="Times,serif" font-size="14.00" style="">setReentrancyGuard</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.setReentrancyGuard -->
<g id="edge10" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.setReentrancyGuard">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M132.47,-1319.89C155.37,-1322.84 185.58,-1326.74 214.26,-1330.43" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="213.49,-1333.86 223.86,-1331.67 214.39,-1326.92 213.49,-1333.86" style=""/>
</g>
<!-- P2PIX.address -->
<g id="node32" class="node" pointer-events="visible" data-name="P2PIX.address">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-582" rx="40" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-577.8" font-family="Times,serif" font-size="14.00" style="">address</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.address -->
<g id="edge14" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.address">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M99.36,-1295.76C112.4,-1243.12 148.93,-1092.15 171.08,-965 181.7,-904.07 165.28,-734.59 207.08,-689 267.3,-623.34 325.02,-689.31 402.88,-646 422.93,-634.85 419.2,-620.81 438.88,-609 451.83,-601.22 467.15,-595.58 481.59,-591.53" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="482.21,-594.99 491.03,-589.12 480.47,-588.21 482.21,-594.99" style=""/>
</g>
<!-- P2PIX.clearReentrancyGuard -->
<g id="node33" class="node" pointer-events="visible" data-name="P2PIX.clearReentrancyGuard">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1416" rx="97.79" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1411.8" font-family="Times,serif" font-size="14.00" style="">clearReentrancyGuard</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.clearReentrancyGuard -->
<g id="edge15" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.clearReentrancyGuard">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M116.53,-1330.61C138.62,-1345.76 174.03,-1368.66 207.08,-1384 216.34,-1388.29 226.33,-1392.28 236.26,-1395.87" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="234.84,-1399.08 245.43,-1399.06 237.14,-1392.47 234.84,-1399.08" style=""/>
</g>
<!-- P2PIX.DepositAdded -->
<g id="node34" class="node" pointer-events="visible" data-name="P2PIX.DepositAdded">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1470" rx="66.65" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1465.8" font-family="Times,serif" font-size="14.00" style="">DepositAdded</text>
</g>
<!-- P2PIX.deposit&#45;&gt;P2PIX.DepositAdded -->
<g id="edge16" class="edge" data-name="P2PIX.deposit-&gt;P2PIX.DepositAdded">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M105.59,-1333.26C123.42,-1361.17 161.22,-1414.21 207.08,-1443 215.51,-1448.29 224.98,-1452.56 234.61,-1456.01" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="233.37,-1459.28 243.96,-1459.02 235.51,-1452.62 233.37,-1459.28" style=""/>
</g>
<!-- SafeTransferLib.safeTransferFrom -->
<g id="node58" class="node" pointer-events="visible" data-name="SafeTransferLib.safeTransferFrom">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-34" rx="80.43" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-29.8" font-family="Times,serif" font-size="14.00" style="">safeTransferFrom</text>
</g>
<!-- P2PIX.deposit&#45;&gt;SafeTransferLib.safeTransferFrom -->
<g id="edge13" class="edge" data-name="P2PIX.deposit-&gt;SafeTransferLib.safeTransferFrom">
<path fill="none" stroke="white" stroke-width="2" d="M99.88,-1295.85C114.26,-1243.41 153.83,-1092.91 171.08,-965 181.2,-890.02 156.72,-341.46 207.08,-285 265.82,-219.14 339.88,-313.79 402.88,-252 464.55,-191.51 380.79,-124.94 438.88,-61 442.81,-56.68 447.38,-53.03 452.35,-49.95" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="453.79,-53.15 461.09,-45.47 450.59,-46.92 453.79,-53.15" style=""/>
</g>
<!-- P2PIX.setValidState -->
<g id="node3" class="node" pointer-events="visible" data-name="P2PIX.setValidState">
<ellipse fill="#ff9797" stroke="#ff9797" stroke-width="3" cx="304.98" cy="-420" rx="62.56" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-415.8" font-family="Times,serif" font-size="14.00" style="">setValidState</text>
</g>
<!-- P2PIX.setValidState&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge17" class="edge" data-name="P2PIX.setValidState-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M366.61,-425.02C380.46,-429.07 393.76,-435.84 402.88,-447 474.2,-534.3 368,-865.34 438.88,-953 442.61,-957.62 447.08,-961.48 452.02,-964.7" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="450.32,-967.76 460.8,-969.36 453.6,-961.57 450.32,-967.76" style=""/>
</g>
<!-- P2PIX.setValidState&#45;&gt;P2PIX.address -->
<g id="edge19" class="edge" data-name="P2PIX.setValidState-&gt;P2PIX.address">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M364.79,-426.71C378.66,-430.74 392.44,-437.07 402.88,-447 439.54,-481.87 402.4,-519.94 438.88,-555 450.07,-565.76 465.29,-572.27 480.17,-576.2" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="479.03,-579.53 489.55,-578.27 480.54,-572.7 479.03,-579.53" style=""/>
</g>
<!-- P2PIX.ValidSet -->
<g id="node35" class="node" pointer-events="visible" data-name="P2PIX.ValidSet">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-528" rx="44.66" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-523.8" font-family="Times,serif" font-size="14.00" style="">ValidSet</text>
</g>
<!-- P2PIX.setValidState&#45;&gt;P2PIX.ValidSet -->
<g id="edge18" class="edge" data-name="P2PIX.setValidState-&gt;P2PIX.ValidSet">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M362.34,-428.53C376.48,-432.52 391,-438.39 402.88,-447 426.23,-463.93 415.61,-483.95 438.88,-501 449.99,-509.15 463.53,-514.85 476.79,-518.83" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="475.86,-522.2 486.42,-521.37 477.64,-515.43 475.86,-522.2" style=""/>
</g>
<!-- P2PIX.NotInitialized -->
<g id="node36" class="node" pointer-events="visible" data-name="P2PIX.NotInitialized">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-474" rx="64.84" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-469.8" font-family="Times,serif" font-size="14.00" style="">NotInitialized</text>
</g>
<!-- P2PIX.setValidState&#45;&gt;P2PIX.NotInitialized -->
<g id="edge20" class="edge" data-name="P2PIX.setValidState-&gt;P2PIX.NotInitialized">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M355.59,-431.85C388.96,-439.82 433.23,-450.4 469.08,-458.96" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="467.9,-462.27 478.44,-461.19 469.53,-455.47 467.9,-462.27" style=""/>
</g>
<!-- P2PIX.lock -->
<g id="node4" class="node" pointer-events="visible" data-name="P2PIX.lock">
<ellipse fill="#ff9797" stroke="#ff9797" stroke-width="3" cx="93.54" cy="-938" rx="27.83" ry="18" style=""/>
<text text-anchor="middle" x="93.54" y="-933.8" font-family="Times,serif" font-size="14.00" style="">lock</text>
</g>
<!-- P2PIX.unlockExpired -->
<g id="node6" class="node" pointer-events="visible" data-name="P2PIX.unlockExpired">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-790" rx="67.78" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-785.8" font-family="Times,serif" font-size="14.00" style="">unlockExpired</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.unlockExpired -->
<g id="edge21" class="edge" data-name="P2PIX.lock-&gt;P2PIX.unlockExpired">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M106.78,-920.91C125.61,-895.79 164.05,-848.97 207.08,-822 216.95,-815.82 228.14,-810.7 239.32,-806.51" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="240.22,-809.9 248.52,-803.31 237.93,-803.29 240.22,-809.9" style=""/>
</g>
<!-- P2PIX._addLock -->
<g id="node16" class="node" pointer-events="visible" data-name="P2PIX._addLock">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1120" rx="49.8" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1115.8" font-family="Times,serif" font-size="14.00" style="">_addLock</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX._addLock -->
<g id="edge32" class="edge" data-name="P2PIX.lock-&gt;P2PIX._addLock">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M108.23,-954.81C126.06,-984.63 160.52,-1042.65 207.08,-1075 224.64,-1087.2 247.58,-1093.91 266.72,-1099.56" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="265.48,-1102.84 276.07,-1102.38 267.51,-1096.14 265.48,-1102.84" style=""/>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX._addLock -->
<g id="edge33" class="edge" data-name="P2PIX.lock-&gt;P2PIX._addLock">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M102.78,-956.41C118.34,-989.03 155.19,-1056.95 207.08,-1093 218.17,-1100.7 231.39,-1106.22 244.45,-1110.16" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="243.36,-1113.49 253.93,-1112.7 245.17,-1106.73 243.36,-1113.49" style=""/>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX._addLock -->
<g id="edge37" class="edge" data-name="P2PIX.lock-&gt;P2PIX._addLock">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M99.12,-957.03C111.86,-994.62 150.55,-1071.72 207.08,-1111 219.84,-1119.86 235.43,-1125.83 250.34,-1129.08" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="249.76,-1132.53 260.2,-1130.74 250.92,-1125.63 249.76,-1132.53" style=""/>
</g>
<!-- P2PIX.merkleVerify -->
<g id="node17" class="node" pointer-events="visible" data-name="P2PIX.merkleVerify">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-602" rx="63.1" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-597.8" font-family="Times,serif" font-size="14.00" style="">merkleVerify</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.merkleVerify -->
<g id="edge31" class="edge" data-name="P2PIX.lock-&gt;P2PIX.merkleVerify">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M96.01,-918.9C101.1,-865.89 122.65,-715.48 207.08,-634 215.28,-626.09 225.47,-620.13 236.14,-615.65" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="237.28,-618.96 245.46,-612.23 234.87,-612.39 237.28,-618.96" style=""/>
</g>
<!-- P2PIX._limiter -->
<g id="node18" class="node" pointer-events="visible" data-name="P2PIX._limiter">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-1066" rx="41.72" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-1061.8" font-family="Times,serif" font-size="14.00" style="">_limiter</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX._limiter -->
<g id="edge35" class="edge" data-name="P2PIX.lock-&gt;P2PIX._limiter">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M109.45,-954.13C129.71,-975.19 167.92,-1011.96 207.08,-1034 221.7,-1042.23 238.76,-1048.75 254.46,-1053.7" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="253.2,-1056.97 263.78,-1056.47 255.19,-1050.26 253.2,-1056.97" style=""/>
</g>
<!-- P2PIX.getBalance -->
<g id="node21" class="node" pointer-events="visible" data-name="P2PIX.getBalance">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-690" rx="53.86" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-685.8" font-family="Times,serif" font-size="14.00" style="">getBalance</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.getBalance -->
<g id="edge25" class="edge" data-name="P2PIX.lock-&gt;P2PIX.getBalance">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M96.39,-918.93C102.25,-870.31 125.17,-741.87 207.08,-689 289.97,-635.5 411.25,-655.56 479.8,-673.64" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="478.47,-676.91 489.04,-676.18 480.32,-670.16 478.47,-676.91" style=""/>
</g>
<!-- P2PIX.getValid -->
<g id="node22" class="node" pointer-events="visible" data-name="P2PIX.getValid">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-548" rx="44.08" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-543.8" font-family="Times,serif" font-size="14.00" style="">getValid</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.getValid -->
<g id="edge23" class="edge" data-name="P2PIX.lock-&gt;P2PIX.getValid">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M94.47,-918.58C95.36,-858.32 106.52,-672.7 207.08,-575 218.14,-564.26 233.09,-557.71 247.94,-553.75" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="248.35,-557.24 257.35,-551.65 246.83,-550.41 248.35,-557.24" style=""/>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge27" class="edge" data-name="P2PIX.lock-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M114.29,-951.51C135.19,-969.13 170.2,-998.11 207.08,-1010 289.9,-1036.71 316.98,-1023.94 402.88,-1010 419.52,-1007.3 422.77,-1003.01 438.88,-998 443.58,-996.53 448.43,-995.05 453.32,-993.59" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="454.02,-997.03 462.63,-990.86 452.05,-990.31 454.02,-997.03" style=""/>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge34" class="edge" data-name="P2PIX.lock-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M105.62,-955.32C124.37,-977.73 164.47,-1014.26 207.08,-1028 289.9,-1054.71 316.98,-1041.94 402.88,-1028 419.52,-1025.3 422.77,-1021.01 438.88,-1016 454.2,-1011.23 471.03,-1006.27 486.12,-1001.45" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="487.03,-1004.84 495.44,-998.4 484.85,-998.19 487.03,-1004.84" style=""/>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.ERC20 -->
<g id="edge22" class="edge" data-name="P2PIX.lock-&gt;P2PIX.ERC20">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M109.86,-922.01C129.99,-902.28 167.45,-869.78 207.08,-857 289.9,-830.29 337.23,-799.88 402.88,-857 490.22,-933 363.09,-1027.48 438.88,-1115 449.21,-1126.94 464.47,-1133.75 479.67,-1137.59" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="478.84,-1141 489.34,-1139.57 480.24,-1134.14 478.84,-1141" style=""/>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.address -->
<g id="edge30" class="edge" data-name="P2PIX.lock-&gt;P2PIX.address">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M95.74,-918.93C100.11,-866.86 119.95,-721.66 207.08,-654 276.37,-600.2 319.52,-656.35 402.88,-629 420.27,-623.29 421.98,-616.04 438.88,-609 453.07,-603.09 468.98,-597.96 483.59,-593.81" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="484.37,-597.23 493.09,-591.22 482.52,-590.47 484.37,-597.23" style=""/>
</g>
<!-- P2PIX.InvalidDeposit -->
<g id="node37" class="node" pointer-events="visible" data-name="P2PIX.InvalidDeposit">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-884" rx="68.36" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-879.8" font-family="Times,serif" font-size="14.00" style="">InvalidDeposit</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.InvalidDeposit -->
<g id="edge24" class="edge" data-name="P2PIX.lock-&gt;P2PIX.InvalidDeposit">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M120.64,-931.27C150.59,-923.55 200.75,-910.62 241.04,-900.23" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="241.73,-903.67 250.53,-897.78 239.98,-896.89 241.73,-903.67" style=""/>
</g>
<!-- P2PIX.NotEnoughTokens -->
<g id="node38" class="node" pointer-events="visible" data-name="P2PIX.NotEnoughTokens">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-938" rx="83.98" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-933.8" font-family="Times,serif" font-size="14.00" style="">NotEnoughTokens</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.NotEnoughTokens -->
<g id="edge26" class="edge" data-name="P2PIX.lock-&gt;P2PIX.NotEnoughTokens">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M122.79,-938C144.67,-938 176.35,-938 207.12,-938" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="206.73,-941.5 216.73,-938 206.73,-934.5 206.73,-941.5" style=""/>
</g>
<!-- P2PIX.NotExpired -->
<g id="node39" class="node" pointer-events="visible" data-name="P2PIX.NotExpired">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="740.22" cy="-1187" rx="56.18" ry="18" style=""/>
<text text-anchor="middle" x="740.22" y="-1182.8" font-family="Times,serif" font-size="14.00" style="">NotExpired</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.NotExpired -->
<g id="edge28" class="edge" data-name="P2PIX.lock-&gt;P2PIX.NotExpired">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M99.45,-957.04C111.01,-998.03 143.84,-1095.11 207.08,-1147 277.44,-1204.73 333.06,-1148.61 402.88,-1207 429.71,-1229.45 409.01,-1258.78 438.88,-1277 510.34,-1320.61 548.27,-1304.8 627.23,-1277 662.12,-1264.72 694.17,-1236.47 714.96,-1214.87" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="717.37,-1217.41 721.63,-1207.71 712.25,-1212.64 717.37,-1217.41" style=""/>
</g>
<!-- P2PIX.AmountNotAllowed -->
<g id="node40" class="node" pointer-events="visible" data-name="P2PIX.AmountNotAllowed">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-992" rx="90.37" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-987.8" font-family="Times,serif" font-size="14.00" style="">AmountNotAllowed</text>
</g>
<!-- P2PIX.lock&#45;&gt;P2PIX.AmountNotAllowed -->
<g id="edge36" class="edge" data-name="P2PIX.lock-&gt;P2PIX.AmountNotAllowed">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M120.64,-944.73C148.78,-951.99 194.77,-963.84 233.64,-973.87" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="232.76,-977.25 243.32,-976.36 234.51,-970.47 232.76,-977.25" style=""/>
</g>
<!-- DT.Lock -->
<g id="node61" class="node" pointer-events="visible" data-name="DT.Lock">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-197" rx="30.76" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-192.8" font-family="Times,serif" font-size="14.00" style="">Lock</text>
</g>
<!-- P2PIX.lock&#45;&gt;DT.Lock -->
<g id="edge29" class="edge" data-name="P2PIX.lock-&gt;DT.Lock">
<path fill="none" stroke="white" stroke-width="2" d="M98.9,-919.02C111.55,-863.46 148.76,-696.27 171.08,-556 181.77,-488.82 169.45,-308.67 207.08,-252 220.44,-231.89 243.8,-218.31 264.24,-209.68" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="265.28,-213.03 273.33,-206.15 262.74,-206.51 265.28,-213.03" style=""/>
</g>
<!-- P2PIX.release -->
<g id="node5" class="node" pointer-events="visible" data-name="P2PIX.release">
<ellipse fill="#ff9797" stroke="#ff9797" stroke-width="3" cx="304.98" cy="-716" rx="37.68" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-711.8" font-family="Times,serif" font-size="14.00" style="">release</text>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge41" class="edge" data-name="P2PIX.release-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M321.74,-698.53C343.46,-690.99 382.25,-697.14 402.88,-721 463.37,-790.98 377.1,-857.15 438.88,-926 454.8,-943.75 481.62,-950.13 502.41,-956.85" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="500.98,-960.05 511.57,-960.16 503.35,-953.47 500.98,-960.05" style=""/>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge44" class="edge" data-name="P2PIX.release-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M342.83,-711.2C363.44,-713.72 387.93,-721.71 402.88,-739 463.37,-808.98 377.1,-875.15 438.88,-944 446.07,-952.02 455.49,-957.72 465.57,-961.89" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="464.16,-965.11 474.76,-965.14 466.49,-958.51 464.16,-965.11" style=""/>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge45" class="edge" data-name="P2PIX.release-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M338.57,-725.93C359.98,-730.65 386.89,-738.5 402.88,-757 463.37,-826.98 377.1,-893.15 438.88,-962 440.96,-964.33 443.24,-966.46 445.66,-968.41" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="443.55,-971.21 453.84,-973.76 447.39,-965.35 443.55,-971.21" style=""/>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge46" class="edge" data-name="P2PIX.release-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M320.66,-733.84C342.15,-744.79 381.91,-750.74 402.88,-775 463.37,-844.98 377.1,-911.15 438.88,-980 454.75,-997.69 481.43,-1004.08 502.18,-1001.73" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="502.82,-1005.18 511.83,-999.61 501.31,-998.34 502.82,-1005.18" style=""/>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX.ERC20 -->
<g id="edge43" class="edge" data-name="P2PIX.release-&gt;P2PIX.ERC20">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M340.76,-723.71C362.25,-730.38 388.41,-742.32 402.88,-763 493.04,-891.85 338.13,-994.25 438.88,-1115 449.08,-1127.22 464.45,-1134.1 479.78,-1137.92" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="479.08,-1141.34 489.57,-1139.86 480.44,-1134.48 479.08,-1141.34" style=""/>
</g>
<!-- P2PIX.AlreadyReleased -->
<g id="node41" class="node" pointer-events="visible" data-name="P2PIX.AlreadyReleased">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="740.22" cy="-880" rx="76.99" ry="18" style=""/>
<text text-anchor="middle" x="740.22" y="-875.8" font-family="Times,serif" font-size="14.00" style="">AlreadyReleased</text>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX.AlreadyReleased -->
<g id="edge38" class="edge" data-name="P2PIX.release-&gt;P2PIX.AlreadyReleased">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M343.91,-716.75C423.52,-718.57 602.73,-724.06 627.23,-737 673.8,-761.59 707.27,-814.95 724.91,-849.08" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="721.75,-850.59 729.34,-857.98 728.02,-847.47 721.75,-850.59" style=""/>
</g>
<!-- P2PIX.LockExpired -->
<g id="node42" class="node" pointer-events="visible" data-name="P2PIX.LockExpired">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-926" rx="61.4" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-921.8" font-family="Times,serif" font-size="14.00" style="">LockExpired</text>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX.LockExpired -->
<g id="edge39" class="edge" data-name="P2PIX.release-&gt;P2PIX.LockExpired">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M343.4,-719.56C363.69,-723.42 387.66,-731.43 402.88,-748 449.55,-798.81 391.28,-849.06 438.88,-899 445.35,-905.79 453.41,-910.9 462.07,-914.75" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="460.72,-917.98 471.32,-918.19 463.16,-911.42 460.72,-917.98" style=""/>
</g>
<!-- P2PIX.TxAlreadyUsed -->
<g id="node43" class="node" pointer-events="visible" data-name="P2PIX.TxAlreadyUsed">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-764" rx="72.93" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-759.8" font-family="Times,serif" font-size="14.00" style="">TxAlreadyUsed</text>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX.TxAlreadyUsed -->
<g id="edge40" class="edge" data-name="P2PIX.release-&gt;P2PIX.TxAlreadyUsed">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M340.81,-723.39C373.18,-730.27 422.33,-740.7 462.7,-749.27" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="461.72,-752.64 472.23,-751.3 463.17,-745.8 461.72,-752.64" style=""/>
</g>
<!-- P2PIX.InvalidSigner -->
<g id="node44" class="node" pointer-events="visible" data-name="P2PIX.InvalidSigner">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-818" rx="63.71" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-813.8" font-family="Times,serif" font-size="14.00" style="">InvalidSigner</text>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX.InvalidSigner -->
<g id="edge42" class="edge" data-name="P2PIX.release-&gt;P2PIX.InvalidSigner">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M341.28,-722.68C360.65,-727.51 384.41,-735.46 402.88,-748 423.5,-762 418,-777.38 438.88,-791 446.92,-796.25 456,-800.49 465.26,-803.93" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="464,-807.2 474.59,-807.04 466.21,-800.56 464,-807.2" style=""/>
</g>
<!-- P2PIX.LockReleased -->
<g id="node45" class="node" pointer-events="visible" data-name="P2PIX.LockReleased">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-872" rx="65.97" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-867.8" font-family="Times,serif" font-size="14.00" style="">LockReleased</text>
</g>
<!-- P2PIX.release&#45;&gt;P2PIX.LockReleased -->
<g id="edge51" class="edge" data-name="P2PIX.release-&gt;P2PIX.LockReleased">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M343.13,-720.39C363.08,-724.51 386.81,-732.51 402.88,-748 435.98,-779.92 405.19,-813.7 438.88,-845 445.18,-850.85 452.67,-855.45 460.63,-859.05" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="459.13,-862.22 469.72,-862.56 461.65,-855.69 459.13,-862.22" style=""/>
</g>
<!-- SafeTransferLib.safeTransfer -->
<g id="node59" class="node" pointer-events="visible" data-name="SafeTransferLib.safeTransfer">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-142" rx="59.08" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-137.8" font-family="Times,serif" font-size="14.00" style="">safeTransfer</text>
</g>
<!-- P2PIX.release&#45;&gt;SafeTransferLib.safeTransfer -->
<g id="edge47" class="edge" data-name="P2PIX.release-&gt;SafeTransferLib.safeTransfer">
<path fill="none" stroke="white" stroke-width="2" d="M314.68,-697.38C334.45,-673.12 382.66,-642.7 402.88,-602 445.74,-515.73 387.68,-251.6 438.88,-170 444.83,-160.51 453.07,-152.45 462.21,-146.02" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="463.99,-149.03 470.68,-140.82 460.33,-143.07 463.99,-149.03" style=""/>
</g>
<!-- P2PIX.release&#45;&gt;SafeTransferLib.safeTransfer -->
<g id="edge48" class="edge" data-name="P2PIX.release-&gt;SafeTransferLib.safeTransfer">
<path fill="none" stroke="white" stroke-width="2" d="M324.44,-699.32C347.34,-682.44 385.47,-655.03 402.88,-620 445.74,-533.73 387.68,-269.6 438.88,-188 446.52,-175.82 457.93,-166 470.16,-158.47" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="471.83,-161.55 478.89,-153.65 468.45,-155.42 471.83,-161.55" style=""/>
</g>
<!-- P2PIX.release&#45;&gt;SafeTransferLib.safeTransfer -->
<g id="edge49" class="edge" data-name="P2PIX.release-&gt;SafeTransferLib.safeTransfer">
<path fill="none" stroke="white" stroke-width="2" d="M336.61,-705.04C359.14,-691.37 388.29,-667.36 402.88,-638 445.74,-551.73 387.68,-287.6 438.88,-206 450.78,-187.03 471.81,-173.79 490.89,-164.16" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="492.19,-167.42 499.68,-159.93 489.15,-161.11 492.19,-167.42" style=""/>
</g>
<!-- P2PIX.release&#45;&gt;SafeTransferLib.safeTransfer -->
<g id="edge50" class="edge" data-name="P2PIX.release-&gt;SafeTransferLib.safeTransfer">
<path fill="none" stroke="white" stroke-width="2" d="M343.79,-716.11C365.02,-704.34 389.8,-682.32 402.88,-656 445.74,-569.73 387.68,-305.6 438.88,-224 455.03,-198.26 487.98,-183.08 510.05,-168.59" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="511.98,-171.51 518,-162.8 507.86,-165.85 511.98,-171.51" style=""/>
</g>
<!-- P2PIX._notExpired -->
<g id="node15" class="node" pointer-events="visible" data-name="P2PIX._notExpired">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1088" rx="58.55" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1083.8" font-family="Times,serif" font-size="14.00" style="">_notExpired</text>
</g>
<!-- P2PIX.unlockExpired&#45;&gt;P2PIX._notExpired -->
<g id="edge52" class="edge" data-name="P2PIX.unlockExpired-&gt;P2PIX._notExpired">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M366.44,-798.86C380.2,-803.62 393.51,-810.9 402.88,-822 472.13,-904.11 368.12,-980.18 438.88,-1061 445.59,-1068.66 454.32,-1074.21 463.76,-1078.22" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="462.53,-1081.5 473.12,-1081.51 464.85,-1074.89 462.53,-1081.5" style=""/>
</g>
<!-- P2PIX.unlockExpired&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge56" class="edge" data-name="P2PIX.unlockExpired-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M365.3,-799.57C379.16,-804.32 392.81,-811.41 402.88,-822 444.47,-865.77 396.48,-910.01 438.88,-953 443.42,-957.6 448.69,-961.43 454.38,-964.62" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="452.46,-967.58 463,-968.69 455.45,-961.25 452.46,-967.58" style=""/>
</g>
<!-- P2PIX.unlockExpired&#45;&gt;P2PIX.ERC20 -->
<g id="edge53" class="edge" data-name="P2PIX.unlockExpired-&gt;P2PIX.ERC20">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M373.64,-792.53C384.82,-797.06 395.24,-803.69 402.88,-813 486.09,-914.44 353.8,-1006.12 438.88,-1106 450.19,-1119.28 467.6,-1126.24 484.33,-1130.4" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="483.4,-1133.79 493.92,-1132.47 484.88,-1126.94 483.4,-1133.79" style=""/>
</g>
<!-- P2PIX.unlockExpired&#45;&gt;P2PIX.ERC20 -->
<g id="edge55" class="edge" data-name="P2PIX.unlockExpired-&gt;P2PIX.ERC20">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M354.53,-803.49C372.31,-808.5 390.97,-816.48 402.88,-831 486.09,-932.44 353.8,-1024.12 438.88,-1124 449.65,-1136.65 465.95,-1143.56 481.92,-1146.76" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="481.16,-1150.18 491.55,-1148.14 482.15,-1143.25 481.16,-1150.18" style=""/>
</g>
<!-- P2PIX.unlockExpired&#45;&gt;P2PIX.MaxBalExceeded -->
<g id="edge54" class="edge" data-name="P2PIX.unlockExpired-&gt;P2PIX.MaxBalExceeded">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M343.6,-806C364.76,-817.08 389.54,-834 402.88,-857 496.89,-1019.06 350.82,-1111.63 438.88,-1277 449.92,-1297.74 469.41,-1314.8 487.71,-1327.38" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="485.75,-1330.28 496.04,-1332.79 489.56,-1324.41 485.75,-1330.28" style=""/>
</g>
<!-- P2PIX.LockReturned -->
<g id="node46" class="node" pointer-events="visible" data-name="P2PIX.LockReturned">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1034" rx="66.05" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1029.8" font-family="Times,serif" font-size="14.00" style="">LockReturned</text>
</g>
<!-- P2PIX.unlockExpired&#45;&gt;P2PIX.LockReturned -->
<g id="edge57" class="edge" data-name="P2PIX.unlockExpired-&gt;P2PIX.LockReturned">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M366.11,-799.14C379.88,-803.9 393.28,-811.1 402.88,-822 458.24,-884.86 382.37,-945.17 438.88,-1007 444.54,-1013.19 451.52,-1018 459.1,-1021.72" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="457.58,-1024.88 468.16,-1025.43 460.23,-1018.4 457.58,-1024.88" style=""/>
</g>
<!-- P2PIX.withdraw -->
<g id="node7" class="node" pointer-events="visible" data-name="P2PIX.withdraw">
<ellipse fill="#ff9797" stroke="#ff9797" stroke-width="3" cx="93.54" cy="-529" rx="47.52" ry="18" style=""/>
<text text-anchor="middle" x="93.54" y="-524.8" font-family="Times,serif" font-size="14.00" style="">withdraw</text>
</g>
<!-- P2PIX.withdraw&#45;&gt;P2PIX.setValidState -->
<g id="edge60" class="edge" data-name="P2PIX.withdraw-&gt;P2PIX.setValidState">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M133,-517.66C145.94,-512.67 159.92,-505.87 171.08,-497 192.52,-479.97 184.63,-462.67 207.08,-447 215.69,-441 225.59,-436.32 235.7,-432.69" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="236.73,-436.04 245.19,-429.67 234.6,-429.37 236.73,-436.04" style=""/>
</g>
<!-- P2PIX.withdraw&#45;&gt;P2PIX.unlockExpired -->
<g id="edge58" class="edge" data-name="P2PIX.withdraw-&gt;P2PIX.unlockExpired">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M98.6,-548.07C108.85,-591.7 139.94,-699.52 207.08,-758 215.2,-765.07 224.91,-770.58 235.02,-774.88" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="233.56,-778.07 244.15,-778.33 236.03,-771.52 233.56,-778.07" style=""/>
</g>
<!-- P2PIX._decBal -->
<g id="node20" class="node" pointer-events="visible" data-name="P2PIX._decBal">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1196" rx="42.85" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1191.8" font-family="Times,serif" font-size="14.00" style="">_decBal</text>
</g>
<!-- P2PIX.withdraw&#45;&gt;P2PIX._decBal -->
<g id="edge62" class="edge" data-name="P2PIX.withdraw-&gt;P2PIX._decBal">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M95.95,-548.37C100.86,-600.6 121.88,-745.25 207.08,-817 275.02,-874.21 342.85,-791.54 402.88,-857 497.21,-959.88 348.76,-1062.4 438.88,-1169 448.46,-1180.34 462.5,-1187.07 476.78,-1191.02" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="475.82,-1194.39 486.34,-1193.17 477.35,-1187.56 475.82,-1194.39" style=""/>
</g>
<!-- P2PIX.withdraw&#45;&gt;P2PIX.getValid -->
<g id="edge59" class="edge" data-name="P2PIX.withdraw-&gt;P2PIX.getValid">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M141.28,-533.24C172.79,-536.1 214.51,-539.88 247.82,-542.91" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="247.31,-546.37 257.58,-543.79 247.94,-539.4 247.31,-546.37" style=""/>
</g>
<!-- P2PIX.withdraw&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge61" class="edge" data-name="P2PIX.withdraw-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M98.28,-548.17C107.81,-591.3 137.35,-695.88 207.08,-743 243.32,-767.49 370.66,-733.42 402.88,-763 466.19,-821.12 381.05,-889.42 438.88,-953 443.05,-957.59 447.95,-961.41 453.28,-964.6" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="451.51,-967.63 462.02,-968.95 454.62,-961.36 451.51,-967.63" style=""/>
</g>
<!-- P2PIX.withdraw&#45;&gt;P2PIX.address -->
<g id="edge65" class="edge" data-name="P2PIX.withdraw-&gt;P2PIX.address">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M140.42,-523.44C214.42,-515.2 356.54,-502.58 402.88,-521 423.33,-529.13 419.77,-544.08 438.88,-555 451.99,-562.5 467.36,-568.06 481.79,-572.12" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="480.66,-575.44 491.22,-574.55 482.41,-568.66 480.66,-575.44" style=""/>
</g>
<!-- P2PIX.DepositWithdrawn -->
<g id="node47" class="node" pointer-events="visible" data-name="P2PIX.DepositWithdrawn">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="304.98" cy="-474" rx="84.54" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-469.8" font-family="Times,serif" font-size="14.00" style="">DepositWithdrawn</text>
</g>
<!-- P2PIX.withdraw&#45;&gt;P2PIX.DepositWithdrawn -->
<g id="edge64" class="edge" data-name="P2PIX.withdraw-&gt;P2PIX.DepositWithdrawn">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M134.43,-518.53C163.05,-511.01 202.33,-500.69 235.88,-491.88" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="236.71,-495.28 245.49,-489.36 234.93,-488.51 236.71,-495.28" style=""/>
</g>
<!-- P2PIX.withdraw&#45;&gt;SafeTransferLib.safeTransfer -->
<g id="edge63" class="edge" data-name="P2PIX.withdraw-&gt;SafeTransferLib.safeTransfer">
<path fill="none" stroke="white" stroke-width="2" d="M136.76,-520.25C149.71,-515.5 162.72,-508.19 171.08,-497 215.24,-437.91 154.1,-218.32 207.08,-167 277.54,-98.76 401.57,-111.47 474.33,-126.79" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="473.38,-130.17 483.9,-128.91 474.89,-123.33 473.38,-130.17" style=""/>
</g>
<!-- P2PIX.setRoot&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge66" class="edge" data-name="P2PIX.setRoot-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M346.83,-1631.17C366.85,-1628.48 389.49,-1621.57 402.88,-1605 486.54,-1501.45 355.74,-1110.98 438.88,-1007 442.59,-1002.36 447.04,-998.49 451.97,-995.26" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="453.55,-998.38 460.72,-990.59 450.25,-992.21 453.55,-998.38" style=""/>
</g>
<!-- P2PIX.RootUpdated -->
<g id="node48" class="node" pointer-events="visible" data-name="P2PIX.RootUpdated">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1578" rx="62.56" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1573.8" font-family="Times,serif" font-size="14.00" style="">RootUpdated</text>
</g>
<!-- P2PIX.setRoot&#45;&gt;P2PIX.RootUpdated -->
<g id="edge67" class="edge" data-name="P2PIX.setRoot-&gt;P2PIX.RootUpdated">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M342.55,-1623.26C376.91,-1615.06 429,-1602.61 469.88,-1592.85" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="470.59,-1596.28 479.5,-1590.55 468.97,-1589.47 470.59,-1596.28" style=""/>
</g>
<!-- P2PIX.OnlySeller -->
<g id="node49" class="node" pointer-events="visible" data-name="P2PIX.OnlySeller">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1632" rx="52.75" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1627.8" font-family="Times,serif" font-size="14.00" style="">OnlySeller</text>
</g>
<!-- P2PIX.setRoot&#45;&gt;P2PIX.OnlySeller -->
<g id="edge68" class="edge" data-name="P2PIX.setRoot-&gt;P2PIX.OnlySeller">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M346.92,-1632C380.11,-1632 427.55,-1632 466.09,-1632" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="466.02,-1635.5 476.02,-1632 466.02,-1628.5 466.02,-1635.5" style=""/>
</g>
<!-- P2PIX.withdrawBalance -->
<g id="node9" class="node" pointer-events="visible" data-name="P2PIX.withdrawBalance">
<ellipse fill="#ffbdb9" stroke="#ffbdb9" stroke-width="3" cx="304.98" cy="-312" rx="78.71" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-307.8" font-family="Times,serif" font-size="14.00" style="">withdrawBalance</text>
</g>
<!-- P2PIX.withdrawBalance&#45;&gt;P2PIX.address -->
<g id="edge69" class="edge" data-name="P2PIX.withdrawBalance-&gt;P2PIX.address">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M375.62,-320.95C385.92,-325.03 395.52,-330.82 402.88,-339 467.93,-411.39 374.2,-482.28 438.88,-555 449.28,-566.7 464.42,-573.46 479.48,-577.33" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="478.54,-580.71 489.04,-579.33 479.97,-573.86 478.54,-580.71" style=""/>
</g>
<!-- P2PIX.FundsWithdrawn -->
<g id="node50" class="node" pointer-events="visible" data-name="P2PIX.FundsWithdrawn">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-312" rx="78.19" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-307.8" font-family="Times,serif" font-size="14.00" style="">FundsWithdrawn</text>
</g>
<!-- P2PIX.withdrawBalance&#45;&gt;P2PIX.FundsWithdrawn -->
<g id="edge71" class="edge" data-name="P2PIX.withdrawBalance-&gt;P2PIX.FundsWithdrawn">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M385.05,-312C402.96,-312 422.13,-312 440.52,-312" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="440.38,-315.5 450.38,-312 440.38,-308.5 440.38,-315.5" style=""/>
</g>
<!-- SafeTransferLib.safeTransferETH -->
<g id="node60" class="node" pointer-events="visible" data-name="SafeTransferLib.safeTransferETH">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-88" rx="78.12" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-83.8" font-family="Times,serif" font-size="14.00" style="">safeTransferETH</text>
</g>
<!-- P2PIX.withdrawBalance&#45;&gt;SafeTransferLib.safeTransferETH -->
<g id="edge70" class="edge" data-name="P2PIX.withdrawBalance-&gt;SafeTransferLib.safeTransferETH">
<path fill="none" stroke="white" stroke-width="2" d="M370.46,-301.05C382.62,-296.34 394.22,-289.61 402.88,-280 453.12,-224.23 387.62,-169.83 438.88,-115 443.11,-110.47 448.05,-106.69 453.41,-103.53" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="454.76,-106.76 462.19,-99.21 451.68,-100.48 454.76,-106.76" style=""/>
</g>
<!-- P2PIX.setReputation&#45;&gt;P2PIX.address -->
<g id="edge73" class="edge" data-name="P2PIX.setReputation-&gt;P2PIX.address">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M368.12,-1572.81C381.51,-1568.71 394.22,-1561.96 402.88,-1551 467.81,-1468.81 374.37,-691.52 438.88,-609 448.78,-596.33 464.26,-589.36 479.77,-585.59" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="479.99,-589.11 489.15,-583.78 478.66,-582.24 479.99,-589.11" style=""/>
</g>
<!-- P2PIX.ReputationUpdated -->
<g id="node51" class="node" pointer-events="visible" data-name="P2PIX.ReputationUpdated">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1524" rx="86.26" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1519.8" font-family="Times,serif" font-size="14.00" style="">ReputationUpdated</text>
</g>
<!-- P2PIX.setReputation&#45;&gt;P2PIX.ReputationUpdated -->
<g id="edge72" class="edge" data-name="P2PIX.setReputation-&gt;P2PIX.ReputationUpdated">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M356.43,-1565.95C387.01,-1558.64 426.51,-1549.21 460.31,-1541.14" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="461.09,-1544.55 470.01,-1538.82 459.47,-1537.74 461.09,-1544.55" style=""/>
</g>
<!-- P2PIX.LockBlocksUpdated -->
<g id="node52" class="node" pointer-events="visible" data-name="P2PIX.LockBlocksUpdated">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1740" rx="90.34" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1735.8" font-family="Times,serif" font-size="14.00" style="">LockBlocksUpdated</text>
</g>
<!-- P2PIX.setDefaultLockBlocks&#45;&gt;P2PIX.LockBlocksUpdated -->
<g id="edge74" class="edge" data-name="P2PIX.setDefaultLockBlocks-&gt;P2PIX.LockBlocksUpdated">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M403.5,-1740C411.73,-1740 420.08,-1740 428.36,-1740" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="428.26,-1743.5 438.26,-1740 428.26,-1736.5 428.26,-1743.5" style=""/>
</g>
<!-- P2PIX.setValidSigners&#45;&gt;P2PIX._castAddrToKey -->
<g id="edge75" class="edge" data-name="P2PIX.setValidSigners-&gt;P2PIX._castAddrToKey">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M373.47,-1678.86C384.81,-1674.68 395.33,-1668.39 402.88,-1659 493.79,-1545.9 348.54,-1120.56 438.88,-1007 442.57,-1002.35 447.02,-998.47 451.94,-995.24" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="453.52,-998.36 460.69,-990.56 450.22,-992.19 453.52,-998.36" style=""/>
</g>
<!-- P2PIX.ValidSignersUpdated -->
<g id="node53" class="node" pointer-events="visible" data-name="P2PIX.ValidSignersUpdated">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1686" rx="94.35" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1681.8" font-family="Times,serif" font-size="14.00" style="">ValidSignersUpdated</text>
</g>
<!-- P2PIX.setValidSigners&#45;&gt;P2PIX.ValidSignersUpdated -->
<g id="edge76" class="edge" data-name="P2PIX.setValidSigners-&gt;P2PIX.ValidSignersUpdated">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M378.76,-1686C393.38,-1686 409.03,-1686 424.55,-1686" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="424.5,-1689.5 434.5,-1686 424.5,-1682.5 424.5,-1689.5" style=""/>
</g>
<!-- P2PIX.&lt;Receive Ether&gt; -->
<g id="node14" class="node" pointer-events="visible" data-name="P2PIX.&lt;Receive Ether&gt;">
<ellipse fill="#ffbdb9" stroke="brown" stroke-width="3" cx="93.54" cy="-470" rx="76.21" ry="18" style=""/>
<text text-anchor="middle" x="93.54" y="-465.8" font-family="Times,serif" font-size="14.00" style="">&lt;Receive Ether&gt;</text>
</g>
<!-- P2PIX._notExpired&#45;&gt;P2PIX.NotExpired -->
<g id="edge77" class="edge" data-name="P2PIX._notExpired-&gt;P2PIX.NotExpired">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M581.93,-1099.01C596.78,-1103.18 612.97,-1108.52 627.23,-1115 655.18,-1127.71 684.28,-1146.62 705.81,-1161.87" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="703.72,-1164.67 713.88,-1167.68 707.82,-1159 703.72,-1164.67" style=""/>
</g>
<!-- P2PIX._notExpired&#45;&gt;P2PIX.AlreadyReleased -->
<g id="edge78" class="edge" data-name="P2PIX._notExpired-&gt;P2PIX.AlreadyReleased">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M587,-1079.82C601.06,-1075.82 615.56,-1069.86 627.23,-1061 678.62,-1021.99 712.01,-951.99 728.04,-911.43" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="731.27,-912.79 731.56,-902.19 724.73,-910.29 731.27,-912.79" style=""/>
</g>
<!-- P2PIX._addLock&#45;&gt;P2PIX._decBal -->
<g id="edge79" class="edge" data-name="P2PIX._addLock-&gt;P2PIX._decBal">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M345.5,-1131.85C363.23,-1137.47 384.29,-1144.57 402.88,-1152 419.31,-1158.56 422.33,-1162.74 438.88,-1169 452.63,-1174.2 467.83,-1178.99 481.9,-1183.05" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="480.94,-1186.42 491.51,-1185.75 482.83,-1179.68 480.94,-1186.42" style=""/>
</g>
<!-- P2PIX.LockAdded -->
<g id="node54" class="node" pointer-events="visible" data-name="P2PIX.LockAdded">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-1250" rx="56.75" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-1245.8" font-family="Times,serif" font-size="14.00" style="">LockAdded</text>
</g>
<!-- P2PIX._addLock&#45;&gt;P2PIX.LockAdded -->
<g id="edge80" class="edge" data-name="P2PIX._addLock-&gt;P2PIX.LockAdded">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M352.13,-1127.47C369.58,-1132.16 388.64,-1139.76 402.88,-1152 429.7,-1175.07 411.63,-1200.43 438.88,-1223 447.17,-1229.87 457.14,-1235 467.43,-1238.82" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="466.1,-1242.06 476.69,-1241.8 468.24,-1235.4 466.1,-1242.06" style=""/>
</g>
<!-- P2PIX.AddressDenied -->
<g id="node55" class="node" pointer-events="visible" data-name="P2PIX.AddressDenied">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-636" rx="70.64" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-631.8" font-family="Times,serif" font-size="14.00" style="">AddressDenied</text>
</g>
<!-- P2PIX.merkleVerify&#45;&gt;P2PIX.AddressDenied -->
<g id="edge82" class="edge" data-name="P2PIX.merkleVerify-&gt;P2PIX.AddressDenied">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M362.74,-610.54C391.47,-614.86 426.73,-620.16 457.62,-624.81" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="456.7,-628.21 467.11,-626.23 457.74,-621.28 456.7,-628.21" style=""/>
</g>
<!-- Merkle.verify -->
<g id="node62" class="node" pointer-events="visible" data-name="Merkle.verify">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-227" rx="34.19" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-222.8" font-family="Times,serif" font-size="14.00" style="">verify</text>
</g>
<!-- P2PIX.merkleVerify&#45;&gt;Merkle.verify -->
<g id="edge81" class="edge" data-name="P2PIX.merkleVerify-&gt;Merkle.verify">
<path fill="none" stroke="white" stroke-width="2" d="M366.73,-596.5C380.4,-592.44 393.59,-585.79 402.88,-575 488.46,-475.56 366.99,-391.75 438.88,-282 450.95,-263.57 471.52,-250.48 490.27,-241.71" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="491.64,-244.93 499.44,-237.75 488.87,-238.5 491.64,-244.93" style=""/>
</g>
<!-- P2PIX.getPixTarget -->
<g id="node23" class="node" pointer-events="visible" data-name="P2PIX.getPixTarget">
<ellipse fill="#ff9797" stroke="#ff9797" stroke-width="3" cx="93.54" cy="-407" rx="60.83" ry="18" style=""/>
<text text-anchor="middle" x="93.54" y="-402.8" font-family="Times,serif" font-size="14.00" style="">getPixTarget</text>
</g>
<!-- P2PIX.getBalances -->
<g id="node24" class="node" pointer-events="visible" data-name="P2PIX.getBalances">
<ellipse fill="#ffbdb9" stroke="#ffbdb9" stroke-width="3" cx="304.98" cy="-366" rx="57.88" ry="18" style=""/>
<text text-anchor="middle" x="304.98" y="-361.8" font-family="Times,serif" font-size="14.00" style="">getBalances</text>
</g>
<!-- P2PIX.getBalances&#45;&gt;P2PIX.getBalance -->
<g id="edge87" class="edge" data-name="P2PIX.getBalances-&gt;P2PIX.getBalance">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M362.61,-370.44C377.69,-374.36 392.62,-381.21 402.88,-393 482.32,-484.35 359.9,-571.24 438.88,-663 446.3,-671.63 456.3,-677.58 466.99,-681.67" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="465.78,-684.96 476.37,-684.63 467.89,-678.28 465.78,-684.96" style=""/>
</g>
<!-- P2PIX.getBalances&#45;&gt;P2PIX.address -->
<g id="edge83" class="edge" data-name="P2PIX.getBalances-&gt;P2PIX.address">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M363.49,-362.77C378.05,-366.4 392.48,-373.04 402.88,-384 453.63,-437.52 388.4,-492.22 438.88,-546 450.72,-558.62 468.08,-565.46 484.61,-569.71" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="483.51,-573.05 494.04,-571.84 485.05,-566.22 483.51,-573.05" style=""/>
</g>
<!-- P2PIX.getBalances&#45;&gt;P2PIX.address -->
<g id="edge84" class="edge" data-name="P2PIX.getBalances-&gt;P2PIX.address">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M353.18,-377.36C370.98,-381.5 390.02,-388.44 402.88,-402 453.63,-455.52 388.4,-510.22 438.88,-564 449.86,-575.7 465.58,-582.44 480.99,-585.81" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="480.33,-589.25 490.76,-587.4 481.46,-582.34 480.33,-589.25" style=""/>
</g>
<!-- P2PIX.NoTokens -->
<g id="node56" class="node" pointer-events="visible" data-name="P2PIX.NoTokens">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-366" rx="51.04" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-361.8" font-family="Times,serif" font-size="14.00" style="">NoTokens</text>
</g>
<!-- P2PIX.getBalances&#45;&gt;P2PIX.NoTokens -->
<g id="edge85" class="edge" data-name="P2PIX.getBalances-&gt;P2PIX.NoTokens">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M364.2,-366C395.96,-366 435.34,-366 467.97,-366" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="467.55,-369.5 477.55,-366 467.55,-362.5 467.55,-369.5" style=""/>
</g>
<!-- P2PIX.LengthMismatch -->
<g id="node57" class="node" pointer-events="visible" data-name="P2PIX.LengthMismatch">
<ellipse fill="#edad56" stroke="#edad56" stroke-width="3" cx="533.05" cy="-420" rx="77.03" ry="18" style=""/>
<text text-anchor="middle" x="533.05" y="-415.8" font-family="Times,serif" font-size="14.00" style="">LengthMismatch</text>
</g>
<!-- P2PIX.getBalances&#45;&gt;P2PIX.LengthMismatch -->
<g id="edge86" class="edge" data-name="P2PIX.getBalances-&gt;P2PIX.LengthMismatch">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M353.1,-377.25C385.16,-384.91 428.1,-395.17 463.89,-403.72" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="462.73,-407.04 473.27,-405.96 464.36,-400.23 462.73,-407.04" style=""/>
</g>
<!-- P2PIX._castKeyToAddr -->
<g id="node26" class="node" pointer-events="visible" data-name="P2PIX._castKeyToAddr">
<ellipse fill="#ff9797" stroke="#ff9797" stroke-width="3" cx="93.54" cy="-335" rx="77.58" ry="18" style=""/>
<text text-anchor="middle" x="93.54" y="-330.8" font-family="Times,serif" font-size="14.00" style="">_castKeyToAddr</text>
</g>
<!-- key -->
<g id="node63" class="node" pointer-events="visible" data-name="key">
<polygon fill="#edad56" stroke="none" stroke-width="3" points="158.54,-1949 28.54,-1949 28.54,-1861 158.54,-1861 158.54,-1949" style=""/>
<text text-anchor="start" x="78.18" y="-1930.4" font-family="Times,serif" font-size="14.00" style="">Internal Call</text>
<text text-anchor="start" x="74.29" y="-1910.4" font-family="Times,serif" font-size="14.00" style="">External Call</text>
<text text-anchor="start" x="51.74" y="-1890.4" font-family="Times,serif" font-size="14.00" style="">Defined Contract</text>
<text text-anchor="start" x="38.14" y="-1870.4" font-family="Times,serif" font-size="14.00" style="">Undefined Contract</text>
</g>
<!-- key2 -->
<g id="node64" class="node" pointer-events="visible" data-name="key2">
<polygon fill="#edad56" stroke="none" stroke-width="3" points="331.98,-1949 277.98,-1949 277.98,-1861 331.98,-1861 331.98,-1949" style=""/>
<text text-anchor="start" x="294.98" y="-1930.4" font-family="Times,serif" font-size="14.00" style="">   </text>
<text text-anchor="start" x="294.98" y="-1910.4" font-family="Times,serif" font-size="14.00" style="">   </text>
<polygon fill="#445773" stroke="none" points="292.98,-1885 292.98,-1905 317.98,-1905 317.98,-1885 292.98,-1885" style=""/>
<text text-anchor="start" x="294.98" y="-1890.4" font-family="Times,serif" font-size="14.00" style="">   </text>
<polygon fill="none" stroke="#e8726d" points="294.98,-1867 294.98,-1883 315.98,-1883 315.98,-1867 294.98,-1867" style=""/>
</g>
<!-- key&#45;&gt;key2 -->
<g id="edge88" class="edge" data-name="key-&gt;key2">
<path fill="none" stroke="#1bc6a6" stroke-width="2" d="M151.54,-1935C209.45,-1935 227.06,-1935 279.15,-1935" style=""/>
<polygon fill="#1bc6a6" stroke="#1bc6a6" stroke-width="2" points="278.95,-1938.5 288.95,-1935 278.95,-1931.5 278.95,-1938.5" style=""/>
</g>
<!-- key&#45;&gt;key2 -->
<g id="edge89" class="edge" data-name="key-&gt;key2">
<path fill="none" stroke="white" stroke-width="2" d="M151.54,-1915C209.45,-1915 227.06,-1915 279.15,-1915" style=""/>
<polygon fill="white" stroke="white" stroke-width="2" points="278.95,-1918.5 288.95,-1915 278.95,-1911.5 278.95,-1918.5" style=""/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@ -1,89 +0,0 @@
# Solidity API
## BaseUtils
### _setUsedTransactions
```solidity
function _setUsedTransactions(bytes32 message) internal
```
███ Helper FX ██████████████████████████████████████████████████████████
### usedTransactions
```solidity
function usedTransactions(bytes32 message) public view returns (bool used)
```
### _signerCheck
```solidity
function _signerCheck(bytes32 _message, bytes _signature) internal view
```
### _merkleVerify
```solidity
function _merkleVerify(bytes32[] _merkleProof, bytes32 _root, address _addr) internal pure
```
### _castBool
```solidity
function _castBool(bool _valid) internal pure returns (uint256 _validCasted)
```
### getStr
```solidity
function getStr(string str) public pure returns (bytes32 strEnc)
```
### _setSellerBalance
```solidity
function _setSellerBalance(address _sellerKey, contract ERC20 _erc20, uint256 _packed, bytes32 _pixTarget) internal
```
### _setValidState
```solidity
function _setValidState(address _sellerKey, contract ERC20 _erc20, uint256 _packed) internal
```
### _addSellerBalance
```solidity
function _addSellerBalance(address _sellerKey, contract ERC20 _erc20, uint256 _amount) internal
```
### _decSellerBalance
```solidity
function _decSellerBalance(address _sellerKey, contract ERC20 _erc20, uint256 _amount) internal
```
### __sellerBalance
```solidity
function __sellerBalance(address _sellerKey, contract ERC20 _erc20) internal view returns (uint256 _packed)
```
### _castAddrToKey
```solidity
function _castAddrToKey(address _addr) public pure returns (uint256 _key)
```
Public method that handles `address`
to `uint256` safe type casting.
_Function sighash: 0x4b2ae980._
### _castKeyToAddr
```solidity
function _castKeyToAddr(uint256 _key) public pure returns (address _addr)
```

View File

@ -1,96 +0,0 @@
# Solidity API
## Constants
### _ROOT_UPDATED_EVENT_SIGNATURE
```solidity
uint256 _ROOT_UPDATED_EVENT_SIGNATURE
```
███ Constants ██████████████████████████████████████████████████████████
### _ALLOWED_ERC20_UPDATED_EVENT_SIGNATURE
```solidity
uint256 _ALLOWED_ERC20_UPDATED_EVENT_SIGNATURE
```
### _TRUSTED_FORWARDER_UPDATED_EVENT_SIGNATURE
```solidity
uint256 _TRUSTED_FORWARDER_UPDATED_EVENT_SIGNATURE
```
### _SELLER_ALLOWLIST_SLOT_SEED
```solidity
uint256 _SELLER_ALLOWLIST_SLOT_SEED
```
_Seller casted to key => Seller's allowlist merkleroot.
mapping(uint256 => bytes32) public sellerAllowList;_
### _ALLOWED_ERC20_SLOT_SEED
```solidity
uint256 _ALLOWED_ERC20_SLOT_SEED
```
_Tokens allowed to serve as the underlying amount of a deposit.
mapping(ERC20 => bool) public allowedERC20s;_
### _SELLER_BALANCE_SLOT_SEED
```solidity
uint256 _SELLER_BALANCE_SLOT_SEED
```
_Value in custom storage slot given by:
mstore(0x20, token)
mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
mstore(0x00, seller)
let value := sload(keccak256(0x0c, 0x34))._
### BITMASK_SB_ENTRY
```solidity
uint256 BITMASK_SB_ENTRY
```
_The bitmask of `sellerBalance` entry._
### BITPOS_VALID
```solidity
uint256 BITPOS_VALID
```
_The bit position of `valid` in `sellerBalance`._
### WAD
```solidity
uint256 WAD
```
_The scalar of BRZ token._
### MAXBALANCE_UPPERBOUND
```solidity
uint256 MAXBALANCE_UPPERBOUND
```
### REPUTATION_LOWERBOUND
```solidity
uint256 REPUTATION_LOWERBOUND
```
### LOCKAMOUNT_UPPERBOUND
```solidity
uint256 LOCKAMOUNT_UPPERBOUND
```

View File

@ -1,29 +0,0 @@
# Solidity API
## DataTypes
### Lock
```solidity
struct Lock {
uint256 counter;
uint256 expirationBlock;
bytes32 pixTarget;
uint80 amount;
contract ERC20 token;
address buyerAddress;
address seller;
}
```
### LockStatus
```solidity
enum LockStatus {
Inexistent,
Active,
Expired,
Released
}
```

View File

@ -1,278 +0,0 @@
# Solidity API
## EventAndErrors
### DepositAdded
```solidity
event DepositAdded(address seller, contract ERC20 token, uint256 amount)
```
_0x63d8d7d5e63e9840ec91a12a160d27b7cfab294f6ba070b7359692acfe6b03bf_
### ValidSet
```solidity
event ValidSet(address seller, contract ERC20 token, bool state)
```
_0xca585721b6b442dc9183932f7c84dc2880efb67c4da52cc06873e78971105d49_
### DepositWithdrawn
```solidity
event DepositWithdrawn(address seller, contract ERC20 token, uint256 amount)
```
_0x2cd6435b1b961c13f55202979edd0765a809f69a539d8a477436c94c1211e43e_
### LockAdded
```solidity
event LockAdded(address buyer, uint256 lockID, address seller, uint256 amount)
```
_0x8fb3989f70bd172a37d15b41b015e48ea09d59329638377304a4198cd0c4ea65_
### LockReleased
```solidity
event LockReleased(address buyer, uint256 lockId, uint256 amount)
```
_0x364537f14276f2a0ce9905588413f96454cbb8fb2e4f5308389307c1098bede8_
### LockReturned
```solidity
event LockReturned(address buyer, uint256 lockId)
```
_0x830501e61b8b075e170b22a430e39454bdb12ed3e9620e586430b6ac00079da5_
### FundsWithdrawn
```solidity
event FundsWithdrawn(address owner, uint256 amount)
```
_0xeaff4b37086828766ad3268786972c0cd24259d4c87a80f9d3963a3c3d999b0d_
### RootUpdated
```solidity
event RootUpdated(address seller, bytes32 merkleRoot)
```
_0x0b294da292f26e55fd442b5c0164fbb9013036ff00c5cfdde0efd01c1baaf632_
### AllowedERC20Updated
```solidity
event AllowedERC20Updated(address token, bool state)
```
_0x5d6e86e5341d57a92c49934296c51542a25015c9b1782a1c2722a940131c3d9a_
### TrustedForwarderUpdated
```solidity
event TrustedForwarderUpdated(address forwarder, bool state)
```
_0xbee55516e29d3969d3cb8eb01351eb3c52d06f9e2435bd5a8bfe3647e185df92_
### ReputationUpdated
```solidity
event ReputationUpdated(address reputation)
```
_0xe127cf589a3879da0156d4a24f43b44f65cfa3570de594806b0bfa2fcf06884f_
### LockBlocksUpdated
```solidity
event LockBlocksUpdated(uint256 blocks)
```
_0x70fa43ca70216ad905ade86b9e650a691b2ce5a01980d0a81bdd8324141b8511_
### ValidSignersUpdated
```solidity
event ValidSignersUpdated(address[] signers)
```
_0x14a422d2412784a5749d03da98921fe468c98577b767851389a9f58ea5a363d7_
### OnlySeller
```solidity
error OnlySeller()
```
_Only seller could call this function.
`msg.sender` and the seller differ.
0x85d1f726_
### NotExpired
```solidity
error NotExpired()
```
_Lock not expired or already released.
Another lock with same ID is not expired yet.
0xd0404f85_
### LoopOverflow
```solidity
error LoopOverflow()
```
_Loop bounds have overflowed.
0xdfb035c9_
### InvalidDeposit
```solidity
error InvalidDeposit()
```
_Deposit not valid anymore.
0xb2e532de_
### NotEnoughTokens
```solidity
error NotEnoughTokens()
```
_Not enough token remaining on deposit.
0x22bbb43c_
### AlreadyReleased
```solidity
error AlreadyReleased()
```
_Lock already released or returned.
0x63b4904e_
### TxAlreadyUsed
```solidity
error TxAlreadyUsed()
```
_Transaction already used to unlock payment.
0xf490a6ea_
### InvalidSigner
```solidity
error InvalidSigner()
```
_Signer is not a valid signer.
0x815e1d64_
### AddressDenied
```solidity
error AddressDenied()
```
_Address doesn't exist in a MerkleTree.
Address not allowed as relayer.
0x3b8474be_
### LengthMismatch
```solidity
error LengthMismatch()
```
_Arrays' length don't match.
0xff633a38_
### NoTokens
```solidity
error NoTokens()
```
_No tokens array provided as argument.
0xdf957883_
### TokenDenied
```solidity
error TokenDenied()
```
_Token address not allowed to be deposited.
0x1578328e_
### AmountNotAllowed
```solidity
error AmountNotAllowed()
```
_Wished amount to be locked exceeds the limit allowed.
0x1c18f846_
### StaticCallFailed
```solidity
error StaticCallFailed()
```
_Reverts when success return value returns false.
0xe10bf1cc_
### LockExpired
```solidity
error LockExpired()
```
_Reverts on an expired lock.
0xf6fafba0_
### DecOverflow
```solidity
error DecOverflow()
```
_0xce3a3d37_
### MaxBalExceeded
```solidity
error MaxBalExceeded()
```
_0xf3fb0eb9_
### EmptyPixTarget
```solidity
error EmptyPixTarget()
```
_0x6a3bc53e_
### NotInitialized
```solidity
error NotInitialized()
```
_0x87138d5c_

View File

@ -1,98 +0,0 @@
# Solidity API
## OwnerSettings
### reputation
```solidity
contract IReputation reputation
```
_List of valid Bacen signature addresses
mapping(uint256 => bool) public validBacenSigners;
Value in custom storage slot given by:
let value := sload(shl(12, address))._
### defaultLockBlocks
```solidity
uint256 defaultLockBlocks
```
_Default blocks that lock will hold tokens._
### constructor
```solidity
constructor(uint256 defaultBlocks, address[] validSigners, address _reputation, contract ERC20[] tokens, bool[] tokenStates) internal
```
███ Constructor ████████████████████████████████████████████████████████
### setTrustedFowarders
```solidity
function setTrustedFowarders(address[] forwarders, bool[] states) external
```
███ Owner Only █████████████████████████████████████████████████████████
### withdrawBalance
```solidity
function withdrawBalance() external
```
_Contract's underlying balance withdraw method.
Function sighash: 0x5fd8c710._
### setReputation
```solidity
function setReputation(contract IReputation _reputation) public
```
### setDefaultLockBlocks
```solidity
function setDefaultLockBlocks(uint256 _blocks) public
```
### setValidSigners
```solidity
function setValidSigners(address[] _validSigners) public
```
### tokenSettings
```solidity
function tokenSettings(contract ERC20[] _tokens, bool[] _states) public
```
### validBacenSigners
```solidity
function validBacenSigners(uint256 signer) public view returns (bool valid)
```
███ View FX ████████████████████████████████████████████████████████████
### sellerAllowList
```solidity
function sellerAllowList(address sellerKey) public view returns (bytes32 root)
```
### allowedERC20s
```solidity
function allowedERC20s(contract ERC20 erc20) public view returns (bool state)
```
### _limiter
```solidity
function _limiter(uint256 _userCredit) internal view returns (uint256 _spendLimit)
```

View File

@ -1,42 +0,0 @@
# Solidity API
## Owned
Simple single owner authorization mixin.
### Unauthorized
```solidity
error Unauthorized()
```
### OwnerUpdated
```solidity
event OwnerUpdated(address user, address newOwner)
```
### owner
```solidity
address owner
```
### onlyOwner
```solidity
modifier onlyOwner()
```
### constructor
```solidity
constructor(address _owner) internal
```
### setOwner
```solidity
function setOwner(address newOwner) public virtual
```

View File

@ -1,10 +0,0 @@
# Solidity API
## IReputation
### limiter
```solidity
function limiter(uint256 _userCredit) external pure returns (uint256 _spendLimit)
```

View File

@ -1,53 +0,0 @@
# Solidity API
## Context
_Provides information about the current execution context, including the
sender of the transaction and its data. While these are generally available
via msg.sender and msg.data, they should not be accessed in such a direct
manner, since when dealing with meta-transactions the account sending and
paying for execution may not be the actual sender (as far as an application
is concerned).
This contract is only required for intermediate, library-like contracts._
### _msgSender
```solidity
function _msgSender() internal view virtual returns (address)
```
### _msgData
```solidity
function _msgData() internal view virtual returns (bytes)
```
## ERC2771Context
_Context variant with ERC2771 support._
### trustedForwarders
```solidity
mapping(address => bool) trustedForwarders
```
### _msgSender
```solidity
function _msgSender() internal view virtual returns (address sender)
```
### isTrustedForwarder
```solidity
function isTrustedForwarder(address forwarder) public view virtual returns (bool)
```
### _msgData
```solidity
function _msgData() internal view virtual returns (bytes)
```

View File

@ -1,224 +0,0 @@
# MockToken
## Methods
### DOMAIN_SEPARATOR
```solidity
function DOMAIN_SEPARATOR() external view returns (bytes32)
```
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | bytes32 | undefined |
### allowance
```solidity
function allowance(address, address) external view returns (uint256)
```
#### Parameters
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | address | undefined |
| \_1 | address | undefined |
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
### approve
```solidity
function approve(address spender, uint256 amount) external nonpayable returns (bool)
```
#### Parameters
| Name | Type | Description |
| ------- | ------- | ----------- |
| spender | address | undefined |
| amount | uint256 | undefined |
#### Returns
| Name | Type | Description |
| ---- | ---- | ----------- |
| \_0 | bool | undefined |
### balanceOf
```solidity
function balanceOf(address) external view returns (uint256)
```
#### Parameters
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | address | undefined |
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
### decimals
```solidity
function decimals() external view returns (uint8)
```
#### Returns
| Name | Type | Description |
| ---- | ----- | ----------- |
| \_0 | uint8 | undefined |
### name
```solidity
function name() external view returns (string)
```
#### Returns
| Name | Type | Description |
| ---- | ------ | ----------- |
| \_0 | string | undefined |
### nonces
```solidity
function nonces(address) external view returns (uint256)
```
#### Parameters
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | address | undefined |
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
### permit
```solidity
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external nonpayable
```
#### Parameters
| Name | Type | Description |
| -------- | ------- | ----------- |
| owner | address | undefined |
| spender | address | undefined |
| value | uint256 | undefined |
| deadline | uint256 | undefined |
| v | uint8 | undefined |
| r | bytes32 | undefined |
| s | bytes32 | undefined |
### symbol
```solidity
function symbol() external view returns (string)
```
#### Returns
| Name | Type | Description |
| ---- | ------ | ----------- |
| \_0 | string | undefined |
### totalSupply
```solidity
function totalSupply() external view returns (uint256)
```
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
### transfer
```solidity
function transfer(address to, uint256 amount) external nonpayable returns (bool)
```
#### Parameters
| Name | Type | Description |
| ------ | ------- | ----------- |
| to | address | undefined |
| amount | uint256 | undefined |
#### Returns
| Name | Type | Description |
| ---- | ---- | ----------- |
| \_0 | bool | undefined |
### transferFrom
```solidity
function transferFrom(address from, address to, uint256 amount) external nonpayable returns (bool)
```
#### Parameters
| Name | Type | Description |
| ------ | ------- | ----------- |
| from | address | undefined |
| to | address | undefined |
| amount | uint256 | undefined |
#### Returns
| Name | Type | Description |
| ---- | ---- | ----------- |
| \_0 | bool | undefined |
## Events
### Approval
```solidity
event Approval(address indexed owner, address indexed spender, uint256 amount)
```
#### Parameters
| Name | Type | Description |
| ----------------- | ------- | ----------- |
| owner `indexed` | address | undefined |
| spender `indexed` | address | undefined |
| amount | uint256 | undefined |
### Transfer
```solidity
event Transfer(address indexed from, address indexed to, uint256 amount)
```
#### Parameters
| Name | Type | Description |
| -------------- | ------- | ----------- |
| from `indexed` | address | undefined |
| to `indexed` | address | undefined |
| amount | uint256 | undefined |

View File

@ -1,16 +0,0 @@
# Solidity API
## MockToken
### constructor
```solidity
constructor(uint256 supply) public
```
### mint
```solidity
function mint(address[] to, uint256 value) public virtual
```

View File

@ -1,128 +0,0 @@
# Solidity API
## ERC20
Modern and gas efficient ERC20 + EIP-2612 implementation.
_Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it._
### Transfer
```solidity
event Transfer(address from, address to, uint256 amount)
```
### Approval
```solidity
event Approval(address owner, address spender, uint256 amount)
```
### name
```solidity
string name
```
### symbol
```solidity
string symbol
```
### decimals
```solidity
uint8 decimals
```
### totalSupply
```solidity
uint256 totalSupply
```
### balanceOf
```solidity
mapping(address => uint256) balanceOf
```
### allowance
```solidity
mapping(address => mapping(address => uint256)) allowance
```
### INITIAL_CHAIN_ID
```solidity
uint256 INITIAL_CHAIN_ID
```
### INITIAL_DOMAIN_SEPARATOR
```solidity
bytes32 INITIAL_DOMAIN_SEPARATOR
```
### nonces
```solidity
mapping(address => uint256) nonces
```
### constructor
```solidity
constructor(string _name, string _symbol, uint8 _decimals) internal
```
### approve
```solidity
function approve(address spender, uint256 amount) public virtual returns (bool)
```
### transfer
```solidity
function transfer(address to, uint256 amount) public virtual returns (bool)
```
### transferFrom
```solidity
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool)
```
### permit
```solidity
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual
```
### DOMAIN_SEPARATOR
```solidity
function DOMAIN_SEPARATOR() public view virtual returns (bytes32)
```
### computeDomainSeparator
```solidity
function computeDomainSeparator() internal view virtual returns (bytes32)
```
### _mint
```solidity
function _mint(address to, uint256 amount) internal virtual
```
### _burn
```solidity
function _burn(address from, uint256 amount) internal virtual
```

View File

@ -1,19 +0,0 @@
# Counters
_buf0t9Modified from OpenZeppelin Contracts (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Counters.sol)_
> Counters
Provides counters that can only be incremented, decrementedor reset.
_Include with `using Counters for Counters.Counter;`_
## Errors
### DecOverflow
```solidity
error DecOverflow()
```
_0xce3a3d37_

View File

@ -1,38 +0,0 @@
# Solidity API
## ECDSA
Gas optimized ECDSA wrapper.
### InvalidSignature
```solidity
error InvalidSignature()
```
_The signature is invalid._
### recoverCalldata
```solidity
function recoverCalldata(bytes32 hash, bytes signature) internal view returns (address result)
```
_Recovers the signer's address from a message digest `hash`,
and the `signature`.
This function does NOT accept EIP-2098 short form signatures.
Use `recover(bytes32 hash, bytes32 r, bytes32 vs)` for EIP-2098
short form signatures instead._
### toEthSignedMessageHash
```solidity
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result)
```
_Returns an Ethereum Signed Message, created from a `hash`.
This produces a hash corresponding to the one signed with the
[`eth_sign`](https://eth.wiki/json-rpc/API#eth_sign)
JSON-RPC method as part of EIP-191._

View File

@ -1,5 +0,0 @@
# FixedPointMathLib
_Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)_
Arithmetic library with operations for fixed-point numbers.

View File

@ -1,14 +0,0 @@
# Solidity API
## MerkleProofLib
Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
### verify
```solidity
function verify(bytes32[] proof, bytes32 root, bytes32 leaf) internal pure returns (bool isValid)
```
_Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`._

View File

@ -1,50 +0,0 @@
# Solidity API
## Multicall
Contract that batches view function calls and aggregates their results.
### CallFailed
```solidity
error CallFailed(string reason)
```
_0x_
### Call
```solidity
struct Call {
address target;
bytes callData;
}
```
### Result
```solidity
struct Result {
bool success;
bytes returnData;
}
```
### constructor
```solidity
constructor() public payable
```
### mtc1
```solidity
function mtc1(struct Multicall.Call[] calls) external returns (uint256, bytes[])
```
### mtc2
```solidity
function mtc2(struct Multicall.Call[] calls) external returns (uint256, bytes32, struct Multicall.Result[])
```

View File

@ -1,34 +0,0 @@
# Solidity API
## ReentrancyGuard
Reentrancy protection for smart contracts.
### Reentrancy
```solidity
error Reentrancy()
```
### nonReentrant
```solidity
modifier nonReentrant()
```
### setReentrancyGuard
```solidity
function setReentrancyGuard() internal virtual
```
_Check guard sentinel value and set it._
### clearReentrancyGuard
```solidity
function clearReentrancyGuard() internal virtual
```
_Unset sentinel value._

View File

@ -1,62 +0,0 @@
# Solidity API
## SafeTransferLib
Safe ETH and ERC20 transfer library that gracefully handles missing return values.
_Caution! This library won't check that a token has code, responsibility is delegated to the caller._
### ETHTransferFailed
```solidity
error ETHTransferFailed()
```
_The ETH transfer has failed._
### TransferFromFailed
```solidity
error TransferFromFailed()
```
_The ERC20 `transferFrom` has failed._
### TransferFailed
```solidity
error TransferFailed()
```
_The ERC20 `transfer` has failed._
### safeTransferETH
```solidity
function safeTransferETH(address to, uint256 amount) internal
```
_Sends `amount` (in wei) ETH to `to`.
Reverts upon failure._
### safeTransferFrom
```solidity
function safeTransferFrom(contract ERC20 token, address from, address to, uint256 amount) internal
```
_Sends `amount` of ERC20 `token` from `from` to `to`.
Reverts upon failure.
The `from` account must have at least `amount` approved for
the current contract to manage._
### safeTransfer
```solidity
function safeTransfer(contract ERC20 token, address to, uint256 amount) internal
```
_Sends `amount` of ERC20 `token` from the current contract to `to`.
Reverts upon failure._

View File

@ -1,207 +0,0 @@
# Solidity API
## P2PIX
### lockCounter
```solidity
uint256 lockCounter
```
### mapLocks
```solidity
mapping(uint256 => struct DataTypes.Lock) mapLocks
```
_List of Locks._
### userRecord
```solidity
mapping(uint256 => uint256) userRecord
```
_Stores an relayer's last computed credit._
### constructor
```solidity
constructor(uint256 defaultBlocks, address[] validSigners, address _reputation, contract ERC20[] tokens, bool[] tokenStates) public payable
```
### deposit
```solidity
function deposit(string pixTarget, bytes32 allowlistRoot, contract ERC20 token, uint96 amount, bool valid) public
```
Creates a deposit order based on a seller's
offer of an amount of ERC20 tokens.
Seller needs to send his tokens to the P2PIX smart contract.
_Function sighash: 0x5e918943_
#### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| pixTarget | string | Pix key destination provided by the offer's seller. |
| allowlistRoot | bytes32 | Optional allow list merkleRoot update `bytes32` value. as the deposit identifier. |
| token | contract ERC20 | |
| amount | uint96 | |
| valid | bool | |
### setValidState
```solidity
function setValidState(contract ERC20 token, bool state) public
```
Enables seller to invalidate future
locks made to his/her token offering order.
This function does not affect any ongoing active locks.
_Function sighash: 0x6d82d9e0_
### lock
```solidity
function lock(address seller, contract ERC20 token, uint80 amount, bytes32[] merkleProof, uint256[] expiredLocks) public returns (uint256 lockID)
```
Public method designed to lock an remaining amount of
the deposit order of a seller.
Transaction forwarding must leave `merkleProof` empty;
otherwise, the trustedForwarder must be previously added
to a seller whitelist.
This method can be performed either by:
- An user allowed via the seller's allowlist;
- An user with enough userRecord to lock the wished amount;
There can only exist a lock per each `_amount` partitioned
from the total `remaining` value.
Locks can only be performed in valid orders.
_Function sighash: 0xdc43221c_
#### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| seller | address | |
| token | contract ERC20 | |
| amount | uint80 | The deposit's remaining amount wished to be locked. |
| merkleProof | bytes32[] | Provided as a pass if the `msg.sender` is in the seller's allowlist; Left empty otherwise; |
| expiredLocks | uint256[] | An array of identifiers to be provided so to unexpire locks using this transaction gas push. |
#### Return Values
| Name | Type | Description |
| ---- | ---- | ----------- |
| lockID | uint256 | The lock identifier. |
### release
```solidity
function release(uint256 lockID, bytes32 pixTimestamp, bytes signature) public
```
Lock release method that liquidate lock
orders and distributes relayer fees.
This method can be called by any public actor
as long the signature provided is valid.
`relayerPremium` gets splitted equaly
if relayer addresses differ.
If the `msg.sender` of this method and `l.relayerAddress` are the same,
`msg.sender` accrues both l.amount and l.relayerPremium as userRecord credit.
In case of they differing:
- `lock` caller gets accrued with `l.amount` as userRecord credit;
- `release` caller gets accrued with `l.relayerPremium` as userRecord credit;
_Function sighash: 0x11fc7f9a_
### unlockExpired
```solidity
function unlockExpired(uint256[] lockIDs) public
```
Unlocks expired locks.
Triggered in the callgraph by both `lock` and `withdraw` functions.
This method can also have any public actor as its `tx.origin`.
For each successfull unexpired lock recovered,
`userRecord[_castAddrToKey(l.relayerAddress)]` is decreased by half of its value.
_Function sighash: 0xb0983d39_
### withdraw
```solidity
function withdraw(contract ERC20 token, uint256 amount, uint256[] expiredLocks) public
```
Seller's expired deposit fund sweeper.
A seller may use this method to recover
tokens from expired deposits.
_Function sighash: 0xfb8c5ef0_
### setRoot
```solidity
function setRoot(address addr, bytes32 merkleroot) public
```
### receive
```solidity
receive() external payable
```
### _addLock
```solidity
function _addLock(uint256 _bal, struct DataTypes.Lock _l) internal
```
### getBalance
```solidity
function getBalance(address seller, contract ERC20 token) public view returns (uint256 bal)
```
### getValid
```solidity
function getValid(address seller, contract ERC20 token) public view returns (bool valid)
```
### getPixTarget
```solidity
function getPixTarget(address seller, contract ERC20 token) public view returns (bytes32 pixTarget)
```
### getPixTargetString
```solidity
function getPixTargetString(address seller, contract ERC20 token) public view returns (string pixTarget)
```
### getBalances
```solidity
function getBalances(address[] sellers, contract ERC20 token) external view returns (uint256[])
```
### getLocksStatus
```solidity
function getLocksStatus(uint256[] ids) external view returns (uint256[], enum DataTypes.LockStatus[])
```
External getter that returns the status of a lockIDs array.
Call will not revert if provided with an empty array as parameter.
_Function sighash: 0x49ef8448_

12
hardhat.config.js Normal file
View File

@ -0,0 +1,12 @@
require("@nomiclabs/hardhat-waffle");
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.17",
networks: {
hardhat: {
blockGasLimit: 30000000,
//hardfork: 'london'
}
}
};

View File

@ -1,157 +0,0 @@
import "@nomicfoundation/hardhat-chai-matchers";
import "@nomicfoundation/hardhat-toolbox";
import { config as dotenvConfig } from "dotenv";
import { HardhatUserConfig } from "hardhat/config";
import { NetworkUserConfig } from "hardhat/types";
import "hardhat-contract-sizer";
import { resolve } from "path";
import "solidity-docgen";
dotenvConfig({ path: resolve(__dirname, "./.env") });
const mnemonic: string | undefined = process.env.MNEMONIC;
if (!mnemonic) {
throw new Error("Please set your MNEMONIC in a .env file");
}
const infuraApiKey: string | undefined =
process.env.INFURA_API_KEY;
if (!infuraApiKey) {
throw new Error(
"Please set your INFURA_API_KEY in a .env file",
);
}
const alchemyApiKey: string | undefined =
process.env.ALCHEMY_API_KEY;
if (!alchemyApiKey) {
throw new Error(
"Please set your ALCHEMY_API_KEY in a .env file",
);
}
const chainIds = {
// "{INSERT_NAME}": {INSERT_ID},
hardhat: 31337,
mainnet: 1,
sepolia: 11155111,
goerli: 5,
"polygon-mumbai": 80001,
rootstock:30,
rsktestnet:31,
};
function getChainConfig(
chain: keyof typeof chainIds,
): NetworkUserConfig {
let jsonRpcUrl: string;
switch (chain) {
case "polygon-mumbai":
jsonRpcUrl =
"https://polygon-mumbai.g.alchemy.com/v2/" +
alchemyApiKey;
break;
case "rsktestnet":
jsonRpcUrl = "https://public-node.testnet.rsk.co/";
break;
case "rootstock":
jsonRpcUrl = "https://public-node.rsk.co/";
break;
case "sepolia":
jsonRpcUrl = "https://rpc.sepolia.online";
break
default:
jsonRpcUrl =
"https://" + chain + ".infura.io/v3/" + infuraApiKey;
}
return {
// Comment out for default hardhat account settings
accounts: {
count: 10,
mnemonic,
path: "m/44'/60'/0'/0",
},
// gasPrice: 8000000000,
chainId: chainIds[chain],
url: jsonRpcUrl,
};
}
const config: HardhatUserConfig = {
defaultNetwork: "hardhat",
etherscan: {
apiKey: {
mainnet: process.env.ETHERSCAN_API_KEY || "",
rinkeby: process.env.ETHERSCAN_API_KEY || "",
goerli: process.env.ETHERSCAN_API_KEY || "",
polygonMumbai: process.env.POLYGONSCAN_API_KEY || "",
},
},
gasReporter: {
enabled: !!(
process.env.REPORT_GAS &&
process.env.REPORT_GAS != "false"
),
showTimeSpent: true,
showMethodSig: true,
token: "ETH",
currency: "USD",
// gasPriceApi: process.env.GASPRICE_API_ENDPOINT,
coinmarketcap: process.env.COINMARKETCAP_API_KEY,
excludeContracts: [],
src: "./contracts",
},
networks: {
hardhat: {
blockGasLimit: 30000000,
accounts: {
mnemonic,
},
chainId: chainIds.hardhat,
},
// network: getChainConfig("{INSERT_NAME}"),
mainnet: getChainConfig("mainnet"),
goerli: getChainConfig("goerli"),
sepolia: getChainConfig("sepolia"),
"polygon-mumbai": getChainConfig("polygon-mumbai"),
rootstock: getChainConfig("rootstock"),
rsktestnet: getChainConfig("rsktestnet"),
},
paths: {
artifacts: "./artifacts",
cache: "./cache",
sources: "./contracts",
tests: "./test",
},
solidity: {
version: "0.8.19",
settings: {
viaIR: true,
evmVersion: "paris",
optimizer: {
enabled: true,
runs: 20_000,
details: {
deduplicate: true,
cse: true,
constantOptimizer: true,
peephole: true,
jumpdestRemover: true,
yul: true,
yulDetails: {
stackAllocation: true,
},
},
},
},
},
typechain: {
outDir: "src/types",
target: "ethers-v5",
},
docgen: {
pages: "files",
}
};
export default config;

29715
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,95 +1,27 @@
{
"name": "p2pix-smart-contracts",
"version": "2.0.0",
"version": "1.0.0",
"description": "Repository for P2Pix EVM contracts to be imported by the project.",
"homepage": "https://github.com/doiim/p2pix-smart-contracts#readme",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/doiim/p2pix-smart-contracts.git"
},
"author": "Filipe Soccol (doiim)",
"license": "MIT",
"bugs": {
"url": "https://github.com/doiim/p2pix-smart-contracts/issues"
},
"scripts": {
"clean": "shx rm -rf ./artifacts ./cache ./coverage ./src/types ./coverage.json && yarn typechain",
"commit": "git-cz",
"compile": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat compile",
"typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain",
"test": "hardhat test",
"deploy1:localhost": "hardhat run scripts/1-deploy-mockToken.ts --network localhost",
"deploy2:localhost": "hardhat run scripts/2-deploy-p2pix.ts --network localhost",
"deploy1:goerli": "hardhat run scripts/1-deploy-mockToken.ts --network goerli",
"deploy2:goerli": "hardhat run scripts/2-deploy-p2pix.ts --network goerli",
"deploy1:mumbai": "hardhat run scripts/1-deploy-mockToken.ts --network polygon-mumbai",
"deploy2:mumbai": "hardhat run scripts/2-deploy-p2pix.ts --network polygon-mumbai",
"coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"test/**/*.ts\" && yarn typechain",
"lint": "yarn lint:sol && yarn lint:ts && yarn prettier:check",
"lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"",
"lint:ts": "eslint --config ./.eslintrc.yaml --ignore-path ./.eslintignore --ext .js,.ts .",
"_postinstall": "husky install",
"postpublish": "pinst --enable",
"prepublishOnly": "pinst --disable",
"prettier": "prettier --config ./.prettierrc.yaml --write \"**/*.{js,json,md,sol,ts,yaml,yml}\"",
"prettier:check": "prettier --check --config ./.prettierrc.yaml \"**/*.{js,json,md,sol,ts,yaml,yml}\""
},
"homepage": "https://github.com/doiim/p2pix-smart-contracts#readme",
"devDependencies": {
"@commitlint/cli": "^17.2.0",
"@commitlint/config-conventional": "^17.2.0",
"@ethersproject/abi": "^5.7.0",
"@ethersproject/abstract-signer": "^5.7.0",
"@ethersproject/bignumber": "^5.7.0",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"@nomicfoundation/hardhat-chai-matchers": "^1.0.4",
"@nomicfoundation/hardhat-network-helpers": "1.0.6",
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
"@nomicfoundation/hardhat-viem": "^2.0.6",
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@nomiclabs/hardhat-etherscan": "^3.1.2",
"@trivago/prettier-plugin-sort-imports": "^3.4.0",
"@typechain/ethers-v5": "^10.1.1",
"@typechain/hardhat": "^6.1.4",
"@types/chai": "^4.3.3",
"@types/fs-extra": "^9.0.13",
"@types/mocha": "^9.1.1",
"@types/node": "^18.11.9",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0",
"@nomiclabs/hardhat-waffle": "^2.0.3",
"chai": "^4.3.6",
"chalk": "4.x",
"commitizen": "^4.2.5",
"cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0",
"dotenv": "^16.0.3",
"eslint": "^8.26.0",
"eslint-config-prettier": "^8.5.0",
"ethers": "^5.7.2",
"fs-extra": "^10.1.0",
"hardhat": "^2.12.2",
"hardhat-contract-sizer": "^2.8.0",
"hardhat-gas-reporter": "^1.0.9",
"husky": "^8.0.1",
"keccak256": "^1.0.6",
"lint-staged": "^13.0.3",
"lodash": "^4.17.21",
"merkletreejs": "^0.2.32",
"mocha": "^10.1.0",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"shx": "^0.3.4",
"solhint": "^3.3.7",
"solhint-plugin-prettier": "^0.0.5",
"solidity-coverage": "^0.8.2",
"solidity-docgen": "^0.6.0-beta.36",
"ts-generator": "^0.1.1",
"ts-node": "^10.9.1",
"typechain": "^8.1.1",
"typescript": "^5",
"viem": "^2.23.14"
"hardhat": "^2.12.0"
},
"files": [
"/contracts"
],
"packageManager": "yarn@3.2.1",
"publishConfig": {
"access": "public"
"dependencies": {
"@openzeppelin/contracts": "^4.7.3"
}
}

View File

@ -1,56 +0,0 @@
import "@nomiclabs/hardhat-ethers";
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/interfaces";
let deploysJson: Deploys;
const supply: BigNumber = ethers.utils.parseEther("20000000");
const main = async () => {
try {
const data = fs.readFileSync(
`./deploys/${network.name}.json`,
{ encoding: "utf-8" },
);
deploysJson = JSON.parse(data);
} catch (err) {
console.log("Error loading Master address: ", err);
process.exit(1);
}
const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with ${deployer.address}`);
const ERC20Factory = await ethers.getContractFactory(
"MockToken",
);
const erc20 = await ERC20Factory.deploy(supply);
await erc20.deployed();
deploysJson.token = erc20.address;
console.log("🚀 Mock Token Deployed:", erc20.address);
await erc20.deployTransaction.wait(6);
fs.writeFileSync(
`./deploys/${network.name}.json`,
JSON.stringify(deploysJson, undefined, 2),
);
/* UNCOMMENT WHEN DEPLOYING TO MAINNET/PUBLIC TESTNETS */
// verify
// await hre.run("verify:verify", {
// address: erc20.address,
// constructorArguments: [supply],
// });
};
main()
.then(() => process.exit(0))
.catch(error => {
console.log(error);
process.exit(1);
});

31
scripts/1-deploy-p2pix.js Normal file
View File

@ -0,0 +1,31 @@
const fs = require('fs');
const { network } = require("hardhat");
async function main() {
let deploysJson = {}
try {
const data = fs.readFileSync(`./deploys/${network.name}.json`, {encoding:"utf-8"});
deploysJson = JSON.parse(data);
} catch (err) {
console.log('Error loading Master address: ', err);
process.exit(1);
}
const P2PIX = await ethers.getContractFactory("P2PIX");
const p2pix = await P2PIX.deploy(2, deploysJson.signers);
await p2pix.deployed();
deploysJson.p2pix = p2pix.address
console.log("🚀 P2PIX Deployed:", p2pix.address);
fs.writeFileSync(`./deploys/${network.name}.json`, JSON.stringify(deploysJson, undefined, 2));
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@ -0,0 +1,31 @@
const fs = require('fs');
const { network } = require("hardhat");
async function main() {
let deploysJson = {}
try {
const data = fs.readFileSync(`./deploys/${network.name}.json`, {encoding:"utf-8"});
deploysJson = JSON.parse(data);
} catch (err) {
console.log('Error loading Master address: ', err);
process.exit(1);
}
const ERC20Factory = await ethers.getContractFactory("MockToken");
const erc20 = await ERC20Factory.deploy(ethers.utils.parseEther('20000000', 'wei'));
await erc20.deployed();
deploysJson.token = erc20.address
console.log("🚀 Mock Token Deployed:", erc20.address);
fs.writeFileSync(`./deploys/${network.name}.json`, JSON.stringify(deploysJson, undefined, 2));
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@ -1,85 +0,0 @@
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/interfaces";
let deploysJson: Deploys;
const main = async () => {
try {
const data = fs.readFileSync(
`./deploys/${network.name}.json`,
{ encoding: "utf-8" },
);
deploysJson = JSON.parse(data);
} catch (err) {
console.log("Error loading Master address: ", err);
process.exit(1);
}
const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with ${deployer.address}`);
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(
10,
deploysJson.signers,
reputation.address,
[deploysJson.token],
[true],
);
await p2pix.deployed();
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(
`./deploys/${network.name}.json`,
JSON.stringify(deploysJson, undefined, 2),
);
/* UNCOMMENT WHEN DEPLOYING TO MAINNET/PUBLIC TESTNETS */
//verify
// await hre.run("verify:verify", {
// address: p2pix.address,
// constructorArguments: [
// 10,
// deploysJson.signers,
// reputation.address,
// [deploysJson.token],
// [true],
// ],
// });
// await hre.run("verify:verify", {
// address: reputation.address,
// constructorArguments: [],
// });
// await hre.run("verify:verify", {
// address: mutlicall.address,
// constructorArguments: [],
// });
};
main()
.then(() => process.exit(0))
.catch(error => {
console.log(error);
process.exit(1);
});

View File

@ -1,38 +0,0 @@
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-etherscan";
import * as fs from "fs";
import { ethers, network } from "hardhat";
import { Deploys } from "../test/utils/interfaces";
import { P2PIX__factory } from "../src/types";
let deploysJson: Deploys;
const main = async () => {
try {
const data = fs.readFileSync(
`./deploys/${network.name}.json`,
{ encoding: "utf-8" },
);
deploysJson = JSON.parse(data);
} catch (err) {
console.log("Error loading Master address: ", err);
process.exit(1);
}
const [deployer] = await ethers.getSigners();
console.log(`Signing transactions with ${deployer.address}`);
const iface = new ethers.utils.Interface(P2PIX__factory.abi);
const calldata = iface.encodeFunctionData("setDefaultLockBlocks", ["10000"]);
const tx = await deployer.sendTransaction({to:deploysJson.p2pix, data: calldata});
const done = await tx.wait();
console.log(done.transactionHash);
};
main()
.then(() => process.exit(0))
.catch(error => {
console.log(error);
process.exit(1);
});

115
test/1-deposit.test.js Normal file
View File

@ -0,0 +1,115 @@
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("P2PIX deposit test", function () {
let owner, wallet2, wallet3, wallet4;
let p2pix; // Contract instance
let erc20; // Token instance
let depositID;
it("Will deploy contracts", async function () {
[owner, wallet2, wallet3, wallet4] = await ethers.getSigners();
const ERC20Factory = await ethers.getContractFactory("MockToken");
erc20 = await ERC20Factory.deploy(ethers.utils.parseEther('20000000', 'wei'));
await erc20.deployed();
// Check initial balance
expect(await erc20.balanceOf(owner.address)).to.equal(ethers.utils.parseEther('20000000', 'wei'));
const P2PIX = await ethers.getContractFactory("P2PIX");
p2pix = await P2PIX.deploy(2, [owner.address, wallet2.address]);
await p2pix.deployed();
// Verify values at deployment
expect(await p2pix.validBacenSigners(owner.address)).to.equal(true);
expect(await p2pix.validBacenSigners(wallet2.address)).to.equal(true);
});
it("Should allow create a deposit", async function () {
let transaction = await erc20.approve(p2pix.address,ethers.utils.parseEther('1000'));
await expect(transaction).to.emit(erc20, 'Approval').withArgs(
owner.address,
p2pix.address,
ethers.utils.parseEther('1000')
)
transaction = await p2pix.deposit(
erc20.address,
ethers.utils.parseEther('1000'),
'SELLER PIX KEY',
{value:ethers.utils.parseEther('0.1')}
);
depositID = ethers.utils.solidityKeccak256(['string', 'uint256'], ['SELLER PIX KEY', ethers.utils.parseEther('1000')])
await expect(transaction).to.emit(p2pix, 'DepositAdded').withArgs(
owner.address,
depositID,
erc20.address,
ethers.utils.parseEther('0.1'),
ethers.utils.parseEther('1000')
)
})
it("Should prevent create same deposit", async function () {
await expect(p2pix.deposit(
erc20.address,
ethers.utils.parseEther('1000'),
'SELLER PIX KEY',
{value:ethers.utils.parseEther('0.1')}
))
.to.be.revertedWith('P2PIX: Deposit already exist and it is still valid');
})
it("Should allow cancel the deposit", async function () {
const transaction = await p2pix.cancelDeposit(depositID);
await expect(transaction).to.emit(p2pix, 'DepositClosed').withArgs(
owner.address,
depositID
)
})
it("Should allow recreate the deposit", async function () {
let transaction = await erc20.approve(p2pix.address,ethers.utils.parseEther('1000'));
await expect(transaction).to.emit(erc20, 'Approval').withArgs(
owner.address,
p2pix.address,
ethers.utils.parseEther('1000')
)
transaction = await p2pix.deposit(
erc20.address,
ethers.utils.parseEther('1000'),
'SELLER PIX KEY',
{value:ethers.utils.parseEther('0.1')}
);
depositID = ethers.utils.solidityKeccak256(['string', 'uint256'], ['SELLER PIX KEY', ethers.utils.parseEther('1000')])
await expect(transaction).to.emit(p2pix, 'DepositAdded').withArgs(
owner.address,
depositID,
erc20.address,
ethers.utils.parseEther('0.1'),
ethers.utils.parseEther('1000')
)
})
it("Should allow cancel the deposit again", async function () {
const transaction = await p2pix.cancelDeposit(depositID);
await expect(transaction).to.emit(p2pix, 'DepositClosed').withArgs(
owner.address,
depositID
)
})
it("Should allow withdraw the deposit", async function () {
const transaction = await p2pix.withdraw(depositID, []);
await expect(transaction).to.emit(p2pix, 'DepositWithdrawn').withArgs(
owner.address,
depositID,
ethers.utils.parseEther('1000')
)
})
})

241
test/2-lock-release.test.js Normal file
View File

@ -0,0 +1,241 @@
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("P2PIX lock/release test", function () {
let owner, wallet2, wallet3, wallet4;
let p2pix; // Contract instance
let erc20; // Token instance
let depositID, lockID;
it("Will deploy contracts", async function () {
[owner, wallet2, wallet3, wallet4] = await ethers.getSigners();
const ERC20Factory = await ethers.getContractFactory("MockToken");
erc20 = await ERC20Factory.deploy(ethers.utils.parseEther('20000000', 'wei'));
await erc20.deployed();
// Check initial balance
expect(await erc20.balanceOf(owner.address)).to.equal(ethers.utils.parseEther('20000000', 'wei'));
const P2PIX = await ethers.getContractFactory("P2PIX");
p2pix = await P2PIX.deploy(3, [owner.address, wallet2.address]);
await p2pix.deployed();
// Verify values at deployment
expect(await p2pix.validBacenSigners(owner.address)).to.equal(true);
expect(await p2pix.validBacenSigners(wallet2.address)).to.equal(true);
expect(await p2pix.validBacenSigners(wallet3.address)).to.equal(false);
});
it("Should allow create a deposit", async function () {
let transaction = await erc20.approve(p2pix.address,ethers.utils.parseEther('1000'));
await expect(transaction).to.emit(erc20, 'Approval').withArgs(
owner.address,
p2pix.address,
ethers.utils.parseEther('1000')
)
transaction = await p2pix.deposit(
erc20.address,
ethers.utils.parseEther('1000'),
'SELLER PIX KEY',
{value:ethers.utils.parseEther('0.1')}
);
depositID = ethers.utils.solidityKeccak256(['string', 'uint256'], ['SELLER PIX KEY', ethers.utils.parseEther('1000')])
await expect(transaction).to.emit(p2pix, 'DepositAdded').withArgs(
owner.address,
depositID,
erc20.address,
ethers.utils.parseEther('0.1'),
ethers.utils.parseEther('1000')
)
console.log('GAS USED:', (await transaction.wait()).cumulativeGasUsed.toString())
})
it("Should allow create a new lock", async function () {
transaction = await p2pix.connect(wallet3).lock(
depositID,
wallet3.address,
ethers.constants.AddressZero,
'0',
ethers.utils.parseEther('100'),
[]
)
lockID = ethers.utils.solidityKeccak256(['bytes32', 'uint256', 'address'], [
depositID,
ethers.utils.parseEther('100'),
wallet3.address
])
await expect(transaction).to.emit(p2pix, 'LockAdded').withArgs(
wallet3.address,
lockID,
depositID,
ethers.utils.parseEther('100')
)
console.log('GAS USED:', (await transaction.wait()).cumulativeGasUsed.toString())
})
it("Should release the locked amount to the buyer", async function () {
const endtoendID = '123';
const messageToSign = ethers.utils.solidityKeccak256(['string', 'uint256', 'uint256'], [
'SELLER PIX KEY',
ethers.utils.parseEther('100'),
endtoendID
])
// Note: messageToSign is a string, that is 66-bytes long, to sign the
// binary value, we must convert it to the 32 byte Array that
// the string represents
//
// i.e.
// // 66-byte string
// "0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba"
// ... vs ...
// // 32 entry Uint8Array
// [ 89, 47, 167, 67, 136, 159, ... 103, 7, 186]
const messageHashBytes = ethers.utils.arrayify(messageToSign)
// Sign the string message
const flatSig = await owner.signMessage(messageHashBytes);
// For Solidity, we need the expanded-format of a signature
const sig = ethers.utils.splitSignature(flatSig);
transaction = await p2pix.connect(wallet3).release(
lockID,
endtoendID,
sig.r,
sig.s,
sig.v
)
await expect(transaction).to.emit(p2pix, 'LockReleased').withArgs(
wallet3.address,
lockID
)
console.log('GAS USED:', (await transaction.wait()).cumulativeGasUsed.toString())
expect(await erc20.balanceOf(wallet3.address)).to.equal(ethers.utils.parseEther('100'));
})
it("Should allow recreate same lock", async function () {
transaction = await p2pix.connect(wallet3).lock(
depositID,
wallet3.address,
ethers.constants.AddressZero,
'0',
ethers.utils.parseEther('100'),
[]
)
lockID = ethers.utils.solidityKeccak256(['bytes32', 'uint256', 'address'], [
depositID,
ethers.utils.parseEther('100'),
wallet3.address
])
await expect(transaction).to.emit(p2pix, 'LockAdded').withArgs(
wallet3.address,
lockID,
depositID,
ethers.utils.parseEther('100'),
)
})
it("Should prevent create again same lock", async function () {
await expect(p2pix.connect(wallet3).lock(
depositID,
wallet3.address,
ethers.constants.AddressZero,
'0',
ethers.utils.parseEther('100'),
[]
)).to.be.revertedWith('P2PIX: Another lock with same ID is not expired yet');
})
it("Should release the locked amount to the buyer", async function () {
const endtoendID = '124';
const messageToSign = ethers.utils.solidityKeccak256(['string', 'uint256', 'uint256'], [
'SELLER PIX KEY',
ethers.utils.parseEther('100'),
endtoendID
])
const messageHashBytes = ethers.utils.arrayify(messageToSign)
const flatSig = await owner.signMessage(messageHashBytes);
const sig = ethers.utils.splitSignature(flatSig);
transaction = await p2pix.connect(wallet3).release(
lockID,
endtoendID,
sig.r,
sig.s,
sig.v
)
expect(await erc20.balanceOf(wallet3.address)).to.equal(ethers.utils.parseEther('200'));
})
it("Should prevent release again the lock", async function () {
const endtoendID = '125';
const messageToSign = ethers.utils.solidityKeccak256(['string', 'uint256', 'uint256'], [
'SELLER PIX KEY',
ethers.utils.parseEther('100'),
endtoendID
])
const messageHashBytes = ethers.utils.arrayify(messageToSign)
const flatSig = await owner.signMessage(messageHashBytes);
const sig = ethers.utils.splitSignature(flatSig);
await expect(p2pix.connect(wallet3).release(
lockID,
endtoendID,
sig.r,
sig.s,
sig.v
)).to.be.revertedWith('P2PIX: Lock already released or returned');
})
it("Should prevent create a 800 lock", async function () {
await expect(p2pix.connect(wallet3).lock(
depositID,
wallet3.address,
ethers.constants.AddressZero,
'0',
ethers.utils.parseEther('900'),
[]
)).to.be.revertedWith('P2PIX: Not enough token remaining on deposit');
})
it("Should allow recreate same lock again", async function () {
transaction = await p2pix.connect(wallet3).lock(
depositID,
wallet3.address,
ethers.constants.AddressZero,
'0',
ethers.utils.parseEther('100'),
[]
)
lockID = ethers.utils.solidityKeccak256(['bytes32', 'uint256', 'address'], [
depositID,
ethers.utils.parseEther('100'),
wallet3.address
])
await expect(transaction).to.emit(p2pix, 'LockAdded').withArgs(
wallet3.address,
lockID,
depositID,
ethers.utils.parseEther('100')
)
})
it("Should allow unlock expired lock", async function () {
await expect(p2pix.unlockExpired([lockID]))
.to.be.revertedWith('P2PIX: Lock not expired or already released');
await network.provider.send("evm_mine");
await network.provider.send("evm_mine");
await network.provider.send("evm_mine");
transaction = await p2pix.unlockExpired([lockID])
await expect(transaction).to.emit(p2pix, 'LockReturned').withArgs(
wallet3.address,
lockID
)
})
it("Should prevent unlock again", async function () {
await expect(p2pix.unlockExpired([lockID]))
.to.be.revertedWith('P2PIX: Lock not expired or already released');
})
})

View File

@ -1,95 +0,0 @@
import "@nomicfoundation/hardhat-chai-matchers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers, network } from "hardhat";
import { Reputation } from "../src/types";
import { curve, repFixture } from "./utils/fixtures";
describe("Reputation", () => {
// contract deployer/admin
let owner: SignerWithAddress;
// Reputation Interface instance;
let reputation: Reputation;
before("Set signers and reset network", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[owner] = await (ethers as any).getSigners();
await network.provider.send("hardhat_reset");
});
beforeEach("Load deployment fixtures", async () => {
({ 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 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",
},
];
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

@ -1,7 +0,0 @@
/* eslint-disable no-useless-escape */
describe("_", () => {
console.log(
"/// ______ __\r\n/// .-----.|__ |.-----.|__|.--.--.\r\n/// | _ || __|| _ || ||_ _|\r\n/// | __||______|| __||__||__.__|\r\n/// |__| |__|\r\n///",
);
});

View File

@ -1,116 +0,0 @@
// import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
// import { expect } from "chai";
// import { ethers } from "hardhat";
// import { MockToken, P2PIX } from "../src/types";
// describe("P2PIX deposit test", () => {
// let owner: SignerWithAddress;
// let wallet2: SignerWithAddress;
// // let wallet3: SignerWithAddress;
// // let wallet4: SignerWithAddress;
// let p2pix: P2PIX; // Contract instance
// let erc20: MockToken; // Token instance
// it("Will deploy contracts", async () => {
// [owner, wallet2 /* wallet3, wallet4 */] =
// await ethers.getSigners();
// const ERC20Factory = await ethers.getContractFactory(
// "MockToken",
// );
// erc20 = await ERC20Factory.deploy(
// ethers.utils.parseEther("20000000"),
// );
// await erc20.deployed();
// // Check initial balance
// expect(await erc20.balanceOf(owner.address)).to.equal(
// ethers.utils.parseEther("20000000"),
// );
// const P2PIX = await ethers.getContractFactory("P2PIX");
// p2pix = await P2PIX.deploy(2, [
// owner.address,
// wallet2.address,
// ]);
// await p2pix.deployed();
// const ownerKey = await p2pix._castAddrToKey(owner.address);
// const wallet2Key = await p2pix._castAddrToKey(wallet2.address);
// // Verify values at deployment
// expect(
// await p2pix.callStatic.validBacenSigners(ownerKey),
// ).to.equal(true);
// expect(
// await p2pix.validBacenSigners(wallet2Key),
// ).to.equal(true);
// });
// it("Should allow create a deposit", async () => {
// let transaction = await erc20.approve(
// p2pix.address,
// ethers.utils.parseEther("2000"),
// );
// await expect(transaction)
// .to.emit(erc20, "Approval")
// .withArgs(
// owner.address,
// p2pix.address,
// ethers.utils.parseEther("2000"),
// );
// transaction = await p2pix.deposit(
// erc20.address,
// ethers.utils.parseEther("1000"),
// "SELLER PIX KEY",
// // { value: ethers.utils.parseEther("0.1") },
// );
// await expect(transaction)
// .to.emit(p2pix, "DepositAdded")
// .withArgs(
// owner.address,
// 0,
// erc20.address,
// // ethers.utils.parseEther("0.1"),
// ethers.utils.parseEther("1000"),
// );
// });
// it("Should allow create second deposit", async () => {
// const transaction = await p2pix.deposit(
// erc20.address,
// ethers.utils.parseEther("1000"),
// "SELLER PIX KEY",
// // { value: ethers.utils.parseEther("0.1") },
// );
// await expect(transaction)
// .to.emit(p2pix, "DepositAdded")
// .withArgs(
// owner.address,
// 1,
// erc20.address,
// // ethers.utils.parseEther("0.1"),
// ethers.utils.parseEther("1000"),
// );
// });
// it("Should allow cancel first deposit", async () => {
// const transaction = await p2pix.cancelDeposit(0);
// await expect(transaction)
// .to.emit(p2pix, "DepositClosed")
// .withArgs(owner.address, 0);
// });
// it("Should allow withdraw the deposit", async () => {
// const transaction = await p2pix.withdraw(0, []);
// await expect(transaction)
// .to.emit(p2pix, "DepositWithdrawn")
// .withArgs(
// owner.address,
// 0,
// ethers.utils.parseEther("1000"),
// );
// });
// });

View File

@ -1,305 +0,0 @@
// import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
// import { expect } from "chai";
// import { ethers, network } from "hardhat";
// import { P2PixErrors } from "./utils/errors";
// import { MockToken, P2PIX } from "../src/types";
// describe("P2PIX deposit test", () => {
// let owner: SignerWithAddress;
// let wallet2: SignerWithAddress;
// let wallet3: SignerWithAddress;
// // let wallet4: SignerWithAddress;
// let p2pix: P2PIX; // Contract instance
// let erc20: MockToken; // Token instance
// let lockID: string;
// it("Will deploy contracts", async () => {
// [owner, wallet2, wallet3 /* , wallet4 */] =
// await ethers.getSigners();
// const ERC20Factory = await ethers.getContractFactory(
// "MockToken",
// );
// erc20 = await ERC20Factory.deploy(
// ethers.utils.parseEther("20000000"),
// );
// await erc20.deployed();
// // Check initial balance
// expect(await erc20.balanceOf(owner.address)).to.equal(
// ethers.utils.parseEther("20000000"),
// );
// const P2PIX = await ethers.getContractFactory("P2PIX");
// p2pix = await P2PIX.deploy(3, [
// owner.address,
// wallet2.address,
// ]);
// await p2pix.deployed();
// const ownerKey = await p2pix._castAddrToKey(owner.address);
// const wallet2key = await p2pix._castAddrToKey(wallet2.address);
// const wallet3key = await p2pix._castAddrToKey(wallet3.address);
// // Verify values at deployment
// expect(
// await p2pix.validBacenSigners(ownerKey),
// ).to.equal(true);
// expect(
// await p2pix.validBacenSigners(wallet2key),
// ).to.equal(true);
// expect(
// await p2pix.validBacenSigners(wallet3key),
// ).to.equal(false);
// });
// it("Should allow create a deposit", async () => {
// let transaction = await erc20.approve(
// p2pix.address,
// ethers.utils.parseEther("1000"),
// );
// await expect(transaction)
// .to.emit(erc20, "Approval")
// .withArgs(
// owner.address,
// p2pix.address,
// ethers.utils.parseEther("1000"),
// );
// transaction = await p2pix.deposit(
// erc20.address,
// ethers.utils.parseEther("1000"),
// "SELLER PIX KEY",
// // { value: ethers.utils.parseEther("0.1") },
// );
// await expect(transaction)
// .to.emit(p2pix, "DepositAdded")
// .withArgs(
// owner.address,
// 0,
// erc20.address,
// // ethers.utils.parseEther("0.1"),
// ethers.utils.parseEther("1000"),
// );
// console.log(
// "GAS USED:",
// (await transaction.wait()).cumulativeGasUsed.toString(),
// );
// });
// it("Should allow create a new lock", async () => {
// const transaction = await p2pix
// .connect(wallet3)
// .lock(
// 0,
// wallet3.address,
// ethers.constants.AddressZero,
// "0",
// ethers.utils.parseEther("100"),
// [],
// );
// lockID = ethers.utils.solidityKeccak256(
// ["uint256", "uint256", "address"],
// [0, ethers.utils.parseEther("100"), wallet3.address],
// );
// await expect(transaction)
// .to.emit(p2pix, "LockAdded")
// .withArgs(
// wallet3.address,
// lockID,
// 0,
// ethers.utils.parseEther("100"),
// );
// console.log(
// "GAS USED:",
// (await transaction.wait()).cumulativeGasUsed.toString(),
// );
// });
// it("Should release the locked amount to the buyer", async () => {
// const endtoendID = "123";
// const messageToSign = ethers.utils.solidityKeccak256(
// ["string", "uint256", "uint256"],
// [
// "SELLER PIX KEY",
// ethers.utils.parseEther("100"),
// endtoendID,
// ],
// );
// // Note: messageToSign is a string, that is 66-bytes long, to sign the
// // binary value, we must convert it to the 32 byte Array that
// // the string represents
// //
// // i.e.
// // // 66-byte string
// // "0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba"
// // ... vs ...
// // // 32 entry Uint8Array
// // [ 89, 47, 167, 67, 136, 159, ... 103, 7, 186]
// const messageHashBytes =
// ethers.utils.arrayify(messageToSign);
// // Sign the string message
// const flatSig = await owner.signMessage(messageHashBytes);
// // For Solidity, we need the expanded-format of a signature
// const sig = ethers.utils.splitSignature(flatSig);
// const transaction = await p2pix
// .connect(wallet3)
// .release(lockID, endtoendID, sig.r, sig.s, sig.v);
// await expect(transaction)
// .to.emit(p2pix, "LockReleased")
// .withArgs(wallet3.address, lockID);
// console.log(
// "GAS USED:",
// (await transaction.wait()).cumulativeGasUsed.toString(),
// );
// expect(await erc20.balanceOf(wallet3.address)).to.equal(
// ethers.utils.parseEther("100"),
// );
// });
// it("Should allow recreate same lock", async () => {
// const transaction = await p2pix
// .connect(wallet3)
// .lock(
// 0,
// wallet3.address,
// ethers.constants.AddressZero,
// "0",
// ethers.utils.parseEther("100"),
// [],
// );
// lockID = ethers.utils.solidityKeccak256(
// ["uint256", "uint256", "address"],
// [0, ethers.utils.parseEther("100"), wallet3.address],
// );
// await expect(transaction)
// .to.emit(p2pix, "LockAdded")
// .withArgs(
// wallet3.address,
// lockID,
// 0,
// ethers.utils.parseEther("100"),
// );
// });
// it("Should prevent create again same lock", async () => {
// await expect(
// p2pix
// .connect(wallet3)
// .lock(
// 0,
// wallet3.address,
// ethers.constants.AddressZero,
// "0",
// ethers.utils.parseEther("100"),
// [],
// ),
// ).to.be.revertedWithCustomError(p2pix, P2PixErrors.NotExpired);
// });
// it("Should release the locked amount to the buyer", async () => {
// const endtoendID = "124";
// const messageToSign = ethers.utils.solidityKeccak256(
// ["string", "uint256", "uint256"],
// [
// "SELLER PIX KEY",
// ethers.utils.parseEther("100"),
// endtoendID,
// ],
// );
// const messageHashBytes =
// ethers.utils.arrayify(messageToSign);
// const flatSig = await owner.signMessage(messageHashBytes);
// const sig = ethers.utils.splitSignature(flatSig);
// await p2pix
// .connect(wallet3)
// .release(lockID, endtoendID, sig.r, sig.s, sig.v);
// expect(await erc20.balanceOf(wallet3.address)).to.equal(
// ethers.utils.parseEther("200"),
// );
// });
// it("Should prevent release again the lock", async () => {
// const endtoendID = "125";
// const messageToSign = ethers.utils.solidityKeccak256(
// ["string", "uint256", "uint256"],
// [
// "SELLER PIX KEY",
// ethers.utils.parseEther("100"),
// endtoendID,
// ],
// );
// const messageHashBytes =
// ethers.utils.arrayify(messageToSign);
// const flatSig = await owner.signMessage(messageHashBytes);
// const sig = ethers.utils.splitSignature(flatSig);
// await expect(
// p2pix
// .connect(wallet3)
// .release(lockID, endtoendID, sig.r, sig.s, sig.v),
// ).to.be.revertedWithCustomError(p2pix, P2PixErrors.AlreadyReleased);
// });
// it("Should prevent create a 900 lock", async () => {
// await expect(
// p2pix
// .connect(wallet3)
// .lock(
// 0,
// wallet3.address,
// ethers.constants.AddressZero,
// "0",
// ethers.utils.parseEther("900"),
// [],
// ),
// ).to.be.revertedWithCustomError(
// p2pix, P2PixErrors.NotEnoughTokens);
// });
// it("Should allow recreate same lock again", async () => {
// const transaction = await p2pix
// .connect(wallet3)
// .lock(
// 0,
// wallet3.address,
// ethers.constants.AddressZero,
// "0",
// ethers.utils.parseEther("100"),
// [],
// );
// lockID = ethers.utils.solidityKeccak256(
// ["uint256", "uint256", "address"],
// [0, ethers.utils.parseEther("100"), wallet3.address],
// );
// await expect(transaction)
// .to.emit(p2pix, "LockAdded")
// .withArgs(
// wallet3.address,
// lockID,
// 0,
// ethers.utils.parseEther("100"),
// );
// });
// it("Should allow unlock expired lock", async () => {
// await expect(
// p2pix.unlockExpired([lockID]),
// ).to.be.revertedWithCustomError(
// p2pix, P2PixErrors.NotExpired);
// await network.provider.send("evm_mine");
// await network.provider.send("evm_mine");
// await network.provider.send("evm_mine");
// const transaction = await p2pix.unlockExpired([lockID]);
// await expect(transaction)
// .to.emit(p2pix, "LockReturned")
// .withArgs(wallet3.address, lockID);
// });
// it("Should prevent unlock again", async () => {
// await expect(
// p2pix.unlockExpired([lockID]),
// ).to.be.revertedWithCustomError(
// p2pix, P2PixErrors.NotExpired);
// });
// });

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
export enum P2PixErrors {
OnlySeller = "OnlySeller",
NotExpired = "NotExpired",
LoopOverflow = "LoopOverflow",
InvalidDeposit = "InvalidDeposit",
NotEnoughTokens = "NotEnoughTokens",
AlreadyReleased = "AlreadyReleased",
TxAlreadyUsed = "TxAlreadyUsed",
InvalidSigner = "InvalidSigner",
Unauthorized = "Unauthorized",
TokenDenied = "TokenDenied",
NoTokens = "NoTokens",
LengthMismatch = "LengthMismatch",
AddressDenied = "AddressDenied",
AmountNotAllowed = "AmountNotAllowed",
LockExpired = "LockExpired",
EmptyPixTarget = "EmptyPixTarget",
MaxBalExceeded = "MaxBalExceeded",
NotInitialized = "NotInitialized",
DecOverflow = "DecOverflow",
CallFailed = "CallFailed",
}

View File

@ -1,136 +0,0 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { BigNumber, Signer } from "ethers";
import { ethers } from "hardhat";
import keccak256 from "keccak256";
import { MerkleTree } from "merkletreejs";
import {
MockToken,
Multicall,
P2PIX,
P2PIX__factory,
Reputation,
} from "../../src/types";
import { Call, RepFixture, P2PixAndReputation, DepositArgs, LockArgs, ReleaseArgs } from "./interfaces";
// exported constants
export const getSignerAddrs = (
amount: number,
addrs: SignerWithAddress[],
): string[] => {
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[] => {
const signers: Signer[] = [];
for (let i = 0; i < amount; i++) {
signers.push(ethers.Wallet.createRandom());
}
return signers;
};
export const getError = (Error: string) =>
ethers.utils
.keccak256(ethers.utils.toUtf8Bytes(Error))
.slice(0, 10);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const padBuffer = (addr: any) => {
return Buffer.from(
addr.substr(2).padStart(32 * 2, 0),
"hex",
);
};
export const curve = (x: number): number => {
return Math.round(
1 + (10 ** 6 * x) / Math.sqrt(2.5 * 10 ** 11 + x * x),
);
};
// exported async functions
export async function repFixture(): Promise<RepFixture> {
const Reputation = await ethers.getContractFactory(
"Reputation",
);
const reputation =
(await Reputation.deploy()) as Reputation;
return { reputation };
}
export async function p2pixFixture(): Promise<P2PixAndReputation> {
const validSigners = getSignerAddrs(
2,
await ethers.getSigners(),
);
const Reputation = await ethers.getContractFactory(
"Reputation",
);
const reputation =
(await Reputation.deploy()) as Reputation;
const ERC20 = await ethers.getContractFactory("MockToken");
const erc20 = (await ERC20.deploy(
ethers.utils.parseEther("20000000"), // 20M
)) as MockToken;
const P2PIX = await ethers.getContractFactory("P2PIX");
const p2pix = (await P2PIX.deploy(
10,
validSigners,
reputation.address,
[erc20.address],
[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 =>
padBuffer(account.address),
);
const tree = new MerkleTree(leaves, keccak256, {
sort: true,
});
const merkleRoot: string = tree.getHexRoot();
const proof: string[] = tree.getHexProof(
padBuffer(whitelisted[1].address),
);
return {
multicall,
reputation,
erc20,
p2pix,
merkleRoot,
proof,
};
}

View File

@ -1,77 +0,0 @@
import { BigNumber } from "ethers";
import {
MockToken,
Multicall,
P2PIX,
Reputation,
} from "../../src/types";
// exported interfaces
export interface Deploys {
signers: string[];
p2pix: string;
token: string;
}
export interface DepositArgs {
pixTarget: string;
allowlistRoot: string;
token: string;
amount: BigNumber;
valid: boolean;
}
export interface LockArgs {
seller: string;
token: string;
amount: BigNumber;
merkleProof: string[];
expiredLocks: BigNumber[];
}
export interface ReleaseArgs {
lockID: BigNumber;
pixTimestamp: string;
signature: string;
}
export interface Lock {
counter: BigNumber;
expirationBlock: BigNumber;
pixTarget: string;
amount: BigNumber;
token: string;
buyerAddress: string;
seller: string;
}
export interface Call {
target: string;
callData: string;
}
export interface Result {
success: boolean;
returnData: string;
}
export interface P2pixFixture {
p2pix: P2PIX;
erc20: MockToken;
proof: string[];
merkleRoot: string;
}
export interface RepFixture {
reputation: Reputation;
}
export interface MtcFixture {
multicall: Multicall;
}
export type P2PixAndReputation = P2pixFixture &
RepFixture &
MtcFixture;

View File

@ -1,22 +0,0 @@
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"lib": ["es6"],
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"removeComments": true,
"resolveJsonModule": true,
"sourceMap": true,
"strict": true,
"target": "es6"
},
"exclude": ["node_modules"],
"files": ["./hardhat.config.ts"],
"include": ["src/**/*", "test/**/*", "scripts/**/*"]
}

9014
yarn.lock

File diff suppressed because it is too large Load Diff