Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a22e2b8
add openzeppelin libs
parketh Oct 16, 2025
a2303a2
chore: forge fmt
parketh Oct 16, 2025
195f159
fmt + inline comments
parketh Oct 20, 2025
afd8ca8
chore: add remappings
parketh Oct 20, 2025
b2506eb
feat: Hasher lib
parketh Oct 20, 2025
6af5c68
add hasher lib tests
parketh Oct 20, 2025
33e9d9b
chore: inline comments
parketh Oct 20, 2025
bb90e6e
remove todo
parketh Oct 20, 2025
08c5a45
test: MerkleTree lib
parketh Oct 20, 2025
c62fe8d
chore: refactor utils
parketh Oct 20, 2025
d410bdd
feat: add circuits
parketh Oct 21, 2025
a9c91c1
add proof gen + verification tests
parketh Oct 21, 2025
f4fdcdb
refactor proof
parketh Oct 21, 2025
4a89b0d
add withdraw tests
parketh Oct 21, 2025
80cfc95
add remaining ETHPrivateBank tests
parketh Oct 22, 2025
6780755
chore: fmt + lint
parketh Oct 22, 2025
bb0d34d
update tests
parketh Oct 22, 2025
8a24f6b
add scripts and deploy contracts
parketh Oct 22, 2025
c622100
chore: fmt
parketh Oct 22, 2025
01563be
refactor Register -> Backup
parketh Oct 22, 2025
ba04b20
test: add Backup tests + utils
parketh Oct 22, 2025
61b3d4d
rm unused timestamp
parketh Oct 22, 2025
03d7ffa
update readme
parketh Oct 22, 2025
1a35455
remove conflicting types
parketh Oct 22, 2025
e28ac89
refactor gen fn
parketh Oct 22, 2025
766a5bf
refactor env
parketh Oct 22, 2025
bae5d64
setup transfer script
parketh Oct 22, 2025
5184ae0
fix height + redeploy contracts
parketh Oct 23, 2025
d030282
backup leafIndex
parketh Oct 23, 2025
b54d486
fix initial root bug
parketh Oct 24, 2025
0c563d0
deploy new contract
parketh Oct 24, 2025
b6978f7
redeploy
parketh Oct 24, 2025
13de7af
update readme instructions
parketh Oct 24, 2025
c6af52a
Merge branch 'master' into feat/private-bank-contracts
parketh Oct 24, 2025
b7bd358
fix ci + format
parketh Oct 24, 2025
b08ae1e
formatting
parketh Oct 24, 2025
4d48671
remove ci for push
parketh Oct 24, 2025
78ac747
fix ci
parketh Oct 24, 2025
c8329b2
fix ci
parketh Oct 24, 2025
83f336f
fix test
parketh Oct 24, 2025
b2844c8
fix ci
parketh Oct 24, 2025
f7762a1
simplify ci
parketh Oct 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
WALLET_PRIVATE_KEY=
SIMULATE=
22 changes: 15 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ permissions:

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

Expand All @@ -13,7 +14,7 @@ env:

jobs:
check:
name: Foundry project
name: Build contracts and run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
Expand All @@ -27,11 +28,18 @@ jobs:
- name: Show Forge version
run: forge --version

- name: Run Forge fmt
run: forge fmt --check
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '23'
cache: 'yarn'
cache-dependency-path: yarn.lock

- name: Install dependencies
run: yarn

- name: Run Forge build
run: forge build --sizes
- name: Check formatting
run: yarn format:check

- name: Run Forge tests
run: forge test -vvv
- name: Build contracts
run: yarn build:contract
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Node files
node_modules/

# Compiler files
cache/
out/
artifacts/

# Build files
build/

# Ignores development broadcast logs
!/broadcast
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
build
cache
lib
artifacts
out
node_modules
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"bracketSpacing": true,
"semi": false,
"printWidth": 100,
"tabWidth": 2
"tabWidth": 2,
"plugins": ["prettier-plugin-solidity"]
}
18 changes: 18 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[solidity]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"prettier.configPath": ".prettierrc",
"solidity.formatter": "prettier"
}
55 changes: 54 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
## Private Money

Private Money is a modernized fork of Tornado Cash allowing private money transfers on Base.
Private Money is a modernized fork of Tornado Cash allowing private money transfers on Base.

This repo contains the core contracts and circuits.

## Setup

1. Install npm dependencies

```bash
yarn
```

2. Install `circom` (note: requires `rust`)

```bash
# refer to latest instructions at: https://docs.circom.io/getting-started/installation/

# clone repo
git clone https://github.com/iden3/circom.git

# install circom
cargo build --release
cargo install --path circom
```

3. Install `snarkjs` (note: requires `node`)

```bash
npm install -g snarkjs
```

4. Download `ptau` file

We use prepared `.ptau` files from the [`privacy-ethereum/perpetualpowersoftau`](https://github.com/privacy-ethereum/perpetualpowersoftau) repo.

The `withdraw` circuit has 27.4k constraints, so we use the `ppot_0080_15.ptau` which has 32.7k points and is 36mb in size.

To download the file, run:

```bash
yarn setup
```

5. Build circuits and contracts

```bash
yarn build
```

7. Run tests

```bash
yarn test
```
53 changes: 53 additions & 0 deletions circuits/merkleTree.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
pragma circom 2.0.0;

include "../node_modules/circomlib/circuits/mimcsponge.circom";

// Hash two values using the MiMCSponge hash function
template MiMCSpongeHash() {
signal input left;
signal input right;
signal output hash;

component hasher = MiMCSponge(2, 220, 1);
hasher.ins[0] <== left;
hasher.ins[1] <== right;
hasher.k <== 0;
hash <== hasher.outs[0];
}

// if s == 0 returns [in[0], in[1]]
// if s == 1 returns [in[1], in[0]]
template DualMux() {
signal input in[2];
signal input s;
signal output out[2];

s * (1 - s) === 0;
out[0] <== (in[1] - in[0])*s + in[0];
out[1] <== (in[0] - in[1])*s + in[1];
}

// Verifies that merkle proof is correct for given merkle root and a leaf
// pathIndices input is an array of 0/1 selectors telling whether given pathElement is on the left or right side of merkle path
template MerkleTreeChecker(levels) {
signal input leaf;
signal input root;
signal input pathElements[levels];
signal input pathIndices[levels];

component selectors[levels];
component hashers[levels];

for (var i = 0; i < levels; i++) {
selectors[i] = DualMux();
selectors[i].in[0] <== i == 0 ? leaf : hashers[i - 1].hash;
selectors[i].in[1] <== pathElements[i];
selectors[i].s <== pathIndices[i];

hashers[i] = MiMCSpongeHash();
hashers[i].left <== selectors[i].out[0];
hashers[i].right <== selectors[i].out[1];
}

root === hashers[levels - 1].hash;
}
64 changes: 64 additions & 0 deletions circuits/withdraw.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
pragma circom 2.0.0;

include "../node_modules/circomlib/circuits/bitify.circom";
include "../node_modules/circomlib/circuits/poseidon.circom";
include "merkleTree.circom";

// Computes Poseidon(nullifier + secret) and Poseidon(nullifier)
template CommitmentHasher() {
signal input nullifier;
signal input secret;
signal output commitment;
signal output nullifierHash;

component commitmentHasher = Poseidon(2);
component nullifierHasher = Poseidon(1);

commitmentHasher.inputs[0] <== nullifier;
commitmentHasher.inputs[1] <== secret;
nullifierHasher.inputs[0] <== nullifier;

commitment <== commitmentHasher.out;
nullifierHash <== nullifierHasher.out;
}

// Verifies that commitment that corresponds to given secret and nullifier is included in the merkle tree of deposits
template Withdraw(levels) {
signal input root;
signal input nullifierHash;
signal input recipient; // not taking part in any computations
signal input relayer; // not taking part in any computations
signal input relayerFee; // not taking part in any computations
signal input gasRefund; // not taking part in any computations
signal input nullifier;
signal input secret;
signal input pathElements[levels];
signal input pathIndices[levels];

component hasher = CommitmentHasher();
hasher.nullifier <== nullifier;
hasher.secret <== secret;
hasher.nullifierHash === nullifierHash;

component tree = MerkleTreeChecker(levels);
tree.leaf <== hasher.commitment;
tree.root <== root;
for (var i = 0; i < levels; i++) {
tree.pathElements[i] <== pathElements[i];
tree.pathIndices[i] <== pathIndices[i];
}

// Add hidden signals to make sure that tampering with recipient or fee will invalidate the snark proof
// Most likely it is not required, but it's better to stay on the safe side and it only takes 2 constraints
// Squares are used to prevent optimizer from removing those constraints
signal recipientSquare;
signal relayerFeeSquare;
signal relayerSquare;
signal gasRefundSquare;
recipientSquare <== recipient * recipient;
relayerFeeSquare <== relayerFee * relayerFee;
relayerSquare <== relayer * relayer;
gasRefundSquare <== gasRefund * gasRefund;
}

component main {public [root, nullifierHash, recipient, relayer, relayerFee, gasRefund]} = Withdraw(24);
2 changes: 2 additions & 0 deletions constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const HEIGHT = 24
export const ZERO = '0x29319238daf40223d6021718c846ac2a0c0ef028ecc765972e999a8ac79662a8'
61 changes: 61 additions & 0 deletions eslint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import eslint from '@eslint/js'
import tseslint from 'typescript-eslint'
import prettier from 'eslint-config-prettier'

export default tseslint.config(
// Ignore patterns
{
ignores: [
'node_modules/',
'build/',
'out/',
'dist/',
'artifacts/',
'cache/',
'typechain/',
'typechain-types/',
'coverage/',
'lib/',
'*.config.js',
],
},

// Base config for all files
eslint.configs.recommended,

// TypeScript-specific config
...tseslint.configs.recommended,

// Custom rules
{
languageOptions: {
ecmaVersion: 2020,
sourceType: 'module',
parserOptions: {
project: './tsconfig.json',
},
globals: {
node: true,
mocha: true,
es2020: true,
},
},
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
'@typescript-eslint/explicit-module-boundary-types': 'off',
'no-console': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
'no-unused-expressions': 'off',
},
},

// Prettier must be last to override other configs
prettier,
)
8 changes: 8 additions & 0 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"lib/openzeppelin-contracts": {
"tag": {
"name": "v5.4.0",
"rev": "c64a1edb67b6e3f4a15cca8909c9482ad33a02b0"
}
}
}
3 changes: 3 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
src = "src"
out = "out"
libs = ["lib"]
optimizer = true
optimizer_runs = 100000
via_ir = false

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
24 changes: 24 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { HardhatUserConfig } from 'hardhat/config'
import '@nomiclabs/hardhat-ethers'
import '@nomiclabs/hardhat-waffle'

const config: HardhatUserConfig = {
solidity: {
version: '0.8.24',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
viaIR: false,
},
},
paths: {
sources: './src',
tests: './test',
cache: './cache',
artifacts: './artifacts',
},
}

export default config
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at c64a1e
Loading