mirror of
https://github.com/0xbow-io/privacy-pools-core.git
synced 2026-01-09 17:37:58 -05:00
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:
committed by
GitHub
parent
c0d80bcf6f
commit
646e5508ce
54
.github/workflows/contracts.yml
vendored
54
.github/workflows/contracts.yml
vendored
@@ -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
|
||||
|
||||
3
packages/contracts/.gitignore
vendored
3
packages/contracts/.gitignore
vendored
@@ -23,6 +23,7 @@ crytic-export
|
||||
|
||||
# Echidna corpus
|
||||
test/invariants/fuzz/echidna_coverage
|
||||
*/**/corpus
|
||||
|
||||
# Coverage files
|
||||
lcov.info
|
||||
lcov.info
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
1
packages/contracts/slither_results.json
Normal file
1
packages/contracts/slither_results.json
Normal file
File diff suppressed because one or more lines are too long
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import {FuzzTest} from './FuzzTest.t.sol';
|
||||
|
||||
contract ForgeReproducer is FuzzTest {}
|
||||
19
packages/contracts/test/invariants/fuzz/FuzzTest.t.sol
Normal file
19
packages/contracts/test/invariants/fuzz/FuzzTest.t.sol
Normal 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');
|
||||
}
|
||||
}
|
||||
68
packages/contracts/test/invariants/fuzz/Setup.t.sol
Normal file
68
packages/contracts/test/invariants/fuzz/Setup.t.sol
Normal 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
9
packages/contracts/test/invariants/fuzz/external/Iden3PoseidonBytecodes.sol
vendored
Normal file
9
packages/contracts/test/invariants/fuzz/external/Iden3PoseidonBytecodes.sol
vendored
Normal file
File diff suppressed because one or more lines are too long
28
packages/contracts/test/invariants/fuzz/external/generate_iden3_poseidon.js
vendored
Normal file
28
packages/contracts/test/invariants/fuzz/external/generate_iden3_poseidon.js
vendored
Normal 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!");
|
||||
@@ -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)});
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
40
packages/contracts/test/invariants/fuzz/helpers/Actors.sol
Normal file
40
packages/contracts/test/invariants/fuzz/helpers/Actors.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
592
packages/contracts/test/invariants/fuzz/helpers/FuzzUtils.sol
Normal file
592
packages/contracts/test/invariants/fuzz/helpers/FuzzUtils.sol
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
508
yarn.lock
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user