Compare commits
5 Commits
dev
...
openzeppel
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98dda3c61f | ||
|
|
4d1a24ecdf | ||
|
|
d9b93fe7d5 | ||
|
|
f56335d9b5 | ||
|
|
407ea51d6a |
@@ -1,5 +1,4 @@
|
||||
MNEMONIC="{INSERT_12_WORD_MNEMONIC}"
|
||||
PRIVATE_KEY="{INSERT_0x_PRIVATE_KEY}"
|
||||
|
||||
INFURA_API_KEY="{INSERT_API_KEY}"
|
||||
ALCHEMY_API_KEY="{INSERT_API_KEY}"
|
||||
|
||||
21
.eslintignore
Normal file
21
.eslintignore
Normal 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
21
.eslintrc.yaml
Normal 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: "_"
|
||||
32
.github/workflows/lint.yml
vendored
32
.github/workflows/lint.yml
vendored
@@ -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
|
||||
35
.github/workflows/test.yml
vendored
35
.github/workflows/test.yml
vendored
@@ -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
8
.gitignore
vendored
@@ -28,11 +28,3 @@ coverage.json
|
||||
npm-debug.log*
|
||||
yarn-debug.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
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
**/node_modules
|
||||
**/types
|
||||
|
||||
# deploy artifacts
|
||||
deploys/old/
|
||||
|
||||
# auto-generated docs
|
||||
docs/
|
||||
|
||||
# files
|
||||
*.env
|
||||
*.log
|
||||
@@ -24,4 +18,6 @@ docs/
|
||||
coverage.json
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
yarn-error.log*
|
||||
|
||||
contracts/p2pix.sol
|
||||
@@ -1,8 +1,10 @@
|
||||
plugins:
|
||||
- prettier-plugin-solidity
|
||||
arrowParens: avoid
|
||||
bracketSpacing: true
|
||||
endOfLine: auto
|
||||
importOrder: ["<THIRD_PARTY_MODULES>", "^[./]"]
|
||||
importOrderParserPlugins: ["typescript"]
|
||||
importOrderSeparation: true
|
||||
importOrderSortSpecifiers: true
|
||||
printWidth: 62
|
||||
singleQuote: false
|
||||
tabWidth: 2
|
||||
|
||||
@@ -14,9 +14,5 @@ module.exports = {
|
||||
providerOptions: {
|
||||
mnemonic: process.env.MNEMONIC,
|
||||
},
|
||||
skipFiles: [
|
||||
"test",
|
||||
"core/BaseUtils.sol",
|
||||
"core/OwnerSettings.sol",
|
||||
],
|
||||
skipFiles: ["test", 'core/BaseUtils.sol', 'core/OwnerSettings.sol'],
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"code-complexity": ["error", 8],
|
||||
"avoid-low-level-calls": "off",
|
||||
"compiler-version": ["error", ">=0.8.4"],
|
||||
"const-name-snakecase": "off",
|
||||
"constructor-syntax": "error",
|
||||
@@ -11,14 +10,7 @@
|
||||
"error",
|
||||
{ "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],
|
||||
"no-inline-assembly": "off",
|
||||
"not-rely-on-time": "off",
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
@@ -26,7 +18,6 @@
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
],
|
||||
"reason-string": ["warn", { "maxLength": 64 }],
|
||||
"use-natspec": "off"
|
||||
"reason-string": ["warn", { "maxLength": 64 }]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# directories
|
||||
**/artifacts
|
||||
**/cache
|
||||
**/node_modules
|
||||
deploys/
|
||||
docs/
|
||||
|
||||
91
README.md
91
README.md
@@ -55,23 +55,25 @@
|
||||
|
||||
| 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 -->
|
||||
<!-- https://sepolia.etherscan.io/address/0x3eBE212377D847828eBb9D2a100f5c5EA1EF3A72#code -->
|
||||
<!-- https://sepolia.etherscan.io/address/0xb7cDAE58C6e715Cfd795BB142041E43b9CB02497#code -->
|
||||
<!-- https://sepolia.etherscan.io/address/0xFd1194A56995Ef7B62730F4061408e79d88E5207#code -->
|
||||
<!-- https://sepolia.etherscan.io/address/0x2f7f9848A803E67d79C0C8aa42b663dCF6E1B5ed#code -->
|
||||
<!-- https://goerli.etherscan.io/address/0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00#code -->
|
||||
<!-- https://goerli.etherscan.io/address/0x2414817FF64A114d91eCFA16a834d3fCf69103d4#code -->
|
||||
<!-- https://goerli.etherscan.io/address/0x2CFD9354Ec7614fEf036EFd6A730dA1d5fC2762A#code -->
|
||||
<!-- https://goerli.etherscan.io/address/0x8FE009992d96A86c7f0Bccdaf1eC3471E302a8a6#code -->
|
||||
|
||||
<!-- https://mumbai.polygonscan.com/address/0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29#code -->
|
||||
<!-- https://mumbai.polygonscan.com/address/0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00#code -->
|
||||
<!-- https://mumbai.polygonscan.com/address/0x570445e3ef413bcdb5de79ed27b1c3840683e385#code -->
|
||||
<!-- https://mumbai.polygonscan.com/address/0x718B2C4DE4F9654E1349F610ff561249bfe1c418#code -->
|
||||
|
||||
## Usage
|
||||
|
||||
### Pre Requisites
|
||||
|
||||
Before installing, create a `.env` file and set the authentication method 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.
|
||||
Before installing, create a `.env` file and set a BIP-39 compatible mnemonic and other env criteria as in `.env.example`.
|
||||
|
||||
### 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";
|
||||
```
|
||||
|
||||
## Deployment (Hardhat Ignition)
|
||||
## Deploying to local environment
|
||||
|
||||
Deployments are orchestrated by
|
||||
[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:
|
||||
On the first teminal, use the following command and import some wallets to your Metamask, then connect to the network pointed:
|
||||
|
||||
```sh
|
||||
yarn hardhat node
|
||||
```
|
||||
|
||||
On the second terminal:
|
||||
On the second teminal, run the following commands:
|
||||
|
||||
```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
|
||||
`ignition/deployments/chain-31337/deployed_addresses.json` (this path is
|
||||
gitignored — local-only state).
|
||||
**_NOTE_:** The second script transfers 2M tokens to the first wallet of the node.
|
||||
To use the P2Pix smart contract first transfer some of the tokens to other wallets.
|
||||
|
||||
### Testnets
|
||||
## Deploying to testnets
|
||||
|
||||
Deploy to Ethereum's Goerli testnet:
|
||||
|
||||
```sh
|
||||
yarn deploy:mock --network sepolia
|
||||
yarn deploy1:goerli
|
||||
yarn deploy2:goerli
|
||||
```
|
||||
|
||||
Requires the matching `ignition/parameters/sepolia.json` and the relevant
|
||||
API keys in `.env`.
|
||||
Deploy to Polygon's Mumbai testnet:
|
||||
|
||||
### Production (real tokens)
|
||||
|
||||
Use the `P2PIX` module when the target network already has the ERC20
|
||||
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`.
|
||||
```sh
|
||||
yarn deploy1:mumbai
|
||||
yarn deploy2:mumbai
|
||||
```
|
||||
|
||||
@@ -45,8 +45,9 @@ abstract contract BaseUtils is
|
||||
!validBacenSigners(
|
||||
_castAddrToKey(
|
||||
ECDSA.recover(
|
||||
MessageHashUtils
|
||||
.toEthSignedMessageHash(_message),
|
||||
MessageHashUtils.toEthSignedMessageHash(
|
||||
_message
|
||||
),
|
||||
_signature
|
||||
)
|
||||
)
|
||||
|
||||
@@ -4,23 +4,19 @@ pragma solidity ^0.8.19;
|
||||
abstract contract Constants {
|
||||
/// ███ Constants ██████████████████████████████████████████████████████████
|
||||
|
||||
uint256 internal constant _ROOT_UPDATED_EVENT_SIGNATURE =
|
||||
uint256 constant _ROOT_UPDATED_EVENT_SIGNATURE =
|
||||
0x0b294da292f26e55fd442b5c0164fbb9013036ff00c5cfdde0efd01c1baaf632;
|
||||
uint256
|
||||
internal constant _ALLOWED_ERC20_UPDATED_EVENT_SIGNATURE =
|
||||
uint256 constant _ALLOWED_ERC20_UPDATED_EVENT_SIGNATURE =
|
||||
0x5d6e86e5341d57a92c49934296c51542a25015c9b1782a1c2722a940131c3d9a;
|
||||
uint256
|
||||
internal constant _TRUSTED_FORWARDER_UPDATED_EVENT_SIGNATURE =
|
||||
uint256 constant _TRUSTED_FORWARDER_UPDATED_EVENT_SIGNATURE =
|
||||
0xbee55516e29d3969d3cb8eb01351eb3c52d06f9e2435bd5a8bfe3647e185df92;
|
||||
|
||||
/// @dev Seller casted to key => Seller's allowlist merkleroot.
|
||||
/// mapping(uint256 => bytes32) public sellerAllowList;
|
||||
uint256 internal constant _SELLER_ALLOWLIST_SLOT_SEED =
|
||||
0x74dfee70;
|
||||
uint256 constant _SELLER_ALLOWLIST_SLOT_SEED = 0x74dfee70;
|
||||
/// @dev Tokens allowed to serve as the underlying amount of a deposit.
|
||||
/// mapping(ERC20 => bool) public allowedERC20s;
|
||||
uint256 internal constant _ALLOWED_ERC20_SLOT_SEED =
|
||||
0xcbc9d1c4;
|
||||
uint256 constant _ALLOWED_ERC20_SLOT_SEED = 0xcbc9d1c4;
|
||||
|
||||
/// @dev `balance` max. value = 10**26.
|
||||
/// @dev `pixTarget` keys are restricted to 160 bits.
|
||||
@@ -36,21 +32,16 @@ abstract contract Constants {
|
||||
/// mstore(0x0c, _SELLER_BALANCE_SLOT_SEED)
|
||||
/// mstore(0x00, seller)
|
||||
/// let value := sload(keccak256(0x0c, 0x34)).
|
||||
uint256 internal constant _SELLER_BALANCE_SLOT_SEED =
|
||||
0x739094b1;
|
||||
uint256 constant _SELLER_BALANCE_SLOT_SEED = 0x739094b1;
|
||||
|
||||
/// @dev The bitmask of `sellerBalance` entry.
|
||||
uint256 internal constant BITMASK_SB_ENTRY =
|
||||
(1 << 94) - 1;
|
||||
uint256 constant BITMASK_SB_ENTRY = (1 << 94) - 1;
|
||||
/// @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.
|
||||
uint256 internal constant WAD = 1e18;
|
||||
uint256 internal constant MAXBALANCE_UPPERBOUND =
|
||||
1e8 ether;
|
||||
uint256 internal constant REPUTATION_LOWERBOUND =
|
||||
1e2 ether;
|
||||
uint256 internal constant LOCKAMOUNT_UPPERBOUND =
|
||||
1e6 ether;
|
||||
uint256 constant WAD = 1e18;
|
||||
uint256 constant MAXBALANCE_UPPERBOUND = 1e8 ether;
|
||||
uint256 constant REPUTATION_LOWERBOUND = 1e2 ether;
|
||||
uint256 constant LOCKAMOUNT_UPPERBOUND = 1e6 ether;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ pragma solidity ^0.8.19;
|
||||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
library DataTypes {
|
||||
|
||||
struct Lock {
|
||||
uint256 counter;
|
||||
uint256 expirationBlock;
|
||||
|
||||
@@ -6,10 +6,9 @@ import { ERC2771Context } from "@openzeppelin/contracts/metatx/ERC2771Context.so
|
||||
|
||||
abstract contract ERC2771 is ERC2771Context(address(0)) {
|
||||
mapping(address => bool) public trustedForwarders;
|
||||
|
||||
function isTrustedForwarder(
|
||||
address forwarder
|
||||
) public view override returns (bool) {
|
||||
return trustedForwarders[forwarder];
|
||||
|
||||
function isTrustedForwarder(address forwarder) public view override returns (bool) {
|
||||
return trustedForwarders[forwarder];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@ contract Multicall {
|
||||
}
|
||||
|
||||
//prettier-ignore
|
||||
//solhint-disable-next-line no-empty-blocks
|
||||
constructor(/* */) payable {/* */}
|
||||
|
||||
function mtc1(
|
||||
Call[] calldata calls
|
||||
) external returns (uint256, bytes[] memory) {
|
||||
function mtc1(Call[] calldata calls)
|
||||
external
|
||||
returns (uint256, bytes[] memory)
|
||||
{
|
||||
uint256 bn = block.number;
|
||||
uint256 len = calls.length;
|
||||
bytes[] memory res = new bytes[](len);
|
||||
@@ -49,14 +49,21 @@ contract Multicall {
|
||||
return (bn, res);
|
||||
}
|
||||
|
||||
function mtc2(
|
||||
Call[] calldata calls
|
||||
) external returns (uint256, bytes32, Result[] memory) {
|
||||
function mtc2(Call[] calldata calls)
|
||||
external
|
||||
returns (
|
||||
uint256,
|
||||
bytes32,
|
||||
Result[] memory
|
||||
)
|
||||
{
|
||||
uint256 bn = block.number;
|
||||
// µ 0 s [0] ≡ P(IHp , µs [0], 0) ∴ P is the hash of a block of a particular number, up to a maximum age.
|
||||
// 0 is left on the stack if the looked for `block.number` is >= to the current `block.number` or more than 256
|
||||
// blocks behind the current block (Yellow Paper, p. 33, https://ethereum.github.io/yellowpaper/paper.pdf).
|
||||
bytes32 bh = blockhash(bn /* - 1 */);
|
||||
bytes32 bh = blockhash(
|
||||
bn /* - 1 */
|
||||
);
|
||||
uint256 len = calls.length;
|
||||
Result[] memory res = new Result[](len);
|
||||
uint256 i;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { OwnerSettings, ERC20, SafeTransferLib } from "contracts/core/OwnerSetti
|
||||
import { BaseUtils } from "contracts/core/BaseUtils.sol";
|
||||
import { DataTypes as DT } from "contracts/core/DataTypes.sol";
|
||||
|
||||
|
||||
contract P2PIX is BaseUtils {
|
||||
// solhint-disable use-forbidden-name
|
||||
// solhint-disable no-inline-assembly
|
||||
@@ -33,18 +34,15 @@ contract P2PIX is BaseUtils {
|
||||
address _reputation,
|
||||
ERC20[] memory tokens,
|
||||
bool[] memory tokenStates
|
||||
)
|
||||
payable
|
||||
)
|
||||
OwnerSettings(
|
||||
defaultBlocks,
|
||||
validSigners,
|
||||
_reputation,
|
||||
tokens,
|
||||
defaultBlocks,
|
||||
validSigners,
|
||||
_reputation,
|
||||
tokens,
|
||||
tokenStates
|
||||
)
|
||||
{
|
||||
/* */
|
||||
}
|
||||
)
|
||||
payable {/* */}
|
||||
|
||||
/// @notice Creates a deposit order based on a seller's
|
||||
/// offer of an amount of ERC20 tokens.
|
||||
@@ -60,16 +58,13 @@ contract P2PIX is BaseUtils {
|
||||
uint96 amount,
|
||||
bool valid
|
||||
) public nonReentrant {
|
||||
if (bytes(pixTarget).length == 0)
|
||||
revert EmptyPixTarget();
|
||||
|
||||
if (bytes(pixTarget).length == 0) revert EmptyPixTarget();
|
||||
if (!allowedERC20s(token)) revert TokenDenied();
|
||||
uint256 _sellerBalance = __sellerBalance(
|
||||
msg.sender,
|
||||
token
|
||||
);
|
||||
uint256 _sellerBalance = __sellerBalance(msg.sender, token);
|
||||
|
||||
uint256 currBal = _sellerBalance & BITMASK_SB_ENTRY;
|
||||
uint256 _newBal = uint256(currBal + amount);
|
||||
uint256 _newBal = uint256(currBal + amount);
|
||||
if (_newBal > MAXBALANCE_UPPERBOUND)
|
||||
revert MaxBalExceeded();
|
||||
|
||||
@@ -81,8 +76,8 @@ contract P2PIX is BaseUtils {
|
||||
uint256 validCasted = _castBool(valid);
|
||||
|
||||
_setSellerBalance(
|
||||
msg.sender,
|
||||
token,
|
||||
msg.sender,
|
||||
token,
|
||||
(_newBal | (validCasted << BITPOS_VALID)),
|
||||
pixTargetCasted
|
||||
);
|
||||
@@ -102,10 +97,7 @@ contract P2PIX is BaseUtils {
|
||||
/// @notice This function does not affect any ongoing active locks.
|
||||
/// @dev Function sighash: 0x6d82d9e0
|
||||
function setValidState(ERC20 token, bool state) public {
|
||||
uint256 _sellerBalance = __sellerBalance(
|
||||
msg.sender,
|
||||
token
|
||||
);
|
||||
uint256 _sellerBalance = __sellerBalance(msg.sender, token);
|
||||
|
||||
if (_sellerBalance != 0) {
|
||||
uint256 _valid = _castBool(state);
|
||||
@@ -123,7 +115,7 @@ contract P2PIX is BaseUtils {
|
||||
/// @notice Public method designed to lock an remaining amount of
|
||||
/// the deposit order of a seller.
|
||||
/// @notice Transaction forwarding must leave `merkleProof` empty;
|
||||
/// otherwise, the trustedForwarder must be previously added
|
||||
/// otherwise, the trustedForwarder must be previously added
|
||||
/// to a seller whitelist.
|
||||
/// @notice This method can be performed either by:
|
||||
/// - An user allowed via the seller's allowlist;
|
||||
@@ -152,36 +144,30 @@ contract P2PIX is BaseUtils {
|
||||
uint256 bal = getBalance(seller, token);
|
||||
if (bal < amount) revert NotEnoughTokens();
|
||||
|
||||
unchecked {
|
||||
unchecked {
|
||||
lockID = ++lockCounter;
|
||||
}
|
||||
|
||||
if (mapLocks[lockID].expirationBlock >= block.number)
|
||||
revert NotExpired();
|
||||
if (
|
||||
mapLocks[lockID].expirationBlock >= block.number
|
||||
) revert NotExpired();
|
||||
|
||||
bytes32 _pixTarget = getPixTarget(seller, token);
|
||||
|
||||
// transaction forwarding must leave `merkleProof` empty;
|
||||
// otherwise, the trustedForwarder must be previously added
|
||||
// otherwise, the trustedForwarder must be previously added
|
||||
// to a seller whitelist.
|
||||
if (merkleProof.length != 0) {
|
||||
_merkleVerify(
|
||||
merkleProof,
|
||||
sellerAllowList(seller),
|
||||
_msgSender()
|
||||
);
|
||||
} else if (
|
||||
amount > REPUTATION_LOWERBOUND &&
|
||||
msg.sender == _msgSender()
|
||||
) {
|
||||
uint256 spendLimit;
|
||||
uint256 userCredit = userRecord[
|
||||
_castAddrToKey(_msgSender())
|
||||
];
|
||||
_merkleVerify( merkleProof, sellerAllowList(seller), _msgSender());
|
||||
|
||||
} else if ( amount > REPUTATION_LOWERBOUND && msg.sender == _msgSender() ) {
|
||||
|
||||
uint256 spendLimit; uint256 userCredit =
|
||||
userRecord[_castAddrToKey(_msgSender())];
|
||||
(spendLimit) = _limiter(userCredit / WAD);
|
||||
if (
|
||||
amount > (spendLimit * WAD) ||
|
||||
amount > LOCKAMOUNT_UPPERBOUND
|
||||
if (
|
||||
amount > (spendLimit * WAD) ||
|
||||
amount > LOCKAMOUNT_UPPERBOUND
|
||||
) revert AmountNotAllowed();
|
||||
}
|
||||
|
||||
@@ -239,21 +225,14 @@ contract P2PIX is BaseUtils {
|
||||
l.amount = 0;
|
||||
l.expirationBlock = 0;
|
||||
_setUsedTransactions(message);
|
||||
|
||||
if (_msgSender() == msg.sender) {
|
||||
if (msg.sender != l.buyerAddress) {
|
||||
userRecord[
|
||||
_castAddrToKey(msg.sender)
|
||||
] += (lockAmount >> 1);
|
||||
userRecord[
|
||||
_castAddrToKey(l.buyerAddress)
|
||||
] += (lockAmount >> 1);
|
||||
} else {
|
||||
userRecord[
|
||||
_castAddrToKey(msg.sender)
|
||||
] += lockAmount;
|
||||
}
|
||||
}
|
||||
|
||||
if (_msgSender() == msg.sender) {
|
||||
if (msg.sender != l.buyerAddress) {
|
||||
userRecord[_castAddrToKey(msg.sender)] += (lockAmount >> 1);
|
||||
userRecord[_castAddrToKey(l.buyerAddress)] += (lockAmount >> 1);
|
||||
} else {
|
||||
userRecord[_castAddrToKey(msg.sender)] += lockAmount;
|
||||
}}
|
||||
|
||||
SafeTransferLib.safeTransfer(
|
||||
t,
|
||||
@@ -270,9 +249,9 @@ contract P2PIX is BaseUtils {
|
||||
/// @notice For each successfull unexpired lock recovered,
|
||||
/// `userRecord[_castAddrToKey(l.relayerAddress)]` is decreased by half of its value.
|
||||
/// @dev Function sighash: 0xb0983d39
|
||||
function unlockExpired(
|
||||
uint256[] calldata lockIDs
|
||||
) public {
|
||||
function unlockExpired(uint256[] calldata lockIDs)
|
||||
public
|
||||
{
|
||||
uint256 i;
|
||||
uint256 locksSize = lockIDs.length;
|
||||
|
||||
@@ -281,21 +260,19 @@ contract P2PIX is BaseUtils {
|
||||
|
||||
_notExpired(l);
|
||||
|
||||
uint256 _sellerBalance = __sellerBalance(
|
||||
l.seller,
|
||||
l.token
|
||||
) & BITMASK_SB_ENTRY;
|
||||
uint256 _sellerBalance =
|
||||
__sellerBalance(l.seller, l.token) & BITMASK_SB_ENTRY;
|
||||
|
||||
if (
|
||||
(_sellerBalance + l.amount) >
|
||||
MAXBALANCE_UPPERBOUND
|
||||
) revert MaxBalExceeded();
|
||||
if ((_sellerBalance + l.amount) > MAXBALANCE_UPPERBOUND)
|
||||
revert MaxBalExceeded();
|
||||
|
||||
_addSellerBalance(l.seller, l.token, l.amount);
|
||||
|
||||
l.amount = 0;
|
||||
|
||||
uint256 userKey = _castAddrToKey(l.buyerAddress);
|
||||
uint256 userKey = _castAddrToKey(
|
||||
l.buyerAddress
|
||||
);
|
||||
uint256 _newUserRecord = (userRecord[userKey] >>
|
||||
1);
|
||||
|
||||
@@ -335,8 +312,7 @@ contract P2PIX is BaseUtils {
|
||||
setValidState(token, false);
|
||||
|
||||
_decBal(
|
||||
(__sellerBalance(msg.sender, token) &
|
||||
BITMASK_SB_ENTRY),
|
||||
(__sellerBalance(msg.sender, token) & BITMASK_SB_ENTRY),
|
||||
amount,
|
||||
token,
|
||||
msg.sender
|
||||
@@ -349,15 +325,18 @@ contract P2PIX is BaseUtils {
|
||||
amount
|
||||
);
|
||||
|
||||
emit DepositWithdrawn(msg.sender, token, amount);
|
||||
emit DepositWithdrawn(
|
||||
msg.sender,
|
||||
token,
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
function setRoot(
|
||||
address addr,
|
||||
bytes32 merkleroot
|
||||
) public {
|
||||
function setRoot(address addr, bytes32 merkleroot)
|
||||
public
|
||||
{
|
||||
assembly ("memory-safe") {
|
||||
// if (addr != msg.sender)
|
||||
// if (addr != msg.sender)
|
||||
if iszero(eq(addr, caller())) {
|
||||
// revert OnlySeller()
|
||||
mstore(0x00, 0x85d1f726)
|
||||
@@ -370,8 +349,8 @@ contract P2PIX is BaseUtils {
|
||||
|
||||
// emit RootUpdated(addr, merkleroot);
|
||||
log3(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
_ROOT_UPDATED_EVENT_SIGNATURE,
|
||||
addr,
|
||||
merkleroot
|
||||
@@ -429,13 +408,14 @@ contract P2PIX is BaseUtils {
|
||||
}
|
||||
|
||||
// we can directly dec from packed uint entry value
|
||||
_decSellerBalance(_k, _t, _amount);
|
||||
_decSellerBalance(_k,_t, _amount);
|
||||
}
|
||||
|
||||
function getBalance(
|
||||
address seller,
|
||||
ERC20 token
|
||||
) public view returns (uint256 bal) {
|
||||
function getBalance(address seller, ERC20 token)
|
||||
public
|
||||
view
|
||||
returns (uint256 bal)
|
||||
{
|
||||
assembly ("memory-safe") {
|
||||
for {
|
||||
/* */
|
||||
@@ -454,10 +434,11 @@ contract P2PIX is BaseUtils {
|
||||
}
|
||||
}
|
||||
|
||||
function getValid(
|
||||
address seller,
|
||||
ERC20 token
|
||||
) public view returns (bool valid) {
|
||||
function getValid(address seller, ERC20 token)
|
||||
public
|
||||
view
|
||||
returns (bool valid)
|
||||
{
|
||||
assembly ("memory-safe") {
|
||||
for {
|
||||
/* */
|
||||
@@ -471,9 +452,7 @@ contract P2PIX is BaseUtils {
|
||||
BITMASK_SB_ENTRY,
|
||||
shr(
|
||||
BITPOS_VALID,
|
||||
sload(
|
||||
add(keccak256(0x0c, 0x34), 0x01)
|
||||
)
|
||||
sload(add(keccak256(0x0c, 0x34), 0x01))
|
||||
)
|
||||
)
|
||||
break
|
||||
@@ -481,10 +460,11 @@ contract P2PIX is BaseUtils {
|
||||
}
|
||||
}
|
||||
|
||||
function getPixTarget(
|
||||
address seller,
|
||||
ERC20 token
|
||||
) public view returns (bytes32 pixTarget) {
|
||||
function getPixTarget(address seller, ERC20 token)
|
||||
public
|
||||
view
|
||||
returns (bytes32 pixTarget)
|
||||
{
|
||||
assembly ("memory-safe") {
|
||||
for {
|
||||
/* */
|
||||
@@ -500,12 +480,9 @@ contract P2PIX is BaseUtils {
|
||||
}
|
||||
}
|
||||
|
||||
function getPixTargetString(
|
||||
address seller,
|
||||
ERC20 token
|
||||
) public view returns (string memory pixTarget) {
|
||||
function getPixTargetString(address seller, ERC20 token) public view returns (string memory pixTarget) {
|
||||
bytes32 _pixEnc = getPixTarget(seller, token);
|
||||
pixTarget = string(abi.encodePacked(_pixEnc));
|
||||
pixTarget = string(abi.encodePacked(_pixEnc));
|
||||
}
|
||||
|
||||
function getBalances(
|
||||
@@ -529,9 +506,7 @@ contract P2PIX is BaseUtils {
|
||||
/// @notice External getter that returns the status of a lockIDs array.
|
||||
/// @notice Call will not revert if provided with an empty array as parameter.
|
||||
/// @dev Function sighash: 0x49ef8448
|
||||
function getLocksStatus(
|
||||
uint256[] memory ids
|
||||
)
|
||||
function getLocksStatus(uint256[] memory ids)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory, DT.LockStatus[] memory)
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
|
||||
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
|
||||
],
|
||||
"p2pix": "",
|
||||
"token": ""
|
||||
"p2pix": "0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29",
|
||||
"token": "0xD38D6367f452D097ccBfDe4490b7de570B6A72Db"
|
||||
}
|
||||
|
||||
224
docs/lib/mock/MockToken.md
Normal file
224
docs/lib/mock/MockToken.md
Normal 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 |
|
||||
16
docs/lib/mock/mockToken.md
Normal file
16
docs/lib/mock/mockToken.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Solidity API
|
||||
|
||||
## MockToken
|
||||
|
||||
### constructor
|
||||
|
||||
```solidity
|
||||
constructor(uint256 supply) public
|
||||
```
|
||||
|
||||
### mint
|
||||
|
||||
```solidity
|
||||
function mint(address[] to, uint256 value) public virtual
|
||||
```
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -9,74 +9,54 @@ import "solidity-docgen";
|
||||
|
||||
dotenvConfig({ path: resolve(__dirname, "./.env") });
|
||||
|
||||
// Default mnemonic used by Hardhat / Anvil for deterministic accounts.
|
||||
// Lets `--network localhost` (and the default in-process `hardhat` net)
|
||||
// run without a .env. Public networks still require a real MNEMONIC.
|
||||
const DEFAULT_MNEMONIC =
|
||||
"test test test test test test test test test test test junk";
|
||||
const mnemonic: string | undefined = process.env.MNEMONIC;
|
||||
if (!mnemonic) {
|
||||
throw new Error("Please set your MNEMONIC in a .env file");
|
||||
}
|
||||
|
||||
const mnemonic: string =
|
||||
process.env.MNEMONIC ?? DEFAULT_MNEMONIC;
|
||||
const alchemyApiKey: string | undefined =
|
||||
process.env.ALCHEMY_API_KEY;
|
||||
const privateKey: string | undefined =
|
||||
process.env.PRIVATE_KEY;
|
||||
if (!alchemyApiKey) {
|
||||
throw new Error(
|
||||
"Please set your ALCHEMY_API_KEY in a .env file",
|
||||
);
|
||||
}
|
||||
|
||||
const chainIds = {
|
||||
// "{INSERT_NAME}": {INSERT_ID},
|
||||
hardhat: 31337,
|
||||
localhost: 31337,
|
||||
mainnet: 1,
|
||||
"eth-sepolia": 11155111,
|
||||
"polygon-mainnet": 137,
|
||||
"arb-mainnet": 42161,
|
||||
rootstock: 30,
|
||||
"rootstock-testnet": 31,
|
||||
"polygon-mumbai": 80001,
|
||||
rootstock:30,
|
||||
"rootstock-testnet":31,
|
||||
};
|
||||
|
||||
function getChainConfig(
|
||||
chain: keyof typeof chainIds,
|
||||
): NetworkUserConfig {
|
||||
if (!alchemyApiKey) {
|
||||
throw new Error(
|
||||
`Please set ALCHEMY_API_KEY in a .env file before targeting ${chain}`,
|
||||
);
|
||||
}
|
||||
const jsonRpcUrl =
|
||||
"https://" + chain + ".g.alchemy.com/v2/" + alchemyApiKey;
|
||||
let jsonRpcUrl = "https://" + chain + ".g.alchemy.com/v2/" + alchemyApiKey;
|
||||
return {
|
||||
accounts: privateKey
|
||||
? [privateKey]
|
||||
: {
|
||||
count: 10,
|
||||
mnemonic,
|
||||
path: "m/44'/60'/0'/0",
|
||||
},
|
||||
// Comment out for default hardhat account settings
|
||||
accounts: {
|
||||
count: 10,
|
||||
mnemonic,
|
||||
path: "m/44'/60'/0'/0",
|
||||
},
|
||||
// gasPrice: 8000000000,
|
||||
chainId: chainIds[chain],
|
||||
url: jsonRpcUrl,
|
||||
};
|
||||
}
|
||||
|
||||
const 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 = {
|
||||
defaultNetwork: "hardhat",
|
||||
etherscan: {
|
||||
apiKey: {
|
||||
mainnet: process.env.ETHERSCAN_API_KEY || "",
|
||||
sepolia: process.env.ETHERSCAN_API_KEY || "",
|
||||
polygon: process.env.POLYGONSCAN_API_KEY || "",
|
||||
arbitrumOne: process.env.ARBISCAN_API_KEY || "",
|
||||
rinkeby: process.env.ETHERSCAN_API_KEY || "",
|
||||
goerli: process.env.ETHERSCAN_API_KEY || "",
|
||||
polygonMumbai: process.env.POLYGONSCAN_API_KEY || "",
|
||||
},
|
||||
},
|
||||
gasReporter: {
|
||||
@@ -102,16 +82,12 @@ const config: HardhatUserConfig = {
|
||||
},
|
||||
chainId: chainIds.hardhat,
|
||||
},
|
||||
// External Anvil / Hardhat node (e.g. `anvil --port 8545`). Used by
|
||||
// the frontend e2e suite to deploy a fresh chain per CI run.
|
||||
localhost: {
|
||||
url: "http://127.0.0.1:8545",
|
||||
chainId: chainIds.localhost,
|
||||
accounts: {
|
||||
mnemonic,
|
||||
},
|
||||
},
|
||||
...liveNetworks,
|
||||
// network: getChainConfig("{INSERT_NAME}"),
|
||||
mainnet: getChainConfig("mainnet"),
|
||||
sepolia: getChainConfig("eth-sepolia"),
|
||||
mumbai: getChainConfig("polygon-mumbai"),
|
||||
rootstock: getChainConfig("rootstock"),
|
||||
rsktestnet: getChainConfig("rootstock-testnet"),
|
||||
},
|
||||
paths: {
|
||||
artifacts: "./artifacts",
|
||||
@@ -147,7 +123,7 @@ const config: HardhatUserConfig = {
|
||||
},
|
||||
docgen: {
|
||||
pages: "files",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -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 };
|
||||
});
|
||||
@@ -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,
|
||||
};
|
||||
});
|
||||
@@ -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,
|
||||
};
|
||||
});
|
||||
@@ -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 };
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"P2PIXWithMock": {
|
||||
"defaultBlocks": 10,
|
||||
"validSigners": []
|
||||
},
|
||||
"MockToken": {
|
||||
"supply": "20000000000000000000000000"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"P2PIX": {
|
||||
"defaultBlocks": 10,
|
||||
"validSigners": [],
|
||||
"tokens": ["0x0000000000000000000000000000000000000000"],
|
||||
"allowed": [true]
|
||||
}
|
||||
}
|
||||
15
package.json
15
package.json
@@ -13,12 +13,16 @@
|
||||
"compile": "hardhat compile",
|
||||
"typechain": "hardhat typechain",
|
||||
"test": "hardhat test",
|
||||
"deploy": "hardhat ignition deploy ignition/modules/P2PIX.ts",
|
||||
"deploy:mock": "hardhat ignition deploy ignition/modules/P2PIXWithMock.ts",
|
||||
"deploy1:localhost": "hardhat run scripts/1-deploy-mockToken.ts --network localhost",
|
||||
"deploy2:localhost": "hardhat run scripts/2-deploy-p2pix.ts --network localhost",
|
||||
"deploy1:goerli": "hardhat run scripts/1-deploy-mockToken.ts --network goerli",
|
||||
"deploy2:goerli": "hardhat run scripts/2-deploy-p2pix.ts --network goerli",
|
||||
"deploy1:mumbai": "hardhat run scripts/1-deploy-mockToken.ts --network polygon-mumbai",
|
||||
"deploy2:mumbai": "hardhat run scripts/2-deploy-p2pix.ts --network polygon-mumbai",
|
||||
"coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"test/**/*.ts\" && yarn typechain",
|
||||
"lint": "yarn lint:sol && yarn lint:ts && yarn prettier:check",
|
||||
"lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"",
|
||||
"lint:ts": "eslint .",
|
||||
"lint:ts": "eslint --config ./.eslintrc.yaml --ignore-path ./.eslintignore --ext .js,.ts .",
|
||||
"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}\""
|
||||
},
|
||||
@@ -52,16 +56,11 @@
|
||||
"lodash": "^4.17.21",
|
||||
"merkletreejs": "^0.5.2",
|
||||
"mocha": "^10.8.2",
|
||||
"prettier": "^3.0.0",
|
||||
"prettier-plugin-solidity": "^1.0.0",
|
||||
"solhint": "^6.2.1",
|
||||
"solhint-plugin-prettier": "^0.1.0",
|
||||
"solidity-coverage": "^0.8.16",
|
||||
"solidity-docgen": "^0.6.0-beta.36",
|
||||
"ts-node": "^10.9.2",
|
||||
"typechain": "^8.3.2",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.60.0",
|
||||
"viem": "^2.33.1"
|
||||
},
|
||||
"files": [
|
||||
|
||||
49
scripts/1-deploy-mockToken.ts
Normal file
49
scripts/1-deploy-mockToken.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import "@nomicfoundation/hardhat-ethers";
|
||||
import * as fs from "fs";
|
||||
import { ethers, network } from "hardhat";
|
||||
|
||||
import { Deploys } from "../test/utils/interfaces";
|
||||
|
||||
let deploysJson: Deploys;
|
||||
const supply: BigInt = ethers.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}`);
|
||||
|
||||
let erc20 = await ethers.deployContract("MockToken", [supply]);
|
||||
erc20 = await erc20.waitForDeployment();
|
||||
|
||||
deploysJson.token = await erc20.getAddress();
|
||||
console.log("🚀 Mock Token Deployed:", await erc20.getAddress());
|
||||
|
||||
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);
|
||||
});
|
||||
75
scripts/2-deploy-p2pix.ts
Normal file
75
scripts/2-deploy-p2pix.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import "@nomicfoundation/hardhat-ethers";
|
||||
import * as fs from "fs";
|
||||
import { ethers, network } 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}`);
|
||||
|
||||
let reputation = await ethers.deployContract("Reputation");
|
||||
let multicall = await ethers.deployContract("Multicall");
|
||||
let p2pix = await ethers.deployContract("P2PIX", [
|
||||
10,
|
||||
deploysJson.signers,
|
||||
reputation.target,
|
||||
[deploysJson.token],
|
||||
[true],
|
||||
]);
|
||||
|
||||
reputation = await reputation.waitForDeployment();
|
||||
multicall = await multicall.waitForDeployment();
|
||||
p2pix = await p2pix.waitForDeployment();
|
||||
|
||||
deploysJson.p2pix = await p2pix.getAddress();
|
||||
console.log("🚀 P2PIX Deployed:", await p2pix.getAddress());
|
||||
console.log("🌠 Reputation Deployed:", await reputation.getAddress());
|
||||
console.log("🛰 Multicall Deployed:", await multicall.getAddress());
|
||||
|
||||
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);
|
||||
});
|
||||
@@ -20,22 +20,12 @@ const main = async () => {
|
||||
}
|
||||
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log(
|
||||
`Signing transactions with ${deployer.address}`,
|
||||
);
|
||||
console.log(`Signing transactions with ${deployer.address}`);
|
||||
|
||||
const iface = new ethers.utils.Interface(
|
||||
P2PIX__factory.abi,
|
||||
);
|
||||
const calldata = iface.encodeFunctionData(
|
||||
"setDefaultLockBlocks",
|
||||
["10000"],
|
||||
);
|
||||
|
||||
const tx = await deployer.sendTransaction({
|
||||
to: deploysJson.p2pix,
|
||||
data: calldata,
|
||||
});
|
||||
const iface = new ethers.utils.Interface(P2PIX__factory.abi);
|
||||
const calldata = iface.encodeFunctionData("setDefaultLockBlocks", ["10000"]);
|
||||
|
||||
const tx = await deployer.sendTransaction({to:deploysJson.p2pix, data: calldata});
|
||||
const done = await tx.wait();
|
||||
console.log(done.transactionHash);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "@nomicfoundation/hardhat-chai-matchers";
|
||||
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
|
||||
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
|
||||
import { expect } from "chai";
|
||||
import { ethers, network } from "hardhat";
|
||||
|
||||
@@ -7,11 +8,14 @@ import { Reputation } from "../src/types";
|
||||
import { curve, repFixture } from "./utils/fixtures";
|
||||
|
||||
describe("Reputation", () => {
|
||||
// contract deployer/admin
|
||||
let owner: SignerWithAddress;
|
||||
// Reputation Interface instance;
|
||||
let reputation: Reputation;
|
||||
|
||||
before("Set signers and reset network", async () => {
|
||||
await ethers.getSigners();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[owner] = await ethers.getSigners();
|
||||
|
||||
await network.provider.send("hardhat_reset");
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-useless-escape */
|
||||
|
||||
describe("_", () => {
|
||||
console.log(
|
||||
"/// ______ __\r\n/// .-----.|__ |.-----.|__|.--.--.\r\n/// | _ || __|| _ || ||_ _|\r\n/// | __||______|| __||__||__.__|\r\n/// |__| |__|\r\n///",
|
||||
|
||||
1085
test/p2pix.test.ts
1085
test/p2pix.test.ts
File diff suppressed because it is too large
Load Diff
@@ -4,13 +4,15 @@ import { ethers } from "hardhat";
|
||||
import keccak256 from "keccak256";
|
||||
import { MerkleTree } from "merkletreejs";
|
||||
|
||||
import { MockToken, P2PIX__factory } from "../../src/types";
|
||||
|
||||
import {
|
||||
Call,
|
||||
RepFixture,
|
||||
P2PixAndReputation,
|
||||
} from "./interfaces";
|
||||
MockToken,
|
||||
Multicall,
|
||||
P2PIX,
|
||||
P2PIX__factory,
|
||||
Reputation,
|
||||
} from "../../src/types";
|
||||
|
||||
import { Call, RepFixture, P2PixAndReputation, DepositArgs, LockArgs, ReleaseArgs } from "./interfaces";
|
||||
|
||||
// exported constants
|
||||
export const getSignerAddrs = (
|
||||
@@ -20,16 +22,18 @@ export const getSignerAddrs = (
|
||||
return addrs.slice(0, amount).map(({ address }) => address);
|
||||
};
|
||||
|
||||
export const getBnFrom = (nums: number[]): bigint[] => {
|
||||
export const getBnFrom = (nums: number[]): BigInt[] => {
|
||||
const bns = nums.map(num => BigInt(num));
|
||||
return bns;
|
||||
};
|
||||
|
||||
export const getLockData = (
|
||||
addr: string,
|
||||
locks: bigint[][],
|
||||
locks: BigInt[][],
|
||||
): Call[] => {
|
||||
const iface = new ethers.Interface(P2PIX__factory.abi);
|
||||
const iface = new ethers.Interface(
|
||||
P2PIX__factory.abi,
|
||||
);
|
||||
return locks.map(lock => ({
|
||||
target: addr,
|
||||
callData: iface.encodeFunctionData("getLocksStatus", [
|
||||
@@ -67,9 +71,8 @@ export const curve = (x: number): number => {
|
||||
|
||||
// exported async functions
|
||||
export async function repFixture(): Promise<RepFixture> {
|
||||
const reputation =
|
||||
await ethers.deployContract("Reputation");
|
||||
return { reputation: await reputation.waitForDeployment() };
|
||||
const reputation = await ethers.deployContract("Reputation");
|
||||
return { reputation: await reputation.waitForDeployment() };
|
||||
}
|
||||
|
||||
export async function p2pixFixture(): Promise<P2PixAndReputation> {
|
||||
@@ -78,12 +81,11 @@ export async function p2pixFixture(): Promise<P2PixAndReputation> {
|
||||
await ethers.getSigners(),
|
||||
);
|
||||
|
||||
const reputation =
|
||||
await ethers.deployContract("Reputation");
|
||||
const reputation = await ethers.deployContract("Reputation");
|
||||
|
||||
const erc20 = (await ethers.deployContract("MockToken", [
|
||||
ethers.parseEther("20000000"), // 20M
|
||||
])) as MockToken;
|
||||
const erc20 = await ethers.deployContract("MockToken", [
|
||||
ethers.parseEther("20000000") // 20M
|
||||
]) as MockToken;
|
||||
|
||||
const p2pix = await ethers.deployContract("P2PIX", [
|
||||
10,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Reputation,
|
||||
} from "../../src/types";
|
||||
|
||||
|
||||
// exported interfaces
|
||||
export interface Deploys {
|
||||
signers: string[];
|
||||
@@ -16,29 +17,29 @@ export interface DepositArgs {
|
||||
pixTarget: string;
|
||||
allowlistRoot: string;
|
||||
token: string;
|
||||
amount: bigint;
|
||||
amount: BigInt;
|
||||
valid: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export interface LockArgs {
|
||||
seller: string;
|
||||
token: string;
|
||||
amount: bigint;
|
||||
amount: BigInt;
|
||||
merkleProof: string[];
|
||||
expiredLocks: bigint[];
|
||||
expiredLocks: BigInt[];
|
||||
}
|
||||
|
||||
export interface ReleaseArgs {
|
||||
lockID: bigint;
|
||||
lockID: BigInt;
|
||||
pixTimestamp: string;
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface Lock {
|
||||
counter: bigint;
|
||||
expirationBlock: bigint;
|
||||
counter: BigInt;
|
||||
expirationBlock: BigInt;
|
||||
pixTarget: string;
|
||||
amount: bigint;
|
||||
amount: BigInt;
|
||||
token: string;
|
||||
buyerAddress: string;
|
||||
seller: string;
|
||||
@@ -71,4 +72,4 @@ export interface MtcFixture {
|
||||
|
||||
export type P2PixAndReputation = P2pixFixture &
|
||||
RepFixture &
|
||||
MtcFixture;
|
||||
MtcFixture;
|
||||
@@ -18,10 +18,5 @@
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"files": ["./hardhat.config.ts"],
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"test/**/*",
|
||||
"scripts/**/*",
|
||||
"ignition/**/*"
|
||||
]
|
||||
"include": ["src/**/*", "test/**/*", "scripts/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user