1 Commits

Author SHA1 Message Date
hueso
4b129e2632 update documentation 2024-02-13 15:50:31 -03:00
65 changed files with 12527 additions and 6451 deletions

2
.commitlintrc.yaml Normal file
View File

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

3
.czrc Normal file
View File

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

View File

@@ -1,5 +1,4 @@
MNEMONIC="{INSERT_12_WORD_MNEMONIC}" MNEMONIC="{INSERT_12_WORD_MNEMONIC}"
PRIVATE_KEY="{INSERT_0x_PRIVATE_KEY}"
INFURA_API_KEY="{INSERT_API_KEY}" INFURA_API_KEY="{INSERT_API_KEY}"
ALCHEMY_API_KEY="{INSERT_API_KEY}" ALCHEMY_API_KEY="{INSERT_API_KEY}"

21
.eslintignore Normal file
View File

@@ -0,0 +1,21 @@
# 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*

21
.eslintrc.yaml Normal file
View File

@@ -0,0 +1,21 @@
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: "_"

View File

@@ -1,32 +0,0 @@
name: Lint
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
concurrency:
group: lint-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Solidity + TypeScript + Prettier
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: 22
cache: yarn
- name: Install dependencies
run: yarn install --immutable
- name: Run lint
run: yarn lint

View File

@@ -1,35 +0,0 @@
name: Test
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
concurrency:
group: test-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Hardhat tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: 22
cache: yarn
- name: Install dependencies
run: yarn install --immutable
- name: Compile contracts
run: yarn compile
- name: Run tests
run: yarn test

8
.gitignore vendored
View File

@@ -28,11 +28,3 @@ coverage.json
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
docs/lib/mock/*.md
.dagrobin/
# Hardhat Ignition local/ephemeral deployments
ignition/deployments/chain-31337/
# Per-developer local deploy state (deterministic per mnemonic)
deploys/localhost.json

1
.husky/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_

4
.husky/commit-msg Executable file
View File

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

4
.husky/pre-commit Executable file
View File

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

View File

@@ -11,12 +11,6 @@
**/node_modules **/node_modules
**/types **/types
# deploy artifacts
deploys/old/
# auto-generated docs
docs/
# files # files
*.env *.env
*.log *.log
@@ -24,4 +18,6 @@ docs/
coverage.json coverage.json
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
contracts/p2pix.sol

View File

@@ -1,8 +1,10 @@
plugins:
- prettier-plugin-solidity
arrowParens: avoid arrowParens: avoid
bracketSpacing: true bracketSpacing: true
endOfLine: auto endOfLine: auto
importOrder: ["<THIRD_PARTY_MODULES>", "^[./]"]
importOrderParserPlugins: ["typescript"]
importOrderSeparation: true
importOrderSortSpecifiers: true
printWidth: 62 printWidth: 62
singleQuote: false singleQuote: false
tabWidth: 2 tabWidth: 2

View File

@@ -14,9 +14,5 @@ module.exports = {
providerOptions: { providerOptions: {
mnemonic: process.env.MNEMONIC, mnemonic: process.env.MNEMONIC,
}, },
skipFiles: [ skipFiles: ["test", 'core/BaseUtils.sol', 'core/OwnerSettings.sol'],
"test",
"core/BaseUtils.sol",
"core/OwnerSettings.sol",
],
}; };

View File

@@ -3,7 +3,6 @@
"plugins": ["prettier"], "plugins": ["prettier"],
"rules": { "rules": {
"code-complexity": ["error", 8], "code-complexity": ["error", 8],
"avoid-low-level-calls": "off",
"compiler-version": ["error", ">=0.8.4"], "compiler-version": ["error", ">=0.8.4"],
"const-name-snakecase": "off", "const-name-snakecase": "off",
"constructor-syntax": "error", "constructor-syntax": "error",
@@ -11,14 +10,7 @@
"error", "error",
{ "ignoreConstructors": true } { "ignoreConstructors": true }
], ],
"function-max-lines": "off",
"gas-calldata-parameters": "off",
"gas-indexed-events": "off",
"gas-strict-inequalities": "off",
"gas-struct-packing": "off",
"interface-starts-with-i": "off",
"max-line-length": ["error", 120], "max-line-length": ["error", 120],
"no-inline-assembly": "off",
"not-rely-on-time": "off", "not-rely-on-time": "off",
"prettier/prettier": [ "prettier/prettier": [
"error", "error",
@@ -26,7 +18,6 @@
"endOfLine": "auto" "endOfLine": "auto"
} }
], ],
"reason-string": ["warn", { "maxLength": 64 }], "reason-string": ["warn", { "maxLength": 64 }]
"use-natspec": "off"
} }
} }

View File

@@ -1,6 +1,3 @@
# directories # directories
**/artifacts **/artifacts
**/cache
**/node_modules **/node_modules
deploys/
docs/

File diff suppressed because one or more lines are too long

786
.yarn/releases/yarn-3.2.1.cjs vendored Executable file

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 +1,7 @@
compressionLevel: mixed
enableGlobalCache: false
nodeLinker: node-modules nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.9.2.cjs plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.2.1.cjs

View File

@@ -55,23 +55,25 @@
| Testnet | Token Address | P2pix Address | Reputation Address | Multicall Address | | Testnet | Token Address | P2pix Address | Reputation Address | Multicall Address |
| ------- | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ | | ------- | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ |
| Sepolia | 0x3eBE212377D847828eBb9D2a100f5c5EA1EF3A72 | 0xb7cDAE58C6e715Cfd795BB142041E43b9CB02497 | 0xFd1194A56995Ef7B62730F4061408e79d88E5207 | 0x2f7f9848A803E67d79C0C8aa42b663dCF6E1B5ed | | Goerli | 0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00 | 0x2414817FF64A114d91eCFA16a834d3fCf69103d4 | 0x2CFD9354Ec7614fEf036EFd6A730dA1d5fC2762A | 0x8FE009992d96A86c7f0Bccdaf1eC3471E302a8a6 |
| Mumbai | 0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29 | 0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00 | 0x570445E3eF413bCDb5De79ed27B1c3840683e385 | 0x718B2C4DE4F9654E1349F610ff561249bfe1c418 |
<!-- All contracts deployed by 0x8dC06F985C131166570825F52447E8c88d64aE20 --> <!-- All contracts deployed by 0x8dC06F985C131166570825F52447E8c88d64aE20 -->
<!-- https://sepolia.etherscan.io/address/0x3eBE212377D847828eBb9D2a100f5c5EA1EF3A72#code --> <!-- https://goerli.etherscan.io/address/0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00#code -->
<!-- https://sepolia.etherscan.io/address/0xb7cDAE58C6e715Cfd795BB142041E43b9CB02497#code --> <!-- https://goerli.etherscan.io/address/0x2414817FF64A114d91eCFA16a834d3fCf69103d4#code -->
<!-- https://sepolia.etherscan.io/address/0xFd1194A56995Ef7B62730F4061408e79d88E5207#code --> <!-- https://goerli.etherscan.io/address/0x2CFD9354Ec7614fEf036EFd6A730dA1d5fC2762A#code -->
<!-- https://sepolia.etherscan.io/address/0x2f7f9848A803E67d79C0C8aa42b663dCF6E1B5ed#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 ## Usage
### Pre Requisites ### Pre Requisites
Before installing, create a `.env` file and set the authentication method and other env criteria as in `.env.example`. Before installing, create a `.env` file and set a BIP-39 compatible mnemonic and other env criteria as in `.env.example`.
You can authenticate with either:
- `PRIVATE_KEY` — a raw hex private key (e.g. `0x123...`). Takes priority if both are set.
- `MNEMONIC` — a BIP-39 compatible 12-word mnemonic phrase. Used as fallback when `PRIVATE_KEY` is not set.
### Install ### Install
@@ -123,71 +125,36 @@ To grab deployment addresses you can just grab from deploys folder:
import localhostDeploys from "p2pix-smart-contracts/deploys/localhost.json"; import localhostDeploys from "p2pix-smart-contracts/deploys/localhost.json";
``` ```
## Deployment (Hardhat Ignition) ## Deploying to local environment
Deployments are orchestrated by On the first teminal, use the following command and import some wallets to your Metamask, then connect to the network pointed:
[Hardhat Ignition](https://hardhat.org/ignition). Each module declares its
contracts; Ignition handles ordering, idempotency, and per-network state
under `ignition/deployments/`.
### Modules
- `ignition/modules/MockToken.ts` — ERC20 mock used in dev / testnets.
- `ignition/modules/Reputation.ts` — Reputation + Multicall.
- `ignition/modules/P2PIX.ts` — production deploy (no MockToken). Token
addresses passed via parameters (`tokens` + `allowed`).
- `ignition/modules/P2PIXWithMock.ts` — dev/test deploy. Wires Reputation +
MockToken and deploys P2PIX.
### Per-network parameters
Network-specific values (e.g. `defaultBlocks`, `validSigners`, MockToken
`supply`) live in `ignition/parameters/<network>.json`. Only
`localhost.json` is checked in; copy it to e.g. `sepolia.json` and set the
network values before running the deploy script.
### Local environment
On the first terminal:
```sh ```sh
yarn hardhat node yarn hardhat node
``` ```
On the second terminal: On the second teminal, run the following commands:
```sh ```sh
yarn deploy:mock --network localhost --parameters ignition/parameters/localhost.json yarn deploy1:localhost
yarn deploy2:localhost
``` ```
Addresses for every deployed contract are written to **_NOTE_:** The second script transfers 2M tokens to the first wallet of the node.
`ignition/deployments/chain-31337/deployed_addresses.json` (this path is To use the P2Pix smart contract first transfer some of the tokens to other wallets.
gitignored — local-only state).
### Testnets ## Deploying to testnets
Deploy to Ethereum's Goerli testnet:
```sh ```sh
yarn deploy:mock --network sepolia yarn deploy1:goerli
yarn deploy2:goerli
``` ```
Requires the matching `ignition/parameters/sepolia.json` and the relevant Deploy to Polygon's Mumbai testnet:
API keys in `.env`.
### Production (real tokens) ```sh
yarn deploy1:mumbai
Use the `P2PIX` module when the target network already has the ERC20 yarn deploy2:mumbai
tokens you want P2PIX to support — no MockToken is deployed. ```
1. Copy `ignition/parameters/prod.example.json` to
`ignition/parameters/<network>.json` and set:
- `defaultBlocks`, `validSigners`
- `tokens` — array of ERC20 addresses on the target network
- `allowed` — array of booleans, same length as `tokens`
2. Run:
```sh
yarn deploy --network <network> --parameters ignition/parameters/<network>.json
```
The deployed Reputation/Multicall/P2PIX addresses are written to
`ignition/deployments/chain-<id>/deployed_addresses.json`.

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.19; pragma solidity 0.8.19;
import { IReputation } from "./lib/interfaces/IReputation.sol"; import { IReputation } from "./lib/interfaces/IReputation.sol";

View File

@@ -1,12 +1,11 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.19; pragma solidity 0.8.19;
import { ERC20, OwnerSettings } from "contracts/core/OwnerSettings.sol"; import { ERC20, OwnerSettings } from "contracts/core/OwnerSettings.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { ECDSA } from "contracts/lib/utils/ECDSA.sol";
import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import { MerkleProofLib as Merkle } from "contracts/lib/utils/MerkleProofLib.sol";
import { MerkleProof as Merkle } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import { ReentrancyGuard } from "contracts/lib/utils/ReentrancyGuard.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
abstract contract BaseUtils is abstract contract BaseUtils is
OwnerSettings, OwnerSettings,
@@ -44,9 +43,10 @@ abstract contract BaseUtils is
if ( if (
!validBacenSigners( !validBacenSigners(
_castAddrToKey( _castAddrToKey(
ECDSA.recover( ECDSA.recoverCalldata(
MessageHashUtils ECDSA.toEthSignedMessageHash(
.toEthSignedMessageHash(_message), _message
),
_signature _signature
) )
) )

View File

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

View File

@@ -1,9 +1,10 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.19; pragma solidity 0.8.19;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20 } from "contracts/lib/tokens/ERC20.sol";
library DataTypes { library DataTypes {
struct Lock { struct Lock {
uint256 counter; uint256 counter;
uint256 expirationBlock; uint256 expirationBlock;

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.19; pragma solidity 0.8.19;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20 } from "contracts/lib/tokens/ERC20.sol";
// prettier-ignore // prettier-ignore
interface EventAndErrors { interface EventAndErrors {

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.19; pragma solidity 0.8.19;
import { ERC2771 } from "contracts/lib/metatx/ERC2771Context.sol"; import { ERC2771Context as ERC2771 } from "contracts/lib/metatx/ERC2771Context.sol";
import { ERC20, SafeTransferLib } from "contracts/lib/utils/SafeTransferLib.sol"; import { ERC20, SafeTransferLib } from "contracts/lib/utils/SafeTransferLib.sol";
import { IReputation } from "contracts/lib/interfaces/IReputation.sol"; import { IReputation } from "contracts/lib/interfaces/IReputation.sol";
import { EventAndErrors } from "contracts/core/EventAndErrors.sol"; import { EventAndErrors } from "contracts/core/EventAndErrors.sol";

View File

@@ -1,15 +1,87 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20; pragma solidity >=0.8.4;
import { ERC2771Context } from "@openzeppelin/contracts/metatx/ERC2771Context.sol"; /// @author OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Context.sol)
abstract contract ERC2771 is ERC2771Context(address(0)) { /// @dev Provides information about the current execution context, including the
mapping(address => bool) public trustedForwarders; /// 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 isTrustedForwarder( function _msgData()
address forwarder internal
) public view override returns (bool) { view
return trustedForwarders[forwarder]; 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,10 +1,10 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.19; pragma solidity 0.8.19;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20 } from "../tokens/ERC20.sol";
contract MockToken is ERC20 { contract MockToken is ERC20 {
constructor(uint256 supply) ERC20("MockBRL", "MBRL") { constructor(uint256 supply) ERC20("MockBRL", "MBRL", 18) {
_mint(msg.sender, supply); _mint(msg.sender, supply);
} }

View File

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

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

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

@@ -19,12 +19,12 @@ contract Multicall {
} }
//prettier-ignore //prettier-ignore
//solhint-disable-next-line no-empty-blocks
constructor(/* */) payable {/* */} constructor(/* */) payable {/* */}
function mtc1( function mtc1(Call[] calldata calls)
Call[] calldata calls external
) external returns (uint256, bytes[] memory) { returns (uint256, bytes[] memory)
{
uint256 bn = block.number; uint256 bn = block.number;
uint256 len = calls.length; uint256 len = calls.length;
bytes[] memory res = new bytes[](len); bytes[] memory res = new bytes[](len);
@@ -49,14 +49,21 @@ contract Multicall {
return (bn, res); return (bn, res);
} }
function mtc2( function mtc2(Call[] calldata calls)
Call[] calldata calls external
) external returns (uint256, bytes32, Result[] memory) { returns (
uint256,
bytes32,
Result[] memory
)
{
uint256 bn = block.number; 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 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 // 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). // blocks behind the current block (Yellow Paper, p. 33, https://ethereum.github.io/yellowpaper/paper.pdf).
bytes32 bh = blockhash(bn /* - 1 */); bytes32 bh = blockhash(
bn /* - 1 */
);
uint256 len = calls.length; uint256 len = calls.length;
Result[] memory res = new Result[](len); Result[] memory res = new Result[](len);
uint256 i; uint256 i;

View File

@@ -0,0 +1,34 @@
// 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,7 +1,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity >=0.8.4; pragma solidity >=0.8.4;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20 } from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @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 Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.19; pragma solidity 0.8.19;
/// ______ __ /// ______ __
/// .-----.|__ |.-----.|__|.--.--. /// .-----.|__ |.-----.|__|.--.--.
@@ -12,6 +12,7 @@ import { OwnerSettings, ERC20, SafeTransferLib } from "contracts/core/OwnerSetti
import { BaseUtils } from "contracts/core/BaseUtils.sol"; import { BaseUtils } from "contracts/core/BaseUtils.sol";
import { DataTypes as DT } from "contracts/core/DataTypes.sol"; import { DataTypes as DT } from "contracts/core/DataTypes.sol";
contract P2PIX is BaseUtils { contract P2PIX is BaseUtils {
// solhint-disable use-forbidden-name // solhint-disable use-forbidden-name
// solhint-disable no-inline-assembly // solhint-disable no-inline-assembly
@@ -33,18 +34,15 @@ contract P2PIX is BaseUtils {
address _reputation, address _reputation,
ERC20[] memory tokens, ERC20[] memory tokens,
bool[] memory tokenStates bool[] memory tokenStates
) )
payable
OwnerSettings( OwnerSettings(
defaultBlocks, defaultBlocks,
validSigners, validSigners,
_reputation, _reputation,
tokens, tokens,
tokenStates tokenStates
) )
{ payable {/* */}
/* */
}
/// @notice Creates a deposit order based on a seller's /// @notice Creates a deposit order based on a seller's
/// offer of an amount of ERC20 tokens. /// offer of an amount of ERC20 tokens.
@@ -59,20 +57,19 @@ contract P2PIX is BaseUtils {
ERC20 token, ERC20 token,
uint96 amount, uint96 amount,
bool valid bool valid
) public nonReentrant { ) public {
if (bytes(pixTarget).length == 0)
revert EmptyPixTarget(); if (bytes(pixTarget).length == 0) revert EmptyPixTarget();
if (!allowedERC20s(token)) revert TokenDenied(); if (!allowedERC20s(token)) revert TokenDenied();
uint256 _sellerBalance = __sellerBalance( uint256 _sellerBalance = __sellerBalance(msg.sender, token);
msg.sender,
token
);
uint256 currBal = _sellerBalance & BITMASK_SB_ENTRY; uint256 currBal = _sellerBalance & BITMASK_SB_ENTRY;
uint256 _newBal = uint256(currBal + amount); uint256 _newBal = uint256(currBal + amount);
if (_newBal > MAXBALANCE_UPPERBOUND) if (_newBal > MAXBALANCE_UPPERBOUND)
revert MaxBalExceeded(); revert MaxBalExceeded();
setReentrancyGuard();
if (allowlistRoot != 0) { if (allowlistRoot != 0) {
setRoot(msg.sender, allowlistRoot); setRoot(msg.sender, allowlistRoot);
} }
@@ -81,8 +78,8 @@ contract P2PIX is BaseUtils {
uint256 validCasted = _castBool(valid); uint256 validCasted = _castBool(valid);
_setSellerBalance( _setSellerBalance(
msg.sender, msg.sender,
token, token,
(_newBal | (validCasted << BITPOS_VALID)), (_newBal | (validCasted << BITPOS_VALID)),
pixTargetCasted pixTargetCasted
); );
@@ -94,6 +91,8 @@ contract P2PIX is BaseUtils {
amount amount
); );
clearReentrancyGuard();
emit DepositAdded(msg.sender, token, amount); emit DepositAdded(msg.sender, token, amount);
} }
@@ -102,10 +101,7 @@ contract P2PIX is BaseUtils {
/// @notice This function does not affect any ongoing active locks. /// @notice This function does not affect any ongoing active locks.
/// @dev Function sighash: 0x6d82d9e0 /// @dev Function sighash: 0x6d82d9e0
function setValidState(ERC20 token, bool state) public { function setValidState(ERC20 token, bool state) public {
uint256 _sellerBalance = __sellerBalance( uint256 _sellerBalance = __sellerBalance(msg.sender, token);
msg.sender,
token
);
if (_sellerBalance != 0) { if (_sellerBalance != 0) {
uint256 _valid = _castBool(state); uint256 _valid = _castBool(state);
@@ -123,12 +119,12 @@ contract P2PIX is BaseUtils {
/// @notice Public method designed to lock an remaining amount of /// @notice Public method designed to lock an remaining amount of
/// the deposit order of a seller. /// the deposit order of a seller.
/// @notice Transaction forwarding must leave `merkleProof` empty; /// @notice Transaction forwarding must leave `merkleProof` empty;
/// otherwise, the trustedForwarder must be previously added /// otherwise, the trustedForwarder must be previously added
/// to a seller whitelist. /// to a seller whitelist.
/// @notice This method can be performed either by: /// @notice This method can be performed either by:
/// - An user allowed via the seller's allowlist; /// - An user allowed via the seller's allowlist;
/// - An user with enough userRecord to lock the wished amount; /// - An user with enough userRecord to lock the wished amount;
/// @notice There can only exist a lock per each `amount` partitioned /// @notice There can only exist a lock per each `_amount` partitioned
/// from the total `remaining` value. /// from the total `remaining` value.
/// @notice Locks can only be performed in valid orders. /// @notice Locks can only be performed in valid orders.
/// @param amount The deposit's remaining amount wished to be locked. /// @param amount The deposit's remaining amount wished to be locked.
@@ -152,36 +148,30 @@ contract P2PIX is BaseUtils {
uint256 bal = getBalance(seller, token); uint256 bal = getBalance(seller, token);
if (bal < amount) revert NotEnoughTokens(); if (bal < amount) revert NotEnoughTokens();
unchecked { unchecked {
lockID = ++lockCounter; lockID = ++lockCounter;
} }
if (mapLocks[lockID].expirationBlock >= block.number) if (
revert NotExpired(); mapLocks[lockID].expirationBlock >= block.number
) revert NotExpired();
bytes32 _pixTarget = getPixTarget(seller, token); bytes32 _pixTarget = getPixTarget(seller, token);
// transaction forwarding must leave `merkleProof` empty; // transaction forwarding must leave `merkleProof` empty;
// otherwise, the trustedForwarder must be previously added // otherwise, the trustedForwarder must be previously added
// to a seller whitelist. // to a seller whitelist.
if (merkleProof.length != 0) { if (merkleProof.length != 0) {
_merkleVerify( _merkleVerify( merkleProof, sellerAllowList(seller), _msgSender());
merkleProof,
sellerAllowList(seller), } else if ( amount > REPUTATION_LOWERBOUND && msg.sender == _msgSender() ) {
_msgSender()
); uint256 spendLimit; uint256 userCredit =
} else if ( userRecord[_castAddrToKey(_msgSender())];
amount > REPUTATION_LOWERBOUND &&
msg.sender == _msgSender()
) {
uint256 spendLimit;
uint256 userCredit = userRecord[
_castAddrToKey(_msgSender())
];
(spendLimit) = _limiter(userCredit / WAD); (spendLimit) = _limiter(userCredit / WAD);
if ( if (
amount > (spendLimit * WAD) || amount > (spendLimit * WAD) ||
amount > LOCKAMOUNT_UPPERBOUND amount > LOCKAMOUNT_UPPERBOUND
) revert AmountNotAllowed(); ) revert AmountNotAllowed();
} }
@@ -239,21 +229,14 @@ contract P2PIX is BaseUtils {
l.amount = 0; l.amount = 0;
l.expirationBlock = 0; l.expirationBlock = 0;
_setUsedTransactions(message); _setUsedTransactions(message);
if (_msgSender() == msg.sender) { if (_msgSender() == msg.sender) {
if (msg.sender != l.buyerAddress) { if (msg.sender != l.buyerAddress) {
userRecord[ userRecord[_castAddrToKey(msg.sender)] += (lockAmount >> 1);
_castAddrToKey(msg.sender) userRecord[_castAddrToKey(l.buyerAddress)] += (lockAmount >> 1);
] += (lockAmount >> 1); } else {
userRecord[ userRecord[_castAddrToKey(msg.sender)] += lockAmount;
_castAddrToKey(l.buyerAddress) }}
] += (lockAmount >> 1);
} else {
userRecord[
_castAddrToKey(msg.sender)
] += lockAmount;
}
}
SafeTransferLib.safeTransfer( SafeTransferLib.safeTransfer(
t, t,
@@ -270,9 +253,9 @@ contract P2PIX is BaseUtils {
/// @notice For each successfull unexpired lock recovered, /// @notice For each successfull unexpired lock recovered,
/// `userRecord[_castAddrToKey(l.relayerAddress)]` is decreased by half of its value. /// `userRecord[_castAddrToKey(l.relayerAddress)]` is decreased by half of its value.
/// @dev Function sighash: 0xb0983d39 /// @dev Function sighash: 0xb0983d39
function unlockExpired( function unlockExpired(uint256[] calldata lockIDs)
uint256[] calldata lockIDs public
) public { {
uint256 i; uint256 i;
uint256 locksSize = lockIDs.length; uint256 locksSize = lockIDs.length;
@@ -281,21 +264,19 @@ contract P2PIX is BaseUtils {
_notExpired(l); _notExpired(l);
uint256 _sellerBalance = __sellerBalance( uint256 _sellerBalance =
l.seller, __sellerBalance(l.seller, l.token) & BITMASK_SB_ENTRY;
l.token
) & BITMASK_SB_ENTRY;
if ( if ((_sellerBalance + l.amount) > MAXBALANCE_UPPERBOUND)
(_sellerBalance + l.amount) > revert MaxBalExceeded();
MAXBALANCE_UPPERBOUND
) revert MaxBalExceeded();
_addSellerBalance(l.seller, l.token, l.amount); _addSellerBalance(l.seller, l.token, l.amount);
l.amount = 0; l.amount = 0;
uint256 userKey = _castAddrToKey(l.buyerAddress); uint256 userKey = _castAddrToKey(
l.buyerAddress
);
uint256 _newUserRecord = (userRecord[userKey] >> uint256 _newUserRecord = (userRecord[userKey] >>
1); 1);
@@ -335,8 +316,7 @@ contract P2PIX is BaseUtils {
setValidState(token, false); setValidState(token, false);
_decBal( _decBal(
(__sellerBalance(msg.sender, token) & (__sellerBalance(msg.sender, token) & BITMASK_SB_ENTRY),
BITMASK_SB_ENTRY),
amount, amount,
token, token,
msg.sender msg.sender
@@ -349,15 +329,18 @@ contract P2PIX is BaseUtils {
amount amount
); );
emit DepositWithdrawn(msg.sender, token, amount); emit DepositWithdrawn(
msg.sender,
token,
amount
);
} }
function setRoot( function setRoot(address addr, bytes32 merkleroot)
address addr, public
bytes32 merkleroot {
) public {
assembly ("memory-safe") { assembly ("memory-safe") {
// if (addr != msg.sender) // if (addr != msg.sender)
if iszero(eq(addr, caller())) { if iszero(eq(addr, caller())) {
// revert OnlySeller() // revert OnlySeller()
mstore(0x00, 0x85d1f726) mstore(0x00, 0x85d1f726)
@@ -370,8 +353,8 @@ contract P2PIX is BaseUtils {
// emit RootUpdated(addr, merkleroot); // emit RootUpdated(addr, merkleroot);
log3( log3(
0, 0,
0, 0,
_ROOT_UPDATED_EVENT_SIGNATURE, _ROOT_UPDATED_EVENT_SIGNATURE,
addr, addr,
merkleroot merkleroot
@@ -429,13 +412,14 @@ contract P2PIX is BaseUtils {
} }
// we can directly dec from packed uint entry value // we can directly dec from packed uint entry value
_decSellerBalance(_k, _t, _amount); _decSellerBalance(_k,_t, _amount);
} }
function getBalance( function getBalance(address seller, ERC20 token)
address seller, public
ERC20 token view
) public view returns (uint256 bal) { returns (uint256 bal)
{
assembly ("memory-safe") { assembly ("memory-safe") {
for { for {
/* */ /* */
@@ -454,10 +438,11 @@ contract P2PIX is BaseUtils {
} }
} }
function getValid( function getValid(address seller, ERC20 token)
address seller, public
ERC20 token view
) public view returns (bool valid) { returns (bool valid)
{
assembly ("memory-safe") { assembly ("memory-safe") {
for { for {
/* */ /* */
@@ -471,9 +456,7 @@ contract P2PIX is BaseUtils {
BITMASK_SB_ENTRY, BITMASK_SB_ENTRY,
shr( shr(
BITPOS_VALID, BITPOS_VALID,
sload( sload(add(keccak256(0x0c, 0x34), 0x01))
add(keccak256(0x0c, 0x34), 0x01)
)
) )
) )
break break
@@ -481,10 +464,11 @@ contract P2PIX is BaseUtils {
} }
} }
function getPixTarget( function getPixTarget(address seller, ERC20 token)
address seller, public
ERC20 token view
) public view returns (bytes32 pixTarget) { returns (bytes32 pixTarget)
{
assembly ("memory-safe") { assembly ("memory-safe") {
for { for {
/* */ /* */
@@ -500,12 +484,9 @@ contract P2PIX is BaseUtils {
} }
} }
function getPixTargetString( function getPixTargetString(address seller, ERC20 token) public view returns (string memory pixTarget) {
address seller,
ERC20 token
) public view returns (string memory pixTarget) {
bytes32 _pixEnc = getPixTarget(seller, token); bytes32 _pixEnc = getPixTarget(seller, token);
pixTarget = string(abi.encodePacked(_pixEnc)); pixTarget = string(abi.encodePacked(_pixEnc));
} }
function getBalances( function getBalances(
@@ -529,9 +510,7 @@ contract P2PIX is BaseUtils {
/// @notice External getter that returns the status of a lockIDs array. /// @notice External getter that returns the status of a lockIDs array.
/// @notice Call will not revert if provided with an empty array as parameter. /// @notice Call will not revert if provided with an empty array as parameter.
/// @dev Function sighash: 0x49ef8448 /// @dev Function sighash: 0x49ef8448
function getLocksStatus( function getLocksStatus(uint256[] memory ids)
uint256[] memory ids
)
external external
view view
returns (uint256[] memory, DT.LockStatus[] memory) returns (uint256[] memory, DT.LockStatus[] memory)

View File

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

View File

@@ -1,9 +0,0 @@
{
"signers": [
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"0x8963E134E6d22Ee9A26ac62a99964aB391ead816"
],
"token": "0xfE841c74250e57640390f46d914C88d22C51e82e",
"p2pix": "0x57Dcba05980761169508886eEdc6f5E7EC0411Dc"
}

View File

@@ -3,6 +3,6 @@
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8" "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
], ],
"p2pix": "0xb7cD135F5eFD9760981e02E2a898790b688939fe", "token": "0xfE841c74250e57640390f46d914C88d22C51e82e",
"token": "0x3eBE67A2C7bdB2081CBd34ba3281E90377462289" "p2pix": "0x98ba35eb14b38D6Aa709338283af3e922476dE34"
} }

View File

@@ -1,29 +1 @@
# Solidity API # DataTypes
## 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,54 +1,63 @@
# Solidity API # EventAndErrors
## EventAndErrors ## Events
### AllowedERC20Updated
```solidity
event AllowedERC20Updated(address indexed token, bool indexed state)
```
#### Parameters
| Name | Type | Description |
| --------------- | ------- | ----------- |
| token `indexed` | address | undefined |
| state `indexed` | bool | undefined |
### DepositAdded ### DepositAdded
```solidity ```solidity
event DepositAdded(address seller, contract ERC20 token, uint256 amount) event DepositAdded(address indexed seller, uint256 depositID, address token, uint256 amount)
``` ```
_0x63d8d7d5e63e9840ec91a12a160d27b7cfab294f6ba070b7359692acfe6b03bf_ ███ Events ████████████████████████████████████████████████████████████
### ValidSet #### Parameters
| Name | Type | Description |
| ---------------- | ------- | ----------- |
| seller `indexed` | address | undefined |
| depositID | uint256 | undefined |
| token | address | undefined |
| amount | uint256 | undefined |
### DepositClosed
```solidity ```solidity
event ValidSet(address seller, contract ERC20 token, bool state) event DepositClosed(address indexed seller, uint256 depositID)
``` ```
_0xca585721b6b442dc9183932f7c84dc2880efb67c4da52cc06873e78971105d49_ #### Parameters
| Name | Type | Description |
| ---------------- | ------- | ----------- |
| seller `indexed` | address | undefined |
| depositID | uint256 | undefined |
### DepositWithdrawn ### DepositWithdrawn
```solidity ```solidity
event DepositWithdrawn(address seller, contract ERC20 token, uint256 amount) event DepositWithdrawn(address indexed seller, uint256 depositID, uint256 amount)
``` ```
_0x2cd6435b1b961c13f55202979edd0765a809f69a539d8a477436c94c1211e43e_ #### Parameters
### LockAdded | Name | Type | Description |
| ---------------- | ------- | ----------- |
```solidity | seller `indexed` | address | undefined |
event LockAdded(address buyer, uint256 lockID, address seller, uint256 amount) | depositID | uint256 | undefined |
``` | amount | uint256 | undefined |
_0x8fb3989f70bd172a37d15b41b015e48ea09d59329638377304a4198cd0c4ea65_
### LockReleased
```solidity
event LockReleased(address buyer, uint256 lockId, uint256 amount)
```
_0x364537f14276f2a0ce9905588413f96454cbb8fb2e4f5308389307c1098bede8_
### LockReturned
```solidity
event LockReturned(address buyer, uint256 lockId)
```
_0x830501e61b8b075e170b22a430e39454bdb12ed3e9620e586430b6ac00079da5_
### FundsWithdrawn ### FundsWithdrawn
@@ -56,39 +65,27 @@ _0x830501e61b8b075e170b22a430e39454bdb12ed3e9620e586430b6ac00079da5_
event FundsWithdrawn(address owner, uint256 amount) event FundsWithdrawn(address owner, uint256 amount)
``` ```
_0xeaff4b37086828766ad3268786972c0cd24259d4c87a80f9d3963a3c3d999b0d_ #### Parameters
### RootUpdated | Name | Type | Description |
| ------ | ------- | ----------- |
| owner | address | undefined |
| amount | uint256 | undefined |
### LockAdded
```solidity ```solidity
event RootUpdated(address seller, bytes32 merkleRoot) event LockAdded(address indexed buyer, bytes32 indexed lockID, uint256 depositID, uint256 amount)
``` ```
_0x0b294da292f26e55fd442b5c0164fbb9013036ff00c5cfdde0efd01c1baaf632_ #### Parameters
### AllowedERC20Updated | Name | Type | Description |
| ---------------- | ------- | ----------- |
```solidity | buyer `indexed` | address | undefined |
event AllowedERC20Updated(address token, bool state) | lockID `indexed` | bytes32 | undefined |
``` | depositID | uint256 | undefined |
| amount | uint256 | undefined |
_0x5d6e86e5341d57a92c49934296c51542a25015c9b1782a1c2722a940131c3d9a_
### TrustedForwarderUpdated
```solidity
event TrustedForwarderUpdated(address forwarder, bool state)
```
_0xbee55516e29d3969d3cb8eb01351eb3c52d06f9e2435bd5a8bfe3647e185df92_
### ReputationUpdated
```solidity
event ReputationUpdated(address reputation)
```
_0xe127cf589a3879da0156d4a24f43b44f65cfa3570de594806b0bfa2fcf06884f_
### LockBlocksUpdated ### LockBlocksUpdated
@@ -96,7 +93,49 @@ _0xe127cf589a3879da0156d4a24f43b44f65cfa3570de594806b0bfa2fcf06884f_
event LockBlocksUpdated(uint256 blocks) event LockBlocksUpdated(uint256 blocks)
``` ```
_0x70fa43ca70216ad905ade86b9e650a691b2ce5a01980d0a81bdd8324141b8511_ #### Parameters
| Name | Type | Description |
| ------ | ------- | ----------- |
| blocks | uint256 | undefined |
### LockReleased
```solidity
event LockReleased(address indexed buyer, bytes32 lockId)
```
#### Parameters
| Name | Type | Description |
| --------------- | ------- | ----------- |
| buyer `indexed` | address | undefined |
| lockId | bytes32 | undefined |
### LockReturned
```solidity
event LockReturned(address indexed buyer, bytes32 lockId)
```
#### Parameters
| Name | Type | Description |
| --------------- | ------- | ----------- |
| buyer `indexed` | address | undefined |
| lockId | bytes32 | undefined |
### ReputationUpdated
```solidity
event ReputationUpdated(address reputation)
```
#### Parameters
| Name | Type | Description |
| ---------- | ------- | ----------- |
| reputation | address | undefined |
### ValidSignersUpdated ### ValidSignersUpdated
@@ -104,81 +143,13 @@ _0x70fa43ca70216ad905ade86b9e650a691b2ce5a01980d0a81bdd8324141b8511_
event ValidSignersUpdated(address[] signers) event ValidSignersUpdated(address[] signers)
``` ```
_0x14a422d2412784a5749d03da98921fe468c98577b767851389a9f58ea5a363d7_ #### Parameters
### OnlySeller | Name | Type | Description |
| ------- | --------- | ----------- |
| signers | address[] | undefined |
```solidity ## Errors
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 ### AddressDenied
@@ -186,36 +157,15 @@ _Signer is not a valid signer.
error AddressDenied() error AddressDenied()
``` ```
_Address doesn't exist in a MerkleTree. _Address doesn&#39;t exist in a MerkleTree.Address not allowed as relayer.0x3b8474be_
Address not allowed as relayer.
0x3b8474be_
### LengthMismatch ### AlreadyReleased
```solidity ```solidity
error LengthMismatch() error AlreadyReleased()
``` ```
_Arrays' length don't match. _Lock already released or returned.0x63b4904e_
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 ### AmountNotAllowed
@@ -223,56 +173,92 @@ _Token address not allowed to be deposited.
error AmountNotAllowed() error AmountNotAllowed()
``` ```
_Wished amount to be locked exceeds the limit allowed. _Wished amount to be locked exceeds the limit allowed.0x1c18f846_
0x1c18f846_
### StaticCallFailed ### DepositAlreadyExists
```solidity ```solidity
error StaticCallFailed() error DepositAlreadyExists()
``` ```
_Reverts when success return value returns false. _Deposit already exist and it is still valid.0xc44bd765_
0xe10bf1cc_
### LockExpired ### InvalidDeposit
```solidity ```solidity
error LockExpired() error InvalidDeposit()
``` ```
_Reverts on an expired lock. _Deposit not valid anymore.0xb2e532de_
0xf6fafba0_
### DecOverflow ### InvalidSigner
```solidity ```solidity
error DecOverflow() error InvalidSigner()
``` ```
_0xce3a3d37_ _Signer is not a valid signer.0x815e1d64_
### MaxBalExceeded ### LengthMismatch
```solidity ```solidity
error MaxBalExceeded() error LengthMismatch()
``` ```
_0xf3fb0eb9_ _Arrays&#39; length don&#39;t match.0xff633a38_
### EmptyPixTarget ### LoopOverflow
```solidity ```solidity
error EmptyPixTarget() error LoopOverflow()
``` ```
_0x6a3bc53e_ _Loop bounds have overflowed.0xdfb035c9_
### NotInitialized ### NoTokens
```solidity ```solidity
error NotInitialized() error NoTokens()
``` ```
_0x87138d5c_ _No tokens array provided as argument.0xdf957883_
### NotEnoughTokens
```solidity
error NotEnoughTokens()
```
_Not enough token remaining on deposit.0x22bbb43c_
### NotExpired
```solidity
error NotExpired()
```
_Lock not expired or already released.Another lock with same ID is not expired yet.0xd0404f85_
### OnlySeller
```solidity
error OnlySeller()
```
_Only seller could call this function.`msg.sender` and the seller differ.0x85d1f726_
### TokenDenied
```solidity
error TokenDenied()
```
_Token address not allowed to be deposited.0x1578328e_
### TxAlreadyUsed
```solidity
error TxAlreadyUsed()
```
_Transaction already used to unlock payment.0xf490a6ea_

224
docs/lib/mock/MockToken.md Normal file
View File

@@ -0,0 +1,224 @@
# 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

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

View File

@@ -1,207 +1,694 @@
# Solidity API # P2PIX
## P2PIX ## Methods
### lockCounter ### \_castAddrToKey
```solidity ```solidity
uint256 lockCounter function _castAddrToKey(address _addr) external pure returns (uint256 _key)
``` ```
### mapLocks Public method that handles `address` to `uint256` safe type casting.
_Function sighash: 0x4b2ae980._
#### Parameters
| Name | Type | Description |
| ------ | ------- | ----------- |
| \_addr | address | undefined |
#### Returns
| Name | Type | Description |
| ----- | ------- | ----------- |
| \_key | uint256 | undefined |
### allowedERC20s
```solidity ```solidity
mapping(uint256 => struct DataTypes.Lock) mapLocks function allowedERC20s(contract ERC20) external view returns (bool)
``` ```
_List of Locks._ _Tokens allowed to serve as the underlying amount of a deposit._
### userRecord #### Parameters
| Name | Type | Description |
| ---- | -------------- | ----------- |
| \_0 | contract ERC20 | undefined |
#### Returns
| Name | Type | Description |
| ---- | ---- | ----------- |
| \_0 | bool | undefined |
### cancelDeposit
```solidity ```solidity
mapping(uint256 => uint256) userRecord function cancelDeposit(uint256 depositID) external nonpayable
``` ```
_Stores an relayer's last computed credit._ Enables seller to invalidate future locks made to his/her token offering order.
### constructor _This function does not affect any ongoing active locks.Function sighash: 0x72fada5c._
#### Parameters
| Name | Type | Description |
| --------- | ------- | ----------- |
| depositID | uint256 | undefined |
### defaultLockBlocks
```solidity ```solidity
constructor(uint256 defaultBlocks, address[] validSigners, address _reputation, contract ERC20[] tokens, bool[] tokenStates) public payable function defaultLockBlocks() external view returns (uint256)
``` ```
_Default blocks that lock will hold tokens._
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
### deposit ### deposit
```solidity ```solidity
function deposit(string pixTarget, bytes32 allowlistRoot, contract ERC20 token, uint96 amount, bool valid) public function deposit(address _token, uint256 _amount, string _pixTarget, bytes32 allowlistRoot) external nonpayable returns (uint256 depositID)
``` ```
Creates a deposit order based on a seller's Creates a deposit order based on a seller&#39;s offer of an amount of ERC20 tokens.
offer of an amount of ERC20 tokens.
Seller needs to send his tokens to the P2PIX smart contract.
_Function sighash: 0x5e918943_ _Seller needs to send his tokens to the P2PIX smart contract.Function sighash: 0xbfe07da6._
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ------------- | ------- | ------------------------------------------------------- |
| pixTarget | string | Pix key destination provided by the offer's seller. | | \_token | address | undefined |
| allowlistRoot | bytes32 | Optional allow list merkleRoot update `bytes32` value. as the deposit identifier. | | \_amount | uint256 | undefined |
| token | contract ERC20 | | | \_pixTarget | string | Pix key destination provided by the offer&#39;s seller. |
| amount | uint96 | | | allowlistRoot | bytes32 | Optional allow list merkleRoot update `bytes32` value. |
| valid | bool | |
### setValidState #### Returns
| Name | Type | Description |
| --------- | ------- | -------------------------------------------------------------- |
| depositID | uint256 | The `uint256` return value provided as the deposit identifier. |
### depositCount
```solidity ```solidity
function setValidState(contract ERC20 token, bool state) public function depositCount() external view returns (uint256 _val)
``` ```
Enables seller to invalidate future #### Returns
locks made to his/her token offering order.
This function does not affect any ongoing active locks.
_Function sighash: 0x6d82d9e0_ | Name | Type | Description |
| ----- | ------- | ----------- |
| \_val | uint256 | undefined |
### lock ### lock
```solidity ```solidity
function lock(address seller, contract ERC20 token, uint80 amount, bytes32[] merkleProof, uint256[] expiredLocks) public returns (uint256 lockID) function lock(uint256 _depositID, address _buyerAddress, address _relayerTarget, uint256 _relayerPremium, uint256 _amount, bytes32[] merkleProof, bytes32[] expiredLocks) external nonpayable returns (bytes32 lockID)
``` ```
Public method designed to lock an remaining amount of Public method designed to lock an remaining amount of the deposit order of a seller.
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_ _This method can be performed either by: - An user allowed via the seller&#39;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: 0x03aaf306._
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---------------- | --------- | ------------------------------------------------------------------------------------------------------------------------ |
| seller | address | | | \_depositID | uint256 | undefined |
| token | contract ERC20 | | | \_buyerAddress | address | The address of the buyer of a `_depositID`. |
| amount | uint80 | The deposit's remaining amount wished to be locked. | | \_relayerTarget | address | Target address entitled to the `relayerPremim`. |
| merkleProof | bytes32[] | Provided as a pass if the `msg.sender` is in the seller's allowlist; Left empty otherwise; | | \_relayerPremium | uint256 | The refund/premium owed to a relayer. |
| expiredLocks | uint256[] | An array of identifiers to be provided so to unexpire locks using this transaction gas push. | | \_amount | uint256 | The deposit&#39;s remaining amount wished to be locked. |
| merkleProof | bytes32[] | This value should be: - Provided as a pass if the `msg.sender` is in the seller&#39;s allowlist; - Left empty otherwise; |
| expiredLocks | bytes32[] | An array of `bytes32` identifiers to be provided so to unexpire locks using this transaction gas push. |
#### Return Values #### Returns
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ------ | ------- | ---------------------------------------------------- |
| lockID | uint256 | The lock identifier. | | lockID | bytes32 | The `bytes32` value returned as the lock identifier. |
### mapDeposits
```solidity
function mapDeposits(uint256) external view returns (uint256 remaining, string pixTarget, address seller, address token, bool valid)
```
_Seller list of deposits_
#### Parameters
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
#### Returns
| Name | Type | Description |
| --------- | ------- | ----------- |
| remaining | uint256 | undefined |
| pixTarget | string | undefined |
| seller | address | undefined |
| token | address | undefined |
| valid | bool | undefined |
### mapLocks
```solidity
function mapLocks(bytes32) external view returns (uint256 depositID, uint256 relayerPremium, uint256 amount, uint256 expirationBlock, address buyerAddress, address relayerTarget, address relayerAddress)
```
_List of Locks._
#### Parameters
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | bytes32 | undefined |
#### Returns
| Name | Type | Description |
| --------------- | ------- | ----------- |
| depositID | uint256 | undefined |
| relayerPremium | uint256 | undefined |
| amount | uint256 | undefined |
| expirationBlock | uint256 | undefined |
| buyerAddress | address | undefined |
| relayerTarget | address | undefined |
| relayerAddress | address | undefined |
### owner
```solidity
function owner() external view returns (address)
```
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | address | undefined |
### release ### release
```solidity ```solidity
function release(uint256 lockID, bytes32 pixTimestamp, bytes signature) public function release(bytes32 lockID, address _relayerTarget, uint256 pixTimestamp, bytes32 r, bytes32 s, uint8 v) external nonpayable
``` ```
Lock release method that liquidate lock Lock release method that liquidate lock orders and distributes relayer fees.
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_ _This method can be called by any public actor as long the signature provided is valid.`relayerPremium` gets splitted equaly if `relayerTarget` 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: 0x4e1389ed._
### unlockExpired #### Parameters
| Name | Type | Description |
| --------------- | ------- | ----------------------------------------------- |
| lockID | bytes32 | undefined |
| \_relayerTarget | address | Target address entitled to the `relayerPremim`. |
| pixTimestamp | uint256 | undefined |
| r | bytes32 | undefined |
| s | bytes32 | undefined |
| v | uint8 | undefined |
### reputation
```solidity ```solidity
function unlockExpired(uint256[] lockIDs) public function reputation() external view returns (contract IReputation)
``` ```
Unlocks expired locks. ███ Storage ████████████████████████████████████████████████████████████
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_ #### Returns
### withdraw | Name | Type | Description |
| ---- | -------------------- | ----------- |
| \_0 | contract IReputation | undefined |
### sellerAllowList
```solidity ```solidity
function withdraw(contract ERC20 token, uint256 amount, uint256[] expiredLocks) public function sellerAllowList(uint256) external view returns (bytes32)
``` ```
Seller's expired deposit fund sweeper. _Seller casted to key =&gt; Seller&#39;s allowlist merkleroot._
A seller may use this method to recover
tokens from expired deposits.
_Function sighash: 0xfb8c5ef0_ #### Parameters
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | bytes32 | undefined |
### setDefaultLockBlocks
```solidity
function setDefaultLockBlocks(uint256 _blocks) external nonpayable
```
#### Parameters
| Name | Type | Description |
| -------- | ------- | ----------- |
| \_blocks | uint256 | undefined |
### setOwner
```solidity
function setOwner(address newOwner) external nonpayable
```
#### Parameters
| Name | Type | Description |
| -------- | ------- | ----------- |
| newOwner | address | undefined |
### setReputation
```solidity
function setReputation(contract IReputation _reputation) external nonpayable
```
#### Parameters
| Name | Type | Description |
| ------------ | -------------------- | ----------- |
| \_reputation | contract IReputation | undefined |
### setRoot ### setRoot
```solidity ```solidity
function setRoot(address addr, bytes32 merkleroot) public function setRoot(address addr, bytes32 merkleroot) external nonpayable
``` ```
### receive #### Parameters
| Name | Type | Description |
| ---------- | ------- | ----------- |
| addr | address | undefined |
| merkleroot | bytes32 | undefined |
### setValidSigners
```solidity ```solidity
receive() external payable function setValidSigners(address[] _validSigners) external nonpayable
``` ```
### _addLock #### Parameters
| Name | Type | Description |
| -------------- | --------- | ----------- |
| \_validSigners | address[] | undefined |
### tokenSettings
```solidity ```solidity
function _addLock(uint256 _bal, struct DataTypes.Lock _l) internal function tokenSettings(address[] _tokens, bool[] _states) external nonpayable
``` ```
### getBalance #### Parameters
| Name | Type | Description |
| -------- | --------- | ----------- |
| \_tokens | address[] | undefined |
| \_states | bool[] | undefined |
### unlockExpired
```solidity ```solidity
function getBalance(address seller, contract ERC20 token) public view returns (uint256 bal) function unlockExpired(bytes32[] lockIDs) external nonpayable
``` ```
### getValid 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: 0x8e2749d6._
#### Parameters
| Name | Type | Description |
| ------- | --------- | ----------- |
| lockIDs | bytes32[] | undefined |
### userRecord
```solidity ```solidity
function getValid(address seller, contract ERC20 token) public view returns (bool valid) function userRecord(uint256) external view returns (uint256)
``` ```
### getPixTarget _Stores an relayer&#39;s last computed credit._
#### Parameters
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
#### Returns
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
### validBacenSigners
```solidity ```solidity
function getPixTarget(address seller, contract ERC20 token) public view returns (bytes32 pixTarget) function validBacenSigners(uint256) external view returns (bool)
``` ```
### getPixTargetString _List of valid Bacen signature addresses_
#### Parameters
| Name | Type | Description |
| ---- | ------- | ----------- |
| \_0 | uint256 | undefined |
#### Returns
| Name | Type | Description |
| ---- | ---- | ----------- |
| \_0 | bool | undefined |
### withdraw
```solidity ```solidity
function getPixTargetString(address seller, contract ERC20 token) public view returns (string pixTarget) function withdraw(uint256 depositID, bytes32[] expiredLocks) external nonpayable
``` ```
### getBalances Seller&#39;s expired deposit fund sweeper.
_A seller may use this method to recover tokens from expired deposits.Function sighash: 0x36317972._
#### Parameters
| Name | Type | Description |
| ------------ | --------- | ----------- |
| depositID | uint256 | undefined |
| expiredLocks | bytes32[] | undefined |
### withdrawBalance
```solidity ```solidity
function getBalances(address[] sellers, contract ERC20 token) external view returns (uint256[]) function withdrawBalance() external nonpayable
``` ```
### getLocksStatus _Contract&#39;s underlying balance withdraw method.Function sighash: 0x5fd8c710._
## Events
### AllowedERC20Updated
```solidity ```solidity
function getLocksStatus(uint256[] ids) external view returns (uint256[], enum DataTypes.LockStatus[]) event AllowedERC20Updated(address indexed token, bool indexed state)
``` ```
External getter that returns the status of a lockIDs array. #### Parameters
Call will not revert if provided with an empty array as parameter.
_Function sighash: 0x49ef8448_ | Name | Type | Description |
| --------------- | ------- | ----------- |
| token `indexed` | address | undefined |
| state `indexed` | bool | undefined |
### DepositAdded
```solidity
event DepositAdded(address indexed seller, uint256 depositID, address token, uint256 amount)
```
███ Events ████████████████████████████████████████████████████████████
#### Parameters
| Name | Type | Description |
| ---------------- | ------- | ----------- |
| seller `indexed` | address | undefined |
| depositID | uint256 | undefined |
| token | address | undefined |
| amount | uint256 | undefined |
### DepositClosed
```solidity
event DepositClosed(address indexed seller, uint256 depositID)
```
#### Parameters
| Name | Type | Description |
| ---------------- | ------- | ----------- |
| seller `indexed` | address | undefined |
| depositID | uint256 | undefined |
### DepositWithdrawn
```solidity
event DepositWithdrawn(address indexed seller, uint256 depositID, uint256 amount)
```
#### Parameters
| Name | Type | Description |
| ---------------- | ------- | ----------- |
| seller `indexed` | address | undefined |
| depositID | uint256 | undefined |
| amount | uint256 | undefined |
### FundsWithdrawn
```solidity
event FundsWithdrawn(address owner, uint256 amount)
```
#### Parameters
| Name | Type | Description |
| ------ | ------- | ----------- |
| owner | address | undefined |
| amount | uint256 | undefined |
### LockAdded
```solidity
event LockAdded(address indexed buyer, bytes32 indexed lockID, uint256 depositID, uint256 amount)
```
#### Parameters
| Name | Type | Description |
| ---------------- | ------- | ----------- |
| buyer `indexed` | address | undefined |
| lockID `indexed` | bytes32 | undefined |
| depositID | uint256 | undefined |
| amount | uint256 | undefined |
### LockBlocksUpdated
```solidity
event LockBlocksUpdated(uint256 blocks)
```
#### Parameters
| Name | Type | Description |
| ------ | ------- | ----------- |
| blocks | uint256 | undefined |
### LockReleased
```solidity
event LockReleased(address indexed buyer, bytes32 lockId)
```
#### Parameters
| Name | Type | Description |
| --------------- | ------- | ----------- |
| buyer `indexed` | address | undefined |
| lockId | bytes32 | undefined |
### LockReturned
```solidity
event LockReturned(address indexed buyer, bytes32 lockId)
```
#### Parameters
| Name | Type | Description |
| --------------- | ------- | ----------- |
| buyer `indexed` | address | undefined |
| lockId | bytes32 | undefined |
### OwnerUpdated
```solidity
event OwnerUpdated(address indexed user, address indexed newOwner)
```
#### Parameters
| Name | Type | Description |
| ------------------ | ------- | ----------- |
| user `indexed` | address | undefined |
| newOwner `indexed` | address | undefined |
### ReputationUpdated
```solidity
event ReputationUpdated(address reputation)
```
#### Parameters
| Name | Type | Description |
| ---------- | ------- | ----------- |
| reputation | address | undefined |
### ValidSignersUpdated
```solidity
event ValidSignersUpdated(address[] signers)
```
#### Parameters
| Name | Type | Description |
| ------- | --------- | ----------- |
| signers | address[] | undefined |
## Errors
### AddressDenied
```solidity
error AddressDenied()
```
_Address doesn&#39;t exist in a MerkleTree.Address not allowed as relayer.0x3b8474be_
### AlreadyReleased
```solidity
error AlreadyReleased()
```
_Lock already released or returned.0x63b4904e_
### AmountNotAllowed
```solidity
error AmountNotAllowed()
```
_Wished amount to be locked exceeds the limit allowed.0x1c18f846_
### DepositAlreadyExists
```solidity
error DepositAlreadyExists()
```
_Deposit already exist and it is still valid.0xc44bd765_
### InvalidDeposit
```solidity
error InvalidDeposit()
```
_Deposit not valid anymore.0xb2e532de_
### InvalidSigner
```solidity
error InvalidSigner()
```
_Signer is not a valid signer.0x815e1d64_
### LengthMismatch
```solidity
error LengthMismatch()
```
_Arrays&#39; length don&#39;t match.0xff633a38_
### LoopOverflow
```solidity
error LoopOverflow()
```
_Loop bounds have overflowed.0xdfb035c9_
### NoTokens
```solidity
error NoTokens()
```
_No tokens array provided as argument.0xdf957883_
### NotEnoughTokens
```solidity
error NotEnoughTokens()
```
_Not enough token remaining on deposit.0x22bbb43c_
### NotExpired
```solidity
error NotExpired()
```
_Lock not expired or already released.Another lock with same ID is not expired yet.0xd0404f85_
### OnlySeller
```solidity
error OnlySeller()
```
_Only seller could call this function.`msg.sender` and the seller differ.0x85d1f726_
### Reentrancy
```solidity
error Reentrancy()
```
### TokenDenied
```solidity
error TokenDenied()
```
_Token address not allowed to be deposited.0x1578328e_
### TxAlreadyUsed
```solidity
error TxAlreadyUsed()
```
_Transaction already used to unlock payment.0xf490a6ea_

View File

@@ -1,55 +0,0 @@
const tseslint = require("typescript-eslint");
const js = require("@eslint/js");
const eslintConfigPrettier = require("eslint-config-prettier");
module.exports = tseslint.config(
{
ignores: [
".yarn/",
"**/.coverage_artifacts",
"**/.coverage_cache",
"**/.coverage_contracts",
"**/artifacts",
"**/build",
"**/cache",
"**/coverage",
"**/dist",
"**/node_modules",
"**/types",
"deploys/old/",
"docs/",
"*.env",
"*.log",
".pnp.*",
"coverage.json",
"npm-debug.log*",
"yarn-debug.log*",
"yarn-error.log*",
".solcover.js",
"eslint.config.js",
],
},
js.configs.recommended,
...tseslint.configs.recommended,
eslintConfigPrettier,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: __dirname,
},
},
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: "_" },
],
"@typescript-eslint/no-unused-expressions": "off",
},
},
);

View File

@@ -1,6 +1,7 @@
import "@nomicfoundation/hardhat-chai-matchers"; import "@nomicfoundation/hardhat-chai-matchers";
import "@nomicfoundation/hardhat-toolbox"; import "@nomicfoundation/hardhat-toolbox";
import { config as dotenvConfig } from "dotenv"; import { config as dotenvConfig } from "dotenv";
import "hardhat-tracer";
import { HardhatUserConfig } from "hardhat/config"; import { HardhatUserConfig } from "hardhat/config";
import { NetworkUserConfig } from "hardhat/types"; import { NetworkUserConfig } from "hardhat/types";
import "hardhat-contract-sizer"; import "hardhat-contract-sizer";
@@ -9,74 +10,79 @@ import "solidity-docgen";
dotenvConfig({ path: resolve(__dirname, "./.env") }); dotenvConfig({ path: resolve(__dirname, "./.env") });
// Default mnemonic used by Hardhat / Anvil for deterministic accounts. const mnemonic: string | undefined = process.env.MNEMONIC;
// Lets `--network localhost` (and the default in-process `hardhat` net) if (!mnemonic) {
// run without a .env. Public networks still require a real MNEMONIC. throw new Error("Please set your MNEMONIC in a .env file");
const DEFAULT_MNEMONIC = }
"test test test test test test test test test test test junk";
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 mnemonic: string =
process.env.MNEMONIC ?? DEFAULT_MNEMONIC;
const alchemyApiKey: string | undefined = const alchemyApiKey: string | undefined =
process.env.ALCHEMY_API_KEY; process.env.ALCHEMY_API_KEY;
const privateKey: string | undefined = if (!alchemyApiKey) {
process.env.PRIVATE_KEY; throw new Error(
"Please set your ALCHEMY_API_KEY in a .env file",
);
}
const chainIds = { const chainIds = {
// "{INSERT_NAME}": {INSERT_ID}, // "{INSERT_NAME}": {INSERT_ID},
hardhat: 31337, hardhat: 31337,
localhost: 31337,
mainnet: 1, mainnet: 1,
"eth-sepolia": 11155111, sepolia: 11155111,
"polygon-mainnet": 137, goerli: 5,
"arb-mainnet": 42161, "polygon-mumbai": 80001,
rootstock: 30, rootstock:30,
"rootstock-testnet": 31, rsktestnet:31,
}; };
function getChainConfig( function getChainConfig(
chain: keyof typeof chainIds, chain: keyof typeof chainIds,
): NetworkUserConfig { ): NetworkUserConfig {
if (!alchemyApiKey) { let jsonRpcUrl: string;
throw new Error( switch (chain) {
`Please set ALCHEMY_API_KEY in a .env file before targeting ${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;
default:
jsonRpcUrl =
"https://" + chain + ".infura.io/v3/" + infuraApiKey;
} }
const jsonRpcUrl =
"https://" + chain + ".g.alchemy.com/v2/" + alchemyApiKey;
return { return {
accounts: privateKey // Comment out for default hardhat account settings
? [privateKey] accounts: {
: { count: 10,
count: 10, mnemonic,
mnemonic, path: "m/44'/60'/0'/0",
path: "m/44'/60'/0'/0", },
}, // gasPrice: 8000000000,
chainId: chainIds[chain], chainId: chainIds[chain],
url: jsonRpcUrl, url: jsonRpcUrl,
}; };
} }
const liveNetworks: Record<string, NetworkUserConfig> =
alchemyApiKey
? {
mainnet: getChainConfig("mainnet"),
sepolia: getChainConfig("eth-sepolia"),
polygon: getChainConfig("polygon-mainnet"),
arbitrum: getChainConfig("arb-mainnet"),
rootstock: getChainConfig("rootstock"),
rsktestnet: getChainConfig("rootstock-testnet"),
}
: {};
const config: HardhatUserConfig = { const config: HardhatUserConfig = {
defaultNetwork: "hardhat", defaultNetwork: "hardhat",
etherscan: { etherscan: {
apiKey: { apiKey: {
mainnet: process.env.ETHERSCAN_API_KEY || "", mainnet: process.env.ETHERSCAN_API_KEY || "",
sepolia: process.env.ETHERSCAN_API_KEY || "", rinkeby: process.env.ETHERSCAN_API_KEY || "",
polygon: process.env.POLYGONSCAN_API_KEY || "", goerli: process.env.ETHERSCAN_API_KEY || "",
arbitrumOne: process.env.ARBISCAN_API_KEY || "", polygonMumbai: process.env.POLYGONSCAN_API_KEY || "",
}, },
}, },
gasReporter: { gasReporter: {
@@ -84,7 +90,6 @@ const config: HardhatUserConfig = {
process.env.REPORT_GAS && process.env.REPORT_GAS &&
process.env.REPORT_GAS != "false" process.env.REPORT_GAS != "false"
), ),
offline: true,
showTimeSpent: true, showTimeSpent: true,
showMethodSig: true, showMethodSig: true,
token: "ETH", token: "ETH",
@@ -102,16 +107,13 @@ const config: HardhatUserConfig = {
}, },
chainId: chainIds.hardhat, chainId: chainIds.hardhat,
}, },
// External Anvil / Hardhat node (e.g. `anvil --port 8545`). Used by // network: getChainConfig("{INSERT_NAME}"),
// the frontend e2e suite to deploy a fresh chain per CI run. mainnet: getChainConfig("mainnet"),
localhost: { goerli: getChainConfig("goerli"),
url: "http://127.0.0.1:8545", sepolia: getChainConfig("sepolia"),
chainId: chainIds.localhost, "polygon-mumbai": getChainConfig("polygon-mumbai"),
accounts: { rootstock: getChainConfig("rootstock"),
mnemonic, rsktestnet: getChainConfig("rsktestnet"),
},
},
...liveNetworks,
}, },
paths: { paths: {
artifacts: "./artifacts", artifacts: "./artifacts",
@@ -120,10 +122,10 @@ const config: HardhatUserConfig = {
tests: "./test", tests: "./test",
}, },
solidity: { solidity: {
version: "0.8.28", version: "0.8.19",
settings: { settings: {
viaIR: true, viaIR: true,
evmVersion: "cancun", evmVersion: "paris",
optimizer: { optimizer: {
enabled: true, enabled: true,
runs: 20_000, runs: 20_000,
@@ -143,11 +145,11 @@ const config: HardhatUserConfig = {
}, },
typechain: { typechain: {
outDir: "src/types", outDir: "src/types",
target: "ethers-v6", target: "ethers-v5",
}, },
docgen: { docgen: {
pages: "files", pages: "files",
}, }
}; };
export default config; export default config;

View File

@@ -1,14 +0,0 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
import { ethers } from "ethers";
const DEFAULT_SUPPLY = ethers
.parseEther("20000000")
.toString();
export default buildModule("MockToken", m => {
const supply = m.getParameter("supply", DEFAULT_SUPPLY);
const token = m.contract("MockToken", [supply]);
return { token };
});

View File

@@ -1,31 +0,0 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
import ReputationModule from "./Reputation";
export default buildModule("P2PIX", m => {
const { reputation, multicall } = m.useModule(
ReputationModule,
);
const defaultBlocks = m.getParameter("defaultBlocks", 10);
const validSigners = m.getParameter<string[]>(
"validSigners",
[],
);
const tokens = m.getParameter<string[]>("tokens");
const allowed = m.getParameter<boolean[]>("allowed");
const p2pix = m.contract("P2PIX", [
defaultBlocks,
validSigners,
reputation,
tokens,
allowed,
]);
return {
p2pix,
reputation,
multicall,
};
});

View File

@@ -1,32 +0,0 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
import MockTokenModule from "./MockToken";
import ReputationModule from "./Reputation";
export default buildModule("P2PIXWithMock", m => {
const { token } = m.useModule(MockTokenModule);
const { reputation, multicall } = m.useModule(
ReputationModule,
);
const defaultBlocks = m.getParameter("defaultBlocks", 10);
const validSigners = m.getParameter<string[]>(
"validSigners",
[],
);
const p2pix = m.contract("P2PIX", [
defaultBlocks,
validSigners,
reputation,
[token],
[true],
]);
return {
p2pix,
reputation,
multicall,
token,
};
});

View File

@@ -1,8 +0,0 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
export default buildModule("Reputation", m => {
const reputation = m.contract("Reputation", []);
const multicall = m.contract("Multicall", []);
return { reputation, multicall };
});

View File

@@ -1,9 +0,0 @@
{
"P2PIXWithMock": {
"defaultBlocks": 10,
"validSigners": []
},
"MockToken": {
"supply": "20000000000000000000000000"
}
}

View File

@@ -1,8 +0,0 @@
{
"P2PIX": {
"defaultBlocks": 10,
"validSigners": [],
"tokens": ["0x0000000000000000000000000000000000000000"],
"allowed": [true]
}
}

View File

@@ -9,65 +9,86 @@
"url": "https://github.com/doiim/p2pix-smart-contracts/issues" "url": "https://github.com/doiim/p2pix-smart-contracts/issues"
}, },
"scripts": { "scripts": {
"clean": "hardhat clean", "clean": "shx rm -rf ./artifacts ./cache ./coverage ./src/types ./coverage.json && yarn typechain",
"compile": "hardhat compile", "commit": "git-cz",
"typechain": "hardhat typechain", "compile": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat compile",
"typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain",
"test": "hardhat test", "test": "hardhat test",
"deploy": "hardhat ignition deploy ignition/modules/P2PIX.ts", "deploy1:localhost": "hardhat run scripts/1-deploy-mockToken.ts --network localhost",
"deploy:mock": "hardhat ignition deploy ignition/modules/P2PIXWithMock.ts", "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", "coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"test/**/*.ts\" && yarn typechain",
"lint": "yarn lint:sol && yarn lint:ts && yarn prettier:check", "lint": "yarn lint:sol && yarn lint:ts && yarn prettier:check",
"lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"", "lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"",
"lint:ts": "eslint .", "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": "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}\"" "prettier:check": "prettier --check --config ./.prettierrc.yaml \"**/*.{js,json,md,sol,ts,yaml,yml}\""
}, },
"devDependencies": { "devDependencies": {
"@ethersproject/abi": "^5.8.0", "@commitlint/cli": "^17.2.0",
"@ethersproject/providers": "^5.8.0", "@commitlint/config-conventional": "^17.2.0",
"@nomicfoundation/hardhat-chai-matchers": "^2.1.0", "@ethersproject/abi": "^5.7.0",
"@nomicfoundation/hardhat-ethers": "^3.1.0", "@ethersproject/abstract-signer": "^5.7.0",
"@nomicfoundation/hardhat-ignition": "^0.15.13", "@ethersproject/bignumber": "^5.7.0",
"@nomicfoundation/hardhat-ignition-ethers": "^0.15.14", "@ethersproject/bytes": "^5.7.0",
"@nomicfoundation/hardhat-network-helpers": "^1.1.0", "@ethersproject/providers": "^5.7.2",
"@nomicfoundation/hardhat-toolbox": "^6.1.0", "@nomicfoundation/hardhat-chai-matchers": "^1.0.4",
"@nomicfoundation/hardhat-verify": "^2.1.0", "@nomicfoundation/hardhat-network-helpers": "1.0.6",
"@nomicfoundation/hardhat-viem": "^2.1.0", "@nomicfoundation/hardhat-toolbox": "^2.0.0",
"@nomicfoundation/ignition-core": "^0.15.13", "@nomiclabs/hardhat-ethers": "^2.2.1",
"@openzeppelin/contracts": "5.5.0", "@nomiclabs/hardhat-etherscan": "^3.1.2",
"@typechain/ethers-v6": "^0.5.1", "@trivago/prettier-plugin-sort-imports": "^3.4.0",
"@typechain/hardhat": "^9.1.0", "@typechain/ethers-v5": "^10.1.1",
"@types/chai": "^4.3.20", "@typechain/hardhat": "^6.1.4",
"@types/mocha": "^10.0.10", "@types/chai": "^4.3.3",
"@types/node": "^24.1.0", "@types/fs-extra": "^9.0.13",
"chai": "4.5.0", "@types/mocha": "^9.1.1",
"dotenv": "^16.6.1", "@types/node": "^18.11.9",
"eslint": "^9.32.0", "@typescript-eslint/eslint-plugin": "^5.42.0",
"eslint-config-prettier": "^10.1.8", "@typescript-eslint/parser": "^5.42.0",
"ethers": "^6.15.0", "chai": "^4.3.6",
"hardhat": "^2.26.1", "chalk": "4.x",
"hardhat-contract-sizer": "^2.10.0", "commitizen": "^4.2.5",
"hardhat-gas-reporter": "^2.3.0", "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",
"hardhat-tracer": "beta",
"husky": "^8.0.1",
"keccak256": "^1.0.6", "keccak256": "^1.0.6",
"lint-staged": "^13.0.3",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"merkletreejs": "^0.5.2", "merkletreejs": "^0.2.32",
"mocha": "^10.8.2", "mocha": "^10.1.0",
"prettier": "^3.0.0", "pinst": "^3.0.0",
"prettier-plugin-solidity": "^1.0.0", "prettier": "^2.7.1",
"solhint": "^6.2.1", "prettier-plugin-solidity": "^1.0.0-rc.1",
"solhint-plugin-prettier": "^0.1.0", "shx": "^0.3.4",
"solidity-coverage": "^0.8.16", "solhint": "^3.3.7",
"solhint-plugin-prettier": "^0.0.5",
"solidity-coverage": "^0.8.2",
"solidity-docgen": "^0.6.0-beta.36", "solidity-docgen": "^0.6.0-beta.36",
"ts-node": "^10.9.2", "ts-generator": "^0.1.1",
"typechain": "^8.3.2", "ts-node": "^10.9.1",
"typescript": "^5.8.3", "typechain": "^8.1.1",
"typescript-eslint": "^8.60.0", "typescript": "^4.8.4"
"viem": "^2.33.1"
}, },
"files": [ "files": [
"/contracts" "/contracts"
], ],
"packageManager": "yarn@4.9.2", "packageManager": "yarn@3.2.1",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
} }

View File

@@ -0,0 +1,56 @@
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);
});

85
scripts/2-deploy-p2pix.ts Normal file
View File

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

@@ -20,22 +20,12 @@ const main = async () => {
} }
const [deployer] = await ethers.getSigners(); const [deployer] = await ethers.getSigners();
console.log( console.log(`Signing transactions with ${deployer.address}`);
`Signing transactions with ${deployer.address}`,
);
const iface = new ethers.utils.Interface( const iface = new ethers.utils.Interface(P2PIX__factory.abi);
P2PIX__factory.abi, const calldata = iface.encodeFunctionData("setDefaultLockBlocks", ["10000"]);
);
const calldata = iface.encodeFunctionData( const tx = await deployer.sendTransaction({to:deploysJson.p2pix, data: calldata});
"setDefaultLockBlocks",
["10000"],
);
const tx = await deployer.sendTransaction({
to: deploysJson.p2pix,
data: calldata,
});
const done = await tx.wait(); const done = await tx.wait();
console.log(done.transactionHash); console.log(done.transactionHash);
}; };

View File

@@ -1,5 +1,6 @@
import "@nomicfoundation/hardhat-chai-matchers"; import "@nomicfoundation/hardhat-chai-matchers";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai"; import { expect } from "chai";
import { ethers, network } from "hardhat"; import { ethers, network } from "hardhat";
@@ -7,11 +8,14 @@ import { Reputation } from "../src/types";
import { curve, repFixture } from "./utils/fixtures"; import { curve, repFixture } from "./utils/fixtures";
describe("Reputation", () => { describe("Reputation", () => {
// contract deployer/admin
let owner: SignerWithAddress;
// Reputation Interface instance; // Reputation Interface instance;
let reputation: Reputation; let reputation: Reputation;
before("Set signers and reset network", async () => { before("Set signers and reset network", async () => {
await ethers.getSigners(); // eslint-disable-next-line @typescript-eslint/no-explicit-any
[owner] = await (ethers as any).getSigners();
await network.provider.send("hardhat_reset"); await network.provider.send("hardhat_reset");
}); });
@@ -56,7 +60,7 @@ describe("Reputation", () => {
}, },
{ {
x: Number.MAX_SAFE_INTEGER, x: Number.MAX_SAFE_INTEGER,
expected: curve(Number.MAX_SAFE_INTEGER), shouldRevert: "overflow",
}, },
{ {
x: Number.POSITIVE_INFINITY, x: Number.POSITIVE_INFINITY,

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,43 @@
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { Signer } from "ethers"; import { BigNumber, Signer } from "ethers";
import { ethers } from "hardhat"; import { ethers } from "hardhat";
import keccak256 from "keccak256"; import keccak256 from "keccak256";
import { MerkleTree } from "merkletreejs"; import { MerkleTree } from "merkletreejs";
import { MockToken, P2PIX__factory } from "../../src/types";
import { import {
Call, MockToken,
RepFixture, Multicall,
P2PixAndReputation, P2PIX,
} from "./interfaces"; P2PIX__factory,
Reputation,
} from "../../src/types";
import { Call, RepFixture, P2PixAndReputation, DepositArgs, LockArgs, ReleaseArgs } from "./interfaces";
// exported constants // exported constants
export const createDepositArgs = (pixTarget: string, allowlistRoot: string, token: string, amount: BigNumber, valid:boolean): DepositArgs => ({
pixTarget,
allowlistRoot,
token,
amount,
valid,
});
export const createLockArgs = (seller: string, token: string, amount: BigNumber, merkleProof: string[], expiredLocks: BigNumber[]): LockArgs => ({
seller,
token,
amount,
merkleProof,
expiredLocks,
});
export const createReleaseArgs = (lockID: BigNumber, pixTimestamp: string, signature: string): ReleaseArgs => ({
lockID,
pixTimestamp,
signature,
});
export const getSignerAddrs = ( export const getSignerAddrs = (
amount: number, amount: number,
addrs: SignerWithAddress[], addrs: SignerWithAddress[],
@@ -20,16 +45,18 @@ export const getSignerAddrs = (
return addrs.slice(0, amount).map(({ address }) => address); return addrs.slice(0, amount).map(({ address }) => address);
}; };
export const getBnFrom = (nums: number[]): bigint[] => { export const getBnFrom = (nums: number[]): BigNumber[] => {
const bns = nums.map(num => BigInt(num)); const bns = nums.map(num => ethers.BigNumber.from(num));
return bns; return bns;
}; };
export const getLockData = ( export const getLockData = (
addr: string, addr: string,
locks: bigint[][], locks: BigNumber[][],
): Call[] => { ): Call[] => {
const iface = new ethers.Interface(P2PIX__factory.abi); const iface = new ethers.utils.Interface(
P2PIX__factory.abi,
);
return locks.map(lock => ({ return locks.map(lock => ({
target: addr, target: addr,
callData: iface.encodeFunctionData("getLocksStatus", [ callData: iface.encodeFunctionData("getLocksStatus", [
@@ -67,9 +94,13 @@ export const curve = (x: number): number => {
// exported async functions // exported async functions
export async function repFixture(): Promise<RepFixture> { export async function repFixture(): Promise<RepFixture> {
const Reputation = await ethers.getContractFactory(
"Reputation",
);
const reputation = const reputation =
await ethers.deployContract("Reputation"); (await Reputation.deploy()) as Reputation;
return { reputation: await reputation.waitForDeployment() };
return { reputation };
} }
export async function p2pixFixture(): Promise<P2PixAndReputation> { export async function p2pixFixture(): Promise<P2PixAndReputation> {
@@ -78,22 +109,30 @@ export async function p2pixFixture(): Promise<P2PixAndReputation> {
await ethers.getSigners(), await ethers.getSigners(),
); );
const Reputation = await ethers.getContractFactory(
"Reputation",
);
const reputation = const reputation =
await ethers.deployContract("Reputation"); (await Reputation.deploy()) as Reputation;
const erc20 = (await ethers.deployContract("MockToken", [ const ERC20 = await ethers.getContractFactory("MockToken");
ethers.parseEther("20000000"), // 20M const erc20 = (await ERC20.deploy(
])) as MockToken; ethers.utils.parseEther("20000000"), // 20M
)) as MockToken;
const p2pix = await ethers.deployContract("P2PIX", [ const P2PIX = await ethers.getContractFactory("P2PIX");
const p2pix = (await P2PIX.deploy(
10, 10,
validSigners, validSigners,
reputation.target, reputation.address,
[erc20.target], [erc20.address],
[true], [true],
]); )) as P2PIX;
const multicall = await ethers.deployContract("Multicall"); const Multicall = await ethers.getContractFactory(
"Multicall",
);
const multicall = (await Multicall.deploy()) as Multicall;
const signers = await ethers.getSigners(); const signers = await ethers.getSigners();
const whitelisted = signers.slice(0, 2); const whitelisted = signers.slice(0, 2);
@@ -109,10 +148,10 @@ export async function p2pixFixture(): Promise<P2PixAndReputation> {
); );
return { return {
multicall: await multicall.waitForDeployment(), multicall,
reputation: await reputation.waitForDeployment(), reputation,
erc20: await erc20.waitForDeployment(), erc20,
p2pix: await p2pix.waitForDeployment(), p2pix,
merkleRoot, merkleRoot,
proof, proof,
}; };

View File

@@ -1,3 +1,5 @@
import { BigNumber } from "ethers";
import { import {
MockToken, MockToken,
Multicall, Multicall,
@@ -5,6 +7,7 @@ import {
Reputation, Reputation,
} from "../../src/types"; } from "../../src/types";
// exported interfaces // exported interfaces
export interface Deploys { export interface Deploys {
signers: string[]; signers: string[];
@@ -16,29 +19,29 @@ export interface DepositArgs {
pixTarget: string; pixTarget: string;
allowlistRoot: string; allowlistRoot: string;
token: string; token: string;
amount: bigint; amount: BigNumber;
valid: boolean; valid: boolean;
} }
export interface LockArgs { export interface LockArgs {
seller: string; seller: string;
token: string; token: string;
amount: bigint; amount: BigNumber;
merkleProof: string[]; merkleProof: string[];
expiredLocks: bigint[]; expiredLocks: BigNumber[];
} }
export interface ReleaseArgs { export interface ReleaseArgs {
lockID: bigint; lockID: BigNumber;
pixTimestamp: string; pixTimestamp: string;
signature: string; signature: string;
} }
export interface Lock { export interface Lock {
counter: bigint; counter: BigNumber;
expirationBlock: bigint; expirationBlock: BigNumber;
pixTarget: string; pixTarget: string;
amount: bigint; amount: BigNumber;
token: string; token: string;
buyerAddress: string; buyerAddress: string;
seller: string; seller: string;
@@ -71,4 +74,4 @@ export interface MtcFixture {
export type P2PixAndReputation = P2pixFixture & export type P2PixAndReputation = P2pixFixture &
RepFixture & RepFixture &
MtcFixture; MtcFixture;

View File

@@ -18,10 +18,5 @@
}, },
"exclude": ["node_modules"], "exclude": ["node_modules"],
"files": ["./hardhat.config.ts"], "files": ["./hardhat.config.ts"],
"include": [ "include": ["src/**/*", "test/**/*", "scripts/**/*"]
"src/**/*",
"test/**/*",
"scripts/**/*",
"ignition/**/*"
]
} }

11870
yarn.lock

File diff suppressed because it is too large Load Diff