project screenshot 1
project screenshot 2
project screenshot 3
project screenshot 4
project screenshot 5

commbank.eth

commbank.eth aims to have the UX of Infinex, with the privacy of Railgun.

Project Description

Commbank.eth - the bank you don't have to trust.

Commbank.eth allows for easy sending and receiving of both public and private ERC20s, by utilising Zero Knowledge circuits written with Noir and compiled to Solidity.

No data ever leaves the browser, except to send transactions and balance checks. All secrets and private balance proofs are stored in the browsers IndexDB, which is guarded by PassKey authentication.

Commbank.eth is able to do this by utilising quite a few cryptographic techniques, mainly:

  • an EVM address and an RSA key pair is created and saved in the browsers indexdb based on a secret stored in the users pass key
  • when a user wishes to deposit to the privacy pool, they create a note hash to add to the contract. A note contains:

asset id (address) amount of asset owner public key secret value (random 32 bytes)

we call our leaf nodes in the tree note commitments, and these leaf hashes are given by:

note_commitment = hash(asset_id, amount, owner, secret).

the NoteVerifier circuit ensures that a user can't falsely inflate their asset amount, and that note_commitment is created correctly (without revealing any details about it). The solidity around the verifier handles the transfer of the erc20.

now that a user has deposited into the private pool, they probably want to send some funds privately. any time a user wishes to receive a private transaction, they give the sender:

pub_key_a = keccak(keccak(rsa.private_key)); pub_key_b = rsa.public_key

pub_key_a is used within ZK proofs to verify ownership of a note. pub_key_b is used to decrypt secrets that the sender attaches to their masked transactions.

The sender takes these 2 pub keys, and creates the following:

input notes

input notes contain all of the fields of regular notes, plus some others that are involved in proving. namely:

  • merkle root: a merkle root in the trees recent history (last ~100 inserts)
  • leaf index: the index of the note being spent in the tree
  • merkle path + values: the steps needed to recreate the known merkle root
  • owner_secret: keccak(rsa.private_key), this is used to prove that they own the note

output notes

output notes are the notes that this transfer is creating, and is very similar to the deposit process in terms of flow. the sum of the output notes assets MUST always equal to sum of the input notes assets, otherwise the proof if not valid. value in the pools can only be added to by depositing, or decreased by withdrawing.

nullifiers

a cryptographic commitment that allows for marking of notes as spent, without revealing which note is spent. these are recorded in the solidity contract so that double spends are not possible.

encrypted payloads

contains (note_secret, asset_id, amount) as one long string, which is encrypted with the receivers pub_key_b. By having this on the contract, this means that no middleman is required to relay messages, and if the user keeps their RSA keypair, they can always decrypt the history just by checking the logs on the chain.

once Alice transfers Bob and Bob:

  • gets the encrypted payload
  • records the secret, amount and asset

Bob is now free to withdraw out of the privacy pool. He can do this by utilising the Withdraw flow. This works pretty similarly to spending, however there are no output notes. Input notes are checked to ensure that they're correct, they're nullifier, and then the total of the input notes is transferred to Bob as the asset in ERC20 form.

How it's Made

The meat and potatoes of the project is 'CommbankDotEth.sol'. It is a contract that allows for:

  • users to deposit any ERC20 to an encrypted account
  • users to send encrypted balances to other encrypted accounts
  • users to swap their encrypted from public -> private or vice versa whenever they please.

CommbankDotEth.sol makes use of 3 Noir Honk Verifier contracts, DepositVerifier.sol, TransactVerifier.sol and WithdrawVerifier.sol to implement the Zero Knowledge proofs required for this kind of system to work, and each are all tested in the circuits/ directory in the repo.

A cool feature of CommbankDotEth.sol is it's note sharing model. When a user transacts, they encrypt their output note(s) with the notes recipients RSA public key. The contract emits this as a 'payload' field as calldata, which the front end can monitor. If the front end can successfully decrypt a payload field, it can store the details of this note in it's indexDB to use later, as well as displaying it as part of the users private balances.

One unique feature of commbank.eth is it's use of keccak256 for merkle tree creation and verification, versus other ZK circuits that use ZK friendly hashes (which are more expensive to compute in the EVM). By doing this, it makes verification cost cheaper for this kind of system, as it is much cheaper to keccak() hash in the EVM, than it is for a ZK friendly hash like pederson or poseidon. In gas estimates I was able to get a transfer for 2.2m gas, which is expensive, but is getting towards the lower end of cost for private transfers.

Unfortunately the most hacky part of this project is the incomplete front end. It's a pretty simple NextJs app, built with Shadcn and tailwind. It looks good and the passkey auth is legit, but I really wanted to be able to get a private transaction to be able to go through just by giving approval with a finger print scan (or face scan on iphone). I'm not far off, but it was just not deliverable in this hackathon timeframe.

background image mobile

Join the mailing list

Get the latest news and updates