Compare commits

11 Commits

Author SHA1 Message Date
hueso
98dda3c61f OZ v5.5.0 2025-11-03 13:59:01 -03:00
hueso
4d1a24ecdf bump OZ version to 5.5.0-rc.1 2025-10-24 21:52:45 -03:00
hueso
d9b93fe7d5 restore trustedForwarders as an OZ override 2025-10-24 21:50:21 -03:00
hueso
f56335d9b5 import ECDSA, ERC2771, ERC20, MerkleProofLib from @openzeppelin 2025-10-16 14:38:49 -03:00
hueso
407ea51d6a WIP: use openzeppelin contracts 2025-08-02 21:12:00 -03:00
hueso
e57428525b updated RPC urls 2025-08-02 21:10:31 -03:00
hueso
538258a709 rootstock testnet re-deployment 2025-08-02 21:10:31 -03:00
hueso
30335bbe5b bump solidity verison 2025-08-02 21:10:31 -03:00
hueso
f924593ee2 Ethers v5 -> v6 migration
Updated dependencies and removed unused ones.
2025-08-02 21:09:40 -03:00
hueso
811d5344a3 Partial hardhat -> viem migration for tests.
Typescript updated to v5.
Notably all calls to p2pix.callStatic.getStr() were replaced by viem.stringToHex() due to bug in ethers (https://github.com/ethers-io/ethers.js/issues/4965)
Full migration to viem might not be possible currently due to hard-chai-matchers incompatibility:
> The hardhat-chai-matchers plugin is designed to work with hardhat-ethers. Attempting to use it in conjunction with hardhat-viem results in compatibility issues.
https://hardhat.org/hardhat-chai-matchers/docs/overview
2025-06-11 11:55:24 -03:00
hueso
4f1f8d6025 sepolia deployment 2024-07-18 17:59:44 -03:00
34 changed files with 4717 additions and 10856 deletions

View File

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

3
.czrc
View File

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

1
.husky/.gitignore vendored
View File

@@ -1 +0,0 @@
_

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

942
.yarn/releases/yarn-4.9.2.cjs vendored Executable file

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
plugins: yarnPath: .yarn/releases/yarn-4.9.2.cjs
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.2.1.cjs

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,11 +1,12 @@
// 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 "contracts/lib/utils/ECDSA.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { MerkleProofLib as Merkle } from "contracts/lib/utils/MerkleProofLib.sol"; import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import { ReentrancyGuard } from "contracts/lib/utils/ReentrancyGuard.sol"; import { MerkleProof as Merkle } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
abstract contract BaseUtils is abstract contract BaseUtils is
OwnerSettings, OwnerSettings,
@@ -43,8 +44,8 @@ abstract contract BaseUtils is
if ( if (
!validBacenSigners( !validBacenSigners(
_castAddrToKey( _castAddrToKey(
ECDSA.recoverCalldata( ECDSA.recover(
ECDSA.toEthSignedMessageHash( MessageHashUtils.toEthSignedMessageHash(
_message _message
), ),
_signature _signature

View File

@@ -1,5 +1,5 @@
// 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 ██████████████████████████████████████████████████████████

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 "contracts/lib/tokens/ERC20.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
library DataTypes { library DataTypes {

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 "contracts/lib/tokens/ERC20.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/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 { ERC2771Context as ERC2771 } from "contracts/lib/metatx/ERC2771Context.sol"; import { 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,87 +1,14 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity >=0.8.4; pragma solidity ^0.8.20;
/// @author OpenZeppelin Contracts v4.4.1 (utils/Context.sol) import { ERC2771Context } from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Context.sol)
/// @dev Provides information about the current execution context, including the abstract contract ERC2771 is ERC2771Context(address(0)) {
/// sender of the transaction and its data. While these are generally available
/// via msg.sender and msg.data, they should not be accessed in such a direct
/// manner, since when dealing with meta-transactions the account sending and
/// paying for execution may not be the actual sender (as far as an application
/// is concerned).
///
/// This contract is only required for intermediate, library-like contracts.
abstract contract Context {
function _msgSender()
internal
view
virtual
returns (address)
{
return msg.sender;
}
function _msgData()
internal
view
virtual
returns (bytes calldata)
{
return msg.data;
}
}
/// @author Modified from OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
/// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/metatx/ERC2771Context.sol
/// @dev Context variant with ERC2771 support.
abstract contract ERC2771Context is Context {
// address private immutable _trustedForwarder;
mapping(address => bool) public trustedForwarders; mapping(address => bool) public trustedForwarders;
/// @custom:oz-upgrades-unsafe-allow constructor function isTrustedForwarder(address forwarder) public view override returns (bool) {
// 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]; 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 "../tokens/ERC20.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockToken is ERC20 { contract MockToken is ERC20 {
constructor(uint256 supply) ERC20("MockBRL", "MBRL", 18) { constructor(uint256 supply) ERC20("MockBRL", "MBRL") {
_mint(msg.sender, supply); _mint(msg.sender, supply);
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity >=0.8.4; pragma solidity >=0.8.4;
import { ERC20 } from "../tokens/ERC20.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/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;
/// ______ __ /// ______ __
/// .-----.|__ |.-----.|__|.--.--. /// .-----.|__ |.-----.|__|.--.--.

View File

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

8
deploys/sepolia.json Normal file
View File

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

View File

@@ -1,7 +1,6 @@
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";
@@ -15,14 +14,6 @@ if (!mnemonic) {
throw new Error("Please set your MNEMONIC in a .env file"); throw new Error("Please set your MNEMONIC in a .env file");
} }
const infuraApiKey: string | undefined =
process.env.INFURA_API_KEY;
if (!infuraApiKey) {
throw new Error(
"Please set your INFURA_API_KEY in a .env file",
);
}
const alchemyApiKey: string | undefined = const alchemyApiKey: string | undefined =
process.env.ALCHEMY_API_KEY; process.env.ALCHEMY_API_KEY;
if (!alchemyApiKey) { if (!alchemyApiKey) {
@@ -35,33 +26,16 @@ const chainIds = {
// "{INSERT_NAME}": {INSERT_ID}, // "{INSERT_NAME}": {INSERT_ID},
hardhat: 31337, hardhat: 31337,
mainnet: 1, mainnet: 1,
sepolia: 11155111, "eth-sepolia": 11155111,
goerli: 5,
"polygon-mumbai": 80001, "polygon-mumbai": 80001,
rootstock:30, rootstock:30,
rsktestnet:31, "rootstock-testnet":31,
}; };
function getChainConfig( function getChainConfig(
chain: keyof typeof chainIds, chain: keyof typeof chainIds,
): NetworkUserConfig { ): NetworkUserConfig {
let jsonRpcUrl: string; let jsonRpcUrl = "https://" + chain + ".g.alchemy.com/v2/" + alchemyApiKey;
switch (chain) {
case "polygon-mumbai":
jsonRpcUrl =
"https://polygon-mumbai.g.alchemy.com/v2/" +
alchemyApiKey;
break;
case "rsktestnet":
jsonRpcUrl = "https://public-node.testnet.rsk.co/";
break;
case "rootstock":
jsonRpcUrl = "https://public-node.rsk.co/"
break;
default:
jsonRpcUrl =
"https://" + chain + ".infura.io/v3/" + infuraApiKey;
}
return { return {
// Comment out for default hardhat account settings // Comment out for default hardhat account settings
accounts: { accounts: {
@@ -90,6 +64,7 @@ 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",
@@ -109,11 +84,10 @@ const config: HardhatUserConfig = {
}, },
// network: getChainConfig("{INSERT_NAME}"), // network: getChainConfig("{INSERT_NAME}"),
mainnet: getChainConfig("mainnet"), mainnet: getChainConfig("mainnet"),
goerli: getChainConfig("goerli"), sepolia: getChainConfig("eth-sepolia"),
sepolia: getChainConfig("sepolia"), mumbai: getChainConfig("polygon-mumbai"),
"polygon-mumbai": getChainConfig("polygon-mumbai"),
rootstock: getChainConfig("rootstock"), rootstock: getChainConfig("rootstock"),
rsktestnet: getChainConfig("rsktestnet"), rsktestnet: getChainConfig("rootstock-testnet"),
}, },
paths: { paths: {
artifacts: "./artifacts", artifacts: "./artifacts",
@@ -122,10 +96,10 @@ const config: HardhatUserConfig = {
tests: "./test", tests: "./test",
}, },
solidity: { solidity: {
version: "0.8.19", version: "0.8.28",
settings: { settings: {
viaIR: true, viaIR: true,
evmVersion: "paris", evmVersion: "cancun",
optimizer: { optimizer: {
enabled: true, enabled: true,
runs: 20_000, runs: 20_000,
@@ -145,7 +119,7 @@ const config: HardhatUserConfig = {
}, },
typechain: { typechain: {
outDir: "src/types", outDir: "src/types",
target: "ethers-v5", target: "ethers-v6",
}, },
docgen: { docgen: {
pages: "files", pages: "files",

View File

@@ -9,10 +9,9 @@
"url": "https://github.com/doiim/p2pix-smart-contracts/issues" "url": "https://github.com/doiim/p2pix-smart-contracts/issues"
}, },
"scripts": { "scripts": {
"clean": "shx rm -rf ./artifacts ./cache ./coverage ./src/types ./coverage.json && yarn typechain", "clean": "hardhat clean",
"commit": "git-cz", "compile": "hardhat compile",
"compile": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat compile", "typechain": "hardhat typechain",
"typechain": "cross-env TS_NODE_TRANSPILE_ONLY=true hardhat typechain",
"test": "hardhat test", "test": "hardhat test",
"deploy1:localhost": "hardhat run scripts/1-deploy-mockToken.ts --network localhost", "deploy1:localhost": "hardhat run scripts/1-deploy-mockToken.ts --network localhost",
"deploy2:localhost": "hardhat run scripts/2-deploy-p2pix.ts --network localhost", "deploy2:localhost": "hardhat run scripts/2-deploy-p2pix.ts --network localhost",
@@ -24,71 +23,50 @@
"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 --config ./.eslintrc.yaml --ignore-path ./.eslintignore --ext .js,.ts .", "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": {
"@commitlint/cli": "^17.2.0", "@ethersproject/abi": "^5.8.0",
"@commitlint/config-conventional": "^17.2.0", "@ethersproject/providers": "^5.8.0",
"@ethersproject/abi": "^5.7.0", "@nomicfoundation/hardhat-chai-matchers": "^2.1.0",
"@ethersproject/abstract-signer": "^5.7.0", "@nomicfoundation/hardhat-ethers": "^3.1.0",
"@ethersproject/bignumber": "^5.7.0", "@nomicfoundation/hardhat-ignition": "^0.15.13",
"@ethersproject/bytes": "^5.7.0", "@nomicfoundation/hardhat-ignition-ethers": "^0.15.14",
"@ethersproject/providers": "^5.7.2", "@nomicfoundation/hardhat-network-helpers": "^1.1.0",
"@nomicfoundation/hardhat-chai-matchers": "^1.0.4", "@nomicfoundation/hardhat-toolbox": "^6.1.0",
"@nomicfoundation/hardhat-network-helpers": "1.0.6", "@nomicfoundation/hardhat-verify": "^2.1.0",
"@nomicfoundation/hardhat-toolbox": "^2.0.0", "@nomicfoundation/hardhat-viem": "^2.1.0",
"@nomiclabs/hardhat-ethers": "^2.2.1", "@nomicfoundation/ignition-core": "^0.15.13",
"@nomiclabs/hardhat-etherscan": "^3.1.2", "@openzeppelin/contracts": "5.5.0",
"@trivago/prettier-plugin-sort-imports": "^3.4.0", "@typechain/ethers-v6": "^0.5.1",
"@typechain/ethers-v5": "^10.1.1", "@typechain/hardhat": "^9.1.0",
"@typechain/hardhat": "^6.1.4", "@types/chai": "^4.3.20",
"@types/chai": "^4.3.3", "@types/mocha": "^10.0.10",
"@types/fs-extra": "^9.0.13", "@types/node": "^24.1.0",
"@types/mocha": "^9.1.1", "chai": "4.5.0",
"@types/node": "^18.11.9", "dotenv": "^16.6.1",
"@typescript-eslint/eslint-plugin": "^5.42.0", "eslint": "^9.32.0",
"@typescript-eslint/parser": "^5.42.0", "eslint-config-prettier": "^10.1.8",
"chai": "^4.3.6", "ethers": "^6.15.0",
"chalk": "4.x", "hardhat": "^2.26.1",
"commitizen": "^4.2.5", "hardhat-contract-sizer": "^2.10.0",
"cross-env": "^7.0.3", "hardhat-gas-reporter": "^2.3.0",
"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.2.32", "merkletreejs": "^0.5.2",
"mocha": "^10.1.0", "mocha": "^10.8.2",
"pinst": "^3.0.0", "solidity-coverage": "^0.8.16",
"prettier": "^2.7.1",
"prettier-plugin-solidity": "^1.0.0-rc.1",
"shx": "^0.3.4",
"solhint": "^3.3.7",
"solhint-plugin-prettier": "^0.0.5",
"solidity-coverage": "^0.8.2",
"solidity-docgen": "^0.6.0-beta.36", "solidity-docgen": "^0.6.0-beta.36",
"ts-generator": "^0.1.1", "ts-node": "^10.9.2",
"ts-node": "^10.9.1", "typechain": "^8.3.2",
"typechain": "^8.1.1", "typescript": "^5.8.3",
"typescript": "^4.8.4" "viem": "^2.33.1"
}, },
"files": [ "files": [
"/contracts" "/contracts"
], ],
"packageManager": "yarn@3.2.1", "packageManager": "yarn@4.9.2",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
} }

View File

@@ -1,14 +1,11 @@
import "@nomiclabs/hardhat-ethers"; import "@nomicfoundation/hardhat-ethers";
import "@nomiclabs/hardhat-etherscan";
import { BigNumber } from "ethers";
import * as fs from "fs"; import * as fs from "fs";
import { ethers, network } from "hardhat"; import { ethers, network } from "hardhat";
import hre from "hardhat";
import { Deploys } from "../test/utils/interfaces"; import { Deploys } from "../test/utils/interfaces";
let deploysJson: Deploys; let deploysJson: Deploys;
const supply: BigNumber = ethers.utils.parseEther("20000000"); const supply: BigInt = ethers.parseEther("20000000");
const main = async () => { const main = async () => {
try { try {
@@ -25,15 +22,11 @@ const main = async () => {
const [deployer] = await ethers.getSigners(); const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with ${deployer.address}`); console.log(`Deploying contracts with ${deployer.address}`);
const ERC20Factory = await ethers.getContractFactory( let erc20 = await ethers.deployContract("MockToken", [supply]);
"MockToken", erc20 = await erc20.waitForDeployment();
);
const erc20 = await ERC20Factory.deploy(supply);
await erc20.deployed();
deploysJson.token = erc20.address; deploysJson.token = await erc20.getAddress();
console.log("🚀 Mock Token Deployed:", erc20.address); console.log("🚀 Mock Token Deployed:", await erc20.getAddress());
await erc20.deployTransaction.wait(6);
fs.writeFileSync( fs.writeFileSync(
`./deploys/${network.name}.json`, `./deploys/${network.name}.json`,

View File

@@ -1,8 +1,6 @@
import "@nomiclabs/hardhat-ethers"; import "@nomicfoundation/hardhat-ethers";
import "@nomiclabs/hardhat-etherscan";
import * as fs from "fs"; import * as fs from "fs";
import { ethers, network } from "hardhat"; import { ethers, network } from "hardhat";
import hre from "hardhat";
import { Deploys } from "../test/utils/interfaces"; import { Deploys } from "../test/utils/interfaces";
@@ -23,32 +21,24 @@ const main = async () => {
const [deployer] = await ethers.getSigners(); const [deployer] = await ethers.getSigners();
console.log(`Deploying contracts with ${deployer.address}`); console.log(`Deploying contracts with ${deployer.address}`);
const Reputation = await ethers.getContractFactory( let reputation = await ethers.deployContract("Reputation");
"Reputation", let multicall = await ethers.deployContract("Multicall");
); let p2pix = await ethers.deployContract("P2PIX", [
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, 10,
deploysJson.signers, deploysJson.signers,
reputation.address, reputation.target,
[deploysJson.token], [deploysJson.token],
[true], [true],
); ]);
await p2pix.deployed();
deploysJson.p2pix = p2pix.address; reputation = await reputation.waitForDeployment();
console.log("🚀 P2PIX Deployed:", p2pix.address); multicall = await multicall.waitForDeployment();
console.log("🌠 Reputation Deployed:", reputation.address); p2pix = await p2pix.waitForDeployment();
console.log("🛰 Multicall Deployed:", mutlicall.address);
await p2pix.deployTransaction.wait(6); 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( fs.writeFileSync(
`./deploys/${network.name}.json`, `./deploys/${network.name}.json`,

View File

@@ -1,6 +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 { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai"; import { expect } from "chai";
import { ethers, network } from "hardhat"; import { ethers, network } from "hardhat";
@@ -15,7 +15,7 @@ describe("Reputation", () => {
before("Set signers and reset network", async () => { before("Set signers and reset network", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
[owner] = await (ethers as any).getSigners(); [owner] = await ethers.getSigners();
await network.provider.send("hardhat_reset"); await network.provider.send("hardhat_reset");
}); });
@@ -60,7 +60,7 @@ describe("Reputation", () => {
}, },
{ {
x: Number.MAX_SAFE_INTEGER, x: Number.MAX_SAFE_INTEGER,
shouldRevert: "overflow", expected: curve(Number.MAX_SAFE_INTEGER),
}, },
{ {
x: Number.POSITIVE_INFINITY, x: Number.POSITIVE_INFINITY,

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import { BigNumber, Signer } from "ethers"; import { 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";
@@ -14,7 +14,6 @@ import {
import { Call, RepFixture, P2PixAndReputation, DepositArgs, LockArgs, ReleaseArgs } from "./interfaces"; import { Call, RepFixture, P2PixAndReputation, DepositArgs, LockArgs, ReleaseArgs } from "./interfaces";
// exported constants // exported constants
export const getSignerAddrs = ( export const getSignerAddrs = (
amount: number, amount: number,
@@ -23,16 +22,16 @@ export const getSignerAddrs = (
return addrs.slice(0, amount).map(({ address }) => address); return addrs.slice(0, amount).map(({ address }) => address);
}; };
export const getBnFrom = (nums: number[]): BigNumber[] => { export const getBnFrom = (nums: number[]): BigInt[] => {
const bns = nums.map(num => ethers.BigNumber.from(num)); const bns = nums.map(num => BigInt(num));
return bns; return bns;
}; };
export const getLockData = ( export const getLockData = (
addr: string, addr: string,
locks: BigNumber[][], locks: BigInt[][],
): Call[] => { ): Call[] => {
const iface = new ethers.utils.Interface( const iface = new ethers.Interface(
P2PIX__factory.abi, P2PIX__factory.abi,
); );
return locks.map(lock => ({ return locks.map(lock => ({
@@ -72,13 +71,8 @@ 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( const reputation = await ethers.deployContract("Reputation");
"Reputation", return { reputation: await reputation.waitForDeployment() };
);
const reputation =
(await Reputation.deploy()) as Reputation;
return { reputation };
} }
export async function p2pixFixture(): Promise<P2PixAndReputation> { export async function p2pixFixture(): Promise<P2PixAndReputation> {
@@ -87,30 +81,21 @@ export async function p2pixFixture(): Promise<P2PixAndReputation> {
await ethers.getSigners(), await ethers.getSigners(),
); );
const Reputation = await ethers.getContractFactory( const reputation = await ethers.deployContract("Reputation");
"Reputation",
);
const reputation =
(await Reputation.deploy()) as Reputation;
const ERC20 = await ethers.getContractFactory("MockToken"); const erc20 = await ethers.deployContract("MockToken", [
const erc20 = (await ERC20.deploy( ethers.parseEther("20000000") // 20M
ethers.utils.parseEther("20000000"), // 20M ]) as MockToken;
)) as MockToken;
const P2PIX = await ethers.getContractFactory("P2PIX"); const p2pix = await ethers.deployContract("P2PIX", [
const p2pix = (await P2PIX.deploy(
10, 10,
validSigners, validSigners,
reputation.address, reputation.target,
[erc20.address], [erc20.target],
[true], [true],
)) as P2PIX; ]);
const Multicall = await ethers.getContractFactory( const multicall = await ethers.deployContract("Multicall");
"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);
@@ -126,10 +111,10 @@ export async function p2pixFixture(): Promise<P2PixAndReputation> {
); );
return { return {
multicall, multicall: await multicall.waitForDeployment(),
reputation, reputation: await reputation.waitForDeployment(),
erc20, erc20: await erc20.waitForDeployment(),
p2pix, p2pix: await p2pix.waitForDeployment(),
merkleRoot, merkleRoot,
proof, proof,
}; };

View File

@@ -1,5 +1,3 @@
import { BigNumber } from "ethers";
import { import {
MockToken, MockToken,
Multicall, Multicall,
@@ -19,29 +17,29 @@ export interface DepositArgs {
pixTarget: string; pixTarget: string;
allowlistRoot: string; allowlistRoot: string;
token: string; token: string;
amount: BigNumber; amount: BigInt;
valid: boolean; valid: boolean;
} }
export interface LockArgs { export interface LockArgs {
seller: string; seller: string;
token: string; token: string;
amount: BigNumber; amount: BigInt;
merkleProof: string[]; merkleProof: string[];
expiredLocks: BigNumber[]; expiredLocks: BigInt[];
} }
export interface ReleaseArgs { export interface ReleaseArgs {
lockID: BigNumber; lockID: BigInt;
pixTimestamp: string; pixTimestamp: string;
signature: string; signature: string;
} }
export interface Lock { export interface Lock {
counter: BigNumber; counter: BigInt;
expirationBlock: BigNumber; expirationBlock: BigInt;
pixTarget: string; pixTarget: string;
amount: BigNumber; amount: BigInt;
token: string; token: string;
buyerAddress: string; buyerAddress: string;
seller: string; seller: string;

11481
yarn.lock

File diff suppressed because it is too large Load Diff