test: property and invariant tests (#61)

- define properties and invariants
- implement a Medusa-based fuzzing campaign

---------

Co-authored-by: drgorillamd <83670532+drgorillamd@users.noreply.github.com>
Co-authored-by: moebius <0xmoebius@protonmail.com>
This commit is contained in:
Simon Something /DrGoNoGo
2025-02-14 13:12:37 +01:00
committed by GitHub
parent c0d80bcf6f
commit 646e5508ce
27 changed files with 2066 additions and 57 deletions

View File

@@ -99,37 +99,6 @@ jobs:
- name: Run tests
run: yarn test:integration
halmos-tests:
name: Symbolic execution tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: "yarn"
- name: Setup .npmrc file
run: |
echo "@defi-wonderland:registry=https://npm.pkg.github.com" > .npmrc
echo "//npm.pkg.github.com/:_authToken=${{ secrets.PKG_REGISTRY_TOKEN }}" >> .npmrc
- name: Install dependencies
run: yarn --frozen-lockfile --network-concurrency 1
- name: Precompile
run: yarn build
- name: Run tests
run: yarn test:symbolic
lint:
name: Lint Commit Messages
runs-on: ubuntu-latest
@@ -162,6 +131,29 @@ jobs:
- run: yarn lint:check
medusa-tests:
name: Medusa Test
runs-on: ubuntu-latest
container: ghcr.io/trailofbits/eth-security-toolbox/ci:nightly-20241223
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .npmrc file
run: |
echo "@defi-wonderland:registry=https://npm.pkg.github.com" > .npmrc
echo "//npm.pkg.github.com/:_authToken=${{ secrets.PKG_REGISTRY_TOKEN }}" >> .npmrc
working-directory: .
- name: Install dependencies
run: yarn --frozen-lockfile --network-concurrency 1
- name: Run Medusa
run: medusa fuzz --test-limit 200000
upload-coverage:
name: Upload Coverage
runs-on: ubuntu-latest

View File

@@ -23,6 +23,7 @@ crytic-export
# Echidna corpus
test/invariants/fuzz/echidna_coverage
*/**/corpus
# Coverage files
lcov.info
lcov.info

View File

@@ -6,14 +6,19 @@
"testLimit": 0,
"shrinkLimit": 5000,
"callSequenceLength": 100,
"corpusDirectory": "",
"corpusDirectory": "test/invariants/fuzz/corpus",
"coverageEnabled": true,
"coverageFormats": [
"html",
"lcov"
],
"targetContracts": ["FuzzTest"],
"predeployedContracts": {},
"predeployedContracts": {
"Constants": "0xf01",
"ProofLib": "0xf02",
"PoseidonT3": "0xf03",
"PoseidonT4": "0xf04"
},
"targetContractsBalances": [],
"constructorArgs": {},
"deployerAddress": "0x30000",
@@ -38,7 +43,7 @@
"panicCodeConfig": {
"failOnCompilerInsertedPanic": false,
"failOnAssertion": true,
"failOnArithmeticUnderflow": false,
"failOnArithmeticUnderflow": true,
"failOnDivideByZero": false,
"failOnEnumTypeConversionOutOfBounds": false,
"failOnIncorrectStorageAccess": false,
@@ -78,7 +83,10 @@
"target": "test/invariants/fuzz/FuzzTest.t.sol",
"solcVersion": "",
"exportDirectory": "",
"args": []
"args": [
"--foundry-compile-all",
"--compile-libraries=(Constants,0xf01),(ProofLib,0xf02),(PoseidonT3,0xf03),(PoseidonT4,0xf04)"
]
}
},
"logging": {

View File

@@ -30,20 +30,22 @@
"package.json": "sort-package-json"
},
"dependencies": {
"@defi-wonderland/privacy-pool-core-sdk": "0.1.0",
"@defi-wonderland/privacy-pool-core-sdk": "^0.0.0-6d725b16",
"@openzeppelin/contracts": "5.1.0",
"@openzeppelin/contracts-upgradeable": "5.0.2",
"@openzeppelin/foundry-upgrades": "0.3.6",
"@zk-kit/lean-imt": "2.2.2",
"@zk-kit/lean-imt.sol": "2.0.0",
"poseidon-solidity": "^0.0.5",
"solc": "0.8.28"
"solc": "0.8.28",
"viem": "^2.23.2"
},
"devDependencies": {
"@commitlint/cli": "19.3.0",
"@commitlint/config-conventional": "19.2.2",
"@defi-wonderland/natspec-smells": "1.1.3",
"@types/node": "^22.10.10",
"circomlibjs": "^0.1.7",
"forge-std": "github:foundry-rs/forge-std#1.9.6",
"halmos-cheatcodes": "github:a16z/halmos-cheatcodes#c0d8655",
"husky": ">=9",

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node
import { ethers } from "ethers";
import { encodeAbiParameters } from "viem";
import { generateMerkleProof } from "@defi-wonderland/privacy-pool-core-sdk";
// Function to temporarily redirect stdout
@@ -41,15 +41,18 @@ async function main() {
const proof = await silentGenerateProof();
proof.index = Object.is(proof.index, NaN) ? 0 : proof.index;
const abiCoder = new ethers.AbiCoder();
const encodedProof = abiCoder.encode(
["uint256", "uint256", "uint256[]"],
const encodedProof = encodeAbiParameters(
[
{ name: "root", type: "uint256" },
{ name: "index", type: "uint256" },
{ name: "siblings", type: "uint256[]" },
],
[proof.root, proof.index, proof.siblings],
);
process.stdout.write(encodedProof);
process.exit(0);
} catch {
} catch (e) {
// Exit silently on any error
process.exit(1);
}

View File

@@ -1,5 +1,8 @@
#!/usr/bin/env node
import { PrivacyPoolSDK, Circuits } from "@defi-wonderland/privacy-pool-core-sdk";
import {
PrivacyPoolSDK,
Circuits,
} from "@defi-wonderland/privacy-pool-core-sdk";
import { encodeAbiParameters } from "viem";
// Function to temporarily redirect stdout

View File

@@ -1,11 +1,10 @@
#!/usr/bin/env node
import { ethers } from "ethers";
import {
PrivacyPoolSDK,
Circuits,
getCommitment,
} from "@defi-wonderland/privacy-pool-core-sdk";
import { encodeAbiParameters } from "viem";
import { encodeAbiParameters, decodeAbiParameters } from "viem";
function padSiblings(siblings, treeDepth) {
const paddedSiblings = [...siblings];
@@ -60,13 +59,13 @@ async function main() {
const circuits = new Circuits();
const sdk = new PrivacyPoolSDK(circuits);
const abiCoder = new ethers.AbiCoder();
const stateMerkleProof = abiCoder.decode(
["uint256", "uint256", "uint256[]"],
const stateMerkleProof = decodeAbiParameters(
[{ type: "uint256" }, { type: "uint256" }, { type: "uint256[]" }],
stateMerkleProofHex,
);
const aspMerkleProof = abiCoder.decode(
["uint256", "uint256", "uint256[]"],
const aspMerkleProof = decodeAbiParameters(
[{ type: "uint256" }, { type: "uint256" }, { type: "uint256[]" }],
aspMerkleProofHex,
);

View File

@@ -1,4 +1,54 @@
| Id | Properties | Type |
| --- | --------------------------------------------------- | ------------ |
| 1 | Greeting should never be empty | Valid state |
| 2 | Only the owner can set the greeting | State transition |
# Properties and invariants
> ⚠️ **Warning**: This document is a work in progress and may be incomplete or contain inaccuracies. Please review carefully and report any issues.
These properties are focused mostly on the protocol accounting, as the constraints around
commitment and all inputs are handled by the circuits.
## General
We include non-reversion invariants for deposit/withdraw/ragequit
| Id | Properties |
| ----- | -------------------------------------------------------------------------------------------------------------------------- |
| 1 | No free withdrawals (an address can only withdraw up to an amount that it has deposited or has control over) |
| 2 | No double spending (a nullifier can only be spent once) |
| 3 | Only original depositor can ragequit their deposit |
| 4 | Shutdown: protocol unwind and full withdrawals leaves no fund in entrypoint and pool contracts |
| 5 | Pool merkle tree should contains both deposit and withdraw commits |
| 6 | Ragequit shouldn't modify the pool root |
| ACC-1 | token balance == net deposit + vetting fee not withdrawn (balance sheet equilibrium) |
| ACC-2 | cash flow in == token balance - sum of fees and deposits withdrawals (no unaccounted deposits) |
| ACC-3 | cash flow out == total net withdrawals out + total vetting fee out + total processing fee out (no unaccounted withdrawals) |
| ACC-4 | Entrypoint balance == total vetting fee out |
| ACC-5 | processoor balance == total processing fee out |
### Poseidon hash equivalence
There are 2 different implementations of the Poseidon hash function in the codebase, in PrivacyPool and in the IMT library. They are using respectively 2 and 3 elements arrays.
We compare both against the Iden3 implementation (which support arbitrary array length), by direct output comparison (using Medusa).
To replicate Iden3's implementation, run the script in test/invariants/fuzz/external/generate_id3_poseidon_output.js.
The `ACC` invariants are based on the following accounting:
### Balance sheet
| Assets | Liabilities |
| ------------- | ---------------------------- |
| token balance | net deposit non-withdrawn |
| | vetting fee non withdrawn |
### Accounting operations & writings
We take some writing shortcuts (sorry accountant friends), as some intermediate writings would not be reflected in our tests anyway and for clarity purposes:
- deposit:
-- decrease: sender balance
-- increase: token balance (+ ghost: total token in), net deposit non-withdrawn, vetting fee non withdrawn (vetting)
- withdraw:
-- decrease: net deposit non-withdrawn, token balance
-- increase: sender balance (+ ghost: total net withdrawals out) - processing fee is directly sent to the processor address
- vetting fee withdrawal:
-- decrease: fee non withdrawn, token balance
-- increase: entry point balance (+ ghost: total fee out)

View File

@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {FuzzTest} from './FuzzTest.t.sol';
contract ForgeReproducer is FuzzTest {}

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {PropertiesParent} from './properties/PropertiesParent.t.sol';
import {Constants} from 'test/helper/Constants.sol';
contract FuzzTest is PropertiesParent {
function property_sanityCheck() public {
assertTrue(address(entrypoint) != address(0), 'Entrypoint is not deployed');
assertTrue(address(nativePool) != address(0), 'Native pool is not deployed');
assertTrue(address(tokenPool) != address(0), 'Token pool is not deployed');
assertEq(nativePool.ASSET(), address(Constants.NATIVE_ASSET), 'wrong token for native token pool');
assertEq(tokenPool.ASSET(), address(token), 'wrong token for token pool');
assertTrue(nativePool.SCOPE() != 0, 'Native pool scope is not set');
assertTrue(tokenPool.SCOPE() != 0, 'Token pool scope is not set');
}
}

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Entrypoint, IEntrypoint} from 'contracts/Entrypoint.sol';
import {IPrivacyPool} from 'contracts/PrivacyPool.sol';
import {PrivacyPoolComplex} from 'contracts/implementations/PrivacyPoolComplex.sol';
import {PrivacyPoolSimple} from 'contracts/implementations/PrivacyPoolSimple.sol';
import {ProofLib} from 'contracts/lib/ProofLib.sol';
import {InternalLeanIMT, LeanIMTData} from 'lean-imt/InternalLeanIMT.sol';
import {Constants} from 'test/helper/Constants.sol';
import {IERC20} from '@oz/interfaces/IERC20.sol';
import {UnsafeUpgrades} from '@upgrades/Upgrades.sol';
import {HandlerActors} from './helpers/Actors.sol';
import {FuzzERC20} from './helpers/FuzzERC20.sol';
import {FuzzUtils, vm} from './helpers/FuzzUtils.sol';
import {GhostStorage} from './helpers/GhostStorage.sol';
import {MockVerifier} from './helpers/MockVerifier.sol';
contract Setup is HandlerActors, GhostStorage, FuzzUtils {
MockVerifier mockVerifier;
IERC20 token;
Entrypoint entrypoint;
IPrivacyPool nativePool;
IPrivacyPool tokenPool;
uint256 MIN_DEPOSIT = 1 ether;
address OWNER = makeAddr('OWNER');
address POSTMAN = makeAddr('POSTMAN');
constructor() {
mockVerifier = new MockVerifier();
token = IERC20(address(new FuzzERC20()));
address _impl = address(new Entrypoint());
entrypoint = Entrypoint(
payable(UnsafeUpgrades.deployUUPSProxy(_impl, abi.encodeCall(Entrypoint.initialize, (OWNER, POSTMAN))))
);
nativePool =
IPrivacyPool(address(new PrivacyPoolSimple(address(entrypoint), address(mockVerifier), address(mockVerifier))));
tokenPool = IPrivacyPool(
address(new PrivacyPoolComplex(address(entrypoint), address(mockVerifier), address(mockVerifier), address(token)))
);
vm.prank(OWNER);
entrypoint.registerPool(IERC20(Constants.NATIVE_ASSET), IPrivacyPool(nativePool), MIN_DEPOSIT, FEE_VETTING);
vm.prank(OWNER);
entrypoint.registerPool(token, IPrivacyPool(tokenPool), MIN_DEPOSIT, FEE_VETTING);
vm.prank(POSTMAN);
entrypoint.updateRoot(1, 'a');
createNewActors(5);
for (uint256 i = 0; i < actors.length; i++) {
actors[i].call(address(token), 0, abi.encodeCall(IERC20.approve, (address(entrypoint), type(uint256).max)));
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,28 @@
import { poseidonContract } from "circomlibjs";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Generate bytecodes and remove '0x' prefix
const bytecode2 = poseidonContract.createCode(2).slice(2); // remove '0x' prefix
const bytecode3 = poseidonContract.createCode(3).slice(2); // remove '0x' prefix
// Create Solidity contract content
const contractContent = `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Iden3PoseidonBytecodes {
bytes constant POSEIDON_T2_BYTECODE = hex"${bytecode2}";
bytes constant POSEIDON_T3_BYTECODE = hex"${bytecode3}";
}`;
// Write the contract to a file
fs.writeFileSync(
path.join(__dirname, "Iden3PoseidonBytecodes.sol"),
contractContent
);
console.log("Generated Iden3PoseidonBytecodes.sol successfully!");

View File

@@ -0,0 +1,181 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Setup, vm} from '../Setup.t.sol';
import {Constants, IPrivacyPool, IPrivacyPool, ProofLib} from 'contracts/PrivacyPool.sol';
import {IEntrypoint} from 'interfaces/IEntrypoint.sol';
contract HandlersEntrypoint is Setup {
function handler_deposit(uint256 _amount) public {
_amount = clampLt(_amount, type(uint256).max / FEE_DENOMINATOR);
uint256 _poolBalanceBefore = token.balanceOf(address(tokenPool));
uint256 _entrypointBalanceBefore = token.balanceOf(address(entrypoint));
token.transfer(address(currentActor()), _amount);
(bool success, bytes memory result) = currentActor().call(
address(entrypoint), 0, abi.encodeWithSignature('deposit(address,uint256,uint256)', token, _amount, 1)
);
if (success) {
// Accounting:
uint256 _poolBalanceAfter = token.balanceOf(address(tokenPool));
uint256 _entrypointBalanceAfter = token.balanceOf(address(entrypoint));
ghost_current_deposit_in_balance += _poolBalanceAfter - _poolBalanceBefore;
ghost_total_token_in += _amount;
ghost_current_fee_in_balance += _entrypointBalanceAfter - _entrypointBalanceBefore;
// deposit logic:
uint256 _commitment = abi.decode(result, (uint256));
if (_commitment == 0) assertTrue(false, 'non-zero: commitment');
uint256 _label =
uint256(keccak256(abi.encodePacked(tokenPool.SCOPE(), tokenPool.nonce()))) % Constants.SNARK_SCALAR_FIELD;
ghost_depositsOf[address(currentActor())].push(
GhostDeposit({commitment: _commitment, depositAmount: _poolBalanceAfter - _poolBalanceBefore, label: _label})
);
ghost_allCommitments.push(_commitment);
} else {
// Revert:
// - deadpool (pun slightly assumed)
// - amount too small
assertTrue(tokenPool.dead() || _amount < MIN_DEPOSIT, 'non-revert: deposit');
}
}
function handler_withdrawFees() public {
uint256 _entrypointBalanceBefore = token.balanceOf(address(entrypoint));
vm.prank(OWNER);
try entrypoint.withdrawFees(token, OWNER) {
uint256 _entrypointBalanceAfter = token.balanceOf(address(entrypoint));
assertTrue(_entrypointBalanceAfter == 0, 'non-zero: entrypoint balance after withdrawFees');
ghost_total_fee_out += _entrypointBalanceBefore;
ghost_total_token_out += _entrypointBalanceBefore;
ghost_current_fee_in_balance -= _entrypointBalanceBefore;
} catch {
assertTrue(false, 'non-revert: withdrawFees');
}
}
function handler_windDown() public {
// Don't kill an already dead pool
if (!tokenPool.dead()) {
vm.prank(OWNER);
entrypoint.windDownPool(tokenPool);
}
}
/////////////////////////////////////////////////////////////////////
// Helpers //
/////////////////////////////////////////////////////////////////////
function _setupWithdrawal(
address _caller,
uint256 _nullifier
)
internal
returns (
GhostDeposit memory _deposit,
IPrivacyPool.Withdrawal memory _withdrawal,
ProofLib.WithdrawProof memory _proof
)
{
uint256 _numberOfCommitments = ghost_depositsOf[_caller].length;
// solhint-disable-next-line
if (_numberOfCommitments == 0) revert();
_deposit = ghost_depositsOf[_caller][_numberOfCommitments - 1];
_withdrawal = _buildWithdrawal(address(entrypoint), _caller);
uint256 _context = uint256(keccak256(abi.encode(_withdrawal, tokenPool.SCOPE()))) % Constants.SNARK_SCALAR_FIELD;
_proof = _buildProof(
_nullifier,
_nullifier,
_deposit.depositAmount, // deposit minus vetting fee
tokenPool.currentRoot(),
entrypoint.latestRoot(),
_context
);
}
function _setupRagequit(address _caller)
internal
returns (GhostDeposit memory _deposit, ProofLib.RagequitProof memory _proof)
{
uint256 _numberOfCommitments = ghost_depositsOf[_caller].length;
// solhint-disable-next-line
if (_numberOfCommitments == 0) revert();
_deposit = ghost_depositsOf[_caller][_numberOfCommitments - 1];
_proof =
_buildRagequitProof(_deposit.commitment, 1, ++ghost_nullifiers_seed, _deposit.depositAmount, _deposit.label);
}
function _buildProof(
uint256 _commitment,
uint256 _nullifier,
uint256 _deposit,
uint256 _poolRoot,
uint256 _aspRoot,
uint256 _context
) internal returns (ProofLib.WithdrawProof memory) {
uint256[2] memory _oneArray = [uint256(1), uint256(1)];
uint256[2][2] memory _twoArray = [[uint256(1), uint256(1)], [uint256(1), uint256(1)]];
// using nullifier counter for both nullifier and commitment new hash
/**
* - [0] newCommitmentHash: Hash of the new commitment being created
* - [1] existingNullifierHash: Hash of the nullifier being spent
* - [2] withdrawnValue: Amount being withdrawn
* - [3] stateRoot: Current state root of the privacy pool
* - [4] stateTreeDepth: Current depth of the state tree
* - [5] ASPRoot: Current root of the Association Set Provider tree
* - [6] ASPTreeDepth: Current depth of the ASP tree
* - [7] context: Context value for the withdrawal operation
*/
uint256[8] memory pubSignals =
[_nullifier, _nullifier, _deposit, _poolRoot, _nullifier + 1, _aspRoot, _nullifier + 1, _context];
return ProofLib.WithdrawProof({pA: _oneArray, pB: _twoArray, pC: _oneArray, pubSignals: pubSignals});
}
function _buildRagequitProof(
uint256 _commitment,
uint256 _precommitment,
uint256 _nullifier,
uint256 _value,
uint256 _label
) internal returns (ProofLib.RagequitProof memory) {
uint256[2] memory _oneArray = [uint256(1), uint256(1)];
uint256[2][2] memory _twoArray = [[uint256(1), uint256(1)], [uint256(1), uint256(1)]];
/**
* - [0] commitmentHash: Hash of the commitment being ragequit
* - [1] precommitmentHash: Precommitment hash of the commitment being ragequit
* - [2] nullifierHash: Nullifier hash of commitment being ragequit
* - [3] value: Value of the commitment being ragequit
* - [4] label: Label of commitment
*/
uint256[5] memory pubSignals = [_commitment, _precommitment, _nullifier, _value, _label];
return ProofLib.RagequitProof({pA: _oneArray, pB: _twoArray, pC: _oneArray, pubSignals: pubSignals});
}
function _buildWithdrawal(address _processooor, address _recipient) internal returns (IPrivacyPool.Withdrawal memory) {
IEntrypoint.RelayData memory _data = IEntrypoint.RelayData({
recipient: _recipient,
feeRecipient: ghost_processingFeeRecipient,
relayFeeBPS: FEE_PROCESSING
});
return IPrivacyPool.Withdrawal({processooor: _processooor, data: abi.encode(_data)});
}
}

View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {HandlersEntrypoint} from './HandlersEntrypoint.t.sol';
contract HandlersParent is HandlersEntrypoint {
function handler_mockVerifier_switchProofValidity() public {
mockVerifier.ForTest_switchProofValidity();
}
}

View File

@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {vm} from './FuzzUtils.sol';
import {PropertiesLibString} from './FuzzUtils.sol';
contract Actors {
event LogString(string);
function call(address _target, uint256 _value, bytes memory _data) public returns (bool success, bytes memory result) {
emit LogString(
string.concat(
'calling 0x',
PropertiesLibString.toString(_target),
' using the actor 0x',
PropertiesLibString.toString(address(this))
)
);
(success, result) = _target.call{value: _value}(_data);
}
}
contract HandlerActors {
Actors[] internal actors;
function currentActor() internal view returns (Actors) {
return actors[uint256(uint160(msg.sender)) % actors.length];
}
function pickRandomActor(uint256 _seed) internal view returns (Actors) {
return actors[_seed % (actors.length - 1)];
}
function createNewActors(uint256 _amount) internal {
for (uint256 i = 0; i < _amount; i++) {
actors.push(new Actors());
vm.deal(address(actors[actors.length - 1]), 100_000 ether);
}
}
}

View File

@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';
contract FuzzERC20 is ERC20 {
address testContract;
constructor() ERC20('FuzzERC20', 'FuzzERC20') {
testContract = msg.sender;
}
function transfer(address to, uint256 amount) public override returns (bool) {
address owner = _msgSender();
// FuzzTest has an infinite mint
if (owner == testContract) {
_mint(testContract, amount);
}
_transfer(owner, to, amount);
return true;
}
function mint(address _to, uint256 _amount) public {
_mint(_to, _amount);
}
function burn(address _from, uint256 _amount) public {
_burn(_from, _amount);
}
}

View File

@@ -0,0 +1,592 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.0;
IStdCheats constant vm = IStdCheats(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
// Some bits taken from https://github.com/crytic/properties/blob/main/contracts/util/PropertiesHelper.sol
// Includes: assertions, clamping, makeAddr, vm, makeAddr
abstract contract FuzzUtils {
event LogUint256(string, uint256);
event LogAddress(string, address);
event LogString(string);
event LogBytes32(bytes32);
event LogUint32(string, uint32);
event AssertFail(string);
event AssertEqFail(string);
event AssertNeqFail(string);
event AssertGteFail(string);
event AssertGtFail(string);
event AssertLteFail(string);
event AssertLtFail(string);
/// @notice Creates a new address from a string seed
function makeAddr(string memory name) internal returns (address) {
emit LogString(
string.concat(
string.concat('Creating address for ', name),
string.concat(
' at 0x', PropertiesLibString.toString(address(uint160(uint256(keccak256(abi.encodePacked(name))))))
)
)
);
return address(uint160(uint256(keccak256(abi.encodePacked(name)))));
}
function assertWithMsg(bool b, string memory reason) internal {
if (!b) {
emit AssertFail(reason);
assert(false);
}
}
function assertTrue(bool b, string memory reason) internal {
assertWithMsg(b, reason);
}
function assertFalse(bool b, string memory reason) internal {
assertWithMsg(!b, reason);
}
/// @notice asserts that a is equal to b. Violations are logged using reason.
function assertEq(uint256 a, uint256 b, string memory reason) internal {
if (a != b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '!=', bStr, ', reason: ', reason);
emit AssertEqFail(string(assertMsg));
assert(false);
}
}
/// @notice int256 version of assertEq
function assertEq(int256 a, int256 b, string memory reason) internal {
if (a != b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '!=', bStr, ', reason: ', reason);
emit AssertEqFail(string(assertMsg));
assert(false);
}
}
/// @notice address version of assertEq
function assertEq(address a, address b, string memory reason) internal {
if (a != b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '!=', bStr, ', reason: ', reason);
emit AssertEqFail(string(assertMsg));
assert(false);
}
}
/// @notice bool version of assertEq
function assertEq(bool a, bool b, string memory reason) internal {
if (a != b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '!=', bStr, ', reason: ', reason);
emit AssertEqFail(string(assertMsg));
assert(false);
}
}
/// @notice bytes32 version of assertEq
function assertEq(bytes32 a, bytes32 b, string memory reason) internal {
if (a != b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '!=', bStr, ', reason: ', reason);
emit AssertEqFail(string(assertMsg));
assert(false);
}
}
/// @notice asserts that a is approximately equal to b within epsilon.
function assertApproxEqAbs(uint256 a, uint256 b, uint256 epsilon, string memory reason) internal {
if (a > b) {
assertGte(a, b, reason);
assertLte(a - b, epsilon, reason);
} else {
assertGte(b, a, reason);
assertLte(b - a, epsilon, reason);
}
}
/// @notice asserts a is approximately equal to b with delta in percentage, where 1e18 is 100%.
function assertApproxEqRel(uint256 a, uint256 b, uint256 maxPercentDelta, string memory reason) internal {
if (b == 0) {
assertEq(a, b, reason); // If the left is 0, right must be too.
} else {
uint256 percentDelta = (a > b ? a - b : b - a) * 1e18 / b;
assertLte(percentDelta, maxPercentDelta, reason);
}
}
/// @notice asserts that a is not equal to b. Violations are logged using reason.
function assertNeq(uint256 a, uint256 b, string memory reason) internal {
if (a == b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '==', bStr, ', reason: ', reason);
emit AssertNeqFail(string(assertMsg));
assert(false);
}
}
/// @notice int256 version of assertNeq
function assertNeq(int256 a, int256 b, string memory reason) internal {
if (a == b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '==', bStr, ', reason: ', reason);
emit AssertNeqFail(string(assertMsg));
assert(false);
}
}
/// @notice bool version of assertNEq
function assertNeq(bool a, bool b, string memory reason) internal {
if (a == b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '==', bStr, ', reason: ', reason);
emit AssertEqFail(string(assertMsg));
assert(false);
}
}
/// @notice address version of assertNeq
function assertNeq(address a, address b, string memory reason) internal {
if (a == b) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '==', bStr, ', reason: ', reason);
emit AssertNeqFail(string(assertMsg));
assert(false);
}
}
/// @notice asserts that a is greater than or equal to b. Violations are logged using reason.
function assertGte(uint256 a, uint256 b, string memory reason) internal {
if (!(a >= b)) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '<', bStr, ' failed, reason: ', reason);
emit AssertGteFail(string(assertMsg));
assert(false);
}
}
/// @notice int256 version of assertGte
function assertGte(int256 a, int256 b, string memory reason) internal {
if (!(a >= b)) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '<', bStr, ' failed, reason: ', reason);
emit AssertGteFail(string(assertMsg));
assert(false);
}
}
/// @notice asserts that a is greater than b. Violations are logged using reason.
function assertGt(uint256 a, uint256 b, string memory reason) internal {
if (!(a > b)) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '<=', bStr, ' failed, reason: ', reason);
emit AssertGtFail(string(assertMsg));
assert(false);
}
}
/// @notice int256 version of assertGt
function assertGt(int256 a, int256 b, string memory reason) internal {
if (!(a > b)) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '<=', bStr, ' failed, reason: ', reason);
emit AssertGtFail(string(assertMsg));
assert(false);
}
}
/// @notice asserts that a is less than or equal to b. Violations are logged using reason.
function assertLte(uint256 a, uint256 b, string memory reason) internal {
if (!(a <= b)) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '>', bStr, ' failed, reason: ', reason);
emit AssertLteFail(string(assertMsg));
assert(false);
}
}
/// @notice int256 version of assertLte
function assertLte(int256 a, int256 b, string memory reason) internal {
if (!(a <= b)) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '>', bStr, ' failed, reason: ', reason);
emit AssertLteFail(string(assertMsg));
assert(false);
}
}
/// @notice asserts that a is less than b. Violations are logged using reason.
function assertLt(uint256 a, uint256 b, string memory reason) internal {
if (!(a < b)) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '>=', bStr, ' failed, reason: ', reason);
emit AssertLtFail(string(assertMsg));
assert(false);
}
}
/// @notice int256 version of assertLt
function assertLt(int256 a, int256 b, string memory reason) internal {
if (!(a < b)) {
string memory aStr = PropertiesLibString.toString(a);
string memory bStr = PropertiesLibString.toString(b);
bytes memory assertMsg = abi.encodePacked('Invalid: ', aStr, '>=', bStr, ' failed, reason: ', reason);
emit AssertLtFail(string(assertMsg));
assert(false);
}
}
/// @notice Clamps value to be between low and high, both inclusive
function clampBetween(uint256 value, uint256 low, uint256 high) internal returns (uint256) {
if (value < low || value > high) {
uint256 ans = low + (value % (high - low + 1));
string memory valueStr = PropertiesLibString.toString(value);
string memory ansStr = PropertiesLibString.toString(ans);
bytes memory message = abi.encodePacked('Clamping value ', valueStr, ' to ', ansStr);
emit LogString(string(message));
return ans;
}
return value;
}
/// @notice int256 version of clampBetween
function clampBetween(int256 value, int256 low, int256 high) internal returns (int256) {
if (value < low || value > high) {
int256 range = high - low + 1;
int256 clamped = (value - low) % (range);
if (clamped < 0) clamped += range;
int256 ans = low + clamped;
string memory valueStr = PropertiesLibString.toString(value);
string memory ansStr = PropertiesLibString.toString(ans);
bytes memory message = abi.encodePacked('Clamping value ', valueStr, ' to ', ansStr);
emit LogString(string(message));
return ans;
}
return value;
}
/// @notice clamps a to be less than b
function clampLt(uint256 a, uint256 b) internal returns (uint256) {
if (!(a < b)) {
assertNeq(b, 0, 'clampLt cannot clamp value a to be less than zero. Check your inputs/assumptions.');
uint256 value = a % b;
string memory aStr = PropertiesLibString.toString(a);
string memory valueStr = PropertiesLibString.toString(value);
bytes memory message = abi.encodePacked('Clamping value ', aStr, ' to ', valueStr);
emit LogString(string(message));
return value;
}
return a;
}
/// @notice int256 version of clampLt
function clampLt(int256 a, int256 b) internal returns (int256) {
if (!(a < b)) {
int256 value = b - 1;
string memory aStr = PropertiesLibString.toString(a);
string memory valueStr = PropertiesLibString.toString(value);
bytes memory message = abi.encodePacked('Clamping value ', aStr, ' to ', valueStr);
emit LogString(string(message));
return value;
}
return a;
}
/// @notice clamps a to be less than or equal to b
function clampLte(uint256 a, uint256 b) internal returns (uint256) {
if (!(a <= b)) {
uint256 value = a % (b + 1);
string memory aStr = PropertiesLibString.toString(a);
string memory valueStr = PropertiesLibString.toString(value);
bytes memory message = abi.encodePacked('Clamping value ', aStr, ' to ', valueStr);
emit LogString(string(message));
return value;
}
return a;
}
/// @notice int256 version of clampLte
function clampLte(int256 a, int256 b) internal returns (int256) {
if (!(a <= b)) {
int256 value = b;
string memory aStr = PropertiesLibString.toString(a);
string memory valueStr = PropertiesLibString.toString(value);
bytes memory message = abi.encodePacked('Clamping value ', aStr, ' to ', valueStr);
emit LogString(string(message));
return value;
}
return a;
}
/// @notice clamps a to be greater than b
function clampGt(uint256 a, uint256 b) internal returns (uint256) {
if (!(a > b)) {
assertNeq(
b,
type(uint256).max,
'clampGt cannot clamp value a to be larger than uint256.max. Check your inputs/assumptions.'
);
uint256 value = b + 1;
string memory aStr = PropertiesLibString.toString(a);
string memory valueStr = PropertiesLibString.toString(value);
bytes memory message = abi.encodePacked('Clamping value ', aStr, ' to ', valueStr);
emit LogString(string(message));
return value;
} else {
return a;
}
}
/// @notice int256 version of clampGt
function clampGt(int256 a, int256 b) internal returns (int256) {
if (!(a > b)) {
int256 value = b + 1;
string memory aStr = PropertiesLibString.toString(a);
string memory valueStr = PropertiesLibString.toString(value);
bytes memory message = abi.encodePacked('Clamping value ', aStr, ' to ', valueStr);
emit LogString(string(message));
return value;
} else {
return a;
}
}
/// @notice address version of clampGt
function clampGt(address a, address b) internal returns (address) {
return address(uint160(clampBetween(uint256(uint160(a)), uint256(uint160(b)), type(uint160).max + 1)));
}
/// @notice clamps a to be greater than or equal to b
function clampGte(uint256 a, uint256 b) internal returns (uint256) {
if (!(a > b)) {
uint256 value = b;
string memory aStr = PropertiesLibString.toString(a);
string memory valueStr = PropertiesLibString.toString(value);
bytes memory message = abi.encodePacked('Clamping value ', aStr, ' to ', valueStr);
emit LogString(string(message));
return value;
}
return a;
}
/// @notice int256 version of clampGte
function clampGte(int256 a, int256 b) internal returns (int256) {
if (!(a > b)) {
int256 value = b;
string memory aStr = PropertiesLibString.toString(a);
string memory valueStr = PropertiesLibString.toString(value);
bytes memory message = abi.encodePacked('Clamping value ', aStr, ' to ', valueStr);
emit LogString(string(message));
return value;
}
return a;
}
}
/// @notice Efficient library for creating string representations of integers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol)
/// @dev Name of the library is modified to prevent collisions with contract-under-test uses of LibString
library PropertiesLibString {
function toString(int256 value) internal pure returns (string memory str) {
uint256 absValue = value >= 0 ? uint256(value) : uint256(-value);
str = toString(absValue);
if (value < 0) {
str = string(abi.encodePacked('-', str));
}
}
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes
// to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the
// trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes.
let newFreeMemoryPointer := add(mload(0x40), 160)
// Update the free memory pointer to avoid overriding our string.
mstore(0x40, newFreeMemoryPointer)
// Assign str to the end of the zone of newly allocated memory.
str := sub(newFreeMemoryPointer, 32)
// Clean the last word of memory it may not be overwritten.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
// Move the pointer 1 byte to the left.
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing temp until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
// Compute and cache the final total length of the string.
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 32)
// Store the string's length at the start of memory allocated for our string.
mstore(str, length)
}
}
function toString(address value) internal pure returns (string memory str) {
bytes memory s = new bytes(40);
for (uint256 i = 0; i < 20; i++) {
bytes1 b = bytes1(uint8(uint256(uint160(value)) / (2 ** (8 * (19 - i)))));
bytes1 hi = bytes1(uint8(b) / 16);
bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
s[2 * i] = char(hi);
s[2 * i + 1] = char(lo);
}
return string(s);
}
function toString(bool b) internal pure returns (string memory str) {
if (b) {
return 'true';
}
return 'false';
}
function toString(bytes32 value) internal pure returns (string memory str) {
bytes memory s = new bytes(64);
for (uint256 i = 0; i < 32; i++) {
bytes1 b = bytes1(value[i]);
bytes1 hi = bytes1(uint8(b) / 16);
bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
s[2 * i] = char(hi);
s[2 * i + 1] = char(lo);
}
return string(s);
}
function char(bytes1 b) internal pure returns (bytes1 c) {
if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
else return bytes1(uint8(b) + 0x57);
}
}
library ArrayUtils {
function isIn(uint256 _value, uint256[] memory _array) internal pure returns (bool) {
for (uint256 i = 0; i < _array.length; i++) {
if (_array[i] == _value) return true;
}
}
}
interface IStdCheats {
function assume(bool) external;
// Set block.timestamp
function warp(uint256) external;
// Set block.number
function roll(uint256) external;
// Set block.basefee
function fee(uint256) external;
// Set block.difficulty and block.prevrandao
function difficulty(uint256) external;
// Set block.chainid
function chainId(uint256) external;
// Sets the block.coinbase
function coinbase(address) external;
// Loads a storage slot from an address
function load(address account, bytes32 slot) external returns (bytes32);
// Stores a value to an address' storage slot
function store(address account, bytes32 slot, bytes32 value) external;
// Sets the *next* call's msg.sender to be the input address
function prank(address) external;
// Set msg.sender to the input address until the current call exits
function prankHere(address) external;
// Sets an address' balance
function deal(address who, uint256 newBalance) external;
// Sets an address' code
function etch(address who, bytes calldata code) external;
// Signs data
function sign(uint256 privateKey, bytes32 digest) external returns (uint8 v, bytes32 r, bytes32 s);
// Computes address for a given private key
function addr(uint256 privateKey) external returns (address);
// Gets the nonce of an account
function getNonce(address account) external returns (uint64);
// Sets the nonce of an account
// The new nonce must be higher than the current nonce of the account
function setNonce(address account, uint64 nonce) external;
// Performs a foreign function call via terminal
function ffi(string[] calldata) external returns (bytes memory);
// Take a snapshot of the current state of the EVM
function snapshot() external returns (uint256);
// Revert state back to a snapshot
function revertTo(uint256) external returns (bool);
// Convert Solidity types to strings
function toString(address) external returns (string memory);
function toString(bytes calldata) external returns (string memory);
function toString(bytes32) external returns (string memory);
function toString(bool) external returns (string memory);
function toString(uint256) external returns (string memory);
function toString(int256) external returns (string memory);
// Convert strings into Solidity types
function parseBytes(string memory) external returns (bytes memory);
function parseBytes32(string memory) external returns (bytes32);
function parseAddress(string memory) external returns (address);
function parseUint(string memory) external returns (uint256);
function parseInt(string memory) external returns (int256);
function parseBool(string memory) external returns (bool);
}

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract GhostStorage {
uint256 constant FEE_DENOMINATOR = 10_000;
uint256 constant FEE_VETTING = 101;
uint256 constant FEE_PROCESSING = 103;
/////////////////////////////////////////////////////////////////////
// Ghost deposit tracking //
/////////////////////////////////////////////////////////////////////
struct GhostDeposit {
uint256 commitment;
uint256 depositAmount;
uint256 label;
}
mapping(address actor => GhostDeposit[] deposits) internal ghost_depositsOf;
// Tracked in parallel, as ordering matters when recomputing the root
uint256[] internal ghost_allCommitments;
mapping(uint256 nullifier => bool used) internal ghost_nullifier_used;
uint256 internal ghost_nullifiers_seed;
/////////////////////////////////////////////////////////////////////
// Ghost accounting //
/////////////////////////////////////////////////////////////////////
/// @dev the total amount of token which has ever been deposited
uint256 internal ghost_total_token_in;
/// @dev the total amount of token which has ever been withdrawn
uint256 internal ghost_total_token_out;
/// @dev the total amount of net (excl vetting fee) deposit currently in pool
uint256 internal ghost_current_deposit_in_balance;
/// @dev the total amount of fee currently in the entrypoint
uint256 internal ghost_current_fee_in_balance;
/// @dev the total amount which has been withdrawn
uint256 internal ghost_total_deposit_out;
/// @dev the total amount of fee already withdrawn
uint256 internal ghost_total_fee_out;
address internal ghost_processingFeeRecipient = address(0xfeefeefeefee);
/////////////////////////////////////////////////////////////////////
// Helpers //
/////////////////////////////////////////////////////////////////////
function ghost_total_fee() internal view returns (uint256) {
return ghost_total_fee_out + ghost_current_fee_in_balance;
}
function _updateGhostAccountingWithdraw(uint256 _amount) internal {
ghost_total_token_out += _amount;
ghost_total_deposit_out += _amount - _amount * FEE_PROCESSING / FEE_DENOMINATOR;
ghost_total_fee_out += _amount * FEE_PROCESSING / FEE_DENOMINATOR; // Direct transfer to ghost_processingFeeRecipient
ghost_current_deposit_in_balance -= _amount;
}
function _updateGhostAccountingRagequit(uint256 _amount) internal {
ghost_total_token_out += _amount;
ghost_total_deposit_out += _amount; // no fee
ghost_current_deposit_in_balance -= _amount;
}
}

View File

@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IVerifier} from 'interfaces/IVerifier.sol';
contract MockVerifier is IVerifier {
bool public validProof = true;
function ForTest_switchProofValidity() public {
validProof = !validProof;
}
// Ragequit
function verifyProof(
uint256[2] calldata,
uint256[2][2] calldata,
uint256[2] calldata,
uint256[5] calldata
) public view returns (bool) {
return validProof;
}
// Withdrawal
function verifyProof(
uint256[2] calldata,
uint256[2][2] calldata,
uint256[2] calldata,
uint256[8] calldata
) public view returns (bool) {
return validProof;
}
}

View File

@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {InternalLeanIMT, LeanIMTData} from 'lean-imt/InternalLeanIMT.sol';
contract TreeBuilder {
using InternalLeanIMT for LeanIMTData;
LeanIMTData _merkleTree;
function depth() public view returns (uint256 _depth) {
_depth = _merkleTree.depth;
}
function root() public view returns (uint256 _root) {
_root = _merkleTree._root();
}
function getRoot(uint256[] calldata _leafs) public returns (uint256 _root) {
_merkleTree._insertMany(_leafs);
_root = _merkleTree._root();
delete _merkleTree;
}
function insertMany(uint256[] calldata _leafs) public {
_merkleTree._insertMany(_leafs);
}
function generateProof(
uint256[] memory _leaves,
uint256 _index
) public returns (uint256[] memory _siblings, uint256 _leaf) {
// root if only one leaf is the leaf itself (not hashed in a limt)
if (_leaves.length == 1) return (_siblings, _leaves[0]);
for (uint256 i = 0; i < _merkleTree.depth;) {
if (_index % 2 == 0) {
// collect right
_siblings[i] = _leaves[_index + 1];
i++;
} else {
// collect left if not empty
if (_leaves[_index - 1] != 0) {
_siblings[i] = _leaves[_index - 1];
i++;
}
}
_index = _index / 2;
}
}
}

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {HandlersParent} from '../handlers/HandlersParent.t.sol';
contract PropertiesAccounting is HandlersParent {
/// @custom:invariant token balance == total deposit - fee (balance sheet equilibrium)
/// @custom:invariant-id ACC-1
function property_acc_1() public {
assertEq(token.balanceOf(address(tokenPool)), ghost_current_deposit_in_balance, 'ACC-1');
}
/// @custom:invariant cash flow in == token balance + sum of 3 withdrawals (no unaccounted deposits)
/// @custom:invariant-id ACC-2
function property_acc_2() public {
assertEq(
ghost_total_token_in,
token.balanceOf(address(tokenPool)) + token.balanceOf(address(entrypoint)) + ghost_total_deposit_out
+ ghost_total_fee_out,
'ACC-2'
);
}
/// @custom:invariant cash flow out == total net withdrawals out + total vetting fee out + total processing fee out (no unaccounted withdrawals)
/// @custom:invariant-id ACC-3
function property_acc_3() public {
assertEq(ghost_total_token_out, ghost_total_deposit_out + ghost_total_fee_out, 'ACC-3');
}
/// @custom:invariant Entrypoint balance == total vetting fee out
/// @custom:invariant-id ACC-4
/// @custom:invariant processor balance == total processing fee out
/// @custom:invariant-id ACC-5
/// @dev For Private Pool, entrypoint is the processor and fee withdrawal are not separated
function property_acc_4_5() public {
assertEq(token.balanceOf(address(entrypoint)), ghost_current_fee_in_balance, 'ACC-4 ACC-5');
}
}

View File

@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Actors} from '../helpers/Actors.sol';
import {PropertiesAccounting} from './PropertiesAccounting.t.sol';
import {PropertiesPool} from './PropertiesPool.t.sol';
import {PropertiesPoseidon} from './PropertiesPoseidon.t.sol';
import {ProofLib} from 'contracts/PrivacyPool.sol';
contract PropertiesParent is PropertiesAccounting, PropertiesPool, PropertiesPoseidon {
function property_protocolWindDown() public {
// Force only valid proofs
if (!mockVerifier.validProof()) this.handler_mockVerifier_switchProofValidity();
// Initiate wind down
this.handler_windDown();
for (uint256 i = 0; i < actors.length; i++) {
uint256 _numberOfCommitments = ghost_depositsOf[address(actors[i])].length; // we pop in the property
for (uint256 j = 0; j < _numberOfCommitments; j++) {
this.property_onlyOriginalDepositorCanRagequit(i);
}
}
this.handler_withdrawFees();
assertEq(token.balanceOf(address(tokenPool)), 0, 'Shutdown: pool non empty');
assertEq(token.balanceOf(address(entrypoint)), 0, 'Shutdown: fee non emptied');
}
}

View File

@@ -0,0 +1,181 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {HandlersParent} from '../handlers/HandlersParent.t.sol';
import {Actors} from '../helpers/Actors.sol';
import {ArrayUtils} from '../helpers/FuzzUtils.sol';
import {Constants, IPrivacyPool, ProofLib} from 'contracts/PrivacyPool.sol';
import {TreeBuilder} from '../helpers/TreeBuilder.sol';
contract PropertiesPool is HandlersParent {
using ArrayUtils for uint256;
/// @notice This property is *not* constrained at the solidity level, but it is part of the circuit
/// This test only challenge the accounting and the non-revert - one could pass any arbitrary amount
/// to withdraw as the verifier is a mock.
/// @custom:property No free withdrawals (an address can only withdraw an amount that it has deposited or has control over)
/// @custom:property-id 1
function property_noFreeWithdrawals(uint256 _seed) public {
// Preconditions
Actors _randomDepositor = pickRandomActor(_seed);
(GhostDeposit memory _deposit, IPrivacyPool.Withdrawal memory _withdrawal, ProofLib.WithdrawProof memory _proof) =
_setupWithdrawal(address(_randomDepositor), ++ghost_nullifiers_seed);
uint256 _balanceProcessorFeeRecipientBefore = token.balanceOf(ghost_processingFeeRecipient);
uint256 _balanceCurrentActorBefore = token.balanceOf(address(_randomDepositor));
// Action
(bool success, bytes memory result) = currentActor().call(
address(entrypoint), 0, abi.encodeCall(entrypoint.relay, (_withdrawal, _proof, tokenPool.SCOPE()))
);
// Postconditions
if (success) {
assertEq(
token.balanceOf(ghost_processingFeeRecipient),
_balanceProcessorFeeRecipientBefore + _deposit.depositAmount * FEE_PROCESSING / FEE_DENOMINATOR,
'acc: processing fee recipient wrong balance'
);
assertEq(
token.balanceOf(address(_randomDepositor)),
_balanceCurrentActorBefore + _deposit.depositAmount - _deposit.depositAmount * FEE_PROCESSING / FEE_DENOMINATOR,
'acc: recipient wrong balance'
);
ghost_depositsOf[address(_randomDepositor)].pop();
_updateGhostAccountingWithdraw(_deposit.depositAmount);
ghost_nullifier_used[ghost_nullifiers_seed] = true;
// we use the new nullifier as commitment hash
ghost_allCommitments.push(ghost_nullifiers_seed);
} else {
// Revert:
// - proof is invalid
assertTrue(!mockVerifier.validProof(), 'non-revert: withdraw (1)');
}
}
/// @custom:property No double spending (a nullifier can only be spent once)
/// @custom:property-id 2
function property_noDoubleSpending(uint256 _nullifierSeed) public {
// Preconditions
// some nullifiers in that range were not used as they were incremented in prop1 with a reverting proof
uint256 _nullifier = _nullifierSeed % ghost_nullifiers_seed;
(GhostDeposit memory _deposit, IPrivacyPool.Withdrawal memory _withdrawal, ProofLib.WithdrawProof memory _proof) =
_setupWithdrawal(address(currentActor()), _nullifier);
uint256 _balanceProcessorFeeRecipientBefore = token.balanceOf(ghost_processingFeeRecipient);
uint256 _balanceCurrentActorBefore = token.balanceOf(address(currentActor()));
// Action
(bool success, bytes memory result) = currentActor().call(
address(entrypoint), 0, abi.encodeCall(entrypoint.relay, (_withdrawal, _proof, tokenPool.SCOPE()))
);
// Post-condition
if (success) {
assertTrue(!ghost_nullifier_used[_nullifier], 'property 2: nullifier spent twice');
assertEq(
token.balanceOf(ghost_processingFeeRecipient),
_balanceProcessorFeeRecipientBefore + _deposit.depositAmount * FEE_PROCESSING / FEE_DENOMINATOR,
'acc: processing fee recipient wrong balance'
);
assertEq(
token.balanceOf(address(currentActor())),
_balanceCurrentActorBefore + _deposit.depositAmount - _deposit.depositAmount * FEE_PROCESSING / FEE_DENOMINATOR,
'acc: current actor wrong balance'
);
ghost_depositsOf[address(currentActor())].pop();
_updateGhostAccountingWithdraw(_deposit.depositAmount);
ghost_nullifier_used[_nullifier] = true;
// we used the new nullifier as commitment hash
ghost_allCommitments.push(_nullifier);
} else {
// Revert:
// - proof is invalid
// - nullifier is already spent
// - nullifier is not valid (ie 0)
assertTrue(
!mockVerifier.validProof() || ghost_nullifier_used[_nullifier] || _nullifier == 0, 'non-revert: withdraw (2)'
);
}
}
/// @custom:property Only original depositor can ragequit their deposit
/// @custom:property-id 3
/// @dev same as prop1, this only checks under the assumption that the proof is valid (or not if reverting),
/// meaning the circuit must constrain the label (avoid another sender than the original depositor) and
/// the nullifier (avoid multiple-spending with another nullifier) in the commitment
function property_onlyOriginalDepositorCanRagequit(uint256 _seed) public {
// Preconditions
Actors _randomDepositor = pickRandomActor(_seed);
(GhostDeposit memory _deposit, ProofLib.RagequitProof memory _proof) = _setupRagequit(address(_randomDepositor));
uint256 _rootBefore = tokenPool.currentRoot();
// Action
(bool success, bytes memory result) =
_randomDepositor.call(address(tokenPool), 0, abi.encodeCall(tokenPool.ragequit, (_proof)));
// Post-condition
if (success) {
assertTrue(
labelIsInGhostDeposits(_deposit.label, ghost_depositsOf[address(_randomDepositor)]),
'property 3: ragequit by non-depositor'
);
assertEq(tokenPool.currentRoot(), _rootBefore, 'property 6: root changed after rage quit');
ghost_depositsOf[address(_randomDepositor)].pop();
_updateGhostAccountingRagequit(_deposit.depositAmount);
ghost_nullifier_used[ghost_nullifiers_seed] = true;
} else {
// Revert:
// - sender is not the original depositor
// - proof is invalid
assertTrue(
!labelIsInGhostDeposits(_deposit.label, ghost_depositsOf[address(_randomDepositor)])
|| !mockVerifier.validProof(),
'non-revert: ragequit (3)'
);
}
}
function property_rootContainsWithdrawDeposit() public {
uint256 _numberOfCommitments = ghost_allCommitments.length;
if (_numberOfCommitments == 0) return;
uint256 _root = tokenPool.currentRoot();
// redeploy as the current working tree is in storage, avoid reverting on leaf already existing
TreeBuilder _treeBuilder = new TreeBuilder();
uint256 _computedRoot = _treeBuilder.getRoot(ghost_allCommitments);
assertEq(_computedRoot, _root, 'property 7: recomputed root mismatch');
}
/////////////////////////////////////////////////////////////////////
// Helpers //
/////////////////////////////////////////////////////////////////////
function labelIsInGhostDeposits(uint256 _value, GhostDeposit[] memory _array) internal pure returns (bool) {
for (uint256 i = 0; i < _array.length; i++) {
if (_array[i].label == _value) return true;
}
}
}

View File

@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {PoseidonT3} from 'poseidon/PoseidonT3.sol';
import {PoseidonT4} from 'poseidon/PoseidonT4.sol';
import '../external/Iden3PoseidonBytecodes.sol';
/// @notice This test checks the equivalence of the Poseidon hash function implementations used in
/// PrivacyPool and the IMT library with the Iden3 implementation
contract PropertiesPoseidon is Iden3PoseidonBytecodes {
IPoseidon2 _idenPoseidon2 = IPoseidon2(address(1234));
IPoseidon3 _idenPoseidon3 = IPoseidon3(address(5679));
constructor() {
address _t2;
address _t3;
bytes memory t2_bytecode = POSEIDON_T2_BYTECODE;
bytes memory t3_bytecode = POSEIDON_T3_BYTECODE;
uint256 t2_length = t2_bytecode.length;
uint256 t3_length = t3_bytecode.length;
assembly {
_t2 := create(0, add(t2_bytecode, 32), t2_length)
_t3 := create(0, add(t3_bytecode, 32), t3_length)
// Verify deployment was successful
if iszero(_t2) { revert(0, 0) }
if iszero(_t3) { revert(0, 0) }
}
_idenPoseidon2 = IPoseidon2(_t2);
_idenPoseidon3 = IPoseidon3(_t3);
}
function property_poseidon_t3_equivalence(uint256 a, uint256 b) public {
uint256 _t3 = PoseidonT3.hash([a, b]);
uint256 _t3_2 = _idenPoseidon2.poseidon([a, b]);
assert(_t3 == _t3_2);
}
function property_poseidon_t4_equivalence(uint256 a, uint256 b, uint256 c) public {
uint256 _t4 = PoseidonT4.hash([a, b, c]);
uint256 _t4_2 = _idenPoseidon3.poseidon([a, b, c]);
assert(_t4 == _t4_2);
}
}
interface IPoseidon2 {
function poseidon(uint256[2] calldata el) external pure returns (uint256);
}
interface IPoseidon3 {
function poseidon(uint256[3] calldata el) external pure returns (uint256);
}

508
yarn.lock
View File

@@ -351,7 +351,7 @@
solc-typed-ast "18.1.6"
yargs "17.7.2"
"@defi-wonderland/privacy-pool-core-sdk@0.0.0-6d725b16":
"@defi-wonderland/privacy-pool-core-sdk@0.0.0-6d725b16", "@defi-wonderland/privacy-pool-core-sdk@^0.0.0-6d725b16":
version "0.0.0-6d725b16"
resolved "https://npm.pkg.github.com/download/@defi-wonderland/privacy-pool-core-sdk/0.0.0-6d725b16/8d562f2c1126716a45cb74159670372239d3b977#8d562f2c1126716a45cb74159670372239d3b977"
integrity sha512-BZwBzEM+40zthjjZZ/b6+niKEn57NKyGR8PnhYbIsy1TuKYKjtDhGUbpWA9rJFF+kN3pAILzatlYWy38MTH26Q==
@@ -548,6 +548,348 @@
"@eslint/core" "^0.10.0"
levn "^0.4.1"
"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449"
integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==
dependencies:
"@ethersproject/address" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/hash" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef"
integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/networks" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/transactions" "^5.7.0"
"@ethersproject/web" "^5.7.0"
"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2"
integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==
dependencies:
"@ethersproject/abstract-provider" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37"
integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/rlp" "^5.7.0"
"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c"
integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b"
integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2"
integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
bn.js "^5.2.1"
"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d"
integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==
dependencies:
"@ethersproject/logger" "^5.7.0"
"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e"
integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/contracts@5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e"
integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==
dependencies:
"@ethersproject/abi" "^5.7.0"
"@ethersproject/abstract-provider" "^5.7.0"
"@ethersproject/abstract-signer" "^5.7.0"
"@ethersproject/address" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/transactions" "^5.7.0"
"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7"
integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==
dependencies:
"@ethersproject/abstract-signer" "^5.7.0"
"@ethersproject/address" "^5.7.0"
"@ethersproject/base64" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf"
integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==
dependencies:
"@ethersproject/abstract-signer" "^5.7.0"
"@ethersproject/basex" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/pbkdf2" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/sha2" "^5.7.0"
"@ethersproject/signing-key" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@ethersproject/transactions" "^5.7.0"
"@ethersproject/wordlists" "^5.7.0"
"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360"
integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==
dependencies:
"@ethersproject/abstract-signer" "^5.7.0"
"@ethersproject/address" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/hdnode" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/pbkdf2" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/random" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@ethersproject/transactions" "^5.7.0"
aes-js "3.0.0"
scrypt-js "3.0.1"
"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a"
integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==
dependencies:
"@ethersproject/bytes" "^5.7.0"
js-sha3 "0.8.0"
"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892"
integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==
"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6"
integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==
dependencies:
"@ethersproject/logger" "^5.7.0"
"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102"
integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/sha2" "^5.7.0"
"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30"
integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==
dependencies:
"@ethersproject/logger" "^5.7.0"
"@ethersproject/providers@5.7.2":
version "5.7.2"
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb"
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
dependencies:
"@ethersproject/abstract-provider" "^5.7.0"
"@ethersproject/abstract-signer" "^5.7.0"
"@ethersproject/address" "^5.7.0"
"@ethersproject/base64" "^5.7.0"
"@ethersproject/basex" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/hash" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/networks" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/random" "^5.7.0"
"@ethersproject/rlp" "^5.7.0"
"@ethersproject/sha2" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@ethersproject/transactions" "^5.7.0"
"@ethersproject/web" "^5.7.0"
bech32 "1.1.4"
ws "7.4.6"
"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c"
integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304"
integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb"
integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
hash.js "1.1.7"
"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3"
integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
bn.js "^5.2.1"
elliptic "6.5.4"
hash.js "1.1.7"
"@ethersproject/solidity@5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8"
integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/sha2" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2"
integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b"
integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==
dependencies:
"@ethersproject/address" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/rlp" "^5.7.0"
"@ethersproject/signing-key" "^5.7.0"
"@ethersproject/units@5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1"
integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==
dependencies:
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/constants" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/wallet@5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d"
integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==
dependencies:
"@ethersproject/abstract-provider" "^5.7.0"
"@ethersproject/abstract-signer" "^5.7.0"
"@ethersproject/address" "^5.7.0"
"@ethersproject/bignumber" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/hash" "^5.7.0"
"@ethersproject/hdnode" "^5.7.0"
"@ethersproject/json-wallets" "^5.7.0"
"@ethersproject/keccak256" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/random" "^5.7.0"
"@ethersproject/signing-key" "^5.7.0"
"@ethersproject/transactions" "^5.7.0"
"@ethersproject/wordlists" "^5.7.0"
"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0":
version "5.7.1"
resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae"
integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==
dependencies:
"@ethersproject/base64" "^5.7.0"
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5"
integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==
dependencies:
"@ethersproject/bytes" "^5.7.0"
"@ethersproject/hash" "^5.7.0"
"@ethersproject/logger" "^5.7.0"
"@ethersproject/properties" "^5.7.0"
"@ethersproject/strings" "^5.7.0"
"@gar/promisify@^1.0.1":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
@@ -1588,6 +1930,11 @@ acorn@^8.11.0, acorn@^8.14.0, acorn@^8.4.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
aes-js@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d"
integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==
aes-js@4.0.0-beta.5:
version "4.0.0-beta.5"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873"
@@ -1786,6 +2133,11 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
bech32@1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9"
integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==
bfj@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.1.0.tgz#c5177d522103f9040e1b12980fe8c38cf41d3f8b"
@@ -1818,6 +2170,15 @@ bl@^4.0.3:
inherits "^2.0.4"
readable-stream "^3.4.0"
blake-hash@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/blake-hash/-/blake-hash-2.0.0.tgz#af184dce641951126d05b7d1c3de3224f538d66e"
integrity sha512-Igj8YowDu1PRkRsxZA7NVkdFNxH5rKv5cpLxQ0CVXSIA77pVYwCPRQJ2sMew/oneUpfuYRyjG6r8SmmmnbZb1w==
dependencies:
node-addon-api "^3.0.0"
node-gyp-build "^4.2.2"
readable-stream "^3.6.0"
blake2b-wasm@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz#9115649111edbbd87eb24ce7c04b427e4e2be5be"
@@ -1826,6 +2187,14 @@ blake2b-wasm@^2.4.0:
b4a "^1.0.1"
nanoassert "^2.0.0"
blake2b@^2.1.3:
version "2.1.4"
resolved "https://registry.yarnpkg.com/blake2b/-/blake2b-2.1.4.tgz#817d278526ddb4cd673bfb1af16d1ad61e393ba3"
integrity sha512-AyBuuJNI64gIvwx13qiICz6H6hpmjvYS5DGkG6jbXMOT8Z3WUJ3V1X0FlhIoT1b/5JtHE3ki+xjtMvu1nn+t9A==
dependencies:
blake2b-wasm "^2.4.0"
nanoassert "^2.0.0"
blakejs@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814"
@@ -1836,6 +2205,16 @@ bluebird@^3.7.2:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
bn.js@^4.11.9:
version "4.12.1"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.1.tgz#215741fe3c9dba2d7e12c001d0cfdbae43975ba7"
integrity sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==
bn.js@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
body-parser@1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
@@ -1876,6 +2255,11 @@ braces@^3.0.3, braces@~3.0.2:
dependencies:
fill-range "^7.1.1"
brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==
browser-stdout@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
@@ -2113,6 +2497,16 @@ circomlib@^2.0.5:
resolved "https://registry.yarnpkg.com/circomlib/-/circomlib-2.0.5.tgz#183c703e53ed7d011811842dbeeeb9819f4cc1d6"
integrity sha512-O7NQ8OS+J4eshBuoy36z/TwQU0YHw8W3zxZcs4hVwpEll3e4hDm3mgkIPqItN8FDeLEKZFK3YeT/+k8TiLF3/A==
circomlibjs@^0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/circomlibjs/-/circomlibjs-0.1.7.tgz#9f5a7d9a23323744b11ee456b05b0cd81f48b554"
integrity sha512-GRAUoAlKAsiiTa+PA725G9RmEmJJRc8tRFxw/zKktUxlQISGznT4hH4ESvW8FNTsrGg/nNd06sGP/Wlx0LUHVg==
dependencies:
blake-hash "^2.0.0"
blake2b "^2.1.3"
ethers "^5.5.1"
ffjavascript "^0.2.45"
clean-stack@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
@@ -2502,6 +2896,19 @@ electron-to-chromium@^1.5.73:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.90.tgz#4717e5a5413f95bbb12d0af14c35057e9c65e0b6"
integrity sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==
elliptic@6.5.4:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
dependencies:
bn.js "^4.11.9"
brorand "^1.1.0"
hash.js "^1.0.0"
hmac-drbg "^1.0.1"
inherits "^2.0.4"
minimalistic-assert "^1.0.1"
minimalistic-crypto-utils "^1.0.1"
emoji-regex@^10.3.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4"
@@ -2789,6 +3196,42 @@ ethereum-cryptography@^2.0.0:
"@scure/bip32" "1.4.0"
"@scure/bip39" "1.3.0"
ethers@^5.5.1:
version "5.7.2"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
dependencies:
"@ethersproject/abi" "5.7.0"
"@ethersproject/abstract-provider" "5.7.0"
"@ethersproject/abstract-signer" "5.7.0"
"@ethersproject/address" "5.7.0"
"@ethersproject/base64" "5.7.0"
"@ethersproject/basex" "5.7.0"
"@ethersproject/bignumber" "5.7.0"
"@ethersproject/bytes" "5.7.0"
"@ethersproject/constants" "5.7.0"
"@ethersproject/contracts" "5.7.0"
"@ethersproject/hash" "5.7.0"
"@ethersproject/hdnode" "5.7.0"
"@ethersproject/json-wallets" "5.7.0"
"@ethersproject/keccak256" "5.7.0"
"@ethersproject/logger" "5.7.0"
"@ethersproject/networks" "5.7.1"
"@ethersproject/pbkdf2" "5.7.0"
"@ethersproject/properties" "5.7.0"
"@ethersproject/providers" "5.7.2"
"@ethersproject/random" "5.7.0"
"@ethersproject/rlp" "5.7.0"
"@ethersproject/sha2" "5.7.0"
"@ethersproject/signing-key" "5.7.0"
"@ethersproject/solidity" "5.7.0"
"@ethersproject/strings" "5.7.0"
"@ethersproject/transactions" "5.7.0"
"@ethersproject/units" "5.7.0"
"@ethersproject/wallet" "5.7.0"
"@ethersproject/web" "5.7.1"
"@ethersproject/wordlists" "5.7.0"
ethers@^6.13.4:
version "6.13.5"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4"
@@ -2967,7 +3410,7 @@ ffjavascript@0.3.1, ffjavascript@^0.3.0:
wasmcurves "0.2.2"
web-worker "1.2.0"
ffjavascript@^0.2.48, ffjavascript@^0.2.56:
ffjavascript@^0.2.45, ffjavascript@^0.2.48, ffjavascript@^0.2.56:
version "0.2.63"
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.2.63.tgz#0c1216a1f123dc9181df69e144473704d2f115eb"
integrity sha512-dBgdsfGks58b66JnUZeZpGxdMIDQ4QsD3VYlRJyFVrKQHb2kJy4R2gufx5oetrTxXPT+aEjg0dOvOLg1N0on4A==
@@ -3393,6 +3836,14 @@ has-unicode@^2.0.1:
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3:
version "1.1.7"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
dependencies:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
@@ -3405,6 +3856,15 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==
dependencies:
hash.js "^1.0.3"
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
homedir-polyfill@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8"
@@ -4251,6 +4711,16 @@ mimic-response@^3.1.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
minimalistic-crypto-utils@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==
minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -4424,11 +4894,21 @@ node-abi@^3.3.0:
dependencies:
semver "^7.3.5"
node-addon-api@^3.0.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
node-addon-api@^7.0.0:
version "7.1.1"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
node-gyp-build@^4.2.2:
version "4.8.4"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8"
integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==
node-gyp@8.x:
version "8.4.1"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"
@@ -5055,6 +5535,11 @@ safe-regex-test@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
scrypt-js@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312"
integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==
semver@^5.5.0:
version "5.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
@@ -5929,6 +6414,20 @@ viem@^2.21.57:
ox "0.6.7"
ws "8.18.0"
viem@^2.23.2:
version "2.23.2"
resolved "https://registry.yarnpkg.com/viem/-/viem-2.23.2.tgz#db395c8cf5f4fb5572914b962fb8ce5db09f681c"
integrity sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==
dependencies:
"@noble/curves" "1.8.1"
"@noble/hashes" "1.7.1"
"@scure/bip32" "1.6.2"
"@scure/bip39" "1.5.4"
abitype "1.0.8"
isows "1.0.6"
ox "0.6.7"
ws "8.18.0"
vite-node@3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.0.5.tgz#6a0d06f7a4bdaae6ddcdedc12d910d886cf7d62f"
@@ -6143,6 +6642,11 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
ws@7.4.6:
version "7.4.6"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
ws@8.17.1:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"