# zkBob

A stablecoin-based privacy wallet build for everyday users

<figure><img src="/files/H2wS6FbqHHmy0btd9r8g" alt=""><figcaption></figcaption></figure>

{% hint style="danger" %}
zkBob is now in withdrawal only mode due to a vulnerability discovered on 05 June, 2026. If you hold any remaining funds in a zkBob account in any pool, go to <https://app.zkbob.com/> to start the withdrawal process.

* [Vulnerability Post Mortem with additional details](/zkbob-overview/vulnerability-post-mortem)
  {% endhint %}

## zkBob Basics

> :man: [zkBob App](https://app.zkbob.com/)\
> :man\_swimming: [Info and Instructions](/zkbob-app/zkbob-app)\
> :person\_running:[Technical Details & Contracts](/implementation/high-level-overview)\
> 🔐 [Security Audit](/resources/security-audit)<br>

## About

[zkBob](https://app.zkbob.com) is a stablecoin-based privacy application deployed on Polygon and Optimism and designed for everyday users and [common use cases](/zkbob-overview/basic-concepts/use-cases). zkBob uses [zkSNARKS](/implementation/zksnarks-and-circuits) to anonymize senders, receivers, and amounts when transferring stable funds. [Compliance features](/zkbob-overview/compliance-and-security) deter bad actors and illicit usage, giving privacy and safety back to ordinary blockchain users.

zkBob now supports multiple tokens, **including USDC on Polygon, USDC and ETH on Optimism and USDT on Tron**. Once tokens are deposited into the zkBob pool, participants can transfer any amount\* amongst themselves in a private, secure manner without needing to connect MetaMask, WalletConnect or any web3 wallet.

<figure><img src="/files/5gnxyIuyM1vebLbvccvY" alt=""><figcaption></figcaption></figure>

When a transfer is initiated, the amount and recipient is never disclosed or published. Transactions are routed through a [relayer](/implementation/relayer-node), abstracting gas fees while providing an efficient transfer environment.&#x20;

Deposits, transfers and withdrawals are all processed on the chain where the app is deployed, with standardized gas fees ($0.10 per tx on MATIC, variable on Optimism) paid using the supported token.

* The [zkBob application](https://app.zkbob.com/) is multichain (**Polygon, Optimism, Tron**) to utilize existing infrastructure (Uniswap v3, Aave, and native USDC), prioritize scalability, and support their commitment to zk-based solutions.

*\*zkBob introduces* [*deposit and withdrawal limits*](/zkbob-overview/deposit-and-withdrawal-limits) *and other* [*compliance features*](/zkbob-overview/compliance-and-security) *to keep the application and its users safe. Transfers are limited by these pool constraints.*&#x20;

## Project Landing Pages

* zkBob application: <https://zkbob.com/>
* Private payment links: <https://zkbob.com/payment-link>

## Newsletter & Blog

[Sign up for the zkBob Blog](https://blog.zkbob.com/) to receive the latest updates about zkBob.


# Basic Concepts

{% hint style="info" %}
Below are descriptions of basic concepts underlying zkBob functionality. For more thorough details, see the [technical implementation ](/implementation/high-level-overview) section.
{% endhint %}

### zkBob

zkBob is a zk-based application built for private transfers. It accepts  stablecoins including USDC and USDT for deposit, as well as ETH on Optimism.Once deposited in the zkBob app, tokens can be transferred anonymously between participants. They can then be withdrawn from the application to an EOA (externally owned address), preserving anonymity of the transfer amounts and participants.

### Zero-Knowledge Proofs (zkSNARKs)

zkBob uses zkSNARKS zero-knowledge proofs to verify an action has been completed (deposit, transfer, withdrawal) without providing any other identifying details about that action. zkSNARKs confirm that the chain state has changed without divulging information about amounts, initiators, or receivers of transactions. [Learn more about zero-knowledge proofs.](https://vitalik.ca/general/2021/01/26/snarks.html)

### zkBob Account

A zkBob account is linked to a user via a private key. The private key is used to identify the account balance, generate proofs to transfer tokens, and withdraw tokens from the pool.&#x20;

zkBob accounts are created either from the private key of an existing MetaMask/WalletConnect wallet or from a stand-alone secret phrase. Users can decide how they would like to create an account following a step-by-step process.

Accounts never appear unencrypted in a public field and can only be decrypted by the account owner.

### zkBob Address

A zkBob account doesn't contain a fixed address for receiving funds. Rather, the user generates a private zkBob address using the application. Through the UI, a new address is generated for each incoming transaction.&#x20;

It is not possible to link different private addresses to one another or to the primary account. Only the account owner can confirm ownership of a private address.

Each created address is encoded in base58 format. For example `5fkW3dXTvA8Kizt1EbuRyjWofuqR4Ud1YTjGgY1r8nGosDeSaUreq6bwfF61jWL`

### **Deposits**

Deposits can be made by sending  tokens (from a regular 0x-based account) to the zkBob Pool contract. Depositors complete 2 steps to get started.&#x20;

1. Approve the contract to access funds.
2. Create and send the deposit.&#x20;

Transactions are routed via a relayer to the pool contract then deposited into a zkAccount.

### **Transfers**

Transfers also use relayers to send private transactions. A user can transfer funds to another zkBob account by sending a zk-proof anonymously to a relayer. The relayer then publishes the transaction to the zkBob contract.

### Withdrawals

Similar to deposits, a user can send a transaction to the zkBob contract to withdraw tokens from the pool. The transaction contains a zero-knowledge proof of token ownership generated using the private key associated with the corresponding zkBob account.

### Relayer

The relayer acts as an intermediary between the user and the smart contracts. Transactions are sent to the relayer which collects transactions, calculates Merkle tree proofs, orders them in a queue, then sends this information to the contract. The relayer cannot see transaction amounts and preserves anonymity by abstracting gas fees for operations. Relayer interaction occurs via a [REST API](/implementation/relayer-node/rest-api).

### Base Layer Neutrality

zkBob stands on the pillars of privacy, anti-censorship, and decentralization and seeks to prevent misuse of the protocol through built-in compliance measures.&#x20;

By enacting these measures, the protocol is designed to function on a neutral base layer. This means that participants on the base layer (validators, sequencers, relayers etc) can operate normally without taking additional measures (like censoring sanctioned addresses) to properly write and order on-chain transactions. [Learn more about base layer neutrality here](https://www.paradigm.xyz/2022/09/base-layer-neutrality).

### BOB

BOB is the name of the multi-chain, multi-collateral stablecoin optimized for the zkBob protocol. It is no longer in use in the protocol. You can access previous details about BOB [here](https://bob-docs.zkbob.com/).


# Getting Started

10 steps to establishing yourself as an early supporter and expert user of zkBob and BOB

To familiarize yourself with the protocol and become an early adopter and experienced zkBob supporter, follow these 10 simple tasks!

1. Follow us on [Twitter](https://twitter.com/zkBob_).
2. Join our [Community Telegram](https://t.me/zkbobcommunity).
3. [Sign up for our Blog](https://blog.zkbob.com/). Get up to date info and Alpha right to your email, before it's released to the general public.
4. [Checkout our Dappback campaign](https://dappback.com/zkbob). This will get you familiar with the product and walk you through various quests to learn and interact with zkBob. You receive a special NFT for participating.
5. [Create a zkBob zkAccount](/zkbob-app/account-creation). Use your MetaMask or WalletConnect wallet, or create a standalone zkAccount on the zkBob deployments on Polygon or Optimism.
6. [Deposit](/zkbob-app/deposits) into your account. Connect your wallet to deposit. The transaction will show the wallet that deposited to the zkBob contract, but will not include any details about your zkAccount.
7. [Transfer](/zkbob-app/transfers) to a friend. All transfers within the zkBob application are private, and do not show the amount or recipient to anyone other than the sender and receiver of the transaction.
8. [Withdraw ](/zkbob-app/withdrawals)to a new or existing address. The wallet receiving a withdrawal will show the withdrawal originated from the zkBob application, but no information about who sent it or the depositing source.
9.


# Open-Source and Decentralized

Several features make zkBob a resilient solution for the community:

1. The [zkBob repo](https://github.com/zkBob) is open-source, including the zkBOB application UI, and licensed under the [MIT License](https://github.com/zkBob/zkbob-ui/blob/main/LICENSE_MIT) and [Apache-2.0 License](https://github.com/zkBob/zkbob-ui/blob/main/LICENSE_APACHE).
2. Anyone can [contribute to the codebase](https://github.com/zkBob/zkbob-ui/blob/main/CONTRIBUTING.md).
3. zkBob.com is hosted on [several IPFS mirrors](https://linktr.ee/zkbob). Anyone can host the website or application, follow the [README](https://github.com/zkBob/zkbob-ui#deploy-to-ipfs-using-dappling) for instructions.


# Multichain Deployment

zkBob is Multichain for several reasons.

## Why Multichain?

Chains with a **mature and robust infrastructure** support the needs of the zkBob application. Useful applications include:

* Uniswap v3
* Aave
* Native USDC implementation

Polygon and Optimism both support these applications and are committed to protecting privacy and neutrality for the Ethereum ecosystem. zkBob is also available on Tron.

&#x20;Future governance decisions may create additional deployments in other on-chain environments in the future.


# Usage Statistics

Deposit and Withdrawal Statistics

Up to date deposit and withdrawal statistics can be monitored via a Dune Analytics dashboard located at <https://dune.com/zkbob/zkbob>.  Stats provide a big picture view of zkBob usage but cannot, by nature of the zk mechanisms in place, contain any transfer information (senders, receivers, amounts transferred).&#x20;

Statistics are tracked on multiple chains and include:

* Current Pool Size
* Deposits: Total amount deposited, number of deposits, number of unique depositors
* Withdrawals: Total amount withdrawn, number of withdrawals, number of unique withdrawers
* Daily Usage (deposits and withdrawals)

<figure><img src="/files/L4E078gTP9LGPRrZleGW" alt=""><figcaption></figcaption></figure>

&#x20;


# Use Cases

zkBob can be used to protect transactional privacy for a variety of use cases. Businesses, philanthropists, DAOs, and individuals can all benefit from zkBob privacy features while maintaining compliance. zkBob is already in use by several projects for stable salary payments. Sample use cases include:

* Businesses who want to [pay salaries in stablecoins](/zkbob-overview/basic-concepts/use-cases/employee-salary) without displaying amounts sent to different employees.
* Business owners who do not want to [divulge vendor addresses or prices they paid for items](/zkbob-overview/basic-concepts/use-cases/vendor-purchasing) to reduce competitive risk.
* Philanthropists who want to donate to causes anonymously.
* Everyday users who want a "digital cash" option to transfer and trade for goods and services with one another privately through a shielded pool.


# Employee Salary

Businesses often prefer to keep salary information private. This creates issues for some businesses who want to use blockchain for payments but also want to keep salary information confidential. With zkBob, employers can keep information private and use the blockchain to send salaries.&#x20;

Several prominent projects in the ecosystem are already using zkBob for salary payments. These [Tier 1](/zkbob-overview/deposit-and-withdrawal-limits#tiered-limits) projects are able to deposit higher amounts into the protocol for sending salary to known employees of the business. Since employees are KYC'd by their employers, this KYC is automatically inherited into the protocol.

Multi-sender functionality and integrations with projects like [Request Finance](https://request.network/en/) make salary processing even easier as more users onboard to zkBob.

## Generic Use Case: Bob :man\_office\_worker: the Business Employer

<figure><img src="/files/fqufzIc8gv07OKPuacbF" alt=""><figcaption><p>Bob pays his employees through zkBob for stability and privacy purposes.</p></figcaption></figure>

Bob has 5 employees (Alice, Carlos, Dave, Erin and Frank) he pays monthly with crypto. Bob’s preference is to keep salaries private while still processing them on-chain and using a stable asset for payment. zkBob makes it easy.

1. Bob and his employees each create their own zkBob zkAccounts, using either a secret phrase or web3 wallet private key.
2. Bob deposits USDC or BOB into the zkBob application (depending on his pool selection).
3. Alice, Carlos, Dave, Erin and Frank each send Bob their zkAddress in a private DM on Slack.
4. Bob transfers USDC or BOB to each employee for their monthly salary.
5. Once received (or at a later time to improve anonymity) Alice, Carlos, Dave, Erin and Frank choose to withdraw any amount of USDC into a 0x wallet on Polygon. This can be a brand new address (which is also sent 0.1 MATIC for txs) or an established 0x address.
6. Alice, Carlos, Dave, Erin and Frank can convert their BOB or USDC to a coin/token of their choice, and use however they wish (ie send to an exchange, bank, pay with crypto etc). &#x20;
7. The history tab within each zkBob account makes it easy for employees to track and report their own transfers and withdrawals.
8. The process is repeated monthly, with employees DM’ing Bob a newly generated zkAddress at the beginning of each month.&#x20;

## Testimonials: What zkBob payroll users are saying

Employees and employers are anonymous, but here is some feedback we've received.

> “I like getting my salary through zkBob. I can withdraw what I need to Polygon. Every month I withdraw enough to pay the bills then save the rest in zkBob for whenever I need it.”&#x20;
>
> -zkEmployee&#x20;

> “I don’t want the whole world to know how much money I make. zkBob keeps my salary private.”&#x20;
>
> -zkEmployee&#x20;

> “We want to pay our employees with a stablecoin while keeping salary information confidential. Using zkBob checks all the boxes for fast payments, ease-of-use, low fees and protection for our valued employees.”&#x20;
>
> -zkEmployer


# Vendor Purchasing

Businesses often need to keep the details of their purchases confidential to maintain a competitive advantage. This includes information about the vendors they use and the prices they pay for wholesale goods. With zkBob, these details can be kept private, while transactions can still be referenced as needed for record keeping purposes.

## Generic Use Case: **Alice** 🐇 the Comic Book Collector&#x20;

<figure><img src="/files/M8jnOpS6P9q6ipyf5ReQ" alt=""><figcaption><p>Alice uses blockchain for her comic book business</p></figcaption></figure>

Alice runs a small business buying and selling comic books. She does a lot of business on the blockchain, but prefers to keep the details of her purchases, sales, and customers private. Luckily, she finds zkBob!

1. Alice uses 100 USDC to purchase 100 BOB stable tokens on Uniswap v3 on Polygon **(Note Alice can just use USDC now and doesn't need BOB)**.
2. She creates a zkBob account using the private key from an existing Ethereum Externally Owned Account (EOA). She could also create directly from a seed phrase, but chooses to create using MetaMask 🦊.
3. Once the account is connected, Alice deposits her 100 USDC into the zkBob pool.
4. Alice wants to buy an original Marvel comic from Carl.  She messages Carl and he sends her a private address generated in the zkBob app. Alice transfers 50 BOB to Carl within the pool and he mails the Marvel.
5. She is also owed some funds from Dave for a Ghost Rider. She sends him a generated address and he sends her 120 BOB. Transfers are catalogued in the UI for her own private records, but there are no details of the sender, receipient or amounts available publicly.
6. Alice is ready to withdraw, but she waits a few days to allow the anonymity set to grow. Alice then orders a withdrawal to a newly generated EOA.&#x20;
7. After the withdrawal, Alice has a new 0x account with 170 BOB and 0.10 MATIC for transactions. She converts the BOB back to tokens of her choice (Eth, USDC, DAI etc) on Uniswap. There is no way to determine who bought or sold the comics and the prices that were paid.

Alice, Carl and Dave are able to preserve their anonymity and grow their comic collections safely and securely, thanks to zkBob!


# Development Timeline

zkBob has been in research and development since December 2020. The original concept was initiated by members of the now defunct xDai research group and the [ZeroPool](https://zeropool.network/) team. Chains with robust infrastructure led contributors to select Polygon and then Optimism for deployment.

## zkBob Timeline

* December 2020: R\&D initiated.
* June 2021: Prototyping and ongoing contract development on xDai.
* December 2021: New contributors improve contracts, implement applications, prepare Kovan deployment.
* March 2022: Testing, bug fixing, protocol updates and enhancements.
* August 2022:  Prep for beta launch and trusted ceremony, select Polygon for initial deployment.
* April 2023: Second deployment on Optimism, making zkBob a multichain app.
* July 2023: First ETH pool deployed on Optimism.
* August 2023: [Payment Link](https://zkbob.com/payment-link) introduced.
* Ongoing: zkBob pools are self-sustaining and available on Polygon, Optimism and Tron. Periodic reviews are conducted to determine appropriate limits and other updates to the protocol.


# zk Privacy Solution Comparison

zkBob | zk.money | Railgun | Tornado.cash |

## How does zkBob compare to other privacy protocols?

zkBob provides everyday privacy to everyday users. Limits, compliant wallet checks, and other security features ensure that zkBob is a safe place for honest users to discreetly transfer stable value.&#x20;

Different privacy solutions offer different tradeoffs for users seeking anonymity on-chain. Below are some features that set zkBob apart.

<table><thead><tr><th width="138"> </th><th width="150">zkBob</th><th>Railgun</th><th>Secret Network</th><th width="119">zk.Money (sunset &#x26; no longer active)</th><th>Tornado.cash (pre-ban)</th></tr></thead><tbody><tr><td>Stable fees</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> <br>$0.10/tx on Polygon<br>$ dynamic stable fees on Optimism</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> <br>variable</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>variable</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> <br>variable</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>variable</td></tr><tr><td>Optional KYC</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> users can submit KYC to increase deposit limits.</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> <br>No KYC option for individuals</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> <br>No KYC option for individuals</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> <br>N/A</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>N/A</td></tr><tr><td>No additional wallet required</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> standalone transfers and withdrawals from app</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>requires wallet</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>requires wallet</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>requires wallet</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>requires wallet</td></tr><tr><td>No native tokens required</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> Tokens used for all interactions and pay for gas fees</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>required for shielding</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>SCRT required for fees</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> <br>required for withdrawal and deposit</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> <br>required for withdrawal/deposit</td></tr><tr><td>Multisender</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> <a href="https://mirror.xyz/0x6132eB883e88CD4E007552b871A6444Bfc34E837/mjYXeD7a005fdCu6dKdohfrSpcqpsuetW6djT46bDFk">read more</a></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr><tr><td>Deposit limits (non-KYC)</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> &#x3C; 10K USDC per address in 24 hours, 300K total protocol per 24 hours</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td> <span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span><br>N/A</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr><tr><td>Tiered deposit limits (KYC'd)</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> &#x3C; 20K USDC per address in 24 hours.</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr><tr><td>Current withdrawal limits</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> &#x3C; 300K per day for the total protocol</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr><tr><td>Current pool size limits</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> 2M USDC Polygon <br><br>10M USDC Optimism</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr><tr><td>TRM compliance integration</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> Risk management and illicit funds detection</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr><tr><td>Cloud API</td><td><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> Beta Gated implementation</td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td><td><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span></td></tr></tbody></table>

A big differentiator between the protocols is zkBob’s use of tokens such as USDC for transactions and fees. With stable pools, transaction fees do not fluctuate for users (hard coded to $0.10 per tx on Polygon/ $0.50 per tx on Optimism). If the actual tx cost is less, the extra amount is collected and dedicated to protocol and user support. In rare cases when it costs more, txs are divided and processed in several transactions.

zkBob pools have the additional utility including the ability to introduce [compounding](/roadmap/exploratory-features/compounding) and lost/expired token redistribution. These features on the roadmap will provide future incentives for zkBob users.

[Multitransfer](/zkbob-app/transfers/multitransfers) is another unique zkBob feature, allowing users to transfer to multiple zk addresses with a single transaction. This is especially useful for employers sending salary payments to many employees. Rather than create proofs and pay fees for each tx, these can be sent in bulk, saving time and overhead costs.

Additional [zkBob roadmap](/roadmap/on-the-roadmap) items include multi-chain deployment, decentralized relayer infrastructure, and fast client state sync also set it apart from competitors in the space.

In the table below we explore some technical and operational differences between zkBob, zk.money and Tornado.cash (pre-ban).

## Operational Comparison Table

{% hint style="warning" %}
Note comparisons were made to show varying technology and zk tech. As zk.money and tornado.cash are no longer in active operation (sunset or banned), zkBob is currently the primary solution for optional privacy.&#x20;
{% endhint %}

|                                               | zkBob                                                             | zk.money (now sunset)                             | Tornado.cash (pre-ban)                                                                                                                                                     |
| --------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Deployment Chain**                          | Polygon                                                           | Aztec Network Rollup                              | Ethereum/Gnosis Chain                                                                                                                                                      |
| **zkSNARK**                                   | Groth’16                                                          | PLONK                                             | Groth’16                                                                                                                                                                   |
| **Verification time**                         | \~3 ms                                                            | \~ 6ms                                            | \~3 ms                                                                                                                                                                     |
| **Prover algorithm RAM requirements**         | \~5MB                                                             | \~900MB                                           | \~10MB                                                                                                                                                                     |
| **Trusted setup**                             | :white\_check\_mark: Per circuit                                  | :white\_check\_mark: Universal                    | :white\_check\_mark: Per circuit                                                                                                                                           |
| **Deposit gas size**                          | 642K                                                              | 51K                                               | 910K                                                                                                                                                                       |
| **Arbitrary Deposits/Transfers/Withdrawals?** | :white\_check\_mark:                                              | :white\_check\_mark:                              | <p><span data-gb-custom-inline data-tag="emoji" data-code="274c">❌</span> Regular TC<br><span data-gb-custom-inline data-tag="emoji" data-code="2705">✅</span> TC Nova</p> |
| **Gas fee token(s)**                          | -> USDC/ETH/BOB deposits & withdrawals (no native token required) | -> ETH deposits & withdrawals                     | -> ETH deposits & withdrawals                                                                                                                                              |
| **Transfer token(s)**                         | USDC, ETH, BOB                                                    | zkETH / zkDAI / zkrenBTC                          | ETH, DAI, USDC                                                                                                                                                             |
| **Transaction times**                         | < 1 minute, Polygon infra                                         | Variable (4hours to < 5 min) based on desired fee | < 1 minute, Nova on Gnosis Chain infra                                                                                                                                     |
| **Tokenomics**                                | :white\_check\_mark: XP Auctions on roadmap                       | :x:                                               | :white\_check\_mark: TORN governance token                                                                                                                                 |
| **Geo restrictions**                          | :white\_check\_mark: implemented                                  | :x:                                               | :x:                                                                                                                                                                        |
| **Multi-chain deployment**                    | :white\_check\_mark: Optimism & Polygon                           | :x:                                               | :white\_check\_mark:  (prior roadmap)                                                                                                                                      |
| **Decentralized relayers**                    | :white\_check\_mark: On roadmap                                   | :x: (Multi-rollup infra on Roadmap)               | :white\_check\_mark:                                                                                                                                                       |
| **Fast client-state sync**                    | :white\_check\_mark: On roadmap                                   | :x:                                               | :x:                                                                                                                                                                        |


# zkBob Pools

There are several zkBob private pools [across multiple chains](/zkbob-overview/basic-concepts/multichain-deployment).

### Optimism

🏊‍[ ETH pool](/zkbob-overview/zkbob-pools/eth-pool-on-optimism)

🏊‍♂️ [USDC pool](/zkbob-overview/zkbob-pools/usdc-pool-on-optimism)

### Polygon

🏊‍♂️ [USDC pool](/zkbob-overview/zkbob-pools/usdc-pool-on-polygon)

*More pools coming soon*


# USDC Pool on Polygon (sunsets January 31, 2025)

BOB pool converted to USDC

{% hint style="danger" %}
The Polygon USDC pool will officially sunset on January 31, 2025. Please [remove funds ](/zkbob-app/withdrawals)prior to that date. More information -> <https://blog.zkbob.com/polygon-sunsets-january-31-2025/>
{% endhint %}

{% hint style="success" %}
The migration to USDC from BOB was completed Jul-18-2023 02:11:08 PM +UTC [0x7cf8f47d95b7e38eea8c9b2b1110d3ce4602f2936f70cdc44a5fa670923e7d58](https://polygonscan.com/tx/0x7cf8f47d95b7e38eea8c9b2b1110d3ce4602f2936f70cdc44a5fa670923e7d58)

-> [Learn more about the migration](https://bob-docs.zkbob.com/bob-dao/governance/protocol-governance/gp-14-migrate-bob-pool-to-usdc-on-polygon)
{% endhint %}

A proposal to [migrate the zkBob BOB pool on Polygon to USDC was added to the zkBob forum June 28, 2023](https://forum.zkbob.com/t/proposal-to-migrate-zkbob-polygon-pool-from-bob-to-usdc/24). This proposal was initiated to address user feedback and concerns regarding usage and adoption. While BOB is a small, niche stablecoin, USDC is a top 5 coin by marketcap with billions in daily volume. USDC is widely used and accepted, and presents better anonymity overall for zkBob users who no longer need to convert USDC to BOB. Eliminating this conversion process not only increases anonymity but also removes a barrier to entry for many users.

**In response to comments and discussion, the proposal has successfully moved forward and all BOB in the pool will be automatically converted to USDC.**

Users don't need to do anything, BOB will be automatically converted in the pool. Anyone with BOB in the pool will see their BOB balance automatically converted to a USDC balance.

On withdrawal, a receiving wallet will receive USDC rather than BOB. USDC deposits will be accepted into zkBob on Polygon and can be transferred and withdrawn privately.

## USDC Migration FAQ

<details>

<summary>What will the migration process look like - will there be downtime and will the app be unavailable?</summary>

Yes, there will be a short downtime period of 1-2 hours where the app will not be available during the migration. All operations in all pools will stop during this time.  Following the migration, all BOB will be replaced 1:1 with USDC.

***Update:** The migration is complete and all operations are functioning normally.*

</details>

<details>

<summary>Will a BOB pool still be available on Polygon?</summary>

No.&#x20;

</details>

<details>

<summary>What about my BOB? How is it converted?</summary>

The default token for the pool is simply swapped. Following the successful proposal, a transaction initiated by the zkBob DAO multisig will:

1. Remove all BOB from the pool
2. Swap BOB for the same amount of USDC
3. Deposit all USDC into the pool.

Transaction details will be posted once the transaction is complete.\
TX: [0x7cf8f47d95b7e38eea8c9b2b1110d3ce4602f2936f70cdc44a5fa670923e7d58](https://polygonscan.com/tx/0x7cf8f47d95b7e38eea8c9b2b1110d3ce4602f2936f70cdc44a5fa670923e7d58)

</details>

<details>

<summary>What changes for the Polygon pool?</summary>

Nothing will change regarding general processes, only the token will change. Deposits, transfers and withdrawals will use USDC instead of BOB. The rest of the functionality remains the same.&#x20;

</details>

&#x20;


# USDC Pool on Optimism

BOB pool converted to USDC

{% hint style="success" %}
The migration to USDC from BOB was completed Oct-09-2023 12:24:29 PM +UTC [0xc87ce1f36ecff741d76f921f09fed979f9ab0520acc6d30afe2a5964920ec77b](https://optimistic.etherscan.io/tx/0xc87ce1f36ecff741d76f921f09fed979f9ab0520acc6d30afe2a5964920ec77b)

-> [Learn more about the migration](https://bob-docs.zkbob.com/bob-dao/governance/protocol-governance/gp-20-migrate-bob-pool-to-usdc-on-optimism)
{% endhint %}

The USDC (formerly BOB) pool on Optimism gives users access to private, stable transactions for USDC. To access, select the Optimism/USDC pool and deposit USDC.

<figure><img src="/files/iuebOWxvxFbPTwmt3g7o" alt=""><figcaption></figcaption></figure>


# ETH Pool on Optimism

The first zkBob ETH pool is deployed on Optimism

{% hint style="success" %}
ETH pool launched. [Read the announcement here](https://blog.zkbob.com/new-eth-pool-on-optimism/).
{% endhint %}

To access, select Optimism in the Network dropdown and choose the ETH pool.

<figure><img src="/files/mep1OeikXNbmm9G033Bx" alt=""><figcaption></figcaption></figure>

You can deposit ETH or WETH into the pool.&#x20;

* **ETH** deposits use the [direct deposit](/zkbob-app/zkbob-direct-deposits) functionality, and can take longer to process. *See the* [*FAQ below*](#faq) *for more details.*
* **WETH** deposits use the standard deposit functionality with proofs constructed locally on your machine.

Note your connected external `0x` account will show your total ETH + WETH balance.&#x20;

<figure><img src="/files/aps9NWUyB6pQmGuq3Wnd" alt=""><figcaption></figcaption></figure>

When depositing, select ETH or WETH from the token dropdown menu. The amount available to deposit will not be combined, you can only choose to deposit your available ETH or your available WETH with a single transaction.

<figure><img src="/files/Jwd59hLm3ELScwNoGcNa" alt=""><figcaption></figcaption></figure>

Once deposited, WETH and ETH both become ETH in the zkAccount.

On withdrawal, select a wallet to receive the ETH from your zkAccount. All withdrawals are processed as ETH, not WETH.

<figure><img src="/files/OlEZdPumQLvaeb1D4DxC" alt=""><figcaption></figcaption></figure>

## FAQ

<details>

<summary>Why can a regular ETH transaction take up to 10 minutes?</summary>

\=ETH deposits use the direct deposit functionality, where proofs are constructed remotely. Deposits can also be batched during this process.

This decreases the gas costs for the relayer and for your transactions, but can result in additional time for the transaction to process.&#x20;

</details>

<details>

<summary>What are the deposit and withdrawal limits for the ETH pool?</summary>

The default limits are 5 ETH deposited per day/per address with a total of 150 ETH total daily deposits from all addresses. All withdrawal from all users cannot exceed 150 ETH per day. \
\
You can increase your deposit limit to 10 ETH per day with [optional KYC through the Know your Cat protocol](/zkbob-app/optional-kyc).

</details>

<details>

<summary>Can I use KYC to increase my limits?</summary>

You can double your deposit limits to 10 WETH/day by using the [Know your Cat protocol for KYC](/zkbob-app/optional-kyc). ETH deposits are not yet available for KYC, but this will be implemented soon. *Note that KYC limit increases are not available in all jurisdictions.*&#x20;

</details>

<details>

<summary>Why are fees different for WETH or ETH deposits?</summary>

A different mechanism (direct deposits) is used for depositing ETH. It involves sending the transaction to a relayer which creates the proof and the relayer may batch transactions depending on the number of transactions. This results in lower fees for ETH deposits but longer transaction times.

For WETH deposits the token is approved for use by the contract and proofs are constructed locally. Deposits are completed more quickly but may have higher gas costs depending on network usage and congestion.

</details>


# USDT Pool on Tron (sunsets Oct 29, 2024)

{% hint style="danger" %}
Tron pool will sunset on October 29, 2024. Please remove funds prior to that date. More information -> <https://blog.zkbob.com/tron-pool-closes-october-29-2024/>
{% endhint %}

Usage works the same as on other pools. You can access the pool directly at [https://tron.zkbob.com](https://tron.zkbob.com/transfer) or follow the menu link from <https://app.zkbob.com/>.

<figure><img src="/files/PEVGxRrZ9R07054mu7KU" alt=""><figcaption></figcaption></figure>


# Fees

A $0.10 fee per tx on Polygon, and a dynamic fee per tx is added on Optimism, is added to each transaction\* and paid with existing USDC tokens. You DO NOT need any native (MATIC/ETH) tokens to deposit, transfer, or withdraw from zkBob.&#x20;

Fees cover the cost of transactions in most situations (*see below*) and can result in a surplus depending on gas and token prices. USDC (or other pool token) accumulates with the fee receiver and is periodically withdrawn, swapped for native tokens, and sent to the relayer to subsidize future transaction costs. Relayers do not keep any profits from extra fees - all collected fees are used to support the protocol.&#x20;

\**In special cases, transactions may incur additional fees when many notes need to be processed at the same time.* [*Learn more*](/zkbob-overview/fees/unspent-note-handling)*.*

### Transaction costs

Exact tx fees are calculated as the cost of the `calldata` for an operation (deposit, transfer, withdrawal) multiplied by the gas price and the token price. Tx gas examples:

* [zkBob deposit](https://polygonscan.com/tx/0xe9755f83c01171446d305d6a1a6988258d1cddeeed6e9fcce5921357f57fd6fc) 783,638 gas
* [zkBob transfer](https://polygonscan.com/tx/0xb2d823f364906bc8e9a47782ee1950a51025d767ae1dbf1f248c281fe89d975b) 601,357 gas
* [zkBob withdrawal](https://polygonscan.com/tx/0xe6674484cbed0f033899cc78b72c023be8b588efbde4468b946a2c9120c11ef5) 620,679 gas

To calculate a baseline for any operation, we take the highest gas cost (deposit) + 15% buffer = 902,000 gas.

Max Fee baseline = `902,000 * gas price * token price`

### Polygon Example

Fees can vary significantly based on the gas price and token price at the time of a transaction. In this table, the top row shows example USD equivalent token prices ($0.35 to $1.05) and the left column shows varying gas costs ranging from 30 to 100 Gwei. These are multiplied with 902,000 to calculate the cost for a max transaction.

|          | $0.35  | $0.80 | $1.05 |
| -------- | ------ | ----- | ----- |
| 30 Gwei  | $0.009 | $0.02 | $0.03 |
| 75 Gwei  | $0.02  | $0.05 | $0.07 |
| 100 Gwei | $0.03  | $0.07 | $0.1  |

### Fee Collection

Fees are currently collected internally within the pool contract and the `accumulatedFee` method. They can be manually withdrawn by a separate operator account (a specified account called the fee recipient) rather than a primary relayer account. This maintains safety in the protocol as a primary relayer is responsible for sending transactions and should not have the ability to call the `withdrawFee` function.

Once enough USDC has accumulated, it can be withdrawn and swapped for native (MATIC/ETH) tokens. These are then sent to the primary relayer to cover gas fees for future operations. &#x20;

### View Accumulated Fees

Anyone can view current fees accumulated by the protocol. This does not represent all fees collected, but only those accumulated since the previous withdrawal.

### **Polygon Example**

1\) Go to the [pool proxy contract on Polyscan](https://polygonscan.com/address/0x72e6b59d4a90ab232e55d4bb7ed2dd17494d62fb#readProxyContract) and click the **Read as Proxy** tab.

<figure><img src="/files/eoeDZ0kP9z4lT6uQwrSp" alt=""><figcaption></figcaption></figure>

2\) Query the `accumulatedFee` method using the Relayer (Operator) Address [0xc2c4ad59b78f4a0afd0cdb8133e640db08fa5b90](https://polygonscan.com/address/0xc2c4ad59b78f4a0afd0cdb8133e640db08fa5b90).

<figure><img src="/files/UF7r9vNygk4XGGoIly0d" alt=""><figcaption></figcaption></figure>

3\) Convert the result (in Gwei) to USD using [Ethereum Unit Converter](https://eth-converter.com/). The Ether amount will be USD equivalent (in the case below $13.70 is currently accumulated in the contract).

<figure><img src="/files/6YmwUuTrb9hZQbdlOTfi" alt=""><figcaption></figcaption></figure>


# Unspent note handling

Processing of multiple unspent notes in a single outgoing operation can result in additional fees

{% hint style="info" %}
Multiple transactions are sometimes required when there are many unspent notes in an account.
{% endhint %}

zkBob uses an Unspent Transaction Output (UTXO) model with strict ordering. Incoming zkAccount transfers are ordered and referred to as '[notes](/implementation/account-and-notes/notes)'. About notes:

* Notes are received as incoming transfers within the zkBob pool. They are collected in the receiver's zkAccount and displayed as part of the account's BOB balance.&#x20;
* Notes are collected in a FIFO (first-in-first-out) order.&#x20;
* Collected (unspent) notes are automatically aggregated and added to any outgoing transfer/ withdrawal transaction. A maximum of 3 notes can be included in 1 transaction.&#x20;

When an account has many incoming and outgoing transactions, notes are aggregated regularly (collected and spent) and do not build up.

However, in certain scenarios there may be a large number of unspent notes which are requested for processing in a single operation. In this case, multiple transactions are performed by the zkBob application for a single operation. Each transaction requires a separate 0.10 (Polygon) or 0.50 (Optimism)  BOB fee.&#x20;

## Example: Carl withdraws 20 BOB on Optimism

Carl starts with a zkBob account funded with 10 BOB. He then receives 10 separate transfers through zkBob of 1 BOB each (ie 10 notes) from his friends. Now, Carl's total account balance displays 20 BOB. He decides to withdraw the entire 20 BOB.&#x20;

In this scenario there are 10 unspent notes that need to be processed to complete the entire operation. They are automatically processed in a series of 4 transactions.

1. **Tx #1**: Aggregates the first 3 notes of 1 BOB each and increases the account balance. The account balance becomes: `10 + 3x1 - 0.1(fee) = 12.9 BOB`
2. **Tx #2:** Aggregates next 3 notes -> acc = `12.9 + 3x1 - 0.1(fee) = 15.8 BOB`
3. **Tx #3**: Aggregates next 3 notes -> acc = `15.8 + 3x1 - 0.1(fee) = 18.7 BOB`
4. **Tx #4**: Aggregates the last note -> acc = `18.7 + 1 - 0.1(fee) = 19.6 BOB` and simultaneously makes an outgoing withdrawal for the full account balance, i.e. `19.6 BOB`

When the operation is complete, Carl receives 19.6 BOB, and 0.4 BOB is spent as fees for the 4 transactions. His new zkAccount balance is 0.

## Example: Carl withdraws 15 BOB on Optimism

If Carl wants to withdraw 15 BOB, the operation only requires 2 txs.&#x20;

1. **Tx #1**: Aggregates first 3 notes ->  `acc = 10 + 3x1 - 0.1(fee) = 12.9 BOB`
2. **Tx #2**: Aggregates next 3 notes -> `acc = 12.9 + 3x1 - 0.1(fee) = 15.8 BOB` and makes a simultaneous withdrawal of 15 BOB  `acc = 15.8 - 15 = 0.8 BOB` . The account also contains 4 unspent notes of 1 BOB each.

Carl receives 15 BOB, and 0.2 BOB is used to pay the transaction fees. His new zkAccount balance displays 4.8 BOB. His account consists of 4 unspent notes of 1 BOB each and 0.8 account balance, however this is abstracted for Carl so he only sees the total account balance of 4.8.


# Deposit & Withdrawal Limits

{% hint style="info" %}
**Limits updates: Limits are updated based on periodic reviews of usage and demand.**&#x20;

\
**06/08/24**\ <mark style="color:red;">**Optimism**</mark> \
[View transaction](https://optimism.blockscout.com/tx/0x25cf6ee81fb7e545c284b642990a88553990570152a46da7871eeff659bd38cb)

* Max pool size reduced to 500,000 USDC
* Daily deposit limit set to 100,000 USDC for tier 1 and tier 2 users
* Additional deposit limits adjusted across tiers (see current values in table below) for the zkBob ETH pool on Optimism.

<mark style="color:purple;">**Polygon**</mark>\
[View transaction](https://polygon.blockscout.com/tx/0xf30fbb2992dde39806fedab4f5751fda5f7bfce3dd811e6c14ff65d868d40d48)

* Max pool size reduced to 750,000 USDC
* Daily deposit limit set to 100,000 USDC for tier 1 and tier 2 users

<mark style="color:green;">**Tron**</mark>

* Pool size reduced to 500,000 USDT
* Single deposit and daily deposit limits set to 1,000 USDT

**23/07/24**  \ <mark style="color:purple;">**Polygon**</mark> USDC pool deposit limits reduced from 10,000 to 1,000 per day.

[View Transaction](https://polygon.blockscout.com/tx/0xa771d0506ca8b0a51246e194240ac1ed2b998af9c7222da4eecac22282335b86)

* Daily user deposit limit set to 1000 USD

* Single deposit limit set to 1000 USD

* Daily user direct deposit capped at 1000 USD
  {% endhint %}

* Limits are imposed to protect against large deposits and withdrawals which may result from illegal activity (for example a hack where attackers need to quickly move a large amount of funds).&#x20;

* By default, new users are limited in the amount they can deposit and withdraw.&#x20;

* Users with [optional KYC through the Know your Cat protocol](/zkbob-app/optional-kyc) or businesses with inherent KYC can access higher limits through tiered limits.

* zkBob is multichain, with different limits for different tiers which can also vary by chain.&#x20;

* There are 2 pools for Optimism, one for USDC and a second for ETH.&#x20;

* Limits are reviewed periodically and adjusted as needed to maintain security and follow industry standards.

## Limits Table

<table><thead><tr><th width="158"> </th><th>Tier 1 </th><th>Tier 2 </th><th>Tier 3</th></tr></thead><tbody><tr><td><strong>Demographic</strong></td><td>New Users with no KYC</td><td>Individuals w/ Know Your Cat KYC</td><td>Businesses with inherent KYC</td></tr><tr><td><strong>Single deposit</strong> </td><td><mark style="color:purple;">Polygon:</mark> <br>1,000 USDC <br><br><mark style="color:red;">Optimism:</mark> <br>10,000 <br>USDC<br><br>3 ETH<br><br><mark style="color:green;">Tron:</mark><br>1,000<br>USDT</td><td>USDC &#x26; USDT<br>20,000 <br><br>6 ETH</td><td>USDC &#x26; USDT<br>100,000 <br><br>30 ETH</td></tr><tr><td><strong>Single address per 24/hr</strong></td><td><mark style="color:purple;">Polygon:</mark> <br>1,000 USDC <br><br><mark style="color:red;">Optimism:</mark> <br>10,000 <br>USDC<br><br>3 ETH<br><br><mark style="color:green;">Tron:</mark><br>1,000<br>USDT</td><td>USDC &#x26; USDT<br>20,000 <br><br>6 ETH</td><td>USDC &#x26; USDT<br>100,000 <br><br>30 ETH</td></tr><tr><td><strong>All deposits from all users</strong></td><td><mark style="color:purple;">Polygon:</mark><br>100,000 <br>USDC<br><br><mark style="color:red;">Optimism:</mark><br>100,000 <br>USDC<br><br>30 ETH<br><br><mark style="color:green;">Tron</mark><br>100,000 <br>USDT</td><td>USDC &#x26; USDT<br>100,000 <br><br>30 ETH</td><td>USDC &#x26; USDT<br>300,000 <br><br>30 ETH</td></tr><tr><td><strong>All withdrawals from all users per 24/hr</strong></td><td><mark style="color:purple;">Polygon:</mark> <br>300,000 USDC<br><br><mark style="color:red;">Optimism:</mark> <br>300,000 USDC<br><br>50 ETH<br><br><mark style="color:green;">Tron</mark><br>50,000 <br>USDT</td><td><mark style="color:purple;">Polygon:</mark> <br>300,000 USDC<br><br><mark style="color:red;">Optimism:</mark> <br>300,000 USDC<br><br>50 ETH<br><br><mark style="color:green;">Tron</mark><br>50,000 <br>USDT</td><td><mark style="color:purple;">Polygon:</mark> <br>300,000 USDC<br><br><mark style="color:red;">Optimism:</mark> <br>300,000 USDC<br><br>50 ETH<br><br><mark style="color:green;">Tron</mark><br>50,000 <br>USDT</td></tr><tr><td><strong>Transfers</strong></td><td><mark style="color:purple;">Polygon:</mark> 0.10/tx<br>USDC<br><br><mark style="color:red;">Optimism:</mark><br>dynamic/tx<br>USDC<br>ETH<br><br><mark style="color:green;">Tron:</mark><br>0<br>USDT</td><td><mark style="color:purple;">Polygon:</mark> 0.10/tx<br>USDC<br><br><mark style="color:red;">Optimism:</mark><br>dynamic/tx<br>USDC<br>ETH<br><br><mark style="color:green;">Tron:</mark><br>0<br>USDT</td><td><mark style="color:purple;">Polygon:</mark> 0.10/tx<br>USDC<br><br><mark style="color:red;">Optimism:</mark><br>dynamic/tx<br>USDC<br>ETH<br><br><mark style="color:green;">Tron:</mark><br>0<br>USDT</td></tr><tr><td><strong>Overall Pool Size</strong></td><td><mark style="color:purple;">Polygon:</mark> <br>750,000 USDC<br><br><mark style="color:red;">Optimism:</mark> <br>500,000 USDC<br><br>150 ETH<br><br><mark style="color:green;">Tron:</mark><br>500,000<br>USDT </td><td><mark style="color:purple;">Polygon:</mark> <br>750,000 USDC<br><br><mark style="color:red;">Optimism:</mark> <br>500,000 USDC<br><br>150 ETH<br><br><mark style="color:green;">Tron:</mark><br>500,000<br>USDT </td><td><mark style="color:purple;">Polygon:</mark> <br>750,000 USDC<br><br><mark style="color:red;">Optimism:</mark> <br>500,000 USDC<br><br>150 ETH<br><br><mark style="color:green;">Tron:</mark><br>500,000<br>USDT </td></tr></tbody></table>


# Compliance & Security

## User Restrictions

zkBob should not be used for any illegitimate or criminal activity nor supported by the direct or indirect proceeds of criminal or fraudulent activity. It should not be used in any way that violates any laws in the jurisdiction of the user.&#x20;

Politically exposed individuals or associates of those individuals are not permitted to use the application under existing laws.&#x20;

**Prior to using zkBob, a user must acknowledge and accept the following terms:**

* If acting as an individual you are of legal age (as applicable in the jurisdiction where you live).
* You are not a politically exposed person, that is, a person who is entrusted with any prominent public function, or a politically exposed person who has stepped down from that role.
* You are not an immediate family member or close associate of a politically exposed person or a politically exposed person who has stepped down.
* You are not engaged in money laundering or the financing of terrorism.
* Your access to the application does not violate any rule, law, regulation or directive of the country of your residence and the jurisdiction in which you reside.
* You have not been arrested or convicted of any offence or crime.
* You are willing to provide and verify your identity upon request.

## User Responsibility

* Use of zkBob is at the sole risk of the user.
* The code repository, decentralized and autonomous protocol and environment, and associated decentralized networks are open-source and not controlled by any team.&#x20;
* zkBob contributors are not responsible for any losses or damages associated with inherent risks associated with cryptography, blockchain, or digital wallet usage.
* The user agrees not to access zkBob using any technology for the purposes of circumventing location-based restrictions.
* Users are responsible for reporting and paying any applicable taxes associated with usage.
* Users acknowledge that addresses may be blocked from using the protocol if they are sanctioned or added to a block list.

## Daily limits

Limits are imposed to prevent large-scale transactions which may be the result of illicit activity. [Deposit limits are detailed here](/zkbob-overview/deposit-and-withdrawal-limits).

## Geo Restrictions

The zkBob application is in Beta with minimal restrictions enabled. Users must take responsibility to abide by local, state, and federal laws. **Please check and confirm prior to usage**.&#x20;

**zkBob usage is currently restricted in several jurisdictions:**

* **United Arab Emirates (UAE)**: Users in the UAE are programmatically restricted from using zkBob due to [Virtual Assets Regulatory Authority (VARA)](https://www.vara.ae/en/) policies which prohibit anonymity-enhanced transactions.&#x20;
* **United States (US)**: Users in the US are restricted from zkBob due to uncertainty around the [proposed rulemaking (NPRM)](https://www.fincen.gov/sites/default/files/federal_register_notices/2023-10-19/FinCEN_311MixingNPRM_FINAL.pdf) restrictions, pursuant to section 311 of the USA PATRIOT Act. This is a proactive measure to protect users until these rules are clarified.

## Wallet Compliance

An integration with TRM and potential future integrations with VAF Compliance and PureFi ensure 0x wallets interacting with the protocol do not contain funds obtained from illicit sources. Suspicious wallets cannot deposit into the protocol, and funds cannont be withdrawn to suspicious wallets.

## Security

### Beta Release

zkBob is now released in a beta version. Contracts [have been audited](/resources/security-audit), and users should use at their own discretion. Risks to users are mitigated through [limits on deposits and withdrawals](/zkbob-overview/deposit-and-withdrawal-limits). In addition, the overall size of a pool cannot exceed 2M BOB tokens on Polygon and 10M BOB on Optimism.

### Bug Bounty

Bounties are one of the most important tools for testing and enhancing application and contract security. We are in the planning phases to implement a bug bounty to help protect users and their funds.


# TRM Labs Integration

[TRM labs](https://www.trmlabs.com/) provides risk management and compliance protocols to identify, monitor, and prevent illegal financial activity. TRM labs tools are used by Circle (USDC), Binance and many other companies in the blockchain space to provide AML/CFT compliance and satisfy regulatory requirements.

The zkBob integration with TRM is a proactive solution to prevent criminal funds from ever entering the zkBob privacy pool. When a wallet is connected to zkBob, the wallet address is screened for prior high-risk or suspicious behavior. This can include:

* The address received or transferred funds from a known hack.
* The address received or transferred funds from a sanctioned address.
* The address participated in dark web transactions.
* Other suspicious behavior as determined by TRM labs screening software.

If an address has engaged in past suspect activities it cannot connect or be used to deposit into zkBob. Withdrawals to a suspicious address are also prevented. These restrictions maintain a safe, secure and untainted environment for private transfers.


# Conferences, Workshops, Videos

zkBob sponsorships and participation

The zkBob decentralized team is spreading the word about personal privacy options, compliance features, and zk tech through participation and sponsorship at many blockchain conferences and hackathons. We are partnering with [ETHGlobal](https://ethglobal.com/) to sponsor several hackathons in 2023, and also participating at other conferences when we can.

Below are some highlights from events we've attended. See the [zkBob blog](https://blog.zkbob.com) for more in-depth overviews from these conferences.

## ETHIndia

Kirill ran a workshop for hackers on the zkBob app and how to integrate v1 for this hackathon. Starts at 57:50.

{% embed url="<https://www.youtube.com/live/P6hbTmh0HpM?feature=share&t=3470>" %}

### Gweithering

Igor presented at the Gweithering, an outside event before ETHIndia put on by our friends at [Biconomy](https://www.biconomy.io/), [Superblock](https://www.superblock.one/), and [Frontier](https://www.frontier.xyz/).&#x20;

<figure><img src="/files/mer39VEpDMLIRjQuz9cZ" alt=""><figcaption></figcaption></figure>

## ETHDenver

Andrew spoke on compliant privacy features and enabling personal financial privacy with zkBob.

{% embed url="<https://youtu.be/H0Nv5iagNns>" %}

## ETHGlobal Tokyo

Kirill presented on BOB and how to integrate zkBob direct deposits into an application. The talk starts at 39:45.

{% embed url="<https://www.youtube.com/live/FWLlFFkqFO0?feature=share&t=2381>" %}

## ETHTaipei

Igor presented at [ETHTaipei](https://ethtaipei.org/) on privacy, compliance, and zk proofs.

{% embed url="<https://youtu.be/B0ZEnFAc5C8>" %}

## ETHGlobal Lisbon

Kirill ran a workshop on integrating zkBob into DeFi workflows. The workshop starts at 4:27:30.

{% embed url="<https://www.youtube.com/live/KCsnFLCAtMw?feature=share&t=16028>" %}

## Leading Privacy Alliance (LPA) Summit

Ulyana gave an overview of zkBob and compliance features that set it apart.

{% embed url="<https://youtu.be/HNJvk9kK9Rc>" %}

## ETHDam

Mike presented on UI issues and solutions for web3 products like zkBob.

{% embed url="<https://youtu.be/-gBAIC0lHbM>" %}

## ETHBelgrade

Mike presented on the importance of user-friendly interfaces to onboard the next generation of web3 users.&#x20;

{% embed url="<https://www.youtube.com/watch?v=ID3av0xJzKc>" %}

## ETHCC

Andrew presented on compliance and privacy and how the 2 can coexist moving forward thanks to zk technology.

{% embed url="<https://youtu.be/QxrRXCVBvsc>" %}

## ETHGlobal Paris

Kirill led a workshop on zkBob and the new SDK available for developers.

{% embed url="<https://youtu.be/TR59XORYO94>" %}


# International Videos

zkBob is available around the 🌏! Our community continues to participate and spread the word about the importance of privacy and stable value transfer. Below are some community produced videos in various languages about zkBob.

## 🇯🇵 Japanese Speaking Community

zkBobについて詳しく見る

{% embed url="<https://twitter.com/i/status/1646466702199365633>" %}

## 🇩🇪 German Speaking Community

Erfahren Sie mehr über zkBob

{% embed url="<https://youtu.be/NcF2rzosgwY>" %}

{% embed url="<https://youtu.be/-2dyz82S6xw>" %}

## 🇷🇺 Russian Speaking Community&#x20;

В этом видео мы знакомимся с функционалом площадки&#x20;

{% embed url="<https://www.youtube.com/watch?v=aAPpbvOWvKA>" %}


# Governance

zkBob protocol updates (along with BOB stablecoin updates) are handled via a governance [multi-sig SAFE.](https://gnosis-safe.io/app/matic:0xd4a3D9Ca00fa1fD8833D560F9217458E61c446d8/home) This process may be transitioned to a larger BOB DAO following the beta implementation.

### Protocol Governance

Updates to the protocol are managed via the governance SAFE. More info on protocol governance, decisions, and implementations is available on the BOB docs [page](https://bob-docs.zkbob.com/bob-dao/governance/protocol-governance).


# BOB Stablecoin

{% hint style="info" %}
zkBob used the BOB stablecoin for it's initial pools. It has since moved to USDC, USDT and ETH, and the BOB stablecoin is no longer in use.
{% endhint %}


# zkBob FAQ

Frequently asked questions

{% hint style="info" %}
**General Questions**

* [Who should use zkBob?](#who-should-use-zkbob)
* [Who should not use zkBob?](#who-should-not-use-zkbob)
* [Is the code open source?](#is-the-code-open-source)
* [Who heads development and deployment?](#who-heads-development-and-deployment)
* [Does zkBob work on mobile?](#does-zkbob-work-on-mobile-devices)
* [Why does zkBob use the BOB stablecoin?](#why-does-zkbob-use-the-bob-stablecoin)
* [Do I need MATIC to use zkBob?](#do-i-need-matic-to-pay-for-transactions)
* [Where can I see zkBob stats?](#where-can-i-see-zkbob-stats)

**Privacy**

* [How long should I keep my tokens in a zkBob anonymity pool to increase privacy?](#how-long-should-i-keep-my-tokens-in-a-zkbob-anonymity-pool-to-increase-privacy)
* [Are deposits and withdrawals completely private?](#are-deposits-into-zkbob-and-withdrawals-from-zkbob-private)
* [Can the zkBob application be compromised and my information made available?](#can-the-zkbob-application-be-compromised-and-my-information-made-available)
* [What is Anonymity Set Awareness?](#what-is-anonymity-set-awareness)

**Accounts and Addresses**

* [I made deposits to 1 zkAccount from multiple 0x addresses on Polygon. Is information tying these deposits to a single zkAccount available in the on-chain data?](#i-made-deposits-to-1-zkaccount-from-multiple-0x-addresses.-is-information-tying-these-deposits-to-a)
* [Why are there multiple accounts?](#why-are-there-multiple-accounts)
* [What is the difference between an account and an address?](#what-is-the-difference-between-an-account-and-an-address)
* [I generated a new zkBob address, will my old one still work?](#i-generated-a-new-zkbob-address-will-my-old-one-still-work)

**Troubleshooting**

* [What if I lose my password?](#what-if-i-lose-my-password)
* [Why do I need to keep entering my password?](#why-do-i-keep-having-to-enter-my-password)
* [I'm getting a deposit error or cannot deposit - possible reasons?](#im-getting-a-deposit-error-or-cannot-deposit)
* [Why is my zkBob transaction taking almost a minute to process?](#why-is-my-zkbob-transaction-taking-almost-a-minute-to-process)
* [I just sent a transactions on zkBob which has not processed yet. Do I need to wait to perform another action (deposit/transfer/withdrawal)?](#related-i-just-sent-a-transaction-on-zkbob.-why-do-i-have-to-wait-to-perform-another-deposit-transfe)
* [I received a "Transaction was Rejected" error.](#i-received-a-transaction-was-rejected-error.)
* [I can't connect to my zkBob account or I am stuck at "Updating Zero Pool State" or "Loading zkAccount."](#i-cant-connect-to-my-zkbob-account-or-i-am-stuck-at-updating-zero-pool-state-or-loading-zkaccount.)
  {% endhint %}

## Who should use zkBob?

Eligible users interested in private business transactions (salary payments, vendor payments etc) and people interested in cash-like transactions should consider zkBob. In the offline world, cash can be used for financial privacy. In the blockchain space, zk-based "digital cash" transactions can offer similar privacy.

Some users of zkBob may include:

* Large cryptocurrency holders who want to protect their identity and reduce possible targeting by cyber criminals.
* Individuals or groups who hold many identifying NFTs in their wallets and may want to make a transaction without exposing all of those details (ie communities/DAO/meetup participation etc).
* Business owners who want to pay salaries in stablecoins without displaying amounts and receivers.
* Business owners who do not want to divulge vendor addresses, prices they paid for items, or other business transactions that can put them at competitive risk.
* DAOs or small businesses who want to pay salaries in stable tokens without revealing amount paid to every employee or member.&#x20;
* Everyday users who want a "digital cash" option to transfer and trade for goods and services with one another privately through a shielded pool.&#x20;
* Philanthropists who want to donate to causes anonymously.
* Individuals interested in privacy, zksnarks, or novel auction mechanisms related to [XP functionality](/roadmap/exploratory-features/xp).

*Please refence the* [*Compliance and Security*](/zkbob-overview/compliance-and-security) *page for additional information regarding zkBob usage.*

## Who should not use zkBob?

zkBob should not be used for any illegitimate or criminal activity. It should not be used in any way that violates any laws in the jurisdiction of the user, and should not be used by politically exposed individuals or associates of those individuals. Prior to using zkBob, you must acknowledge and accept these terms of usage:

* If acting as an individual you are of legal age (as applicable in the jurisdiction where you live).
* You are not a politically exposed person, that is, a person who is entrusted with any prominent public function, or a politically exposed person who has stepped down from that role.
* You are not an immediate family member or close associate of a politically exposed person or a politically exposed person who has stepped down.
* You are not engaged in money laundering or the financing of terrorism.
* Your access to the application does not violate any rule, law, regulation or directive of the country of your residence and the jurisdiction in which you reside.
* You have not been arrested or convicted of any offense or crime.
* You are willing to provide and verify your identity upon request.

## Is the code open-source?

The underlying code is open-source and anyone can deploy contracts, a relayer, and a cli to perform basic operations. The UI is not currently open-source to avoid simple clones. [Source code is available here](https://github.com/zkbob).&#x20;

## Who heads development and deployment?

zkBob is a decentralized, open-source application with a distributed & decentralized team and no centralized head of development. The app was initiated by the xDai research group in collaboration with [ZeroPool](https://zeropool.network/) and community support. zkBob is multichain and [deployed on Polygon and Optimism](https://app.zkbob.com/).

## Does zkBob work on mobile devices?

Yes! However, operations can take longer as Proof generation is resource intensive on many mobile devices. Ideally, we recommend using a desktop/laptop environment for zkBob, and we continue to optimize mobile for faster processing.&#x20;

## Why does zkBob use the BOB stablecoin?

zkBob no longer uses the BOB stablecoin. The protocol was originally designed to use the BOB stablecoin, however after v1 was complete, the protocol was opened up to additional pools including a USDC pool on Polygon and an ETH pool on Optimism. For questions related to BOB, please see the [BOB details](broken://pages/7MCzQ4AARy6LkXxBDUDz) page.

## Do I need MATIC/ETH to pay for transactions?

**No**, all fees are paid in USDC with a stable $0.10(Polygon) or a stable dynamic (Optimism) fee per tx! This includes deposits into the protocol, transfers within zkBob, and withdrawals from the application.

Withdrawals from zkBob on Polygon will also result in a small amount of MATIC (Polygon) sent to the receiving address if certain criteria are met. To receive, the address must have a 0 balance, and the withdrawal amount must be 10 or more USDC. *Note a faucet is not available on Optimism.*

## Where can I see zkBob stats?

zkBob dune analytics dashboard <https://dune.com/zkbob/zkbob>[ ](https://dune.com/maxaleks/zkbob)is a good place to start. zkBob is also supported by Defillama [https://defillama.com/protocol/zkbob.](https://defillama.com/protocol/zkbob)

## Privacy

## How long should I keep my tokens in a zkBob anonymity pool to increase privacy?

It depends on how large the pool set is and how much activity has happened since your deposit. Looking at the number of deposits since your deposit can provide a rough estimation of safety (though not complete because it does not account for transfers within the protocol). In general, waiting longer to withdraw (several days minimum) increases privacy. It is also useful to withdraw a different amount than the amount deposited to increase privacy.

## Are deposits into zkBob and withdrawals from zkBob private?

Not completely, there is some information which is recorded on chain. It is transparent that an address interacted with the zkBob application and how much was deposited or withdrawn.

**Deposits:** When you deposit into zkBob, the 0x address on Polygon or Optimism you deposited from and how much of any tokenyou deposited into the zkBob protocol can be viewed in a block explorer. [Example Transaction](https://polygonscan.com/tx/0xe5269af7e735c45976f0c627cd138dfd000d99b160ae5c5868abcdf3c540f0cb).

<figure><img src="/files/vFUWr2mU1rR2dppcv2K0" alt=""><figcaption><p>Deposit transaction from an EOA to the zkBob Pool</p></figcaption></figure>

**Withdrawals:** When withdrawing to a regular 0x address on Polygon or Optimism, the address you withdrew to and amount withdrawn from the protocol can be viewed in a block explorer. [Example Transaction](https://polygonscan.com/tx/0x232df7f7a8b8051950323cb9a19450f1cfd1e19fd6618500b081e515e16fe3a4).

<figure><img src="/files/7r8JbD4h1xWRtyY1VefX" alt=""><figcaption><p>Withdrawal transaction from the zkBob pool to an EOA. </p></figcaption></figure>

## Can the zkBob application be compromised and my information made available?

With zkproof transactions specific identifying information is never recorded (such as sender, receiver, and amount sent) so it is not possible to connect these bits of information within zkBob. However, there are other ways your information may be compromised. To maintain privacy, be aware of the following.

* **Secret phrase / private key discovery**: Never write a secret phrase on any internet-connected device. Do not take a screenshot of it. Do not copy and paste it anywhere. Write it on a physical piece of paper and keep somewhere secure.
* **Ip-tracing**: Internet service providers collect logs that can show all ip addresses that connected to an application, including zkBob.&#x20;
* **Transaction inference:** Depositing a specific amount of tokens and then withdrawing that exact amount (especially unconventional amounts in a short timeframe) can create a compelling case for connection. Withdrawing different amounts than deposit amounts, and maintaining funds in the pool for longer periods of time, increases privacy.&#x20;
* **Address trail:** Using the same generated zkAddress for transactions with many different parties creates a connection string which can compromise privacy. It is best to generate a new address for each transfer within the application.
* **Secure address messaging**:  When sending a generated address to another party, carefully choose the application you use (ie telegram, discord etc).  It is best to choose a secure and private channel.
* **Withdrawals**: When withdrawing from the application, sending to a newly generated address without a prior transaction history increases privacy.

## What is Anonymity Set Awareness?

{% hint style="info" %}
To maintain privacy it is advisable to keep funds in the anonymity set for some time and consider transferring/withdrawing different arbitrary amounts than deposited when performing transactions. These measures allow for additional pool entropy and make it more difficult to infer any transaction details.&#x20;
{% endhint %}

Although internal transaction details are hidden, user privacy can be compromised when entering in or leaving the solution **in cases when there is a small anonymity set.**

Imagine you want to send money to Alice through zkBob. You decide to create a new account and deposit the exact necessary amount of tokens.&#x20;

Alice also creates an account and provides her private address. You transfer funds to Alice and she makes a withdrawal transaction for all received tokens.&#x20;

If you and Alice are the only active users of the zkBob solution, any neutral observer can infer your funds movement with a high degree of accuracy.

**There are no time limits for funds withdrawal.** It is recommended to keep funds inside the solution some extra time to increase the anonymity set and safeguard your privacy.

## Accounts and Addresses

## I made deposits to 1 zkAccount from multiple `0x` addresses. Is information tying these deposits to a single zkAccount available in the on-chain data?    &#x20;

**No.** The only available information available is that the `0x` addresses made a deposit for a specific amount of BOB into the zkBob app. Due to the zk proving mechanisms, zkAccount details are not revealed (only confirmation that the amount was deposited), and thus there is no public record of which zkAccount the funds were deposited to in zkBob.&#x20;

## Why are there multiple accounts?

There are 2 different types of accounts displayed in the interface. One is for transferring funds **to and from zkBob (Wallet account)** and the second is for transferring funds **within zkBob (zkAccount)**.

![Accounts: Wallet is not yet connected and user is not logged into their zkAccount](/files/UGC2xnHfU7q0w7Hcun6c)

1. **Wallet Account**. This is a standard EOA (Externally owned address) you can connect to fund or withdraw from your zkAccount. It can also be used to generate a zkAccount using the private key from the EOA.&#x20;
2. **zkAccount.** This is the shielded account where you can transact privately with other zkAccount holders. It is auto-named when created. [More on zkAccounts](/implementation/account-and-notes/accounts).

![MM wallet is connected and user has logged into zkAccount which is auto-named zkGerti](/files/6rgWBJlycYCP8BX0Xck5)

## What is the difference between an account and an address?

-> **Accounts** are used to perform actions with zkBob. You may use an EOA wallet account to deposit or withdraw, and your zkBob account to initiate deposits, withdrawals, transfers, or view your transaction history.&#x20;

-> **Addresses** are used for shielded token transfers between users in zkBob. Ideally, a new address is generated for each transfer. It is not possible to link different private addresses to one another or to the primary account. Only the account owner can confirm ownership. Each created address is encoded in base58 format.

Learn more about [generating a receiving address here.](/zkbob-app/generate-a-secure-address)

## I generated a new zkBob address, will my old one still work?

Yes, it will still work. You can generate as many addresses as you would like. Each one is encrypted and cannot be connected to one another or to the primary account except by the account owner, and each one will work indefinitely.

## Troubleshooting

## What if I lose my password?

It's ok. You can restore your account depending on how you created it. If you used a secret phrase to create, restore your account by entering your secret phrase. If you created your account using MM or WalletConnect, you can restore it by going through the create account process and **signing the message using the same wallet address**

Once restored, you will be asked to create a new password. The password is kept in local storage. &#x20;

<figure><img src="/files/p1odMoTo4vzeyq4htohQ" alt=""><figcaption></figcaption></figure>

## Why do I keep having to enter my password?

Encrypted data is stored in local storage for safety purposes. When the app is refreshed from the browser, the password is needed to decrypt the locally-stored account data. A built-in refresh function lets you update history and balance without needing to refresh the entire application. This is useful to view your current zkAccount state once a transaction has been processed on-chain.&#x20;

The in-app refresh, located next to your zkAccount name, should be used rather than a 'hard refresh' to avoid password re-entry.

<figure><img src="/files/PHo8ZW1kQYFDzhQvbqsW" alt=""><figcaption></figcaption></figure>

## I'm getting a deposit error or cannot deposit

There are several applications that can interfere with zkBob. Some known ones include:

* **Revoke.cash** browser extension (<https://discord.com/channels/1095673887389392916/1112786753133220042/1130467777975697408>). Deactivate this plugin when using zkBob.
* Using a **Ledger wallet connected through the Rabby wallet**. Connecting your ledger to MetaMask is a known workaround.

## Why is my zkBob transaction taking almost a minute to process?

Several processes are required for a successful transaction. Proof generation takes place on the client side and typically takes 10 seconds or less. Transactions are then processed in parallel rather than sequentially through the relayer, which optimizes the zkBob application. However, once a transaction is submitted to the chain, timing can vary based on blockchain congestion, resulting in some longer transaction times.

## Related: I just sent a transaction on zkBob. Why do I have to wait to perform another deposit/transfer/withdrawal?

For safety purposes and to prevent potential double-spend scenarios, the application waits until a transaction is confirmed before another transaction can be initiated.

## I received a "Transaction was Rejected" error.

Likely your local state has been corrupted and you can either:&#x20;

a) clear your current browser storage\
b) switch to a different browser, or \
c) connect to zkBob using incognito mode.&#x20;

After choosing from the options above, reconnect to zkBob using your active MetaMask or WalletConnect account or your secret phrase and create a new password. Your old account should populate, and you can try the transaction again.&#x20;

-> Instructions on [clearing browser storage](#clear-browser-storage).

If the problem persists, [contact the team via Telegram](https://t.me/zkbobcommunity).

## I can't connect to my zkBob account or I am stuck at "Updating Zero Pool State" or "Loading zkAccount."

{% hint style="info" %}
If you are experiencing these issues on a mobile device please switch to a desktop/laptop. zkBob is currently not supported on mobile.
{% endhint %}

If you have entered the correct information but your account is not loading you can try clearing the cache and storage. Version updates can sometimes result in artifacts that must be cleared for the latest version to work.&#x20;

Chrome / Firefox are the recommended browsers for the zkBob UI, the following are instructions for refreshing the zkBob app in Chrome.\
\
1\) Open the developer console. \
`Ctrl` `Shift` `J` (Windows)  \
`Cmd` `Option` `J` (Mac)

or in the Chrome menu go to View -> Developer -> Developer Tools

![](/files/HxAKtefFwqpWDnpMw1Iv)

2\) With dev tools open, **right click** the reload symbol and select **Empty Cache and Hard Reload**.

![](/files/WSND7Qz13ou5hzjp2mUN)

{% hint style="warning" %}
If this doesn't fix the issue, proceed to clear site data. **Note this process will clear all local storage and require you to reenter your secret phrase. If you do not have it your account information will be unrecoverable.**
{% endhint %}

#### Clear Browser Storage

3\) Right click the lock🔒 icon and select **Site Settings**.

![](/files/jw7anVQ9GSiZSyDB3GkM)

4\) Click the **Clear Data** button.

![](/files/RyWWRzhyBaJIu1ZvEj0g)

5\) Restore your previous account with your secret phrase and create a new password to re-enter the application.


# Vulnerability Post Mortem

## Summary

On 06/05/26 the internal zkBob security team discovered a critical double-spend vulnerability in one of the ZK circuits used by the protocol.

There is no public evidence that the vulnerability was ever exploited. However, we cannot rule out the possibility entirely.

As a result, all zkBob pools are being deprecated and placed into withdraw-only mode. If you hold funds in any active zkBob account, go to <https://app.zkbob.com/> and withdraw them now.

## Impact

All live zkBob pools were affected and required immediate action:

* Polygon PoS USDC pool
* Optimism USDC pool
* Optimism ETH pool

## Timeline

* 2026/06/05 — Potential circuit logic flaw identified during a routine security review
* 2026/06/05 — All zkBob pools paused as a precaution, pending further investigation
* 2026/06/06 — Vulnerability confirmed as critical
* 2026/06/07 — Fix planning started
* 2026/06/11 — First public statement acknowledging the vulnerability released
* 2026/06/15 — Circuit fix applied; new version of the protocol circuits released
* 2026/06/18 — Updated UI/SDK packages released
* 2026/06/22 — Updated circuit parameters released following an internal Groth16 ceremony
* 2026/06/23 — Updated smart contract supporting withdraw-only mode released and deployed
* 2026/07/02 — All updated components (libraries, SDK, UI, smart contracts) passed internal review
* 2026/07/03 — Smart contract upgrade scheduled through the governance multisig via a timelock
* 2026/07/05 — Smart contract upgrade executed
* 2026/07/06 — Updated UI released; zkBob pools restarted in withdraw-only mode; public restart announcement released

## Root cause

zkBob uses a hybrid model that combines UTXO and account-balance accounting. One of its ZK circuits is designed to prove that only valid account state transitions are accepted, ensuring that every inbound UTXO note can be spent only once and that the sum of all consumed UTXO notes matches the change in account balance during a state update.

For existing accounts, the circuit worked as intended. However, an edge-case vulnerability was identified for accounts being created for the first time while already holding unspent incoming UTXO notes.

Because such an account is being created for the first time, some state-transition rules were relaxed. In particular, since no prior account snapshot existed in the global zkBob Merkle tree, the circuit did not validate the Merkle proof attesting to the correctness of the prior account state. An empty account with zero values was used as a stub, and the zkBob SDK filled the Merkle proof with zeros. This Merkle proof data is also used as an input when computing `input_pos_index` and `nullifier`, where the `nullifier` ensures that all older account states are invalidated during each state transition.

However, because the circuit would accept any arbitrary data as the Merkle proof for a just-initialized account, an attacker could control `input_pos_index` and `nullifier` by crafting malicious Merkle proof data. As a result, an attacker could initialize multiple accounts that double-spend the same inbound UTXO notes: each circuit run would produce a different `nullifier` and therefore fail to correctly invalidate the previously used zero-account states.

Vulnerable circuit: <https://github.com/zkBob/libzeropool-zkbob/blob/903369517b3f8133cd398787478ba5bf46cca2c2/src/circuit/tx.rs#L187-L195>

```rust
//assuming input_pos_index <= current_index
let ref input_pos_index = c_from_bits_le(s.in_proof.0.path.as_slice());

//check nullifier
(&p.nullifier - c_nullfifier(&in_account_hash, &eta, input_pos_index, params)).assert_zero();

let cur_root = c_poseidon_merkle_proof_root(&in_account_hash, &s.in_proof.0, params.compress());
//assert root == cur_root || account.is_dummy()
//all uninitialized empty accounts considered to be in the privacy set
(cur_root.is_eq(&p.root) | s.tx.input.0.is_initial(&poolid)).assert_const(&true);
```

## Fix

An additional constraint was introduced to enforce that only one valid `nullifier` and account state can exist for a not-yet-initialized account, preventing any new double-spend transactions in the future.

```diff
//assuming input_pos_index <= current_index
let ref input_pos_index = c_from_bits_le(s.in_proof.0.path.as_slice());

//check nullifier
(&p.nullifier - c_nullfifier(&in_account_hash, &eta, input_pos_index, params)).assert_zero();

let cur_root = c_poseidon_merkle_proof_root(&in_account_hash, &s.in_proof.0, params.compress());
//assert root == cur_root || account.is_dummy()
//all uninitialized empty accounts considered to be in the privacy set
(cur_root.is_eq(&p.root) | s.tx.input.0.is_initial(&poolid)).assert_const(&true);
+(input_pos_index.clone() * s.tx.input.0.is_initial(&poolid).as_num()).assert_zero();
```

**Due to the nature of the zkBob protocol, it is technically impossible to reliably prove whether the bug was ever exploited on any of the publicly deployed pools.** This is a common limitation of ZK-based privacy solutions. We have no reason to believe it was exploited: no public evidence or indication was found suggesting otherwise, and no suspicious token balances or withdrawal activity were identified during our internal retroactive review.

Nevertheless, we cannot rule out the possibility entirely. We have therefore decided to permanently deprecate the existing zkBob pools, disable all deposits, and place the pools into withdraw-only mode. As a precaution, all withdrawals will pass through a manually reviewed queue; we expect all submitted withdrawals to be processed smoothly and redeemed at a 1:1 rate on a regular schedule.

## User action

If you hold any remaining funds in a zkBob account in any pool, go to <https://app.zkbob.com/> and start a withdrawal submission.

**Please note that submitted withdrawals will not be processed immediately.** All withdrawal claims are recorded on-chain and processed manually within 1–2 weeks, after which funds are automatically credited to your wallet address. Withdrawals are processed in FIFO order in batches, unless suspicious activity or a potential solvency issue is identified and requires further action from the team.

**Deposits and transfers in the existing pools will remain disabled.**


# UI Overview

An application for zk-based transactions

## UI actions

* [Setup](/zkbob-app/account-creation) a zkAccount using an existing web3 wallet or a secret phrase.
* [Deposit ](/zkbob-app/deposits)arbitrary token amounts of a supported token into the application.
* [Transfer](/zkbob-app/transfers) deposits to other participants using zkSnarks technology (proof of transaction without disclosing details of the sender, recipient, and value).
* [Withdraw](/zkbob-app/withdrawals) arbitrary token amounts securely and privately.
* [Generate secure addresses](/zkbob-app/generate-a-secure-address) for funds transfer within the application.
* [Create and use payment links](/zkbob-app/payment-links) to accept donations or payments from other users who may even know about zkBob. They do not need a zkBob account and can pay with any token.

## ZeroPool Library

The zkBob application uses the [Zero Pool](https://zeropool.network/) library to prepare zkproof transactions and interact with the relayer. The application is responsible for:

* Processing transactions
* Restoring local state
* Building zkproofs from user input (ie amount, address)
* Interacting with the relayer


# Account Creation

{% hint style="success" %}
When you arrive at [app.zkbob.com](https://app.zkbob.com) follow the prompts to get started!
{% endhint %}

## 1. Get started

Press the **Get started** butto&#x6E;**.**

<figure><img src="/files/qizyAqtAlss0BEUz4GmR" alt=""><figcaption></figcaption></figure>

## **2. Create a new zkAccount**

CIick **Create new zkAccount**. If you already have a zkAccount, select I already have a zkAccount and follow the instructions here.

<figure><img src="/files/zMj8MCkwnh9NOfvhT1mY" alt=""><figcaption></figcaption></figure>

## **3. Choose account creation method**

Choose to use your web3 wallet (like MetaMask or others through WalletConnect) or create a standalone zkBob secret phrase.  With the web3 wallet method, you will connect your web3 wallet to the application to create an account. With the secret phrase, you do not need to connect your wallet, but will need to securely save your phras&#x65;**.**

1. **Web3 wallet**. The application collects a signature locally on your browser - not on the blockchain - using your MetaMask or WalletConnect connected wallet. It uses the private key from your web3 wallet to derive a secure zkAccount address.
2. **Use a secret phrase**. The application generates a secret phrase for you to store securely. You can use this secret phrase to restore your account and/or password as needed from any computer. **With this method, you are responsible for securely saving your secret phrase**.

<figure><img src="/files/Udor8h5KP7KVf9aaxWGR" alt=""><figcaption></figcaption></figure>

Account creation methods are described below. The [**web3 wallet method** ](#metamask-walletconnect)is described first; then the [**secret phrase method**](#secret-recovery-phrase) is described.

## Web3 Wallet

### 1) Select **Use my Web3 wallet**

<figure><img src="/files/oqd0KQL8WAMleFDP4Xuj" alt=""><figcaption><p>Be sure to read the Terms of Service before creating your account.</p></figcaption></figure>

### 2) Select **MetaMask** or **WalletConnect**.&#x20;

With WalletConnect you can use a variety of web3 wallets to connect to zkBob.

<figure><img src="/files/ImX7QtxWdE5r0nPt5aAG" alt=""><figcaption></figcaption></figure>

### 3a) **MetaMask**

Connect your wallet and sign the message to create a zkAccount using your wallet's private key.&#x20;

{% hint style="warning" %}
**Note**: Be sure to [double check the address is correct](/zkbob-app/account-creation/metamask-web3-wallet-warning) (<https://app.zkbob.com>) to prevent phishing.
{% endhint %}

<figure><img src="/files/lFvtzokDjeH9c69AjVmo" alt=""><figcaption></figcaption></figure>

### 3b) **WalletConnect**&#x20;

Select a wallet or scan the QR to connect your wallet and complete the signature request.

{% hint style="warning" %}
**Note:** Be sure to [double check the address is correct](/zkbob-app/account-creation/metamask-web3-wallet-warning) (<https://app.zkbob.com>) to prevent phishing.
{% endhint %}

<figure><img src="/files/sSQ0wkbTuDVMCiQVLPX3" alt=""><figcaption><p>Connect to WalletConnect with a preferred wallet or by scanning the QR</p></figcaption></figure>

<figure><img src="/files/jEzCRgUy1XrBRLBkfuhb" alt=""><figcaption><p>Connect your wallet and Sign the message (be sure to check you are connecting to app.zkbob.com)</p></figcaption></figure>

### 4) **Create and re-enter a password**.&#x20;

This password is used to access the application and is stored locally. You can create a new password at any time using your connected wallet. Press **Verify** to proceed.

<figure><img src="/files/4ip5RxikimEyOuX7xEKs" alt=""><figcaption></figcaption></figure>

### 5) Account Created

You will see a **Loading zkAccount** message while your account is created. Initial account creation can take up to a minute depending on transaction volume in the pool. \
\
Once the zkAccount is created, you will see 2 connected accounts. You are ready to start interacting with zkBob!

1. Your web3 wallet (MetaMask or WalletConnect).
2. Your zkAccount.

<figure><img src="/files/iAwUiqPcBgSZAwedgt6g" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
Once you've created your account, you can [generate a receiving address](/zkbob-app/generate-a-secure-address) to receive a transfer, or [make a deposit](/zkbob-app/deposits) if you have BOB on Polygon or Optimism.
{% endhint %}

## Use zkBob Secret Phrase

{% hint style="info" %}
Also known as a seed phrase, you can create a new secret phrase specifically for your zkBob account. You will use this secret phrase to recover your account if needed. Be sure to store it securely off-line. If lost, there is no way to recover your account.
{% endhint %}

### 1) Use secret phrase

Select **Use zkBob secret phrase** in the popup box.

<figure><img src="/files/C6hPsehd6FgbWzxW8Ztz" alt=""><figcaption></figcaption></figure>

### 2) View and write down your phrase somewhere safe (offline).&#x20;

It is not wise to keep the secret phrase on your computer.&#x20;

<figure><img src="/files/IgKtVKutXgssDJBXnmP0" alt=""><figcaption></figcaption></figure>

### 3) Confirm your phrase&#x20;

Clicking on the words in the correct order. Click **Verify** when you are done.

<figure><img src="/files/ZK19RaAtb6b2X91AFrvX" alt=""><figcaption></figcaption></figure>

### 4) **Create and re-enter a password**

This password is used to access the application and is stored locally. You can create a new password at any time using your connected wallet. Press **Verify** to proceed.

<figure><img src="/files/o7vpZLPw2lQR53AzAEZy" alt=""><figcaption></figcaption></figure>

### 5) Account Created

You will see the **Loading zkAccount** message while your account is created.  Once created, you can use this account to receive transfers and withdraw to a different `0x` address without ever connecting a web3 wallet.

<figure><img src="/files/EulalOjPYXYpKabZx6og" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
If you want to [deposit funds](/zkbob-app/deposits) to your zkAccount, [connect your 0x wallet](#web3-wallet).
{% endhint %}

{% hint style="info" %}
If you want to receive a transfer, [generate a receiving address](/zkbob-app/generate-a-secure-address).
{% endhint %}

{% hint style="warning" %}
**Note:** When using the same computer/browser for future zkBob sessions, you can access your account with just your password (assuming local storage has not been cleared). \
\
If you change browsers/computers or clear local storage, re-enter your secret phrase (or connect your web3 wallet if you used that method) and create a new password to restore the account.
{% endhint %}

{% hint style="success" %}
Next:&#x20;

* [Make a Deposit](/zkbob-app/deposits)
* [Receive a Transfer](/zkbob-app/transfers)
* [Withdraw Funds](/zkbob-app/withdrawals)
  {% endhint %}


# Login to an existing account

If password is cleared from local storage, or accessing an existing account from a new device.

{% hint style="success" %}
When you arrive at [app.zkbob.com](https://app.zkbob.com) follow the prompts to get started!
{% endhint %}

## 1. Get started

Press the **Get started** butto&#x6E;**.**

<figure><img src="/files/qizyAqtAlss0BEUz4GmR" alt=""><figcaption></figcaption></figure>

## 2. I already have a zkAccount

<figure><img src="/files/abuG19t7ljpZfL1ENuwj" alt=""><figcaption></figcaption></figure>

## 3. How did you create your account?

1. [**Web3 wallet**](#3.1-web3-wallet)**:** You created your previous account by connecting MetaMask or WalletConnect to the application to generate your zkAccount.
2. [**zkBob secret phrase**](#3.2-zkbob-secret-phrase): You created a standalone secret phrase to create your account and have this secret phrase written down somewhere safe.

<figure><img src="/files/Nmwh8y3Lx3JiZWYmsO4A" alt=""><figcaption></figcaption></figure>

## 3.1 Web3 Wallet

You will see a prompt to connect your wallet. [**Starting from step 2**](https://docs.zkbob.com/zkbob-app/account-creation/pages/I3llDXmUjV6XRRodoK2X#2-select-metamask-or-walletconnect.), continue through the Account Creation flow to reconnect your web3 wallet, sign a message, create a password and proceed to the application.

<figure><img src="/files/ImX7QtxWdE5r0nPt5aAG" alt=""><figcaption></figcaption></figure>

## 3.2 zkBob Secret Phrase

Enter the existing secret phrase and wrote down in a secure location during initial zkAccount creation.

<figure><img src="/files/xZF8XOeI8HhqU7cvZY7d" alt=""><figcaption></figcaption></figure>

Follow the remaining flow from the[ Account Creation section](/zkbob-app/account-creation#4-create-and-re-enter-a-password) to create a new password and connect to the application.


# Lost Password

Passwords for zkBob are saved in local storage to provide a quick path to accessing an account. If you clear local storage, connect on a new device, or lose your password you can easily create a new password.

To create a new password and access your account press **Lost password? Click here to restore access**.

<figure><img src="/files/p1odMoTo4vzeyq4htohQ" alt=""><figcaption></figcaption></figure>

You will be directed to the zkAccount setup screen. Follow the [login to an existing account prompts](/zkbob-app/account-creation/login-to-an-existing-account) to restore your account with a new local password.

<figure><img src="/files/FUSfsRzSnKzdl5D6CMZN" alt=""><figcaption></figcaption></figure>


# Metamask / Web3 Wallet Warning

When deriving a zkBob account using MetaMask

zkBob provides a convenient method to create an account using Metamask (MM) or another web3 wallet using Wallet Connect. When deriving an account with a web3 wallet there is no need to write down or remember a new secret phrase for your shielded account.&#x20;

<figure><img src="/files/uIpmxv9sQED97mmMaJpI" alt=""><figcaption></figcaption></figure>

With this method, a zkBob private key is derived using your Metamask private key. You sign a message to enable this derivation. There is no fee, the operation is performed on your local machine and nothing is sent to the blockchain. The zkBob application collects your signature and creates a private key for the shielded account.&#x20;

No one can reproduce the signature without access to your Metamask private key. If you change your browser or device, simply authorize Metamask and sign the same message in the zkBob application to get access to your shielded funds.&#x20;

This method is secure and anonymous; it is not possible to reveal your private key through signature analysis.

{% hint style="danger" %}
**Security Warning**

Be vigilant when using Metamask or another web3 wallet, as an adversary can ask you to sign the *same message* on a spoofed site.

If you provide a signature to the attacker your zkBob private key is leaked and the attacker can steal all funds from the zkBob account. To protect against this threat you must always **check the source URL** sending the signing request.&#x20;

The genuine application is hosted on [**https://app.zkbob.com/**](https://app.zkbob.com/) only.

<img src="/files/th1CVX9e2EFNdTonBPeB" alt="" data-size="original">
{% endhint %}


# Deposits

{% hint style="warning" %}
Examples are performed with BOB on Polygon. However, **the BOB pool is now a USDC pool on Polygon**. You can also follow the instructions to deposit BOB or ETH on Optimism.
{% endhint %}

Deposits to the zkBob pool involve moving the supported token (USDC on Polygon, ETH or BOB on Optimism) from a web3 wallet address (MetaMask/WalletConnect) into a zkAccount. You can use the UI to easily connect a wallet and deposit into your zkAccount.

1\) Check your [accounts are connected](/zkbob-app/account-creation).

1. Web3 wallet address (MetaMask, WalletConnect)
2. zkAccount

*If you have previously setup your zkBob account using the Metamask/WalletConnect method, but the accounts are not instantly connected,* [*follow the prompts* ](/zkbob-app/account-creation#metamask-walletconnect-already-connected)*to reconnect your account.*

<figure><img src="/files/kHhfGzegAqVO9Oe2o2Zc" alt=""><figcaption></figcaption></figure>

2\) You should have some amount of tokens available to deposit. Enter the amount and press **Deposit**.&#x20;

{% hint style="warning" %}
Note that a 0.10 fee is added to any amount you deposit to pay the relayer fee. If you deposit 10 USDC 10 USDC is added to your zkAccount and an additional 0.10 USDC is deducted from your 0x address to pay the relayer. The total cost for this tx is 10.1 USDC.
{% endhint %}

<figure><img src="/files/c5AjLvtvTjzjJ1i2Rijn" alt=""><figcaption></figcaption></figure>

3\) A proof is generated on your local machine. Next, sign the Metamask notification to allow the contract to use your tokens, and wait for the relayer to complete the deposit.

<figure><img src="/files/E9qJxHawYOCJ36Xuhzia" alt=""><figcaption></figcaption></figure>

4\) Your deposit is complete! Once the transaction is finalized you can transfer to another zkAddress in the pool or withdraw.&#x20;

<figure><img src="/files/ShLlQx2xv7pXSdV1c6Ut" alt=""><figcaption></figcaption></figure>

5\) Return to the application. You will see the pending transaction, and can click through to view on Polygonscan.

<figure><img src="/files/EEJ6WAMgDr4dEWUCcEm4" alt=""><figcaption></figcaption></figure>

6\) View updated balances.

{% hint style="info" %}
Note: It may take several minutes to update the pool state. If your balances do not update (in the case below the MM address has not yet updated) you can refresh your wallet balances **from within the application**. This is easier than refreshing the page itself -  you will need to re-enter your password to access the app if you do a page refresh.
{% endhint %}

<figure><img src="/files/n0VFZenSOZcKxUCX9WTN" alt=""><figcaption><p><em>Before refresh</em></p></figcaption></figure>

<figure><img src="/files/CR4bQ5fLNd0FTzwcSOQz" alt=""><figcaption><p><em>After refresh</em></p></figcaption></figure>

*Tx details on Polygonscan :* [*https://polygonscan.com/tx/0xbb9330d77f8903b91cfcdab32d590a3298962a718ab4951262696f0b18d354c9*](https://polygonscan.com/tx/0xbb9330d77f8903b91cfcdab32d590a3298962a718ab4951262696f0b18d354c9)

<figure><img src="/files/2U5rVHU6XjWnpXKRzOUB" alt=""><figcaption></figcaption></figure>


# Transfers

Shielded transfers through the zkBob privacy pool

{% hint style="warning" %}
Examples are performed with BOB on Polygon. However, **the BOB pool is now a USDC pool on Polygon**. You can also follow the instructions to deposit BOB or ETH on Optimism.
{% endhint %}

Transfers are made within the zkpool between zkAccounts. You do not transfer to an 0x account, but rather to another zkAccount holder's generated receiving address. Typically, the receiver will send you a [generated receiving address](/zkbob-app/generate-a-secure-address) via a private channel, invoice, or some other private method.&#x20;

This also means you do not need to connect MetaMask, WalletConnect or any other web3 wallet. Transfers occur within the application with no need for outside wallet connections.

zkAccount addresses are not fixed. You can generate a new address for each incoming transaction. **It is not possible to link different private addresses to one another or to the primary account.** Only the account owner can confirm ownership of a private address.

Each created address is encoded in base58 format. For example `5fkW3dXTvA8Kizt1EbuRyjWofuqR4Ud1YTjGgY1r8nGosDeSaUreq6bwfF61jWL`

Any previously generated address can be used indefinitely, so if you send an address to one party and then generate a new address to send to another party, both can be used to receive token transfers.

See [Receiving a Transfer](#receiving-a-transfer) for instructions on generating a zkAccount address.

Interested in sending to multiple zkAddresses with 1 tx? Try the [Multitransfer feature](/zkbob-app/transfers/multitransfers).

{% hint style="info" %}
The minimum transfer amount is 0.05 BOB. A 0.10 fee is added to each transfer.
{% endhint %}

## Sending a Transfer

1\) Check your zkAccount is connected (you will see zk*AccountName* and an amount of BOB/USDC/ETH if it is connected). Since you will not be depositing funds, it is your choice whether or not to connect your MetaMask/WalletConnect wallet.

<figure><img src="/files/BZTWBb6C9Dd2QVEKUBdG" alt=""><figcaption></figcaption></figure>

2\) Enter information and initiate the transfer.

1. Amount to transfer.
2. Receiver's zkAddress.
3. Press **Transfer.**

{% hint style="info" %}
Receiver should send you a generated address from the application *\<see* [*Receiving a Transfer*](#receiving-a-transfer)*>*
{% endhint %}

<figure><img src="/files/YT8qJJSb9XFH8X8tmCVF" alt=""><figcaption></figcaption></figure>

3\) **Confirm** transfer details.

<figure><img src="/files/HskOMhQzPFNb5CSwtz8z" alt=""><figcaption></figcaption></figure>

4\) Wait for proof generation and transfer completion. Transfers cost 0.10 BOB per transfer.

{% hint style="warning" %}
Do not refresh during the transfer process. This typically takes less than 1 minute but can be longer during times of high network congestion.
{% endhint %}

![](/files/c1HzH5VUvSfWyooqGGpj)

![](/files/zLO1GgztpE7ftOrKdEVs)

Once completed you will see the latest transfer in the app. You can also check the History tab to see transfer details. Click the transaction hash to view the private tx in Polyscan.

<figure><img src="/files/pOyl2YtXhhFPHsrt9w0H" alt=""><figcaption><p>Latest transfer</p></figcaption></figure>

<figure><img src="/files/5L0zkFsHwlLeg8UTfFh4" alt=""><figcaption></figcaption></figure>

Tx details in Polyscan: [0xca5f18509ae64f4e24def56453afbce8edb86ff2f4146bbe5db1205002ed785a](https://polygonscan.com/tx/0xca5f18509ae64f4e24def56453afbce8edb86ff2f4146bbe5db1205002ed785a)

<figure><img src="/files/UHlTW1XIsv4efjBbZIuA" alt=""><figcaption></figcaption></figure>

## Receiving a Transfer

Generate and send a zkAddress to the sender to receive a shielded transfer.

1\) Press the zkAccount button.

<figure><img src="/files/BZTWBb6C9Dd2QVEKUBdG" alt=""><figcaption></figcaption></figure>

2\) Press **Generate receiving address**.

<figure><img src="/files/ZxYrDLHThtI63ZC9ZQXy" alt=""><figcaption></figcaption></figure>

3\) Copy generated address and send to your friend via a private channel of your choice.

<figure><img src="/files/mzhYMjiTEhXDDgYBRust" alt=""><figcaption></figcaption></figure>

4\) Wait for receipt. If your zkBob application remains open, you can refresh using the refresh button next to your zk account. If you refresh the browser itself, you will need to re-enter your password.

Once received, check the history tab to see the transaction, and press the tx hash for more details.


# Multitransfers

Now Bob can transfer to the whole crew\....Alice, Carlos, Dave, Erin, Frank....with a single transaction!

{% hint style="warning" %}
Examples are performed with BOB on Polygon. However, **the BOB pool is now a USDC pool on Polygon**. You can also follow the instructions to deposit BOB or ETH on Optimism.
{% endhint %}

Multitransfer allows multiple transfers to be processed in a single transaction. There are no limits to the number of transfers, and the entire process can be completed with a single proof and a single 0.10 relayer fee.  Each transfer adds an additional note to the zkproof, which can slightly increase proof generation time. Other than that, multitransfer is as smooth and seamless as an ordinary zkBob transfer!

{% hint style="info" %}
💡 Learn more about how multitransfers sets zkBob apart as a privacy solution i[n this mirror post](https://mirror.xyz/0x6132eB883e88CD4E007552b871A6444Bfc34E837/mjYXeD7a005fdCu6dKdohfrSpcqpsuetW6djT46bDFk).
{% endhint %}

## Get Started

1\) To start a multitransfer, go to the **Transfer tab** and toggle the multitransfer switch.&#x20;

<figure><img src="https://lh5.googleusercontent.com/6kqw8dXidUF5PbSnyA4lG8hcKvUsE14PLyvZ3e0sZLOPpVPmHk30rEMnXWYRu1CZRqMpK420XRTI6jrM76O0lYtmNZvgDALDn6lFjEg1nQcM0lR9iXkAN7mk1J-w3lXmhjLcCn83-JQ9v0BQBCTqGNJoenkAsiqv0kfdGJEJUUv8Uc_yQEvNINgxJNWv3g" alt=""><figcaption></figcaption></figure>

2\) The interface will change to allow entry of multiple zkAddresses and amounts. You can either add manually or by uploading a csv.\
\
**Manually**: Add in the zkAddress along with the amount to transfer, 1 per line. The format should be ZKADDRESS, AMOUNT

Press Proceed to continue.

<figure><img src="https://lh5.googleusercontent.com/PxBlXi8rVqpzwHRqR_A4q6vV27g44mo6CSs7ei01UGTGin5LmFy3hK0yfxY6i0uNuP1B97HKqonRWn4F13FytFUIGogLnkQdXsPBZ3yppdH07zp1Ew46LXvu08tDf5Tkk25pS2frp3Ds8K5WoEILxGFc2ucRBRT5TlV0L7oi8dfMMezc-JHJjUKJ9ND3BA" alt=""><figcaption></figcaption></figure>

**CSV:** If preferred, you can add directly from a CSV. Click Upload CSV and select a completed csv file from your computer (ZKADDRESS, AMOUNT  one  per line). The information will automatically populate into the box on the app.

<figure><img src="/files/kxAiPZ8yrQE5aDpXTveP" alt=""><figcaption></figcaption></figure>

<figure><img src="https://lh5.googleusercontent.com/2XZ2X-urfp79wscz8hwNLTaC-_0Qftk6srWxcjutvgnT8WBHHWhG4H54aCRFuJRF4v5Rmpf6Y4kN8IQdnL4Lt8fXhbEDxbBOW3eHIfxBnfM5wsFX6SC-uqVavwxjru7At6jvV_0Mv-CPBDJnSNPuk-BskE3w1FNIPwCiefjii3l9ysaOHgRTC_k8O8lLGA" alt=""><figcaption></figcaption></figure>

<figure><img src="https://lh5.googleusercontent.com/PxBlXi8rVqpzwHRqR_A4q6vV27g44mo6CSs7ei01UGTGin5LmFy3hK0yfxY6i0uNuP1B97HKqonRWn4F13FytFUIGogLnkQdXsPBZ3yppdH07zp1Ew46LXvu08tDf5Tkk25pS2frp3Ds8K5WoEILxGFc2ucRBRT5TlV0L7oi8dfMMezc-JHJjUKJ9ND3BA" alt=""><figcaption></figcaption></figure>

3\) Next, check the details (Click view all).

<figure><img src="https://lh6.googleusercontent.com/y7EeH0urhAiAO2BBXOz2eIaaHqcHIlOX-QHUTCMCbzUs4kFmSkVr9pKefH-VFFaGUlXtOcSH7hEjV6iB-zlciUKGAsZwpbZGrlQH_bPwWuTE8BLsGkc1zu61FaA5VzkCCb83GRyXjuUV0Jpf7e8ilZJs1d-Q8SwjPUEVGlpMrsd8BpIZ2BsEx5pfDsKWAg" alt=""><figcaption><p><br></p></figcaption></figure>

<figure><img src="https://lh3.googleusercontent.com/QElUsXGgKkj8CP4e7Mi1mVo7aaaStZ6IdJIjfZUtEMOcpF5cUoZhxDpPg3M0MMdk_OdC-LUE4rvXy-a5Ezyu9zXJwMpYy6rT-OEbDUwvGcnbk6YGTriq8rdAbHbGKeXV8S1bdnr-zMvunN42uQAIX9l58qwecmEjIsijUJsh8A4UlmRPNqcPSOQzCvlO0Q" alt=""><figcaption></figcaption></figure>

4\) If all looks good close the modal and click **Confirm multitransfer**.&#x20;

<figure><img src="https://lh5.googleusercontent.com/9a92rjirE0L9tspgeeRZWlK919bkp0415VxOkXewNWVHL0-qA7d0p9UvcVIZvMF0tyVWCt2nqY9RRSX1NiSv-Fs-h2SKv9TC7BjNZjum8QkKbhhiFOJZ_7kmHYRs3WLcyF0ox3CwYN6fKVVDFt6pJ-pZVL6nCV_enDL1v_N1cNDytQnNkvGLMkqOr4tahQ" alt=""><figcaption></figcaption></figure>

5\) The application will generate a proof and your transaction will process. Once completed you can see details about multitransfers in your transaction history.

<figure><img src="https://lh6.googleusercontent.com/pHK-0t-T8G9kL9F5LIdhxdQHHw9STsOXKDyxBfdZALUG7q7NEV68jju2VSkBdn64kSPdczzl8p5X9W-fVdE-YzZ2C4Klxp3KAcmrreJ7squYpGBtyoWilERhkL54y1dHpLEQ3djgTRPsJHfbC-aR1LbDeaLaPq7S_mnYSHu0_NMAGzAfWbijUq8ynZBGOw" alt=""><figcaption></figcaption></figure>

<figure><img src="https://lh4.googleusercontent.com/JMS7bDOkYkvl3vPka2D5Dpl_Z8GtvRUAmrQVhh02JaVf6vv85wblvBAYiFa3fQxnHW7wv_OxBXrlFbsX27NubIHdjIqqehD9S0oOaA27Mo0ypfOs-AkkjEPdwT36PIJW0HKBo_WRVjZJV-SizhlpOdw7ik3HXlMGGOHvekoJ8X9lRSuXMaSrbHHBD0EfJA" alt=""><figcaption></figcaption></figure>


# Withdrawals

{% hint style="warning" %}
Examples are performed with BOB on Polygon. However, **the BOB pool is now a USDC pool on Polygon**. You can also follow the instructions to deposit BOB or ETH on Optimism.
{% endhint %}

Withdrawals to move assets from the zkpool to an 0x address on Polygon. You can specify any account to receive the funds, and do not need to connect your wallet to complete a withdrawal. This can be a newly generated address or any address you prefer.

{% hint style="success" %}
When you withdraw 10.0 or more BOB to a receiving address on Polygon with a 0 MATIC balance, 0.1 MATIC will be automatically deposited to that address. This allows you to use an address immediately to swap or send BOB without needing to get MATIC. The faucet is not currently available on the Optimism instance.

**Typically this will work quickly, however faucet requests for 0.1 MATIC can be delayed in case of high network activity and gas price spikes.**
{% endhint %}

1\) Connect your zkAccount.

<figure><img src="/files/vxIt2bjhKwA6hMx0o2ZF" alt=""><figcaption></figcaption></figure>

2\) Specify details.

1. Amount of tokens to withdraw.
2. Receiver's 0x address.
3. Press **Withdraw.**

<figure><img src="/files/coxFyEkSNlBYV5d8Ho0i" alt=""><figcaption></figcaption></figure>

3\) **Confirm** to proceed with the withdrawal.

<figure><img src="/files/B2k0LQX24GC84jlZlEM9" alt=""><figcaption></figcaption></figure>

4\) Application will generate a proof and confirm the withdrawal.

![](/files/7edPnzZlAvEUuyOlzvpG)

![](/files/jJb3L7yCXeLGDBxDi3hi)

5\) Close the modal and wait for the withdrawal to finalize. Until the transaction is complete your balances may not reflect accurate amounts.

<figure><img src="/files/XuGKeBBDAM9fsnHxryJX" alt=""><figcaption></figcaption></figure>

Once the transaction is finalized you will see your latest withdrawal in the app, and can click on the History tab to view all transactions. Click on the hash to view details on Polyscan.

<figure><img src="/files/b9z4xC0owgo8Iyd9rreC" alt=""><figcaption></figcaption></figure>

Polyscan tx: [0x8bd6f1279c038b79816b1ddb3523e51090a9b20036ee72fb1b57f89dac5d0948](https://polygonscan.com/tx/0x8bd6f1279c038b79816b1ddb3523e51090a9b20036ee72fb1b57f89dac5d0948)

<figure><img src="/files/0UQXFxFsRXe7AkBhbVIV" alt=""><figcaption></figcaption></figure>


# Native Token Conversion

{% hint style="warning" %}
Examples are performed with BOB on Polygon. However, **the BOB pool is now a USDC pool on Polygon**. You can also follow the instructions to deposit BOB or ETH on Optimism.
{% endhint %}

You may want to withdraw BOB to a new address on Polygon or Optimism. To use this address immediately, it helps to have some native tokens sent along with BOB to cover any gas fees.

The native token conversion feature lets you convert 1, 5, or 10 BOB into native tokens (MATIC on Polygon, ETH on Optimism) during the withdrawal process.

## Token Conversion Withdrawal Process

Go to the Withdraw tab in the application and enter the amount you'd like to withdraw.

1. Toggle the switch to convert some BOB to ETH (or MATIC) on withdrawal.
2. Select the amount to convert. You can choose 1 BOB, 5 BOB, or 10 BOB if you are withdrawing enough to cover these amounts.
3. Enter the address you want to withdraw to. This can be a newly created address or any existing `0x` address.
4. Click Withdraw.

<figure><img src="/files/Xr14mFlvQKn8f4ZkDkxa" alt=""><figcaption></figcaption></figure>

## Complete the Withdrawal

A popup with details will allow you to confirm and complete the transaction. Relayer fees (dynamic fees on Optimism, 0.1 fee on Polygon) are paid with BOB during the withdrawal process and you will not need to sign a transaction in any connected wallet. It may take a minute to create the proof and initiate the withdrawal from the zero knowledge pool.

<figure><img src="/files/WWyuZAApprEBcFBqVr6a" alt=""><figcaption></figcaption></figure>


# Generate a Receiving Address

If you are receiving funds from another zkBob user, you will want to send them a secure address. A new secure receiving address can be generated for each transfer.&#x20;

**It is not possible to link different private addresses to one another or to the primary account.** Only the account owner can confirm ownership of a private address.

Each  zkAddress is encoded in base58 format. For example `5fkW3dXTvA8Kizt1EbuRyjWofuqR4Ud1YTjGgY1r8nGosDeSaUreq6bwfF61jWL`

Any previously generated address can be used indefinitely, so if you provide an address to one party and then generate a new address to provide to a second party, both can be used to receive token transfers.

## Generate a zkAddress to receive a transfer

*Generate and send a zkAddress to the sender through any private channel.*&#x20;

1\) Press the zkAccount button (your account should already be connected to initiate this process).

<figure><img src="/files/BZTWBb6C9Dd2QVEKUBdG" alt=""><figcaption></figcaption></figure>

2\) Press **Generate receiving address**.

<figure><img src="/files/ZxYrDLHThtI63ZC9ZQXy" alt=""><figcaption></figcaption></figure>

3\) Copy generated address and send to your friend via a private channel of your choice.

<figure><img src="/files/mzhYMjiTEhXDDgYBRust" alt=""><figcaption></figcaption></figure>

4\) Wait for receipt of the transfer. If your zkBob application remains open, you may need to refresh the page to see the changes. It is recommended to refresh from within the application (next to your zkAddress) rather than using the browser refresh.

<figure><img src="/files/n0VFZenSOZcKxUCX9WTN" alt=""><figcaption></figcaption></figure>

Once the transfer is received, you can check the history tab to see the transaction, and press the tx hash for more details.


# Optional KYC

<figure><img src="/files/2Z3jmnQYU7gxtylOBS3E" alt=""><figcaption></figcaption></figure>

Optional KYC is now implemented with zkBob! You can [mint a BAB token on Binance](https://www.binance.com/en/support/faq/how-to-mint-binance-account-bound-bab-token-bacaf9595b52440ea2b023195ba4a09c) and use it to increase your deposit limits on zkBob thanks to the Know your Cat protocol.&#x20;

Instructions for the KYC (Know Your Cat) protocol with zkBob are [available on the zkBob Blog](https://blog.zkbob.com/zkbob-x-kyc-know-your-cat/).

Learn more about the integration here ⬇️

{% embed url="<https://medium.com/@andreybelyakov/know-your-cat-zkbob-integration-66af7675e381>" %}


# zkBob Direct Deposits

A decentralised way of depositing to a zkBob account without using zk core components

{% hint style="warning" %}
Examples are performed with BOB on Polygon. However, **the BOB pool is now a USDC pool on Polygon**. You can also follow the instructions to deposit USDC or ETH on Optimism.
{% endhint %}

{% hint style="info" %}
While there is a test application on Sepolia and Goerli, **we strongly recommend** using the application in production on **Optimism or Polygon**.&#x20;

If you need some live tokens to get started, please contact us through a supported channel (for hackathons use the official the hackathon [Discord](https://discord.gg/ethglobal) *sponsor-zkbob* channel) and we will send BOB!&#x20;

* [Local contract deployment](/deployment/contracts-deployment)
* Production UI on Polygon/Optimism: [https://app.zkbob.com](https://app.zkbob.com/deposit)
* Testnet UI on Sepolia/Goerli: <https://staging--zkbob.netlify.app/>
  {% endhint %}

{% hint style="success" %}
Find our new [**Client Library SDK**](/implementation/client-library-sdk) for integration methods and examples.

See the [FAQs at the end](#faqs) for commonly asked questions.
{% endhint %}

## Direct Deposits Introduction

zkBob supports 5 different operations:

1. **Deposit** - Add funds to your zkAccount.
2. **Withdraw** - Withdraw tokens from your private account to a public `0x` address.
3. **Private transfer** - Make an anonymous transfer from one user to another with their zkAddress.
4. **Private multi-transfer** - Make an anonymous atomic batch transfer to multiple zkAddresses.
5. **Direct deposit -** Send tokens directly to someone’s zkAddress from outside the zkBob application.

The first 4 operations can only be performed by end users via zkBob clients, such as the zkBob UI.

However, with **direct deposits**, private deposits can be performed while abstracting away zk complexity. Depositors only need to know the private zkAddress of the receiver. This makes it very easy to integrate it into various automated web3 workflows.

Direct deposits allow any user, smart contract, or third party protocol to deposit any amount\* ([within accepted limits](#direct-deposit-limits)) of pool token into the queue, which is then processed by the zkBob relayer in a trustless manner. The only function of the relayer is to include deposits in the state tree, providing a safe mechanism for deposits.

The relayer can process multiple direct deposits at once, however, users may need to wait some time - possibly up to a few hours - until the deposit is reflected in the zkBob account. [More on transaction types here.](/implementation/transaction-overview/transaction-types)

{% hint style="warning" %}
For now, pending or cancelled direct deposits are not shown in the zkBob UI. Users will only see already processed deposits.
{% endhint %}

## Direct deposits

Direct deposits allow end users, intermediary protocols and smart contracts to make private transfers directly to the receiver zk address. Any EVM-based workflow containing one or more stablecoin transfers might be easily integrated with zkBob direct deposits on the smart contract level, with very little modification required on the UI side.

Examples where zkBob direct deposits can be integrated:

* Swap and cross-chain bridge aggregators
* Payroll and funds streaming protocols
* Donation / fundraising / tipping applications
* Vendor purchases
* Open-source wallets and browser extensions

## Direct deposits integration

Direct deposits can be easily integrated on the smart contract level using the simple interface (available at [GitHub](https://github.com/zkBob/zkbob-contracts/blob/develop/src/interfaces/IZkBobDirectDeposits.sol)).

<details>

<summary>IZkBobDirectDeposits.sol</summary>

```solidity
// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.0;

interface IZkBobDirectDeposits {
    enum DirectDepositStatus {
        Missing, // requested deposit does not exist
        Pending, // requested deposit was submitted and is pending in the queue
        Completed, // requested deposit was successfully processed
        Refunded // requested deposit was refunded to the fallback receiver
    }

    struct DirectDeposit {
        address fallbackReceiver; // refund receiver for deposits that cannot be processed
        uint96 sent; // sent amount in BOB tokens (18 decimals)
        uint64 deposit; // deposit amount, after subtracting all fees (9 decimals)
        uint64 fee; // deposit fee (9 decimals)
        uint40 timestamp; // deposit submission timestamp
        DirectDepositStatus status; // deposit status
        bytes10 diversifier; // receiver zk address, part 1/2
        bytes32 pk; // receiver zk address, part 2/2
    }

    /**
     * @notice Retrieves the direct deposits from the queue by its id.
     * @param depositId id of the submitted deposit.
     * @return deposit recorded deposit struct
     */
    function getDirectDeposit(uint256 depositId) external view returns (DirectDeposit memory deposit);

    /**
     * @notice Performs a direct deposit to the specified zk address.
     * In case the deposit cannot be processed, it can be refunded later to the fallbackReceiver address.
     * @param fallbackReceiver receiver of deposit refund.
     * @param amount direct deposit amount.
     * @param zkAddress receiver zk address.
     * @return depositId id of the submitted deposit to query status for.
     */
    function directDeposit(
        address fallbackReceiver,
        uint256 amount,
        bytes memory zkAddress
    )
        external
        returns (uint256 depositId);

    /**
     * @notice Performs a direct deposit to the specified zk address.
     * In case the deposit cannot be processed, it can be refunded later to the fallbackReceiver address.
     * @param fallbackReceiver receiver of deposit refund.
     * @param amount direct deposit amount.
     * @param zkAddress receiver zk address.
     * @return depositId id of the submitted deposit to query status for.
     */
    function directDeposit(
        address fallbackReceiver,
        uint256 amount,
        string memory zkAddress
    )
        external
        returns (uint256 depositId);

    /**
     * @notice ERC677 callback for performing a direct deposit.
     * Do not call this function directly, it's only intended to be called by the token contract.
     * @param from original tokens sender.
     * @param amount direct deposit amount.
     * @param data encoded address pair - abi.encode(address(fallbackReceiver), bytes(zkAddress))
     * @return ok true, if deposit of submitted successfully.
     */
    function onTokenTransfer(address from, uint256 amount, bytes memory data) external returns (bool ok);

    /**
     * @notice Tells the direct deposit fee, in zkBOB units (9 decimals).
     * @return fee direct deposit submission fee.
     */
    function directDepositFee() external view returns (uint64 fee);

    /**
     * @notice Tells the timeout after which unprocessed direct deposits can be refunded.
     * @return timeout duration in seconds.
     */
    function directDepositTimeout() external view returns (uint40 timeout);

    /**
     * @notice Tells the nonce of next direct deposit.
     * @return nonce direct deposit nonce.
     */
    function directDepositNonce() external view returns (uint32 nonce);

    /**
     * @notice Refunds specified direct deposit.
     * Can be called by anyone, but only after the configured timeout has passed.
     * Function will revert for deposit that is not pending.
     * @param index deposit id to issue a refund for.
     */
    function refundDirectDeposit(uint256 index) external;

    /**
     * @notice Refunds multiple direct deposits.
     * Can be called by anyone, but only after the configured timeout has passed.
     * Function will do nothing for non-pending deposits and will not revert.
     * @param indices deposit ids to issue a refund for.
     */
    function refundDirectDeposit(uint256[] memory indices) external;
}

```

</details>

<details>

<summary>IZkBobDirectDepositsETH.sol</summary>

```solidity
// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.0;

import "./IZkBobDirectDeposits.sol";

interface IZkBobDirectDepositsETH is IZkBobDirectDeposits {
    /**
     * @notice Performs a direct deposit to the specified zk address in native token.
     * In case the deposit cannot be processed, it can be refunded later to the fallbackReceiver address.
     * @param fallbackReceiver receiver of deposit refund.
     * @param zkAddress receiver zk address.
     * @return depositId id of the submitted deposit to query status for.
     */
    function directNativeDeposit(
        address fallbackReceiver,
        bytes memory zkAddress
    )
        external
        payable
        returns (uint256 depositId);

    /**
     * @notice Performs a direct deposit to the specified zk address in native token.
     * In case the deposit cannot be processed, it can be refunded later to the fallbackReceiver address.
     * @param fallbackReceiver receiver of deposit refund.
     * @param zkAddress receiver zk address.
     * @return depositId id of the submitted deposit to query status for.
     */
    function directNativeDeposit(
        address fallbackReceiver,
        string memory zkAddress
    )
        external
        payable
        returns (uint256 depositId);
}
```

</details>

### Creating a zkAddress for the receiver

{% hint style="info" %}
The most convenient way for users to generate a receiving zkAddress is to use the zkBob UI.  \
\
Open an account and generate an address: [Generate a Receiving Address](/zkbob-app/generate-a-secure-address)

zkAddresses can be [formatted in different ways](#zkaddress-format) for direct deposit submission.&#x20;
{% endhint %}

### Submitting a direct deposit

Direct deposits are submitted directly to the direct deposits queue contract (e.g. [0x318e2C1f5f6Ac4fDD5979E73D498342B255fC869](https://optimism.blockscout.com/address/0x318e2C1f5f6Ac4fDD5979E73D498342B255fC869) on Optimism, see [Deployed Contracts](/implementation/deployed-contracts) for addresses on other networks), using one of three approaches:

1. Common `approve` + `directDeposit` approach, suitable for a majority of use-cases. Available for all supported tokens.
2. Shortcut approach using `transferAndCall`, suitable for simple workflows. Available only for BOB token.
3. Shortcut `directNativeDeposit` approach, suitable for depositing native ETH. Available only for ETH deposits in the ETH pool.

{% hint style="info" %}
The  ERC677 `transferAndCall` approach is more gas efficient. However, it does not return the `depositId`. If you need to implement any post-submission logic in your application on the smart contract level and you need a `depositId`, please use the 1st approach.
{% endhint %}

Calling either of these methods requires 3 input arguments:

1. `fallbackReceiver` - regular user public `0x` address, which will receive the funds in the unlikely scenario that the deposit is rejection by the system \
   (**DO NOT** **set this address to zero or address that does not belong to the intended receiver**)
2. `amount` - direct deposit amount. A small flat fee is subtracted from each deposit to account for relayer expenses.
3. `zkAddress` - private zk address of the intended receiver, see info below for the supported address formats

**Polygon USDC Example**

```solidity
IERC20 usdc = IERC20(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174);
IZkBobDirectDeposits queue = IZkBobDirectDeposits(0x668c5286eAD26fAC5fa944887F9D2F20f7DDF289);

// zkbob_polygon:QsnTijXekjRm9hKcq5kLNPsa6P4HtMRrc3RxVx3jsLHeo2AiysYxVJP86mz6t7k
bytes memory zkAddress = bytes("QsnTijXekjRm9hKcq5kLNPsa6P4HtMRrc3RxVx3jsLHeo2AiysYxVJP86mz6t7k");

address fallbackReceiver = msg.sender;

// Option A, through pool contract
usdc.approve(address(queue), 100 * 1e6);
uint256 depositId = queue.directDeposit(fallbackReceiver, 100 * 1e6, zkAddress);
```

**Optimism BOB Example**

```solidity
IERC20 bob = IERC20(0xB0B195aEFA3650A6908f15CdaC7D92F8a5791B0B);
IZkBobDirectDeposits queue = IZkBobDirectDeposits(0x15B8C75c024acba8c114C21F42eb515A762c0014);

// zkbob_optimism:CFiQ36Ghy3LRDFY7YmGvQE8nQaFGQmnbgorjFxDsDqKSdmfzUYtb6FTaWnHZBkP
bytes memory zkAddress = bytes("CFiQ36Ghy3LRDFY7YmGvQE8nQaFGQmnbgorjFxDsDqKSdmfzUYtb6FTaWnHZBkP");

address fallbackReceiver = msg.sender;

// Option A, through pool contract
// Note that ether is an alias for 10**18 multiplier, as BOB token has 18 decimals
bob.approve(address(queue), 100 ether);
uint256 depositId = queue.directDeposit(fallbackReceiver, 100 ether, zkAddress);

// Option B, through ERC677 token interface
bob.transferAndCall(address(queue), 100 ether, abi.encode(fallbackReceiver, zkAddress));
```

**Optimism ETH Example**

```solidity
IERC20 weth = IERC20(0x4200000000000000000000000000000000000006);
IZkBobDirectDepositsETH queue = IZkBobDirectDepositsETH(0x318e2C1f5f6Ac4fDD5979E73D498342B255fC869);

// zkbob_optimism_eth:Pny2VS1uphaJkHZdvfzhLdmruM6BMPSqyACzZkcUbJpmAe1hKwpi6Em56gKYsz3
bytes memory zkAddress = bytes("Pny2VS1uphaJkHZdvfzhLdmruM6BMPSqyACzZkcUbJpmAe1hKwpi6Em56gKYsz3");

address fallbackReceiver = msg.sender;

// Option A, through pool contract
weth.approve(address(queue), 1 ether);
uint256 depositId1 = queue.directDeposit(fallbackReceiver, 1 ether, zkAddress);

// Option C, through native ETH deposit
uint256 depositId2 = queue.directNativeDeposit{value: 1 ether}(fallbackReceiver, zkAddress);
```

Calls to either of the deposit methods can revert with corresponding revert reasons in case of the following scenarios:

1. Insufficient token allowance for `directDeposit` call.
2. Zero `fallbackReceiver` parameter.
3. Insufficient deposit amount to cover the implied deposit fee.
4. Deposit amount exceeding the single deposit limit for a particular user.
5. Sum of daily direct deposits exceeding daily deposit limit for a particular user.
6. Invalid zkAddress format/length/checksum.

The most straightforward way to test direct deposit submission is through the Blockscout UI:\
\
<https://optimism.blockscout.com/address/0x318e2C1f5f6Ac4fDD5979E73D498342B255fC869?tab=write_proxy>

<figure><img src="/files/HyK7enW6j8dPtBxj0xTO" alt="" width="563"><figcaption><p>Making a direct deposit from the Blockscout UI</p></figcaption></figure>

{% hint style="warning" %}
Submitting a direct deposit in most scenarios will require the end user to pay transaction fees in MATIC or ETH, which is not the same for regular zkBob operations.
{% endhint %}

### Checking deposit status

Submitted direct deposits are not executed immediately and instead are placed into the queue with `Pending` status to be later processed by the zkBob relayer.

In case your workflow requires any post-execution logic, you can request the actual deposit status from the pool contract.

```solidity
IZkBobDirectDeposits.DirectDeposit memory deposit = queue.getDirectDeposit(depositId);
require(deposit.status == IZkBobDirectDeposits.DirectDepositStatus.Completed);
```

### Refund pending direct deposit

In the unlikely event the deposit is rejected by the system due to compliance or AML reasons, a previously submitted direct deposit can be refunded back to the specified fallback receiver address 1 day following the deposit submission.

```solidity
queue.refundDirectDeposit(depositId);
```

### Request extra system information

```solidity
uint64 fee = queue.directDepositFee();
uint40 timeout = queue.directDepositTimeout();
uint32 nextDepositId = queue.directDepositNonce();
```

## zkAddress format

zkBob smart contracts supports 3 different zkAddress formats. These are identical to one to another with the exception of a small difference in their processing.

zkAddress consists of two main parts - a diversifier and a public key. More information here - [Address derivation](/implementation/zkbob-keys/address-derivation).&#x20;

zkBob smart contracts support multiple address formats, which contain a different encoding of the two parts.

{% hint style="info" %}
The most convenient way to generate a receiving zkAddress is to use the zkBob UI to open an account and an address - [Generate a Receiving Address](/zkbob-app/generate-a-secure-address)
{% endhint %}

1. zkBob UI format (**recommended for Optimism**):
   * Example: `QsnTijXekjRm9hKcq5kLNPsa6P4HtMRrc3RxVx3jsLHeo2AiysYxVJP86mz6t7k` (simply remove the `zkbob_polygon:` human readable prefix)
   * Base58 encoding of the following string: `bytes10(diversifier_le) ++ bytes32(pk_le) ++ bytes4(checksum)`
   * Length of up to 63 base58 symbols
   * Base58 decoding is done on the smart contract level at the moment of deposit submission and is gas intensive (\~610k gas)
2. Pre-decoded zkBob UI format (**recommended for Polygon / Optimism**):
   * Example: `0xda9ee1b1b651c87a76c2efe3e4b9b0a0e53e5b66ed19ad100afe5289ea732bfd5ac002969523f26e6f2ff9ddd34b`
   * Same format as in the zkBob UI, but omitting the base58 encoding - `bytes10(diversifier_le) ++ bytes32(pk_le) ++ bytes4(checksum)`
   * Much more gas efficient decoding than the previous format (\~3,400 gas)
3. Raw format:
   * Example: `0xc2767ac851b6b1e19eda2f6f6ef223959602c05afd2b73ea8952fe0a10ad19ed665b3ee5a0b0b9e4e3ef`
   * Raw format without checksum - `bytes10(diversifier_be) ++ bytes32(pk_be)`
   * Most gas efficient decoding (\~1,100 gas)
   * Addresses without checksums can be error-prone and are generally not recommended for usage

{% hint style="warning" %}
Note the difference in endianness between encodings. Addresses coming from zkBob UI use little endian for zk address encoding.

Sending direct deposit to the incorrectly serialized address might result in the loss of funds.
{% endhint %}

### Checksum

zkBob address checksum is first 4 bytes of `keccak256(bytes3(pool_id) ++ keccak256(bytes10(diversifier_le) ++ bytes32(pk_le)))`

### Solidity Library

zkBob smart contracts use the following Solidity library for address parsing - [GitHub](https://github.com/zkBob/zkbob-contracts/blob/develop/src/libraries/ZkAddress.sol)

Use it to test off-chain zkAddress validation logic, or call read-only `parseZkAddress` function to convert zkBob UI address format to raw format&#x20;

## Direct deposit limits

All submitted direct deposits are subject to per-user limits at the time of their submission. Submission of direct deposits exceeding configured limits will revert.

Default direct deposits limits for all users:

1. Max amount of a single deposit - **1,000 BOB / 1,000 USDC / 5 ETH**
2. Max daily sum of all single user deposits - **10,000 BOB / 10,000 USDC / 5 ETH**

Limits can be adjusted in the future. Please refer to the up-to-date limit values displayed in the UI, or obtained through the smart contract interface.

## Direct deposit fee

Apart from associated gas costs for the deposit transaction submission, which is paid by the user, each direct deposit submission also costs **0.1** **BOB / 0.1 USDC / 0.0002 ETH** to process.

Fees can be adjusted in the future. Please refer to the up-to-date fee values displayed in the UI, or obtained through the smart contract interface.

## FAQs

### zkBob is on Polygon/Optimism, does that mean my app also needs to be deployed there?

We have production ready zkBob deployments on **Polygon and Optimism**, ideally your app should be deployed on either of those chains. Potentially direct deposits could be available on other chains as well, if properly integrated into one of the cross-chain bridges.

For testing purposes, we also support staging zkBob deployments on Sepolia and Goerli testnet. These are open for public use.

Read more about all relevant contract addresses for all networks zkBob is present at in [Deployed Contracts](/implementation/deployed-contracts)

### How to get testnet tokens in Goerli/Sepolia?

There are BOB/USDC faucet contracts available in both Goerli/Sepolia ([Deployed Contracts](/implementation/deployed-contracts)), which allow anyone to get up to 10k BOB/USDC per transaction using the Blockscout UI.

### Does a direct deposit operation require MATIC/ETH?

Yes, a native token (like MATIC or ETH) is required to pay the gas fees for a direct deposit transaction. In addition, a fee of 0.10 BOB / 0.0002 ETH will be charged for each direct deposit tx. For regular deposits, transfers or withdrawals within the zkBob application, MATIC/ETH is not required.

### How do I get started?

If you need BOB to get started (hackathon participants), please send us a message in the [Discord](https://discord.gg/ethglobal) *sponsor-zkbob* channel and we will send you some. We are also available for any questions there. If you are a developer, you can contact us via the [zkBob discord](https://discord.com/invite/zkbob).

### What is the address of the zkBob UI?

* **Polygon / Optimism** instance: [https://app.zkbob.com](https://app.zkbob.com/deposit)
* **Testnet** (Sepolia/Goerli) instance: <https://staging--zkbob.netlify.app/>


# Support ID

If you encounter issues with the application the Support ID can help with troubleshooting any problems

Due to the anonymous nature of transactions within the zkBob pool, it is difficult to troubleshoot and resolve issues surrounding any failed transactions. Errors within the application can then result in lost funds for users who cannot be identified.&#x20;

To remedy this, we issue a session-based Support ID,  a random uuid string randomly generated for each user’s session. This unique Support ID is created and sent to the relayer along with any actions completed during a single session. The support team can then follow the transaction thread to determine where and why the error occurred and help users recover funds when appropriate.&#x20;

If a transaction is rejected or another error occurs, an error will appear along with the Support ID. Copy this info and [submit a help request on Canny](#if-you-receive-an-error-while-using-zkbob) for troubleshooting purposes.

<figure><img src="/files/DXh5Xa1YeUYLBt25Yy1c" alt=""><figcaption><p>Transaction rejected - copy whole error and submit on canny</p></figcaption></figure>

<figure><img src="/files/BiTjjdnpXqUsYQtmieGN" alt=""><figcaption><p>Support ID is also listed in the footer of the zkBob application</p></figcaption></figure>

## If you receive an error while using zkBob

Contact us [in Discord](https://discord.gg/zkbob) and we will help you troubleshoot the problem.


# Payment Links

## Payment links

Now you can create a link that can be further distributed for the sake of receiving donations or other payments. All this happens through ZkBob, thanks to which your privacy is preserved...

1\) First you need to switch to the Polygon network with USDC.

![](https://img1.teletype.in/files/c9/39/c939d18e-0ab8-494e-bef1-28dd82ce96f2.png)

2\) Click on zkAccount:

![](https://img4.teletype.in/files/33/3c/333c2316-8e22-40a0-9e81-a708b99a2f6a.png)

3\) "Get payment link":

![](https://img4.teletype.in/files/b1/35/b135f510-c389-49f4-816b-04b2bafee69e.png)

4\) You can copy or share. In this example we follow the link to show you how everything works:

![](https://img1.teletype.in/files/05/ce/05cebf28-46ed-4c4a-b213-dbb18948ff6d.png)

5\) The amount has already been entered. Click "Send" and sign in your web3 wallet:

![](https://img1.teletype.in/files/89/97/899760ea-782e-4f30-aa5a-dbc96a47d884.png)

6\) Confirm in the wallet and that's it:

![](https://img2.teletype.in/files/93/67/93677e96-dce1-4289-8bb3-e854389f5776.png)

7\) Go to History and see what has been received. The balance will be updated in the near future:

![](https://img2.teletype.in/files/18/48/18489607-e4ad-448a-921d-5b477e577de4.png)

*Instructions submitted via* [*community member PR* ](https://github.com/zkBob/docs/pull/17)


# Integrated Services

zkBob uses several services to collect generalized analytics information to improve the product offering. Tools and services include:

* [Pendo](https://www.pendo.io/) - user engagement statistics
* [Chameleon ](https://www.chameleon.io/)- tutorials and UI enhancements
* Google Analytics -basic usage statistics


# Multilingual support

The zkBob UI is available for several languages

zkBob has been translated by the community into several languages. The following are currently available:

* English (default)
* [Português](/zkbob-app/multilingual-support/portugues)
* [Русский](https://docs.zkbob.com/zkbob-app/multilingual-support/russkii)
* [中文](https://docs.zkbob.com/zkbob-app/multilingual-support/zhong-wen)
* *More coming soon*

### Add support for new languages

To leave a support request fora new localization with zkBob:

1. Open [the docs repo](https://github.com/zkBob/docs) and move to the "Issues" tab
2. Create an Issue based on the following structure:
   1. Title: "Add \<LANG> localization to the zkBob app."
   2. Add a file with the translation to the comment and leave any other notices. An example with the English translation can be found [here](https://ipfs.filebase.io/ipfs/QmQEdzJH9qUE5UU8y5b8dehNVFCp3b8yyJPZfW58HTdegY).
3. Submit the Issue. Our team reviews all issues at least once per week.&#x20;

Immediately after reviewing the file, we will come back with comments to consider or rough estimates of integration.


# Português

zkBob is available in Português

To access the Português interface:

1\) Click the 3 dots to access More about zkBob.\
2\) Select the English language icon.

<figure><img src="/files/mVQXsd44EkXvWVCiNQvO" alt=""><figcaption></figcaption></figure>

3\) Select Português from the dropdown list.

<figure><img src="/files/EbGBqiWlEovRxhhVmA1p" alt=""><figcaption></figcaption></figure>

4\) The zkBob UI will be updated to Português for all operations.

<figure><img src="/files/d0UbI4s3OBC0LBbX9iPf" alt=""><figcaption></figcaption></figure>


# Русский

{% hint style="success" %}
RU Translation added by community member [maxvell3337](https://github.com/maxvell3337) in <https://github.com/zkBob/docs/issues/15>
{% endhint %}

### To access the Русский interface&#x20;

1\) Click the 3 dots to access More about zkBob.\
2\) Select the English language icon.

<figure><img src="/files/mVQXsd44EkXvWVCiNQvO" alt=""><figcaption></figcaption></figure>

3\) Select Русский from the dropdown list.

<figure><img src="/files/n9vozQweJUChZVN4k7bv" alt=""><figcaption></figcaption></figure>

4\) The zkBob UI will be updated to Русский for all operations.

<figure><img src="/files/iftdHGbzD4KVulJK5BGO" alt=""><figcaption></figcaption></figure>


# 中文

{% hint style="success" %}
ZH-CN Translation added by community member [**Yondcutie**](https://github.com/Yondcutie)  in <https://github.com/zkBob/docs/issues/16>
{% endhint %}

### To access the 中文 interface:

1\) Click the 3 dots to access More about zkBob.\
2\) Select the English language icon.

<figure><img src="/files/mVQXsd44EkXvWVCiNQvO" alt=""><figcaption></figcaption></figure>

3\) Select 中文 from the dropdown list.

<figure><img src="/files/xV4d9d5UAMIvRYotJwkk" alt=""><figcaption></figcaption></figure>

4\) The zkBob UI will be updated to 中文 for all operations.

<figure><img src="/files/rlg8KyvCdXgGAeq3lgzq" alt=""><figcaption></figcaption></figure>


# zkBob Application Overview

Main system components

## Components&#x20;

{% hint style="info" %}
[View deployed contracts here](/implementation/deployed-contracts).
{% endhint %}

The zkBob application is comprised of the following components:

* [**zkBob Pool**](/implementation/contracts-and-circuits/the-pool-contract)**:** The main contract which holds the zero-knowledge state. It's the core solution component. All transactions are sent here.
* [**Bob Token**](/implementation/contracts-and-circuits/token-contract): The BOB token contract, an ERC20-based fungible token with additional features.
* [**Relayer**](/implementation/relayer-node)**:** An intermediate actor between users and the Pool. The Relayer receives user transactions, calculates zkSNARKs, and sends transactions to the Pool in a serial manner. While users can send transactions directly to the Pool, the collision probability increases (and user transactions can be reverted due to incorrect rollup state), so it makes sense to send to the Relayer.
* [**Operator Manager**](/implementation/contracts-and-circuits/operator-manager-contract)**:** The contract which manages access to the Pool. It can be used to activate a single relayer for a defined period of time or provide unlimited access to the Pool. It also checks that the fee receiver and relayer are separate entities.
* [**Application**](/zkbob-app/zkbob-app): The solution front-end and UI. All transactions are initiated here. The application synchronises a user account with the rollup, and helps to create transactions.


# Deployed Contracts

zkBob deployed contracts

{% hint style="info" %}
Contract deployments in this section cover all aspects of the zkBob application deployed on several networks.
{% endhint %}

## Polygon (USDC.e)

RPC: <https://rpc.ankr.com/polygon>\
Chain ID: 137\
zkBob Pool ID: 0

<table><thead><tr><th width="289">Contract name</th><th>Address</th></tr></thead><tbody><tr><td>USDC.e Token</td><td><a href="https://polygonscan.com/address/0x2791bca1f2de4661ed88a30c99a7a9449aa84174">0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174</a></td></tr><tr><td>zkBob Pool</td><td><a href="https://polygonscan.com/address/0x72e6b59d4a90ab232e55d4bb7ed2dd17494d62fb">0x72e6b59d4a90ab232e55d4bb7ed2dd17494d62fb</a></td></tr><tr><td>zkBob Direct Deposits Queue</td><td><a href="https://polygonscan.com/address/0x668c5286ead26fac5fa944887f9d2f20f7ddf289">0x668c5286eAD26fAC5fa944887F9D2F20f7DDF289</a></td></tr><tr><td>Operator Manager</td><td><a href="https://polygonscan.com/address/0x8aEb89D5C689C2cf373Fe8b56c7A0cD5BDc74CE6">0x8aEb89D5C689C2cf373Fe8b56c7A0cD5BDc74CE6</a></td></tr><tr><td>KYC Manager</td><td><a href="https://polygonscan.com/address/0xb8580ea6312dd2311d72bc932b0354a07d974138">0xb8580Ea6312DD2311d72bC932b0354A07d974138</a></td></tr><tr><td>Transfer Verifier</td><td><a href="https://polygonscan.com/address/0xa86c511832ead78d30ad49711874a9f3a1dfb840">0xA86C511832eAd78d30ad49711874a9F3a1dfb840</a></td></tr><tr><td>Tree Update Verifier</td><td><a href="https://polygonscan.com/address/0x82907eAeB25D248dC82033E45b00A3E012Ba2d0D">0x82907eAeB25D248dC82033E45b00A3E012Ba2d0D</a></td></tr><tr><td>Batch Direct Deposit Verifier</td><td><a href="https://polygonscan.com/address/0x9a7b4198065efe631a962e737bdfe1f44f2cb3ee">0x9a7B4198065efE631a962e737bDfE1f44f2CB3EE</a></td></tr><tr><td>Relayer (Operator) Address</td><td><a href="https://polygonscan.com/address/0xc2c4AD59B78F4A0aFD0CDB8133E640Db08Fa5b90">0xc2c4AD59B78F4A0aFD0CDB8133E640Db08Fa5b90</a></td></tr><tr><td>MATIC Faucet (sends 0.1 MATIC to addresses with 0 balance on withdrawal >= 10 BOB)</td><td><a href="https://polygonscan.com/address/0xdf59ba2cf116397de71cf18d85eaa286ce6759ba">0xdf59ba2cf116397de71cf18d85eaa286ce6759ba</a></td></tr><tr><td>Upgradeability timelock (24h)</td><td><a href="https://polygonscan.com/address/0x9eC9D8B2Ff9b9f93D7eD3362D714d751B4f8982a">0x9eC9D8B2Ff9b9f93D7eD3362D714d751B4f8982a</a></td></tr></tbody></table>

## Optimism (USDC)

RPC: <https://rpc.ankr.com/optimism>\
Chain ID: 10\
zkBob Pool ID: 1

<table><thead><tr><th width="294">Contract name</th><th>Address</th></tr></thead><tbody><tr><td>USDC Token</td><td><a href="https://optimism.blockscout.com/token/0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85">0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85</a></td></tr><tr><td>zkBob Pool</td><td><a href="https://optimism.blockscout.com/address/0x1CA8C2B9B20E18e86d5b9a72370fC6c91814c97C">0x1CA8C2B9B20E18e86d5b9a72370fC6c91814c97C</a></td></tr><tr><td>zkBob Direct Deposits Queue</td><td><a href="https://optimism.blockscout.com/address/0x15B8C75c024acba8c114C21F42eb515A762c0014">0x15B8C75c024acba8c114C21F42eb515A762c0014</a></td></tr><tr><td>Operator Manager</td><td><a href="https://optimism.blockscout.com/address/0xF853E272893035a8C6A82616B7b442aB329D92D9">0xF853E272893035a8C6A82616B7b442aB329D92D9</a></td></tr><tr><td>Transfer Verifier</td><td><a href="https://optimism.blockscout.com/address/0x7aD8d97c60BFb59e501e3b6C1d8E564b0bB8195d">0x7aD8d97c60BFb59e501e3b6C1d8E564b0bB8195d</a></td></tr><tr><td>Tree Update Verifier</td><td><a href="https://optimism.blockscout.com/address/0x2C34aFcB1c51796c3c0C7710c72a56Eb72E1E81D">0x2C34aFcB1c51796c3c0C7710c72a56Eb72E1E81D</a></td></tr><tr><td>Batch Direct Deposit Verifier</td><td><a href="https://optimism.blockscout.com/address/0x85afa00f38aD5F353C2B80985407b8E8A27eA38f">0x85afa00f38aD5F353C2B80985407b8E8A27eA38f</a></td></tr><tr><td>Relayer (Operator) Address</td><td><a href="https://optimism.blockscout.com/address/0xb9CD01c0b417b4e9095f620aE2f849A84a9B1690">0xb9CD01c0b417b4e9095f620aE2f849A84a9B1690</a></td></tr><tr><td>Upgradeability timelock (24h)</td><td><a href="https://optimism.blockscout.com/address/0xbe7D4E55D80fC3e67D80ebf988eB0E551cCA4eB7">0xbe7D4E55D80fC3e67D80ebf988eB0E551cCA4eB7</a></td></tr></tbody></table>

## Optimism (ETH)

RPC: <https://rpc.ankr.com/optimism>\
Chain ID: 10\
zkBob Pool ID: 2

<table><thead><tr><th width="294">Contract name</th><th>Address</th></tr></thead><tbody><tr><td>WETH Token</td><td><a href="https://optimism.blockscout.com/address/0x4200000000000000000000000000000000000006">0x4200000000000000000000000000000000000006</a></td></tr><tr><td>zkBob Pool</td><td><a href="https://optimism.blockscout.com/address/0x58320A55bbc5F89E5D0c92108F762Ac0172C5992">0x58320A55bbc5F89E5D0c92108F762Ac0172C5992</a></td></tr><tr><td>zkBob Direct Deposits Queue</td><td><a href="https://optimism.blockscout.com/address/0x318e2C1f5f6Ac4fDD5979E73D498342B255fC869">0x318e2C1f5f6Ac4fDD5979E73D498342B255fC869</a></td></tr><tr><td>Operator Manager</td><td><a href="https://optimism.blockscout.com/address/0xEe174e75c206498649d04050528008020FCEb88A">0xEe174e75c206498649d04050528008020FCEb88A</a></td></tr><tr><td>Transfer Verifier</td><td><a href="https://optimism.blockscout.com/address/0xFc84D2963a1711c98EA7592C91bb207d75Ed1040">0xFc84D2963a1711c98EA7592C91bb207d75Ed1040</a></td></tr><tr><td>Tree Update Verifier</td><td><a href="https://optimism.blockscout.com/address/0xe4F2Ab4eC79A0D23Fb96489b57d558b637C68303">0xe4F2Ab4eC79A0D23Fb96489b57d558b637C68303</a></td></tr><tr><td>Batch Direct Deposit Verifier</td><td><a href="https://optimism.blockscout.com/address/0xf52e8c8eBde32495A9a79B61E0b91f65A71F343A">0xf52e8c8eBde32495A9a79B61E0b91f65A71F343A</a></td></tr><tr><td>Relayer (Operator) Address</td><td><a href="https://optimism.blockscout.com/address/0x65Eb51b16678d57Bb0bB8d160D1b9D0a57880512">0x65Eb51b16678d57Bb0bB8d160D1b9D0a57880512</a></td></tr><tr><td>Upgradeability timelock (24h)</td><td><a href="https://optimism.blockscout.com/address/0xbe7D4E55D80fC3e67D80ebf988eB0E551cCA4eB7">0xbe7D4E55D80fC3e67D80ebf988eB0E551cCA4eB7</a></td></tr></tbody></table>

***

## Sepolia Testnet (BOB)

RPC: [https://rpc.sepolia.org](https://rpc.sepolia.org/)\
Chain ID: 11155111\
Faucet: <https://sepolia-faucet.pk910.de> , <https://grabteeth.xyz/>\
zkBob Pool ID: 0

<table><thead><tr><th width="300">Contract name</th><th>Address</th></tr></thead><tbody><tr><td>BOB Token</td><td><a href="https://sepolia.etherscan.io/token/0x2c74b18e2f84b78ac67428d0c7a9898515f0c46f">0x2C74B18e2f84B78ac67428d0c7a9898515f0c46f</a></td></tr><tr><td>zkBob Pool</td><td><a href="https://sepolia.etherscan.io/address/0x3bd088c19960a8b5d72e4e01847791bd0dd1c9e6">0x3bd088C19960A8B5d72E4e01847791BD0DD1C9E6</a> </td></tr><tr><td>zkBob Direct Deposits Queue</td><td><a href="https://sepolia.etherscan.io/address/0xE3Dd183ffa70BcFC442A0B9991E682cA8A442Ade">0xE3Dd183ffa70BcFC442A0B9991E682cA8A442Ade</a></td></tr><tr><td>Operator Manager</td><td><a href="https://sepolia.etherscan.io/address/0xe342ca03a553383c1983b892c24290c1ce1b614f">0xe342ca03a553383c1983b892c24290c1ce1b614f</a> </td></tr><tr><td>KYC Manager</td><td><a href="https://sepolia.etherscan.io/address/0x98DB3A72BeF2145A8F8d8B94F81317341Af2b08C">0x98DB3A72BeF2145A8F8d8B94F81317341Af2b08C</a></td></tr><tr><td>Transfer Verifier</td><td><a href="https://sepolia.etherscan.io/address/0x5573D865cF113d44d219FaF1b26F5785Cb2eA3EE">0x5573D865cF113d44d219FaF1b26F5785Cb2eA3EE</a></td></tr><tr><td>Tree Update Verifier</td><td><a href="https://sepolia.etherscan.io/address/0x4b6f007a91c5733cd4f8bbec5ba5951f8303cdab">0x4b6f007a91c5733cd4f8bbec5ba5951f8303cdab</a> </td></tr><tr><td>Batch Direct Deposit Verifier</td><td><a href="https://sepolia.etherscan.io/address/0xB5Fe2F991db54B6c9362FE2ace8f78C9Dd05277e">0xB5Fe2F991db54B6c9362FE2ace8f78C9Dd05277e</a></td></tr><tr><td>Relayer (Operator) Address</td><td><a href="https://sepolia.etherscan.io/address/0xba6f711e1d4db0cbfbc09d1d11c5fb7445160673">0xBA6f711e1D4dB0CBfbC09D1d11C5Fb7445160673</a></td></tr><tr><td>BOB Faucet</td><td><a href="https://sepolia.etherscan.io/address/0xb9988D599A64723462955BfC8441F1Af90335796#writeContract#F1">0xb9988D599A64723462955BfC8441F1Af90335796</a></td></tr></tbody></table>

## Goerli Testnet (BOB->USDC)

RPC: <https://rpc.ankr.com/eth_goerli>\
Chain ID: 5\
Faucet: <https://grabteeth.xyz/>\
zkBob Pool ID: 16776962 (`0xffff02`)

<table><thead><tr><th width="301">Contract name</th><th>Address</th></tr></thead><tbody><tr><td>BOB Token (pre-migration)</td><td><a href="https://eth-goerli.blockscout.com/address/0x97a4ab97028466FE67F18A6cd67559BAABE391b8">0x97a4ab97028466FE67F18A6cd67559BAABE391b8</a></td></tr><tr><td>USDC Token</td><td><a href="https://eth-goerli.blockscout.com/address/0x28B531401Ee3f17521B3772c13EAF3f86C2Fe780">0x28B531401Ee3f17521B3772c13EAF3f86C2Fe780</a></td></tr><tr><td>zkBob Pool</td><td><a href="https://eth-goerli.blockscout.com/address/0x49661694a71B3Dab9F25E86D5df2809B170c56E6">0x49661694a71B3Dab9F25E86D5df2809B170c56E6</a></td></tr><tr><td>zkBob Direct Deposits Queue</td><td><a href="https://eth-goerli.blockscout.com/address/0xE4C77B7787cC116A5E1549c5BB36DE07732100Bb">0xE4C77B7787cC116A5E1549c5BB36DE07732100Bb</a></td></tr><tr><td>Operator Manager</td><td><a href="https://eth-goerli.blockscout.com/address/0x3f6c6Aaa1674e7178dbD521b1b65DBf61580F00d">0x3f6c6Aaa1674e7178dbD521b1b65DBf61580F00d</a></td></tr><tr><td>Transfer Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0xAb76044aA1Bd8E55461263BA7d25d122638DAD6d">0xAb76044aA1Bd8E55461263BA7d25d122638DAD6d</a></td></tr><tr><td>Tree Update Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0x0043e6fF8032299616C770a264a9c6FD1157EF48">0x0043e6fF8032299616C770a264a9c6FD1157EF48</a></td></tr><tr><td>Batch Direct Deposit Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0x7a5f24D03Aa69F3aB02968CdCA796a8B11E2527d">0x7a5f24D03Aa69F3aB02968CdCA796a8B11E2527d</a></td></tr><tr><td>Relayer (Operator) Address</td><td><a href="https://eth-goerli.blockscout.com/address/0xc45da11d73e6BA6c6b866b26feaae106406D95b5">0xc45da11d73e6BA6c6b866b26feaae106406D95b5</a></td></tr><tr><td>USDC Faucet</td><td><a href="https://eth-goerli.blockscout.com/address/0x56c105D40f637eF3ac04905682b3DAEfF3F13D24?tab=write_contract">0x56c105D40f637eF3ac04905682b3DAEfF3F13D24</a></td></tr></tbody></table>

## Goerli Testnet (USDC)

RPC: <https://rpc.ankr.com/eth_goerli>\
Chain ID: 5\
Faucet: <https://grabteeth.xyz/>\
zkBob Pool ID: 16776965 (`0xffff05`)

<table><thead><tr><th width="301">Contract name</th><th>Address</th></tr></thead><tbody><tr><td>USDC Token</td><td><a href="https://eth-goerli.blockscout.com/address/0x28B531401Ee3f17521B3772c13EAF3f86C2Fe780">0x28B531401Ee3f17521B3772c13EAF3f86C2Fe780</a></td></tr><tr><td>zkBob Pool</td><td><a href="https://eth-goerli.blockscout.com/address/0xCF6446Deb67b2b56604657C67DAF54f884412531">0xCF6446Deb67b2b56604657C67DAF54f884412531</a></td></tr><tr><td>zkBob Direct Deposits Queue</td><td><a href="https://eth-goerli.blockscout.com/address/0x2b031c78cf958fc72e43f1fed75d70cead910a99">0x2b031c78cf958fc72e43f1fed75d70cead910a99</a></td></tr><tr><td>Operator Manager</td><td><a href="https://eth-goerli.blockscout.com/address/0xbae9d18a1d7f21b6b005d18ae49623a156c6f177">0xbae9d18a1d7f21b6b005d18ae49623a156c6f177</a></td></tr><tr><td>Transfer Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0x2da1128639c2ad6ebf780364c299b1c8206ce6db">0x2da1128639c2ad6ebf780364c299b1c8206ce6db</a></td></tr><tr><td>Tree Update Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0x31da60e61275dab3033986111df80477c6259860">0x31da60e61275dab3033986111df80477c6259860</a></td></tr><tr><td>Batch Direct Deposit Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0x7c95959192a92c3b1fb99cfe079be7cd8d31b03b">0x7c95959192a92c3b1fb99cfe079be7cd8d31b03b</a></td></tr><tr><td>Relayer (Operator) Address</td><td><a href="https://eth-goerli.blockscout.com/address/0x6562a1ea6723be0224dc64843e7abf79e6a88c2a">0x6562a1ea6723be0224dc64843e7abf79e6a88c2a</a></td></tr><tr><td>USDC Faucet</td><td><a href="https://eth-goerli.blockscout.com/address/0x56c105D40f637eF3ac04905682b3DAEfF3F13D24?tab=write_contract">0x56c105D40f637eF3ac04905682b3DAEfF3F13D24</a></td></tr></tbody></table>

## Goerli Testnet (ETH)

RPC: <https://rpc.ankr.com/eth_goerli>\
Chain ID: 5\
Faucet: <https://grabteeth.xyz/>\
zkBob Pool ID: 16776964 (`0xffff04`)

<table><thead><tr><th width="294">Contract name</th><th>Address</th></tr></thead><tbody><tr><td>WETH Token</td><td><a href="https://eth-goerli.blockscout.com/address/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6">0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6</a></td></tr><tr><td>zkBob Pool</td><td><a href="https://eth-goerli.blockscout.com/address/0xf9dbCF4005497e042838dE9082C817fCa790e945">0xf9dbCF4005497e042838dE9082C817fCa790e945</a></td></tr><tr><td>zkBob Direct Deposits Queue</td><td><a href="https://eth-goerli.blockscout.com/address/0x8F7020DD968B7F8510d889CcDAda4b041C4f3B0b">0x8F7020DD968B7F8510d889CcDAda4b041C4f3B0b</a></td></tr><tr><td>Operator Manager</td><td><a href="https://eth-goerli.blockscout.com/address/0xcb84E60Cf7dAB1dF405bf07113737C2B39d14799?tab=txs">0xcb84E60Cf7dAB1dF405bf07113737C2B39d14799</a></td></tr><tr><td>Transfer Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0x9C76fA2E25BdD7F4a2827434297538dfF39d05D7">0x9C76fA2E25BdD7F4a2827434297538dfF39d05D7</a></td></tr><tr><td>Tree Update Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0x351Af131B080F9C458fA0f58b55Ef32143D64492">0x351Af131B080F9C458fA0f58b55Ef32143D64492</a></td></tr><tr><td>Batch Direct Deposit Verifier</td><td><a href="https://eth-goerli.blockscout.com/address/0x49cD0a25a0c453B70cf3EE71018875fC9Fa39260">0x49cD0a25a0c453B70cf3EE71018875fC9Fa39260</a></td></tr><tr><td>Relayer (Operator) Address</td><td><a href="https://eth-goerli.blockscout.com/address/0xb72E1E3Ec1fBaD9B2b5E35597e698027Bd8F06E0">0xb72E1E3Ec1fBaD9B2b5E35597e698027Bd8F06E0</a></td></tr></tbody></table>

## Goerli Optimism Testnet (BOB)

RPC: <https://goerli.optimism.io>\
Chain ID: 420\
zkBob Pool ID: 16776963 (`0xffff03`)

<table><thead><tr><th width="284">Contract name</th><th>Address</th></tr></thead><tbody><tr><td>BOB Token</td><td><a href="https://optimism-goerli.blockscout.com/address/0x0fA7E69b9344D6434Bd6b79c5950bb5234245a5F">0x0fA7E69b9344D6434Bd6b79c5950bb5234245a5F</a></td></tr><tr><td>zkBob Pool</td><td><a href="https://optimism-goerli.blockscout.com/address/0x55B81b0730399974Ccad8AC858e766Cf54126596">0x55B81b0730399974Ccad8AC858e766Cf54126596</a></td></tr><tr><td>zkBob Direct Deposits Queue</td><td><a href="https://optimism-goerli.blockscout.com/address/0x598223503E5A6d8d5b6a6cD5266Bc803169C9633">0x598223503E5A6d8d5b6a6cD5266Bc803169C9633</a></td></tr><tr><td>Operator Manager</td><td><a href="https://optimism-goerli.blockscout.com/address/0x2e7c89E5b5FA64982d2Aff65d52b9cBCdBF20D7A">0x2e7c89E5b5FA64982d2Aff65d52b9cBCdBF20D7A</a></td></tr><tr><td>Transfer Verifier</td><td><a href="https://optimism-goerli.blockscout.com/address/0x9B5F6C35bfE688C61A71164d5d9B37bF340d206C">0x9B5F6C35bfE688C61A71164d5d9B37bF340d206C</a></td></tr><tr><td>Tree Update Verifier</td><td><a href="https://optimism-goerli.blockscout.com/address/0x4f6Cd600d614b54ef2de2E1c8b9146D3e875b09A">0x4f6Cd600d614b54ef2de2E1c8b9146D3e875b09A</a></td></tr><tr><td>Batch Direct Deposit Verifier</td><td><a href="https://optimism-goerli.blockscout.com/address/0xf256149df8e258B4E7Adc55CE28123734A07f91f">0xf256149df8e258B4E7Adc55CE28123734A07f91f</a></td></tr><tr><td>Relayer (Operator) Address</td><td><a href="https://optimism-goerli.blockscout.com/address/0xC325D80FF4A883E0E6bCfaF239B7d9405D6830fb">0xC325D80FF4A883E0E6bCfaF239B7d9405D6830fb</a></td></tr><tr><td>BOB Faucet</td><td><a href="https://optimism-goerli.blockscout.com/address/0x357cA353dbCad28418d5F3110727B2af62803F20?tab=write_contract">0x357cA353dbCad28418d5F3110727B2af62803F20</a></td></tr></tbody></table>

## Governance

All zkBob pool deployments on public networks are controlled by an established governance process. Read more about the current governance setup - [Governance](/zkbob-overview/governance)


# Smart Contracts

EVM-based

The zkBob solution is based on several interrelated smart contracts. The main purpose of the contract subsystem is to store the current [Merkle tree](/implementation/untitled) state inside the base blockchain. Each zkBob transaction changes the Merkle tree, so it should be processed by a smart contract. See the subsystem below, where the Pool contract is the primary contract for processing transactions.

<figure><img src="/files/LielUTNDwDtKWK6Sb7tv" alt=""><figcaption></figcaption></figure>

{% hint style="success" %}
[Contracts Github repo.](https://github.com/zkBob/zkbob-contracts) Contract code is also linked in the contract pages referenced below.
{% endhint %}

* [Pool contract ](/implementation/contracts-and-circuits/the-pool-contract)is the main contract which holds the current Merkle tree state. It process all transactions and holds a current Merkle tree root.
* [Token contract](/implementation/contracts-and-circuits/token-contract) mints a shielded coin protected by the current zkBob solution. This token is deposited in the Pool contract and withdrawn from it.
* [Verifier contracts](/implementation/contracts-and-circuits/verifier-contracts) are used by the Pool contract to check transaction correctness / validate zkSNARK proofs.
* [Operator contract](/implementation/contracts-and-circuits/operator-manager-contract) helps the Pool contract to determine whether transactions can be accepted from a sender. It is used in the multi-relayer configuration to serialize the transaction sequence from the different nodes and minimize transaction collisions.
* [Voucher token contract](/implementation/contracts-and-circuits/voucher-token-contract) mints a token ([XP](/roadmap/exploratory-features/xp)) to reward users for their contribution in the anonymity set.


# zkBob Pool Contract

Main transaction processor

The main purpose of the zkBobPool contract is to process user transactions. It receives transactions from operators (relayers) or users directly, checks the proofs, and updates the current contract state.

### zkBob Pool Initialization

Each pool serves a single token. To support multi-pool solutions every pool has it's own identifier (`pool_id`). Currently the pool\_id is an unsigned 24-bit arbitrary integer.

All linked contracts (verifiers, operator manager, token, and voucher token) should be provided prior to Pool contract deployment. The Pool contract is initialized by the following constructor method:

```solidity
constructor(
uint256 __pool_id,
address _token,
ITransferVerifier _transfer_verifier,
ITreeVerifier _tree_verifier)
```

### Denominators

There are two denominators used in the Pool and applied to the token (`TOKEN_DENOMINATOR`*)* and native coin (`NATIVE_DENOMINATOR`).   They are initially defined as 1 gwei constants.

```solidity
uint256 constant TOKEN_DENOMINATOR = 1 gwei;
uint256 constant NATIVE_DENOMINATOR = 1 gwei;
```

{% hint style="info" %}
Example usage: When a user wants to withdraw 5 tokens from the pool, they specify '5' as the value in the corresponding transaction field. The pool will multiply this value by the TOKEN\_DENOMINATOR and send the resulting token value to the receiver address (in wei). Denominators are also used to calculate limits.
{% endhint %}

### Root Parameter

The initial Merkle tree root value should be provided for the `_root` parameter. For a Merkle tree with a desired total height 48 without any leaves (all leaves are zero) the root is a fixed value:

```
11469701942666298368112882412133877458305516134926649826543144744382391691533
```

### Limits

The [ability to set limits](/zkbob-overview/deposit-and-withdrawal-limits) is introduced with the `setLimits` method.&#x20;

```
function setLimits(
        uint256 _tvlCap,
        uint256 _dailyDepositCap,
        uint256 _dailyWithdrawalCap,
        uint256 _dailyUserDepositCap,
        uint256 _depositCap
    )
```

### Making a transaction

The Pool contract processes incoming transactions via the `transact()` method. All required data for the transaction passes through the calldata.

```solidity
function transact() external payable onlyOperator;
```

Before the transaction is processed the proofs are checked by the verifier contracts.

{% hint style="info" %}
Note the `onlyOperator` modifier. It checks the transaction sender address via the Operator Manager and reverts a transaction when the origin sender is not currently allowed to interact with the Pool contract.
{% endhint %}

### Source and Deployment data

* The contract source code: [zkBobPool.sol](https://github.com/zkBob/zkbob-contracts/blob/master/src/zkbob/ZkBobPool.sol)
* [Contract Addresses](/implementation/deployed-contracts)


# Transaction Calldata

Reference tables with calldata structure for different transaction types

All transaction data are transferred to the Pool contract through the calldata. The following reference table is useful for transaction analysis.

Withdrawal transactions contain addition fields `memo.nativeamount` and `memo.receiver` . Due to this, the calldata table for withdrawal transactions is presented separately.

{% tabs %}
{% tab title="Deposit\Transfer" %}

<table><thead><tr><th width="150.3351011589079">Field</th><th width="150">Size (bytes)</th><th width="150">Offset (bytes)</th><th>Description</th></tr></thead><tbody><tr><td>selector</td><td>4</td><td>0</td><td>Contract method selector (0xaf989083)</td></tr><tr><td>nullifier</td><td>32</td><td>4</td><td>The unique <a href="/pages/-MlLp94HnLxBKsl_i3Ik">transaction nullifier</a></td></tr><tr><td>out_commit</td><td>32</td><td>36</td><td>Transaction commitment (transaction subtree root in the <a href="/pages/-MjTjOpgW5PtMbUrohJf">Merkle tree</a>)</td></tr><tr><td>tx_index</td><td>6</td><td>68</td><td>Transaction's first leaf index within Merkle tree (0-based counter)</td></tr><tr><td>energy_amount</td><td>14</td><td>74</td><td>Account XP delta (signed integer, Gwei)</td></tr><tr><td>token_amount</td><td>8</td><td>88</td><td>Account token delta (signed integer, Gwei)</td></tr><tr><td>tx_proof</td><td>256</td><td>96</td><td>zkSNARK proof</td></tr><tr><td>root_after</td><td>32</td><td>352</td><td>Merkle tree root after adding the transaction</td></tr><tr><td>tree_proof</td><td>256</td><td>384</td><td>zkSNARK proof</td></tr><tr><td>tx_type</td><td>2</td><td>640</td><td><p>0 - deposit</p><p>1 - transfer</p><p>2 - withdraw</p></td></tr><tr><td>memo_size</td><td>2</td><td>642</td><td>The following memo block size in bytes</td></tr><tr><td>memo.fee</td><td>8</td><td>644</td><td>The tokens amount to be transferred to the operator (the Pool contract will multiply the fee by denominator)</td></tr><tr><td>memo.ItemsNum</td><td>4</td><td>652</td><td>Number of encrypted items in the memo block</td></tr><tr><td>memo.Hash_acc</td><td>32</td><td>656</td><td>Output account hash (with updated balance)</td></tr><tr><td>memo.Hash_notes</td><td>32 * (memo.ItemsNum - 1)</td><td>688</td><td>Output note hash</td></tr><tr><td>memo.Ap_x</td><td>32</td><td>656 + 32 ∗ memo.ItemsNum</td><td>Ephemeral public key (using to decrypt <code>memo.keys_enc</code> by transaction sender)</td></tr><tr><td>memo.keys_enc</td><td>32 * memo.ItemsNum + 16</td><td>688+ 32 ∗ memo.ItemsNum</td><td>Account and notes encryption keys</td></tr><tr><td>memo.acc_enc</td><td>86</td><td>704 + 64 * memo.ItemsNum</td><td>Encrypted account with an updated balance</td></tr><tr><td>memo.notes_enc</td><td>108*(memo.ItemsNum - 1)</td><td>790+ 64 * memo.ItemsNum</td><td><p>A single encrypted note  <span class="math">(A_i, Note_i^{enc})</span> takes 108 bytes:</p><p><span class="math">sizeof(A_i)</span>= 32<br><span class="math">sizeof(Note_i^{enc})</span>= 76</p></td></tr><tr><td>ECDSA signature <code>(r, s)</code></td><td>64</td><td>682 + 172 * memo.ItemsNum</td><td>Used to recover deposit spender in the corresponding transactions</td></tr></tbody></table>
{% endtab %}

{% tab title="Withdrawal" %}

<table><thead><tr><th width="150.3351011589079">Field</th><th width="150">Size (bytes)</th><th width="150">Offset (bytes)</th><th>Description</th></tr></thead><tbody><tr><td>selector</td><td>4</td><td>0</td><td>Contract method selector (0xaf989083)</td></tr><tr><td>nullifier</td><td>32</td><td>4</td><td>The unique <a href="/pages/-MlLp94HnLxBKsl_i3Ik">transaction nullifier</a></td></tr><tr><td>out_commit</td><td>32</td><td>36</td><td>Transaction commitment (transaction subtree root in the <a href="/pages/-MjTjOpgW5PtMbUrohJf">Merkle tree</a>)</td></tr><tr><td>tx_index</td><td>6</td><td>68</td><td>Transaction's first leaf index within Merkle tree (0-based counter)</td></tr><tr><td>energy_amount</td><td>14</td><td>74</td><td>Account energy delta (signed integer, Gwei)</td></tr><tr><td>token_amount</td><td>8</td><td>88</td><td>Account token delta (signed integer, Gwei)</td></tr><tr><td>tx_proof</td><td>256</td><td>96</td><td>zkSNARK proof</td></tr><tr><td>root_after</td><td>32</td><td>352</td><td>Merkle tree root after adding the transaction</td></tr><tr><td>tree_proof</td><td>256</td><td>384</td><td>zkSNARK proof</td></tr><tr><td>tx_type</td><td>2</td><td>640</td><td><p>0 - deposit</p><p>1 - transfer</p><p>2 - withdraw</p></td></tr><tr><td>memo_size</td><td>2</td><td>642</td><td>The following memo block size in bytes</td></tr><tr><td>memo.fee</td><td>8</td><td>644</td><td>The tokens amount to be transferred to the operator (the Pool contract will multiply the fee by denominator)</td></tr><tr><td><em>memo.nativeamount</em></td><td>8</td><td>652</td><td>Count of the native coin to withdraw</td></tr><tr><td><em>memo.receiver</em></td><td>20</td><td>660</td><td>Destination native address for withdrawal</td></tr><tr><td>memo.ItemsNum</td><td>4</td><td>680</td><td>Number of encrypted items in the memo block</td></tr><tr><td>memo.Hash_acc</td><td>32</td><td>684</td><td>Output account hash (with updated balance)</td></tr><tr><td>memo.Hash_notes</td><td>32 * (memo.ItemsNum - 1)</td><td>716</td><td>Output note hash</td></tr><tr><td>memo.Ap_x</td><td>32</td><td>684 + 32 ∗ memo.ItemsNum</td><td>Ephemeral public key (using to decrypt <code>memo.keys_enc</code> by transaction sender)</td></tr><tr><td>memo.keys_enc</td><td>32 * memo.ItemsNum + 16</td><td>716 + 32 ∗ memo.ItemsNum</td><td>Account and notes encryption keys</td></tr><tr><td>memo.acc_enc</td><td>86</td><td>732 + 64 * memo.ItemsNum</td><td>Encrypted account with an updated balance</td></tr><tr><td>memo.notes_enc</td><td>108*(memo.ItemsNum - 1)</td><td>818 + 64 * memo.ItemsNum</td><td><p>A single encrypted note  <span class="math">(A_i, Note_i^{enc})</span> takes 108 bytes:</p><p><span class="math">sizeof(A_i)</span>= 32<br><span class="math">sizeof(Note_i^{enc})</span>= 76</p></td></tr><tr><td>ECDSA signature <code>(r, s)</code></td><td>64</td><td>710 + 172 * memo.ItemsNum</td><td>Used to recover deposit spender in the corresponding transactions</td></tr></tbody></table>

{% endtab %}
{% endtabs %}


# Bob Token Contract

The BOB token serviced by the Pool

### Basic BOB Token Attributes

* ERC20-based fungible token
* Upgradeable & Mintable (note upgradeability account and minting account must never be the same account)
* Meta-transaction support
* EIP677 support for `transferAndCall` functionality
* Address block list capability (similar to USDC)
* Recovery function(s) for lost/mis-sent tokens

### Source and Deployment Data

* Contract source code: [BobToken.sol](https://github.com/zkBob/zkbob-contracts/blob/master/src/BobToken.sol)
* [Contract Addresses](/implementation/deployed-contracts)


# Verifier contracts

Used to verify transaction and tree zkSNARK proofs

The verifier contracts are used to validate zkSNARK proofs. There are two verifiers in the zkBob solution:

* **Transfer verifier** is used to check transaction correctness. The associated proofs are generated by the user on transaction creation and calculated with the [transaction circuit](/implementation/zksnarks-and-circuits/transaction-verifier-circuit). The main question the verifier answers is whether the user is able to make the corresponding deposit\transfer\withdrawal.
* **Tree verifier** is used to check the Merkle tree updating sequence. The transaction subtree (which should be added to the general tree) should be placed on the correct offset and update the Merkle tree root properly. The associated proofs are usually generated by the relayer (but the user should calculate the proof himself when he wants to interact with the Pool contract directly). The main question the verifier answers is whether the Merkle tree has been updated correctly.

Verifier contracts are built within the [Groth16](http://www.zeroknowledgeblog.com/index.php/groth16) proving system. The Solidity source code is generated by a [fixed template](https://github.com/zeropoolnetwork/libzeropool/blob/master/res/verifier_groth16.sol.tpl). Building the verifier contracts require verification keys generated during the [Ceremony](/deployment/trusted-setup-ceremony) process.

### Source and Deployment data

* Contracts source code: generated during the [Trusted Ceremony.](/deployment/trusted-setup-ceremony#phase-2-create-circuit-parameters-and-mvp-launch)
* [Contract Addresses](/implementation/deployed-contracts)

{% hint style="info" %}
A new verifier contract will be generated following the Community participation phase of the Trusted Ceremony.
{% endhint %}


# Operator Manager Contract

Manages relayers access to the Pool

{% hint style="info" %}
The **operator** is a native chain address which interacts with the Pool contract. In general the relayer or a standalone user can become the operator. In most cases the operator is a relayer.
{% endhint %}

The Operator Manager contract organizes and limits operators access to the Pool. Transactions should be appended to the Merkle tree strictly in a serial manner. The relayer node (or a user in case of direct Pool interaction) should calculate a Merkle tree proof (zkSNARK) before sending a transaction.&#x20;

However, the transaction will revert if the Merkle tree has changed during this calculation. If several relayers send transactions simultaneously collisions will appear and the transaction will be unsuccessful (along with spent gas fees).

To prevent this case we restrict Pool interactions using the Operator Manager contract. The Operator manager contract has three methods:

#### 1) `isOperator`

```solidity
function isOperator(address _addr) external view returns (bool);
```

Returns true when the transaction origin sender is the operator and can interact with the Pool.

#### 2) `isOperatorFeeReceiver`

```solidity
function isOperatorFeeReceiver(address _operator, address _addr) external view returns (bool);
```

Checks if the primary operator is also the fee receiver. There are two operator accounts. The first (\_operator) sends service transactions, the second (\_feeReceiver) receives fees and covers expenses. **These should not be the same account.**

#### 3) `operatorURI`

```solidity
function operatorURI() external view returns (string memory);
```

This string URI identifier for the Operator (typically the URL endpoint of the  relayer).

### Source and Deployment data

* The contract interface source code: [IOperatorManager.sol](https://github.com/zkBob/zkbob-contracts/blob/master/src/interfaces/IOperatorManager.sol)
* For the contract implementation please refer to the [actual operator manager page](/implementation/contracts-and-circuits/operator-manager-contract/mutable-operator-manager#source-and-deployment-data)


# Mutable Operator Manager

The simple operator manager with relayer change support

This is a simple operator manager with single operator support which can be changed by the contract owner. There are no limits to the frequency at which operators can be changed. The Mutable operator manager contract also holds an operator URL address. This is used by clients to determine the current relayer's endpoint.

The initial operator address, feeReceiver (a second operator responsible for receiving fees)  and URL address is set using the contract constructor:

```solidity
constructor(address _operator, address _feeReceiver, string memory _operatorURI)
```

The Mutable operator manager has the following functions (along with the [protocol](/implementation/contracts-and-circuits/operator-manager-contract) implementation):

```solidity
function setOperator(address _operator, address _feeReceiver, string memory _endpoint) external onlyOwner
```

It can only be invoked by the contract owner. To provide unlimited access to the Pool set the `_addr` parameter to `address(0)` and any string (preference for an empty string `""`) for `_endpoint`.

To get the current operator URL use the following function:

```solidity
function operatorURI() external view returns(string memory)
```

This routine returns an empty string `""` when access to the Pool is granted for any sender.

### Source and Deployment data

* The contract source code: [MutableOperatorManager.sol](https://github.com/zkBob/zkbob-contracts/blob/master/src/zkbob/manager/MutableOperatorManager.sol)
* [Contract Addresses](/implementation/deployed-contracts)


# Voucher (XP) Token Contract

Reward users for account usage, future version implementation

{% hint style="info" %}
This feature is optional and will not be activated for users in phase 1. More information is available for the [XP token here](/roadmap/exploratory-features/xp).
{% endhint %}


# Accounts and Notes

The common entities

A set of accounts and notes implement the base functionality of private transactions. The Merkle tree is built from them. They are arranged in a strictly ordered sequence and represent tree leafs. A new account state is added to this sequence to update the account balance.

* [Accounts](/implementation/account-and-notes/accounts) contain information about a user's wallet which can only be decrypted by the owner.
* [Notes](/implementation/account-and-notes/notes) specify a user's available assets. They are also encrypted and can only be accessed by the sender or receiver.​


# Accounts

Define a customer wallet state

The account contains complete information about a user's private wallet. It is linked by the intermediate key $$\eta$$ derived from the spending key $$\sigma$$owned by the user. The holder of the spending key is called an account owner.

The account holds the current balance value and `spent offset` which separates all notes into spent and unspent. This is sufficient data to retrieve a wallet state.

An account is a tuple $$(d, P\_d, i, b, e)$$ where:

* $$d$$ (10 bytes) is a random diversifier which is updated every time an account is updated. It acts like a salt.
* $$P\_d$$ (32 bytes) is diversified public key (a value derived from the $$d$$ and intermediate key $$\eta$$): $$P\_d = \eta \* \text{ToSubGroupHash}\_{E(F\_r)}(d)$$
* $$i$$(6 bytes) is the spent offset. It separates used (spent) and unused notes in the [Merkle tree](/implementation/untitled) . Note that indexes below $$i$$ are considered to be spent.
* $$b$$(8 bytes) is the current account balance
* $$e$$(8 bytes) is an [energy](/roadmap/exploratory-features/xp)(XP) unit ("integral" account balance)

The raw account size is 64 bytes.

Account transaction data never appears unencrypted in a public field. Only the account owner can decrypt it.

{% hint style="info" %}

#### Zero account

When a user creates their first transaction there will not be an existing account in the Merkle tree. In this case a zero account should be used where all fields are zero except $$\eta$$.​
{% endhint %}


# Notes

The note specifies a user's available assets. It indicates that an asset is held by the owner of the spending key $$\sigma$$which was used to [derive the private address](/implementation/zkbob-keys/address-derivation).

A private address is a value strictly associated with the user account. It consists of:

1. &#x20;a random value called a diversifier&#x20;
2. a diversified public key derived from the diversifier using the spending key.

The note is created by the asset sender. A note and its hash are placed in the transaction.

A note is a tuple $$(d, P\_d, b, t)$$where

* $$d$$ (10 bytes) is a diversifier (a random value generated by a note owner)
* $$P\_d$$ (32 bytes) is a diversified public key (a value derived from the $$d$$ and intermediate key $$\eta$$)
* $$b$$(8 bytes) is the note balance (an asset amount transferred by this note)
* $$t$$(10 bytes) is a salt. Since the transaction contains note hashes we must use a random salt to hide notes with the same owners and balances.

The raw note size is 60 bytes.

Due to the private nature of transactions, all notes are encrypted. They can be decrypted by the sender or receiver (note owner) only.&#x20;

The sender decrypts notes to obtain the transaction history outcome. The note receiver (owner) can decrypt it with the spending key $$\sigma$$ which was used to generate a [private address](/implementation/zkbob-keys/address-derivation) $$(d, P\_d)$$specified in the note.

{% hint style="info" %}

#### Zero note

There is an additional state for the note. When a user creates a transaction one or more zero output notes may be created. They will be ignored during transaction parsing. In such a note **all** fields are zero.
{% endhint %}


# Relayer Node

An intermediary between zkBob clients and contract

The relayer is a client interface to the zkBob solution. The main purpose of the relayer is to process an input transaction, calculate Merkle tree proofs, and send them to the contract.

The relayer node is not a mandatory component of the system - users can create transactions and interact with the Pool contract directly. However, possible issues can arise with direct interaction:&#x20;

1. If the Merkle tree on the client side is not continuously synced it is not possible to create the correct proof required for the transaction.&#x20;
2. When the Pool is congested (many users sending simultaneous transactions), the time delay between proof calculation and transaction delivery to the pool may lead to collisions. If a transaction is sent during this time interval the Merkle tree root will change and the transaction will be reverted (while still consuming gas).

The relayer solves these issues by enforcing strict transaction ordering. Moreover it takes on the heavy tree proof computations and pool state synchronisation. You can simply query the current pool state instead of extracting it from the pool's calldata. In general the relayer helps to facilitate contract interaction.

{% hint style="info" %}
The relayer cannot compromise transactions or conduct "Man in the Middle" type attacks.  Due to the underlying zero-knowledge infrastructure of the protocol, it cannot steal money, reveal shielded balances, decrypt transactions receiver\amount or change transactions. The worst it can do is provide an incorrect state to the user.
{% endhint %}

{% hint style="success" %}
An advantage of using a relayer is increased anonymity due to abstracted gas payments. When you directly interact with the zkBob contract your actions can be disclosed implicitly because you pay gas fees on the native blockchain.
{% endhint %}

* The relayer consists of a Merkle tree instance and transaction storage.
* The Merkle tree is based on a key-value database. It supports adding new leaves, root updating, and zkSNARK proofs building.
* Transaction storage is used to to save encrypted accounts and notes. The relayer can provide a fast reply to the client during the state synchronization.
* Clients interact with the relayer via the [REST API](/implementation/relayer-node/rest-api).
* [Relayer github repo](https://github.com/zkBob/zeropool-relayer).

{% hint style="warning" %}

#### Relayer scalability

zkBob is developed to support a multi-relayer configuration, however the current implementation assumes a single relayer in the network. A multiple relayer scheme is planned for the future.
{% endhint %}


# Relayer Operations

Relayer lifecycle detailed description

The relayer is a mediator between clients and the zkBob contracts subsystem. Following are typical use cases when interacting with the relayer.&#x20;

### Fetching state

The user must keep and synchronize the current pool state to build proper transaction proofs. The pool state is held in the client as an ordered set of encrypted transactions ([memo blocks](/implementation/transaction-overview/untitled-1)) and the user should decrypt each of these to obtain account and note information.  To receive this info, the client uses the `eth_getLogs` RPC endpoint method, and during periods of heavy volume RPC endpoints can become overloaded.&#x20;

The relayer keeps the actual pool transaction set, synchronizes it continuously, and returns a desired set of transactions to the users. It significantly decreases overhead for mainnet nodes.

### Sending transactions

When sending a transaction, a user must create the tx body (memo block), calculate the corresponding tx proof, and send it to the relayer. In this case there is no need to worry about the Merkle tree root, only the actual state (balance and notes) is needed as all tree-related work is performed by the relayer.


# Optimistic State

Optimistic state description

### Optimistic State Addresses Congestion Issues

The relayer must calculate the new state root and generate the corresponding proof. The proof generation process is resource intensive and time-consuming, and all of the transactions and proof must be processed in a strict order since the old root value is used as an argument to a new proof. This creates a potential bottleneck and a risk of interference between users that could lead to system degradation, increased latency and so on.

There is no way to avoid the problem altogether but there could be some improvements and trade offs to be made. Specifically in the first version we introduce a so called "Optimistic client/relayer state" which seems like a a reasonable tradeoff between a much better UX and slightly worse reliability.

With optimistic state the relayer always assumes that a transaction will be eventually mined, unless proven otherwise (thus the name "optimistic"). This solution implies that the client sees a pending transaction and pending balance, which could be potentially rolled back by another invalid transaction, that was processed by the relayer a little earlier. Such an invalid transaction leads to cancellation of all of the subsequent transactions in the queue, so the user will have to resend those transactions once more.

{% hint style="warning" %}
It is essential that whatever happens with the client application or the relayer, all funds are protected by the on-chain smart contract, which acts as the ultimate backstop. You can always make a clean boot of the relayer and client applications.
{% endhint %}

### Technical Overview

* The client sends a transaction
* The relayer
  * receives a transaction
  * computes a new "Optimistic" root
  * schedules proof generation
  * saves the transaction to inner database
  * responses with some job Id
* The client synchronizes the state and sees the transaction as "pending", the balance is **unchanged**
* The async worker 1
  * takes a scheduled transaction
  * generates a proof
  * sends a transaction to the Pool contract
  * schedules Ethereum transaction status check by TxHash
* The async worker 2
  * Takes a TxHash
  * checks contract transaction execution status
  * If ok, the updates corresponding status in the Tx Db, otherwise it launches rollback mode and marks all the later transactions ( already processed by the relayer ) as failed
* The client eventually loads the updated transaction status. If the transaction was successful, then the balance is updated, otherwise the transaction is shown as failed.

![](/files/XoCefzDU7GoqYZiqO2KC)


# REST API

To interact with the relayer

## Send a transaction to the contract

<mark style="color:green;">`POST`</mark> `http://relayer/transaction`

This method checks an incoming transaction, builds the zkSNARK Merkle tree proof, and sends the transaction to the Pool contract. The transaction doesn’t process immediately because contract interaction is completed in a serial manner. Incoming transactions are put into the job queue. The method returns `jobId` on success

#### Request Body

| Name                                       | Type       | Description                                                                                     |
| ------------------------------------------ | ---------- | ----------------------------------------------------------------------------------------------- |
| proof<mark style="color:red;">\*</mark>    | Dictionary | Transaction proof (built by a client)                                                           |
| memo<mark style="color:red;">\*</mark>     | String     | Memo block, Base64-encoded                                                                      |
| tx\_type<mark style="color:red;">\*</mark> | Integer    | 0: deposit, 1: transfer, 2: withdrawal                                                          |
| depositSignature                           | String     | Account nullifier signature with the client's native chain private key (for withdrawal tx only) |

{% tabs %}
{% tab title="201: Created Transaction has been pushed to the job queue" %}

```javascript
{
    "jobId": "1"
}
```

{% endtab %}

{% tab title="400: Bad Request Error while parsing the input JSON" %}

```javascript
{
    error: "Error while parsing the input JSON",
    description: "tx_type is incorrect"
}
```

{% endtab %}

{% tab title="500: Internal Server Error Something went wrong" %}

```javascript
{
    error: "An internal error has occured. Please try again later",
    description: "optional error description"
}
```

{% endtab %}
{% endtabs %}

## Get the job status

<mark style="color:blue;">`GET`</mark> `http://relayer/job/:id`

Returns incoming transaction processing state. `jobId` is returned by /transaction method

#### Query Parameters

| Name                                 | Type    | Description    |
| ------------------------------------ | ------- | -------------- |
| id<mark style="color:red;">\*</mark> | Integer | Job identifier |

{% tabs %}
{% tab title="200: OK Job status in body" %}

```javascript
{
    "state": "failed",
    "txHash": null
}
```

{% endtab %}

{% tab title="404: Not Found Job with specified ID not found" %}

```javascript
"Job 2 not found"
```

{% endtab %}

{% tab title="500: Internal Server Error Something went wrong" %}

```javascript
{
    error: "An internal error has occured. Please try again later",
    description: "optional error description"
}}
```

{% endtab %}
{% endtabs %}

## Query transactions

<mark style="color:blue;">`GET`</mark> `http://relayer/transactions/:limit/:offset`

Returns memo blocks and out commits for transactions at the specified offset. This method is used by clients to synchronize account state.

#### Query Parameters

| Name                                     | Type    | Description                                                                           |
| ---------------------------------------- | ------- | ------------------------------------------------------------------------------------- |
| limit<mark style="color:red;">\*</mark>  | Integer | Number of transactions to query                                                       |
| offset<mark style="color:red;">\*</mark> | Integer | The Index of the first transaction (in the Merkle tree, should be  a multiple of 128) |

{% tabs %}
{% tab title="200: OK Array of requested transactions" %}

```javascript
[
    (Buffer|null)
]
```

{% endtab %}

{% tab title="400: Bad Request Check query parameters" %}

```javascript
{
    error: "Incorrect parameters",
    description: "limit is too high"
}
```

{% endtab %}

{% tab title="500: Internal Server Error Something went wrong" %}

```javascript
{
    error: "An internal error has occured. Please try again later",
    description: "optional error description"
}
```

{% endtab %}
{% endtabs %}

## Get Merkle tree proofs at the specified position

<mark style="color:blue;">`GET`</mark> `http://relayer/merkle/proof?[index]`

{% tabs %}
{% tab title="200: OK Success" %}

```javascript
{
    root, // Merkle tree root for this proofs
    deltaIndex, // this index should be used for building tx proof
    proofs // Merkle tree proofs
}
```

{% endtab %}

{% tab title="404: Not Found Specified index doesn't exist in the current tree" %}

```javascript
"Incorrect index"
```

{% endtab %}

{% tab title="500: Internal Server Error Something went wrong" %}

```javascript
{
    error: "An internal error has occured. Please try again later",
    description: "optional error description"
}
```

{% endtab %}
{% endtabs %}

## Get Merkle tree root node at the specified index

<mark style="color:blue;">`GET`</mark> `http://relayer/merkle/root/:index`

#### Query Parameters

| Name  | Type    | Description |
| ----- | ------- | ----------- |
| index | Integer |             |

{% tabs %}
{% tab title="200: OK Success" %}

```javascript
"11469701942666298368112882412133877458305516134926649826543144744382391691533"
```

{% endtab %}

{% tab title="404: Not Found Index not exist in the Merkle tree" %}

```javascript
"Index not exist in the Merkle tree"
```

{% endtab %}

{% tab title="500: Internal Server Error Something went wrong" %}

```javascript
{
    error: "An internal error has occured. Please try again later",
    description: "optional error description"
}
```

{% endtab %}
{% endtabs %}

## Calculate transaction proof

<mark style="color:green;">`POST`</mark> `http://relayer/proof_tx`

Builds zkSNARK proof for the transaction based on public and secret transaction input calculated by a client.

**WARNING:** This is a debug method used to decrease client overhead. DO NOT use  in production, as the client should pass public and secret transactional data. This significantly decreases overall security!

#### Request Body

| Name                                  | Type       | Description                   |
| ------------------------------------- | ---------- | ----------------------------- |
| pub<mark style="color:red;">\*</mark> | Dictionary | Public inputs for the circuit |
| sec<mark style="color:red;">\*</mark> | Dictionary | Secret inputs for the circuit |

{% tabs %}
{% tab title="200: OK Proof has been calculated successfully" %}

```javascript
{
    proof // Transaction proof
}
```

{% endtab %}

{% tab title="400: Bad Request Error in the public or secret input" %}

```javascript
{
    error: "Error while parsing the input JSON",
    description: "cannot find pub field"
}
```

{% endtab %}

{% tab title="500: Internal Server Error Something went wrong" %}

```javascript
{
    error: "An internal error has occured. Please try again later",
    description: "optional error description"
}
```

{% endtab %}
{% endtabs %}

## Get the next index in the Merkle tree

<mark style="color:blue;">`GET`</mark> `http://relayer/delta_index`

{% tabs %}
{% tab title="200: OK An integer value of the index" %}
0
{% endtab %}

{% tab title="500: Internal Server Error Something went wrong" %}

```javascript
{
    error: "An internal error has occured. Please try again later",
    description: "optional error description"
}
```

{% endtab %}
{% endtabs %}

## Get current Merkle tree root and delta index

<mark style="color:blue;">`GET`</mark> `http://relayer/info`

{% tabs %}
{% tab title="200: OK Success" %}

```javascript
{
    "root": "11469701942666298368112882412133877458305516134926649826543144744382391691533",
    "deltaIndex": 0
}
```

{% endtab %}

{% tab title="500: Internal Server Error Something went wrong" %}

```javascript
{
    error: "An internal error has occured. Please try again later",
    description: "optional error description"
}
```

{% endtab %}
{% endtabs %}


# zkBob Keys

Different key types

zkBob is based on complex cryptography. There are several keys needed for private transactions. The relationship between these keys is presented in the scheme below.

![](/files/a5ZN0UwEWJ5Rm4toksTc)

* **Spending key (**$$\sigma$$**)** is the top secret key. It is used to derive other keys and to sign transactions. At a high level it is just a random 256-bit number which should be stored securely on the client side. The simplest way to get a spending key is to produce a random number.  But in a production client software implementation more complex approaches should be used (e.g. [hierarchical deterministic wallets](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)). Concrete approaches for spending key derivation by a client are not discussed here.
* **Transaction verifier key (**$$A$$**)** is used for transaction signature verification. It's derived from the spending key and multiplied by the generator point in the JubJub elliptic curve field: $$A = \sigma G$$
* **Intermediate key (**$$\eta$$**)** is derived from the $$A$$ key by the[ $$Poseidon$$ hash function](/implementation/untitled/the-poseidon-hash):$$\eta = Hash(A.x)$$. It is used in several cases:
  * to calculate the account[ nullifier](/implementation/transaction-overview/the-nullifiers)
  * to derive the outgoing viewing key ($$\kappa$$)
  * to decrypt incoming notes
* **Receiving key** is used to decrypt incoming notes in the memo block. It is a combination of the intermediate key and ephemeral key generated for each note.
* **Outgoing viewing key (**$$\kappa$$**)** is used to decrypt the whole memo block in the transaction which is initiated by itself. The key is derived from the intermediate key by the keccak hash function: $$\kappa = keccak\_{256}(\eta, \text{"this is the suffix for the symmetric encryption key"})$$
* **Private payment address** $$(d, P\_d)$$ - is a set of random diversifiers $$d$$ and point $$P\_d = \eta G\_p = \eta \text{ToSubGroupHash}\_{E(F\_r)}(d)$$


# Address derivation

Private payment address

The zkBob account doesn't contain any fixed address. Instead if you want to receive funds you should generate and provide private addresses. In general a new private address can be generated for every incoming transaction. It is not possible to link different private addresses derived from the single account to one another or to the primary account. Only the account owner can confirm a private address belongs to the account.

A new private payment address is generated by:

* Generate a random 80-bit diversifier $$d$$
* Calculate diversifier subgroup generator point: $$G\_d = \text{ToSubGroupHash}\_{E(F\_r)}(d)$$
* Derive diversifier public part: $$P\_d=\eta G\_d$$​
* Prepare address data buffer ($$buf$$, 42 bytes): join 10 byte of the diversifier with 32 bytes of the $$P\_d.x$$
* Get address checksum: $$checksum = keccak256(buf)$$
* Attach $$checksum$$ first 4 bytes to the $$buf$$
* Encode $$buf$$ with Base58 to the string

Thus the address string contains the diversifier public key $$(d, P\_d)$$ protected with checksum to avoid typos. Checking any private addresses for ownership is very straightforward. You decode the address string and extract $$d$$ and $$P\_d$$ values. Next you derive $$P'\_d$$​ with the your $$\eta$$ key. The private address belongs to your account only if $$P'\_d = P\_d$$.

### Address derivation example

Let's imagine you have an account with the intermediate key:

$$\eta = \mathrm{0x2dedcb9b32000d350bf1055d764302b9d4f4a3820015ea49aaf02438aaa72a85}$$​

{% hint style="info" %}
**The big numbers representation**

All big numbers on this page are presented in the hexadecimal form to reduce line width. If you want to convert them to the decimal form please use a third-party tool ([example](https://www.rapidtables.com/convert/number/hex-to-decimal.html))
{% endhint %}

To derive a private address we should generate a random diversifier $$d$$ and calculate the [Poseidon](/implementation/untitled/the-poseidon-hash) hash for it:

$$d = \mathrm{0xc2767ac851b6b1e19eda}$$

$$Hash(d) = \mathrm{0x998ed1a2c59ea1ac23ea4519bd11e88cefe5c888d22bf245b8c22923b4b5488}$$​

Convert scalar $$Hash(d)$$ to the subgroup generator point:

$$G\_d = {\x = \mathrm{0x2f6f6ef223959602c05afd2b73ea8952fe0a10ad19ed665b3ee5a0b0b9e4e3ef}, \ y = \mathrm{0x2e23e2751abbb64461e9a852b7b20c8337fc279ed748c77dfa23cf6158f6a6c3}}$$​

Put $$d$$ and $$G\_d.x$$ into the buffer as little-endian numbers (start with the last significant byte):

$$
\mathrm{da\ 9e\ e1\ b1\ b6\ 51\ c8\ 7a\ 76\ c2\ ef\ e3\ e4\ b9\ b0\ a0\ } \ \mathrm{e5\ 3e\ 5b\ 66\ ed\ 19\ ad\ 10\ 0a\ fe\ 52\ 89\ ea\ 73\ 2b\ fd\ } \ \mathrm{5a\ c0\ 02\ 96\ 95\ 23\ f2\ 6e\ 6f\ 2f}
$$

Add a checksum. To do it we must compute keccak256 hash from the buffer above:

$$\mathrm{f4\ e1\ d3\ a9\ 45\ a0\ c6\ 4a\ 2c\ 8c\ 60\ a6\ 4b\ ad\ 38\ 04\ 0f\ 3f\ 75\ 24\ 30\ 79\ 7c\ 30\ d1\ 41\ 91\ a8\ 0a\ b5\ 4a\ be\ }$$​

Get the first 4 bytes from the hash above and append them to the end of buffer:

$$
\mathrm{da\ 9e\ e1\ b1\ b6\ 51\ c8\ 7a\ 76\ c2\ ef\ e3\ e4\ b9\ b0\ a0\ } \ \mathrm{e5\ 3e\ 5b\ 66\ ed\ 19\ ad\ 10\ 0a\ fe\ 52\ 89\ ea\ 73\ 2b\ fd\ } \ \mathrm{5a\ c0\ 02\ 96\ 95\ 23\ f2\ 6e\ 6f\ 2f\ f4\ e1\ d3\ a9}
$$

Finally encode this buffer with Base58 to get private address:

***QsnTijXekjRm9hKcq5kLNPsa6P4HtMRrc3RxVx3jsLHeo2AiysYxVJP86mriHfN***


# Ephemeral keys

Receiving key

Ephemeral keys are generated during the process of notes encryption. The symmetric cipher algorithm ([ChaCha20Poly1305](https://datatracker.ietf.org/doc/html/rfc8439)) which applies to the memo block is required to use different keys for security reasons. They are derived from the random secret keys generated before the each encryption operation. The account's intermediate key is used for the derivation process.

* The **receiving key** is a set of the intermediate key $$\eta$$ and ephemeral public key $$A\_i$$. These are used to derive the note encryption key. Successfully decrypted notes with the $$\eta$$ key are considered received notes by a client.


# zkSNARKs & Circuits

&#x20;There are two verifiers in the zkBob solution:

* **Transfer verifier** is used to check transaction correctness. The associated proofs are generated by the user on transaction creation and calculated with the [transaction circuit](/implementation/zksnarks-and-circuits/transaction-verifier-circuit). The main question the verifier answers is whether the user is able to make the corresponding deposit\transfer\withdrawal.
* **Tree verifier** is used to check the Merkle tree updating sequence. The transaction subtree (which should be added to the general tree) should be placed on the correct offset to update the Merkle tree root properly. The associated proofs are usually generated by the relayer (but the user should calculate the proof himself when he wants to interact with the Pool contract directly). The main question the verifier answers is whether the Merkle tree has been updated correctly.

### Repositories

* [Fawkes-Crypto zkSnarks framework](https://github.com/zkBob/fawkes-crypto): A lightweight framework for building circuits in bellman, using groth16 proving system and BN254 curve.library with circuits and cryptography of ZeroPool.
* [libzeropool](https://github.com/zkBob/libzeropool): Library with circuits and cryptography of ZeroPool.
* [libzeropool-rs](https://github.com/zeropoolnetwork/libzeropool-rs) Rust library wrapper for libzeropool which includes:
  * libzeropool-rs::merkle -  Merkle Tree implementation
  * libzeropool-rs::client - UserAccount implementation. Transaction data created here.


# Transfer verifier circuit overview

{% hint style="info" %}
The following is from the libzeropool library description. For more details, please see

-> [Library description ](https://hackmd.io/_Xm5DjqUTyykcBtDgMxLwA)\
-> [libzeropool github repo](https://github.com/zkBob/libzeropool)
{% endhint %}

This circuit is used to built a private transactions engine with the following properties:&#x20;

1. It is not possible for anyone to forge balances.
2. Any unspent note or account with an existing owner is spendable.

**The circuit proves the following conditions:**

1. Input notes are unique.
2. Output notes are unique or blank (all fields equal zero).
3. Tx is signed and the signer is the owner of input account, output account, and input notes.
4. Nullifiers correspond to notes.
5. out\_commitment is the root of Merkle subtree of output account and notes.
6. Input account and notes should be inside anonymity set (have valid merkle proofs) or blank (all fields of account excluding η are zeros, balance of note is zero).
7. All non-blank input notes correspond to the following equation\
   `tx.in.Account.i≤pos(tx.in.Note)<tx.out.Account.i`\
   where pos is the position of the note in the Merkle tree.
8. Public balance, XP changes, and the size of anonymity set should be stored at special public δ input as little endian (b:i64, e:i96, i:u32), two’s complement is used for signed types. After unpacking, negative values should be represented as corresponding field elements.
9. Sum of all balances (with negative signs for outputs) should be zero.
10. Account indexes are limited:\
    `tx.in.Account.i≤tx.out.Account.i≤δ.i`
11. The following equation for XP:\
    `δ.e+(tx.out.Account.i−tx.in.Account.i)tx.in.Account.b+∑k(tx.out.Account.i−pos(tx.in.Notek))tx.in.Notek.b+tx.in.Account.e−tx.out.Account.e=0`

{% hint style="info" %}
XP is referred to as energy in the zeropool library. While accounts accumulate XP there is no current application for accumulated XP in v1 of the protocol.&#x20;
{% endhint %}


# zkBob Merkle Tree

The main data structure

![Full Merkle tree](/files/u8UP9UCy8BQfq3EqHRwr)

The merkle tree in the zkBob solution is used to link and store encrypted transaction data (accounts and notes) within a strict sequence. The accounts and note hashes are placed in the tree leaves.

The Merkle tree leaves and nodes contain hashes. Each node is calculated as a hash of two child nodes. Each leaf depends on its type (account and note leaves). The [Poseidon](/implementation/untitled/the-poseidon-hash) function is used to calculate hashes with the appropriate parameters.

The full Merkle tree can be divided into a transaction subtree and a commitment subtree (with transaction commitments as leaves).

The commitment subtree ensures the correct transaction sequence without transaction data processing.

The transaction subtree links underlying data (accounts and notes) within a single operation. The transaction subtree's root is called the "transaction commitment." The commitments are using as commitment subtree leaves. Each transaction subtree is built using the corresponding [memo block](/implementation/transaction-overview/untitled-1).

{% hint style="warning" %}

#### Merkle tree height

Note the tree in the picture above is not on its actual height. It's just a simple representation.

The zkBob solution uses a Merkle tree with a total height of 48 (transaction subtree with a height of 7 and commitment subtree with a height of 41).

Due to this the height transaction supports up to 128 leaves (a single account plus 127 notes) and the tree supports up to $$2^{41} \approx 2.2 \* 10^{12}$$ transactions
{% endhint %}

According to the scheme above, a new transaction determines a new transaction subtree which will be added to the full Merkle tree. Adding a new transaction affects the Merkle tree root. So the Merkle tree root determines a current tree state.

The pool contract holds the current leaves count (the number of transactions multiplied by 128) and the Merkle tree roots for all transactions.


# The Poseidon Hash

Used for the different purposes

The Poseidon is a hash function designed for zero-knowledge proof systems like zkSNARKs. It operates over the $$GF(p)$$ prime field. The main advantage of the Poseidon hash is simplification in circuits building.

The Poseidon contains a series of rounds each based on input permutations (add constants, exponentiation and mixing). An S-box routine is just an exponentiation number in the $$GF(p)$$ field (the power of 5).

The round constants and S-box operations count depend on the parameter set. The Poseidon parameters are a tuple $$(t, f, p, c, m)$$, where

* $$t$$ is a number of S-box routines in one round. It also specifies an input dimension: hash function supports up to $$t$$ input numbers.
* $$f$$ is a full rounds count ($$t$$ S-box routines)
* $$p$$ is a partial rounds count (single S-box routine)
* $$c$$ is a round constants array ($$(f+p) \times t$$ dimension)
* $$m$$ is a square array used for a mixing function ($$t \times t$$ dimension)

The Poseidon routine produces a resulting hash (over prime field) after $$(f+p)$$ rounds.

As mentioned previously there are different parameter sets used for hashes in the Merkle tree. These hash types are explained in the table below. The parameter set is presented in the reduced form (just a tuple$$(t, f, p)$$):&#x20;

<table><thead><tr><th width="174">Label</th><th width="150">Parameters</th><th width="255.7142857142857">Hash purpose</th><th>Inputs</th></tr></thead><tbody><tr><td><span class="math">Hash</span>​</td><td>2, 8, 56</td><td>Key derivation (<span class="math">\eta</span> and <span class="math">P_d</span>)</td><td>Transaction verifier key<span class="math">A</span> or diversifier <span class="math">d</span></td></tr><tr><td><span class="math">Hash_{merkle}</span>/ <span class="math">Hash_{nullifier}</span></td><td>3, 8, 56</td><td>Merkle tree's node;<br>Nullifier</td><td>two child nodes or leafs;<br>Account hash and intermediate nullifier hash (<a href="/pages/-MlLp94HnLxBKsl_i3Ik">details</a>)</td></tr><tr><td><span class="math">Hash_{eddsa}</span>/<span class="math">Hash_{inh}</span></td><td>4, 8, 56</td><td>EDDSA sign and verify;  Intermediate nullifier hash (inh)</td><td><span class="math">R.x, A.x, H</span>​ (<a href="/pages/-MlGhjv9i1WZtZbCPpbz#signing">details</a>)<br><span class="math">Hash(acc)</span>, <span class="math">\eta</span>, <span class="math">path</span> (<a href="/pages/-MlLp94HnLxBKsl_i3Ik">details</a>)</td></tr><tr><td><span class="math">Hash_{note}</span></td><td>5, 8, 56</td><td>Note hash</td><td><span class="math">d, P_d, b, t</span> (<a href="/pages/-MkDFIT39dsWh_gvqkeo">details</a>)</td></tr><tr><td><span class="math">Hash_{account}</span>/<span class="math">Hash_{sponge}</span></td><td>6, 8, 57</td><td><p>Account hash;</p><p>Transaction hash</p></td><td><p><span class="math">d, P_d, i, b</span> (<a href="/pages/-MkDFAWUIHvMiHb2NuC6">details</a>)​;</p><p>account and notes hashes with transaction commitment (<a href="/pages/-MlGhjv9i1WZtZbCPpbz#transaction-hashing">details</a>)</p></td></tr></tbody></table>

{% hint style="info" %}
**Poseidon specification**

This page provides just a simple description of the Poseidon hash function. For additional details please refer to the [original publication](https://eprint.iacr.org/2019/458.pdf). It contains exhaustive materials, security investigations, implementation details, proof system applications and so on.
{% endhint %}


# Elliptic Curve Cryptography

Baby-JubJub curve

{% hint style="info" %}
The Baby-JubJub curve is explained in more detail in [*Ethereum Improvement Proposals*, no. 2494](https://eips.ethereum.org/EIPS/eip-2494).
{% endhint %}

The zkBob solution uses twisted Edwards elliptic curve Baby-JubJub with the following parameters:

**The curve equation:** $$a x^2 + y^2 = 1 + dx^2y^2$$

**Coefficient a:** 168700

**Coefficient d:** 168696

**The curve finite field modulus:** 21888242871839275222246405745257275088548364400416034343698204186575808495617

**The curve order:** 21888242871839275222246405745257275088614511777268538073601725287587578984328

**Subgroup cofactor:** 8

**Subgroup order:** 2736030358979909402780800718157159386076813972158567259200215660948447373041

**Generator point** $$(x, y)$$**:** 15432774951723927157031250336277790668279068029556328898354949310072202318295,  1094793399012674419577823790376044935246658671901627841542961578327961423020


# Transaction Overview

An atomic operation on the blockchain

The main purpose of any transaction is to move funds between parties. Each transaction has several inputs and outputs. [Account](/implementation/account-and-notes/accounts) and [notes](/implementation/account-and-notes/notes) can be used as inputs and outputs.

The current implementation assumes that each transaction has:

1. A single input and output account and&#x20;
2. A fixed number of input (3) and output (127) notes.

The input and output accounts designate a transaction initiator. Account changes (including balance, energy and spent offset) are reflected in the output account.


# Common Structure

Public and secret fields

The transaction sent to the zkBob contract contains the following fields:

* Account nullifier - protects from double-spending, if the nullifier already exists the transaction is not valid and will not be executed.
* Transaction commitment - transaction's subtree root hash
* Transfer index
* XP (energy) amount - for withdrawal transaction
* Token amount - a positive value for deposit transaction, negative for withdrawal.
* Transaction proof
* Merkle tree root after transaction inclusion
* Merkle tree proof
* [Transaction type](/implementation/transaction-overview/transaction-types)
* Transaction specific fields (depending on transaction type)
* Memo block containing encrypted transaction details (accounts and notes)
* Deposit signature - for retrieving source account (exists in the deposit transaction only)

In general, a transaction sender must prepare all fields before submitting a tx to the contract. When using the relayer, the relayer should calculate proofs and send the transaction to the contract.

There are two parts to a transaction created by a sender -  public and secret.&#x20;

1. The public input containing encrypted transaction data and other fields which do not disclosure sender, receiver, internal transfer amount, etc.
2. The secret portion contains unencrypted data such as input and output accounts and notes, proofs, and EDDSA signature.

#### Public transaction components

* The current Merkle tree root (before transaction processing by the contract)
* Input account nullifier
* Transaction commitment
* Delta value (a composition of the transaction index, token delta and energy delta)
* Memo block

#### Secret transaction components

* A set of unencrypted input\output account and notes for the transaction
* Input account and notes proofs
* Transaction signature $$(S, R)$$
* Transaction verifier key $$A$$ to verify signature


# Memo Block

The main transaction part containing the encrypted data to restore balances and history

### Fields overview

The memo block contains detailed transaction data such as output notes, accounts, transaction fee, and other specific fields.

The memo block consists of the fields below. There are two optional fields in the memo block, dependent on transaction type: `native amount` and `receiver`. These values are only included in the withdrawal transaction's memo block.

![The memo block structure](/files/-MkLhwIx6uf8Y8qdxih0)

| Field name                | Size (bytes)            | Description                                                                                                                                                       |
| ------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| fee                       | 8                       | Asset amount transferred to the relayer.                                                                                                                          |
| native amount             | 8                       | Asset amount for withdrawal. This field is optional for withdrawal transactions only.                                                                             |
| receiver                  | 20                      | Destination address for withdrawal transaction (chain-specific).                                                                                                  |
| $$ItemsNum$$              | 4                       | Number of encrypted elements in the memo block. An element is an account or a note. Note has a fixed number of items currently: one output account and 127 notes. |
| $$Hash\_{account}(acc)$$  | 32                      | Output account hash (with updated balance).                                                                                                                       |
| $$Hash\_{note}(Note\_i)$$ | 32                      | Output note hash.                                                                                                                                                 |
| $$nonce$$                 | 24                      | XChaCha20 nonce. Used to decrypt $$Keys^\text{enc}$$by transaction sender.                                                                                        |
| $$Keys^\text{enc}$$       | 32 \* $$ItemsNum$$ + 16 | Encrypted keys to decrypt output account and notes. Only the transaction sender can decrypt these keys.                                                           |
| $$acc^\text{enc}$$        | 86                      | Encrypted output account with an updated balance.                                                                                                                 |
| $$A\_i$$                  | 32                      | Ephemeral public key for notes. Used to decrypt $$Note\_i^\text{enc}$$ by note owner (receiver).                                                                  |
| $$Note\_i^\text{enc}$$    | 76                      | Encrypted note. It can be decrypted with $$A\_i$$ by owner or by transaction sender through a $$Keys^\text{enc}$$ disclosure.                                     |


# Memo Block Encryption

Used to encrypt accounts, notes and shared keys

The main purpose of the memo block is to publish transaction details. Due to the secret nature of a transaction, the list of admitted users able to access data is strictly restricted. There are two critical data components encrypted in the memo block: an output account and output notes.

Only the transaction sender can access the output account and notes. The output account is used by the sender to maintain the wallet state, and notes are used to retrieve the outgoing history of transactions. The output account should not be available to the transaction receiver. Shared secrets are used to implement this feature.

There are three encrypted entities in the memo block.

* Account Encryption
* Notes Encryption
* Shared Secrets Encryption

### Account Encryption

An output account in the memo block is encrypted with the random key $$key\_a$$ by the symmetric algorithm [ChaCha20Poly1305](https://tools.ietf.org/html/rfc8439):

* Generate a random symmetric 256-bit key: $$key\_a = random()$$
* Encrypt output account: $$acc^{enc} = ChaCha20Poly1305\_{key\_a}^{nonce}(acc)$$

{% hint style="info" %}
Nonce for $$ChaCha20Poly1305$$ is a fixed value. It's the first 12 bytes of the $$keccak256("ZeroPool")$$: `0x5bbdffc6fe73c460f1b2b85d`
{% endhint %}

To decrypt output account user should obtain $$key\_a$$ from the memo block. The transaction sender only can access this key.

### Notes Encryption

Output notes are encrypted with the ephemeral keys. Steps to encrypt a $$Note\_i$$:

* Generate a random 256-bit ephemeral secret key: $$a\_i = random()$$
* Calculate an ephemeral public key for the $$Note\_i$$: $$A\_i = a\_i \text{ToSubGroupHash}\_{E(F\_r)}(Note\_i.d)$$
* Derive a symmetric encryption key for the note: $$key\_i = keccak256(a\_i Note\_i.P\_d)$$
* Encrypt note: $$Note\_i^{enc} = ChaCha20Poly1305\_{key\_i}^{nonce}(Note\_i)$$
* There are two public values for the each note in the memo block: $$A\_i$$ and $$Note\_i^{enc}$$

To decrypt a note the user obtains the corresponding $$key\_i$$. There are two ways to get it:

* to obtain $$key\_i$$ from the shared secrets (a sender case)
* to derive $$key\_i$$ from the $$A\_i$$ and account's key $$\eta$$ (a receiver case): $$key\_i = keccak256(A\_i \eta)$$

### Shared secrets encryption

Shared secrets block contains symmetric keys for account and notes encryption: $$keys = (key\_a, key\_1, key\_2, ...)$$. The following actions are used to encrypt these keys:

* Generate a random 192-bit nonce: $$nonce = random()$$
* Encrypt $$keys$$: $$keys^{enc} = XChaCha20Poly1305\_{\kappa}^{nonce}(keys)$$
* Put $$(nonce, keys^{enc})$$ in the shared secrets block

{% hint style="info" %}
$$keys^{enc}$$ could be decrypted with the $$\kappa$$ key only.
{% endhint %}


# Transaction Types

Deposit, transfer, withdrawal

The current protocol implementation supports three transaction types: deposit, transfer and withdrawal. Each type has a numeric code included in the transaction calldata.

{% tabs %}
{% tab title="Deposit" %}

<figure><img src="/files/hXid8Mbvwqp5SOmdQHf8" alt=""><figcaption></figcaption></figure>

A deposit transaction delivers external funds to the user's account. It is assumed the user initiating the deposit transaction has made a prior token approval to the zkBob contract.

The deposit transaction has no input and output notes. The deposit amount is added to the output account balance to account for incoming funds.

The deposit transaction is checked on the contract side. Approved tokens should be successfully transferred to the contract address to finalize the transaction and update the Merkle tree.&#x20;

The user must include a deposit signature field to the transaction. This field contains an account nullifier signed by the client's private key from the native chain. The contract extracts the client public address via the `ecrecover` Solidity function.

#### Transaction specific:

* Tx type = 0
* has no output notes
* `token_amount` field contains deposit amount (a positive value)
* `energy_amount` field equals 0
* Output account balance is increased to the deposit amount
* `depositSignature` field exists
  {% endtab %}

{% tab title="Transfer" %}

<figure><img src="/files/5ITW3jVnSJjh5Gx1zgm3" alt=""><figcaption></figcaption></figure>

This transaction moves an amount of funds to one or more internal (zkBob) receivers. The transfer transaction does not change the receiver's account balance immediately. Instead one or more payment notes are generated and sent inside the transaction's memo block.

The transfer transaction emits an output account and output notes to push them into the Merkle tree

#### Transaction specific:

* Tx type = 1
* Output notes are acceptable in the memo block
* `token_amount` field equals 0
* `energy_amount` field equals 0
* Output account balance is decreased by the transfer amount
  {% endtab %}

{% tab title="Withdrawal" %}

<figure><img src="/files/YCCV71otFfoopUr5cSE6" alt=""><figcaption></figcaption></figure>

A withdrawal transaction move funds from the zkBob account to the external destination point. Destination address is specified in the `memo.receiver` field (a chain-specific address).

Each withdrawal transaction must zero out the account energy (XP) value. This value is converted into a voucher token which will be minted to the destination address. So `energy_amount` should be less than or equal to zero.

The withdrawal amount is specified in the `token_amount` field. This amount is transferred to the destination address when the transaction is processed.

{% hint style="warning" %}

#### Native Coin Redirection

Withdrawal transaction supports the native coin redirection mechanism. When the relayer sends a native coin to the contract within the withdrawal transaction (`memo.native_amount` should be equal to coin amount), the contract should send native coins to the destination address.
{% endhint %}

#### Transaction specific:

* Tx type = 2
* has no output notes
* `token_amount` contains token withdrawal amount (less than or equal to zero)
* `energy_amount` contains voucher token mint amount (less than or equal to zero)
* memo block contains extra fields: `memo.native_amount` and `memo.receiver`

  * `memo.native_amount` contains native coin amount to withdraw
  * `memo.receiver` contains receiver for output payments

{% endtab %}
{% endtabs %}


# Nullifiers

Double-spending protection

The nullifier is a unique value calculated on the transaction *input* account. It is included in the public transaction portion. The nullifier depends on the input account, the intermediate key $$\eta$$ and the account position in the Merke tree ($$path$$):

$$Nullifier(Acc^\text{in}) = Hash\_{nullifier}(Hash\_{account}(Acc^\text{in}), I)$$

where $$I$$is intermediate nullifier hash calculated as:

$$I = Hash\_{inh}(Hash\_{account}(Acc^{in}), \eta, path)$$

$$Hash\_{nullifier}$$, $$Hash\_{inh}$$ and $$Hash\_{account}$$ is a[ $$Poseidon$$ hash functions](/implementation/untitled/the-poseidon-hash)

There is an archive on the contract side which holds nullifiers. In the case of account double-spending the nullifiers for the same accounts will equal one another. In this case the contract will reject a second transaction with the repeated nullifier value.

A nullifier pre-image could be safely disclosed without opening any sensitive data, like an intermediate key $$\eta$$ used for encryption and decryption. For example the nullifier disclosure could be useful in compliance reports to prove account-chain linkage.


# Signing a Transaction

To prove an input account ownership

Transactions in zkBob are signed by the spending key $$\sigma$$. To verify a transaction signature the prover should use an intermediate key A.

### Transaction hashing

A client application should sign a 'composite' transaction hash instead of full transaction data. The transaction hash is calculated from the input and output hashes:

$$H = Hash\_{sponge}(Hash\_{account}(Acc^\text{in}), Hash\_{note}(Note\_0^\text{in}), Hash\_{note}(Note\_1^\text{in}), Hash\_{note}(Note\_2^\text{in}), TxCommit)$$&#x20;

where

* $$Hash$$ is a [Poseidon multi-hash (sponged) routine](/implementation/untitled/the-poseidon-hash) in the different modes
* $$Acc^\text{in}$$is an input account
* $$Note\_i^\text{in}$$is an input notes,
* $$TxCommit$$ - is a transaction commitment hash (Merkle subtree root). It depends on transaction output account and notes.

### Signing

Next, a client uses the account spending key to sign a transaction hash $$H$$:

$$r = Blake2s(\sigma, H)$$, where[$$Blake2s$$ is the 256-bit hash function ](https://www.blake2.net/blake2x.pdf)

$$R = rG$$, $$A=\sigma G$$ (moving $$r$$ and $$\sigma$$ to the JubJub Elliptic curve field)

$$S = r + Hash\_{eddsa}(R.x, A.x, H)\sigma$$

The output signature $$(S, R)$$ will be sent with a intermediate key $$A = \sigma G$$

### Verifying

To verify a transaction signature a validator should perform the following computations:

$$SG == R + Hash\_{eddsa}(R.x, A.x, H)A$$


# The Transaction Lifecycle

From the user to the contract

A transaction proceeds through several stages before being accepted by the contract.

<figure><img src="/files/CF3g57pgkKHNmCU6dpAZ" alt=""><figcaption></figcaption></figure>

First the initiator (zkBob client) forms the desired transaction type. Output account and notes are calculated, encrypted, and packed into the memo block. The zkSNARK proof is also calculated. The client sends the public portion of the transaction along with the proof to the relayer node.

{% hint style="info" %}

#### The public transaction data sent to the relayer:

* Transaction proof containing the following input fields:
  * Merkle tree root (at the moment the transaction is created)
  * Transaction nullifier
  * The commitment
  * The delta value (a composition of the transaction index, token delta and energy delta)
* Encrypted memo block
* Transaction type
* Deposit signature (for deposit transactions only)
  {% endhint %}

The relayer calculates the Merkle tree proof for the received transaction and composes a transaction sent to the zkBob Pool contract.

On transaction receipt the pool contract checks proofs with the verifier contracts and approves the transaction.

{% hint style="info" %}

#### Sending a transaction without a Relayer

If a relayer node is down or compromised, a user can create a transaction without the relayer. To do this the user should build an actual zkBob Merkle tree to calculate the proof.

*Directions in process*
{% endhint %}


# Client Library SDK

Client library v5.4.0 development guide

The `zkbob-client-js` library SDK is used to interact with the zkBob ecosystem from the client side. The SDK supports different privacy pool deployments and can be used to create custom clients.

## Get Started

The latest stable version of the library is published on [npmjs.com](https://www.npmjs.com/package/zkbob-client-js) with the `latest` tag. To use the library install it with your preferred packet manager like `npm`:

```sh
npm i zkbob-client-js
```

or `yarn`:

```bash
yarn add zkbob-client-js
```

...or manually include it in the `package.json` file as follows:

```json
"dependencies": {
    .........
    "zkbob-client-js": "5.4.0"
    .........
}
```

All interactions with the solution are performed within the instance of the `ZkBobClient` object which should be initialized with the desired [pool configuration](/implementation/client-library-sdk/configuration/initializing-the-client/client-configuration). There are two available modes for the `ZkBobClient` instance:

* **Account-less mode**. The library can interact with most of the system components (such as relayers, contracts and so on) to retrieve publicly available data (transaction fee, limits, Merkle tree indexes and other data needed to render the UI), but cannot perform any actions belonging to the concrete user account (e.g. getting user's balance, making deposits\transfers\withdrawals and so on). The library starts on this state when the `ZkBobClient` is [instantiated](/implementation/client-library-sdk/configuration/initializing-the-client).
* **Full mode**. To switch to full mode you must provide user credentials (spending key) to the library. Do this by [attaching](/implementation/client-library-sdk/configuration/attaching-a-user-account#login) a prepared user's [account configuration](/implementation/client-library-sdk/configuration/attaching-a-user-account/account-configuration). [Logout](/implementation/client-library-sdk/configuration/attaching-a-user-account#logout) of the account any time

## Next Steps

1. Start with the [Configuration](/implementation/client-library-sdk/configuration) section to understand which data you need start designing your own client.&#x20;
   * Associated **examples** are available for [client](/implementation/client-library-sdk/configuration/initializing-the-client/client-configuration#the-client-library-multipool-configuration-example) and [account](/implementation/client-library-sdk/configuration/attaching-a-user-account/account-configuration#account-configuration-example) configurations.
   * Actual **production and development configurations** are available for [download here](/implementation/client-library-sdk/configuration/initializing-the-client/client-configuration#minimal-configurations-for-the-deployed-solutions).
2. [Initialize](/implementation/client-library-sdk/configuration/initializing-the-client) a `ZkBobClient` instance and check that the account-less mode works properly. For example you can [request the relayer fee](/implementation/client-library-sdk/account-less-mode-operations/transaction-fees#getting-relayer-raw-fee) or [fetch privacy pool limits](/implementation/client-library-sdk/account-less-mode-operations/transaction-constraints#getting-pool-limits). All available functions for this mode are described in the [Account-less Mode Operations](/implementation/client-library-sdk/account-less-mode-operations) section.
3. Create you own [account](/implementation/client-library-sdk/configuration/attaching-a-user-account/account-configuration) and [attach](/implementation/client-library-sdk/configuration/attaching-a-user-account#login) it to the `ZkBobClient` instance. Remember only one account can be attached at a time.
4. [Synchronize](/implementation/client-library-sdk/full-mode-operations/account-state#syncing-the-local-state) the local state with the relayer and privacy pool state. This operation can take some time depending on pool size (overall number of transactions in the pool).
5. Retrieve account related data (e.g. [fetch shielded balance](/implementation/client-library-sdk/full-mode-operations/balances-and-history#getting-the-account-balance) or [generate a zk-address](/implementation/client-library-sdk/full-mode-operations/shielded-addresses#generating-shielded-address)). All routines associated with the account are located in the [Full Mode Operations](/implementation/client-library-sdk/full-mode-operations) section

## Examples

You can always refer to the existing client implementations to get a better understanding and find answers to your questions:

* [A brief how-to example](https://github.com/zkBob/zkbob-client-js/blob/main/README.md) located in the client repository's README file.
* [zkbob-console](https://github.com/zkBob/zkbob-console) - a simple text Web-client based on jQuery Terminal. It's always synced with the last library version.
* [react-template](https://github.com/zkBob/react-template) - a React App template project with integrated `zkbob-client-js` library.
* **Waiting for your cool implementation to include it here...!**

## FAQ

#### **What can I build using ZkBob client SDK?**

You can build a custom client for the platform of your choice or add extended functionality like a custodial wallet service or mobile wallet. It should also be noted that the SDK relies heavily on underlying low-level Rust code compiled to WASM, so for example mobile support in React Native would impose additional challenges.&#x20;

#### How do I use ZkBob SDK in my React app?

The SDK can be imported as a regular JS library, but it requires some customization of the webpack configuration, which can be challenging if you use `create-react-app`. This is why we have created a ready-to-go [ react-template](https://github.com/zkBob/react-template) that utilizes react-app-rewired tool to override some configurations and make it all work.\
Alternatively you can use Next.js or Vite to bootstrap your React app that allows you to access the webpack configuration.

#### Where do I get Snark parameters for client initialization?

In order for a client to create a valid transaction proof, parameters and a verification key are required (for a quick sanity check before sending a transaction). These parameters are generated using a trusted setup ceremony for production usage, while the development environment uses some random values.&#x20;

All the testing and development environments on testnets use the following parameters:&#x20;

```javascript
snarkParams: {
      transferParamsUrl: 'https://r2-staging.zkbob.com/transfer_params_20022023.bin',
      transferVkUrl: 'https://r2-staging.zkbob.com/transfer_verification_key_20022023.json'
    }
```

All of the production environments use these parameters:

```javascript
snarkParams: {
      transferParamsUrl: 'https://r2.zkbob.com/transfer_params_22022023.bin',
      transferVkUrl: 'https://r2.zkbob.com/transfer_verification_key_22022023.json'
    }
```

{% hint style="warning" %}
The CDN is configured so that only requests from localhost and zkbob domains are processed, so you would have to download params files and include them in the bundle. You can see how to do that in the [#examples](#examples "mention")
{% endhint %}

#### How do I login to an existing account using the SDK?

Currently the API between the front end and the SDK allows you to pass the account credentials in the form of a spending key that is derived from the mnemonic seed phrase. You can export your seed phrase and pass it to the function `deriveSpendingKeyZkBob` to get a secret key:&#x20;

```typescript
const sk = deriveSpendingKeyZkBob("general team spot pass shoulder domain axis crazy since kind athlete buzz")
```

Then pass the resulting spending key to the `accountConfig` object to login.

```typescript
    const client = await ZkBobClient.create(config, 'BOB-sepolia');
    const accountConfig: AccountConfig = {
      sk,
      pool: 'BOB-sepolia',
      proverMode: ProverMode.Local,
    };
    await client.login(accountConfig);
```

If you'd rather derive a spending key from your Web3 wallet (such as MetaMask) signature then you need to get the menmonic first:

```typescript
const mnemonic = ethers.utils.entropyToMnemonic(hexToBuf("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"))
```

{% hint style="info" %}
See the full code for client instantiation and login in [#examples](#examples "mention")
{% endhint %}

#### How do I generate an address for receiving funds?

In the current version of the protocol, only the user can generate a new address for receiving funds inside the pool (zkAddress). Generating a zkAddress requires a secret encryption key which can only be provided by the user. Address generation itself is very easy, simply call the corresponding method of the client object.

```typescript
const zkAddress = await zkClient?.generateAddress()
```


# Configuration

There are two configuration points: [client](/implementation/client-library-sdk/configuration/initializing-the-client/client-configuration) and [account](/implementation/client-library-sdk/configuration/attaching-a-user-account/account-configuration)

The **client** configuration is applied during library initialization (`ZkBobClient` instantiation). It's a base level config. The client interacts with concrete private pools, designated relayers and RPCs, and other common properties.

The **account** configuration is applied to the `ZkBobClient` when we want to interact with customer's funds. Account configuration defines a customer wallet inside a privacy pool.


# Initializing the client

Instantiating the client object

Before interacting with the privacy pool you must instantiate the `ZkBobClient` object. Use the static factory method to create a `ZkBobClient` instance. Using a constructor is not supported.

```typescript
static async create(
    config: ClientConfig,
    activePoolAlias: string,
    callback?: ClientStateCallback
): Promise<ZkBobClient>
```

### Parameters

`config` - the [client configuration](/implementation/client-library-sdk/configuration/initializing-the-client/client-configuration) which defines available pools, associated chains and other parameters.

`activePoolAlias` - the name of the pool which should be activated during instance creation. The names of the pools (aka aliases) are defined in the client configuration. The client should always have an active pool.

`callback` - is an optional parameter of the type [ClientStateCallback](/implementation/client-library-sdk/common-types#client-library-state-callback) which can be helpful to monitor the current library state. You can set it anytime by setting the `stateCallback` client property.

### Returns

An instance of the client in a `Promise`

### Example

The client configuration `clientConfig` defined in [this example](/implementation/client-library-sdk/configuration/initializing-the-client/client-configuration#the-client-library-multipool-configuration-example).

```typescript
console.log('Creating ZkBob client...');
const zkClient = await ZkBobClient.create(clientConfig, 'BOB-sepolia');
console.log('You can dive into a privacy pool right now!');
```


# Client Configuration

Configure the common parameters

Your should provide a client configuration when instantiating `ZkBobClient`. This includes pools and chains description according to the following parameters:

```typescript
interface ClientConfig {
  pools: Pools;
  chains: Chains;
  snarkParams: SnarkConfigParams;  // global SNARK parameters (for all pools)
  snarkParamsSet?: Parameters;     // customized SNARK parameters
  supportId?: string;
  forcedMultithreading?: boolean;
}
```

### ClientConfig.pools

**The `ClientConfig.pools`** field contains descriptions of the supported privacy pools and maps the arbitrary pool name (aka pool alias) with the associated pool parameters.&#x20;

{% code fullWidth="false" %}

```typescript
type Pools = { [name: string]: Pool; }

interface Pool {
  chainId: number;
  poolAddress: string;
  tokenAddress: string;
  relayerUrls: string[];
  delegatedProverUrls: string[];
  depositScheme: DepositType;
  coldStorageConfigPath?: string;
  minTxAmount?: bigint;
  feeDecimals?: number;
  isNative?: boolean;
  ddSubgraph?: string;
  parameters?: string;
}
```

{% endcode %}

where

* `chainId` - the chain identifier (`137` for pool on Polygon, `1` for Ethereum Mainnet etc)
* `poolAddress` - 0x-prefixed pool smart-contract address
* `tokenAddress` - 0x-prefixed token smart-contract address served by the current pool
* `relayerUrls` - the list of the relayer URLs to interact with the pool *(please note the most of the pool operations are performed through the relayer)*. The list should be non-empty.

{% hint style="info" %}
Currently the multi-relayer configuration isn't supported. So if you provide several relayer URLs the first one will be used.
{% endhint %}

* `delegatedProverUrls` - the optional list of external entities called `prover` which are designated to perform proof calculations. Helpful for low-performance devices.
* `depositScheme` - a deposit scheme  for depositing tokens. It depends on pool's token. It's an `DepositType` enum value as follows:

```typescript
enum DepositType {
  Approve = 'approve',     // deprecated but still supported deposit scheme
  SaltedPermit = 'permit', // based on EIP-2612 (salt was added to the signing message)
  PermitV2 = 'permit2',    // Uniswap Permit2 scheme (used for WETH)
  AuthUSDC = 'usdc',       // EIP-3009 (for most of USDC deployments)
  AuthPolygonUSDC = 'usdc-polygon',  // EIP-3009 (used by USDC token on Polygon)
}
```

* `coldStorageConfigPath` - optional field which contains URL for the coldstorage config. Coldstorage is pre-downloaded files with pool transactions. It's needed to speed-up the user's state synchronization process.
* `minTxAmount` - the minimum transaction amount in the pool dimension (`500,000,000` is the default if that parameter is omitted).
* `feeDecimals` - the number of digits after the decimal point in the transaction fee (it will always rounded-up). Fees will not round if `feeDecimals` is `undefined`.
* `isNative` - should be true for WETH deployments.
* `ddSubgraph` - name of the subgraph to fetch pending direct deposit transactions.
* `parameters` - SNARK parameters set the alias (string name). The set MUST exist in `ClientConfig.snarkParamsSet` otherwise the global parameters set in `ClientConfig.snarkParams` will used if that parameter is omitted.

### ClientConfig.chains

**`ClientConfig.chains`** field contains chain description which are used in the pools defined in the client config. For the every `Pool.chainId` the chain with the associated ID should be defined here.&#x20;

```typescript
type Chains = { [chainId: string]: Chain; }

interface Chain {
  rpcUrls: string[];
}
```

where `rpcUrls` contain a list of JSON RPC endpoints which can interact with that chain. It's recommended to use several endpoints to decrease the probability of RPC interaction errors.

### ClientConfig.snarkParams

**`ClientConfig.snarkParams`** contains URLs of the zk-SNARK parameters and associated verification keys. It's needed to calculate and verify transactions proofs. The parameters are created before the pool deployment (during the Ceremony) and should be public available for the every pool.

```typescript
interface SnarkConfigParams {
  transferParamsUrl: string;
  transferVkUrl: string;
}
```

{% hint style="warning" %}
`snarkParams` is a common parameters applicable for all defined pools without `parameters` customization field
{% endhint %}

### ClientConfig.snarkParamsSet

**`ClientConfig.snarkParamsSet`** contains customized URLs of the zk-SNARK parameters and associated verification keys. This field implements the multiparameters support feature.

```typescript
type Parameters = {
  [name: string]: SnarkConfigParams;
}
```

### **ClientConfig.supportID**

**`ClientConfig.supportId`** is a unique random string sent to the relayer in every request. It's needed to track the user's activity for support purposes. The most common way to generate it is using the `uuid.v4()` method of the [uuid package](https://www.npmjs.com/package/uuid).&#x20;

{% hint style="info" %}
Please note the `supportId` updates every time the library loads so there is no way to link all user actions on the relayer side. That field is optional but in most cases the relayer requires it and all requests without `supportId` will be rejected.
{% endhint %}

### ClientConfig.forcedMultithreading

**`ClientConfig.forcedMultithreading`** is an optional field needed to override the multithreading mode selection (by default it selects automatically depending on the browser). The mode will select automatically when undefined. For example, to activate singlethread mode set `forcedMultithreading` to `false`.

### Client library multipool configuration example

```ts
const clientConfig: ClientConfig = {
    pools: {
        'USDC-polygon': {
            'chainId': 137,
            'parameters': 'prod',
            'poolAddress': '0x72e6B59D4a90ab232e55D4BB7ed2dD17494D62fB',
            'tokenAddress': '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
            'relayerUrls': ['https://relayer-mvp.zkbob.com'],
            'delegatedProverUrls': ['https://remoteprover-mvp.zkbob.com/'],
            'feeDecimals': 2,
            'depositScheme': 'usdc-polygon',
            'ddSubgraph': 'zkbob-usdc-polygon'
        },
        'BOB-sepolia': {
            'chainId': 11155111,
            'parameters': 'stage',
            'poolAddress': '0x3bd088C19960A8B5d72E4e01847791BD0DD1C9E6',
            'tokenAddress': '0x2C74B18e2f84B78ac67428d0c7a9898515f0c46f',
            'relayerUrls': ['https://relayer.thgkjlr.website/'],
            'delegatedProverUrls': [],
            'feeDecimals': 2,
            'depositScheme': 'permit',
        },

    },
    chains: {
        '137': {
            rpcUrls: ['https://rpc.ankr.com/polygon']
        },
        '11155111': {
            rpcUrls: ['https://rpc.sepolia.org']
        }
    },
    snarkParamsSet: {
        'prod': {
            transferParamsUrl: '/path/to/prod/transfer/params',
            transferVkUrl: '/path/to/prod/transfer/vk'
        },
        'stage': {
            transferParamsUrl: '/path/to/stage/transfer/params',
            transferVkUrl: '/path/to/stage/transfer/vk'
        }
    },
    
    supportId: 'unique_string_generated_with_uuidv4',
    forcedMultithreading: undefined
};
```

### Minimal Configurations for Deployed Solutions

To get started with already deployed privacy pools you can download the minimal config file for the production or development environment.

{% hint style="warning" %}
The following configurations contain only the mandatory fields. To instantiate the ZkBobClient object do not forget to provide other parameters. For example the client can be initialized without `supportId` but most likely the relayer will reject any request without this field.
{% endhint %}

{% file src="/files/pIOplclLdyXP2fCnLMN4" %}
**Production Pools:** USDC-polygon, BOB-optimism, WETH-optimism
{% endfile %}

{% file src="/files/KVwPiP1uJkiKZKbODwT1" %}
**Development Pools:** BOB-sepolia, BOB2USDC-goerli, WETH-goerli, USDC-goerli, BOB-op-goerli
{% endfile %}


# Attaching a User Account

Switching library to the full state mode

## <mark style="background-color:green;">Login</mark>

To set the customer's wallet you should create the [account configuration](/implementation/client-library-sdk/configuration/attaching-a-user-account/account-configuration) first. Next use the following routine to attach the user account and switch the client object to the full state:

```typescript
async login(account: AccountConfig): Promise<void>
```

### Parameters

`account` - an account configuration with credentials and other parameters.

### Return value

No return value is required but you must wait until login is completed before the next library interaction.

{% hint style="info" %}
When login is finished the client will switch into the full mode state and all of the public routines are avialable fo use.
{% endhint %}

## <mark style="background-color:green;">Logout</mark>

To set the customer's wallet create the [account configuration](/implementation/client-library-sdk/configuration/attaching-a-user-account/account-configuration) first. Next, use the following routine to attach the user account and switch the client object to the full state:

```typescript
async logout(): Promise<void>
```

### Return value

No return value is required but you must wait logout completed before the next library interaction.

{% hint style="info" %}
When logout is complete the client switches to the account-less state.
{% endhint %}

### Example

The client configuration `clientConfig` defined in [this example](/implementation/client-library-sdk/configuration/initializing-the-client/client-configuration#the-client-library-multipool-configuration-example)

```typescript
console.log('Attaching account...');
await client.login(accountConfig);
console.log('Seems all right!');

await client.logout();
console.log('See you later...');
```

## <mark style="background-color:green;">Is Account is Attached</mark>

Check library is in full mode:

```typescript
hasAccount(): boolean
```

### Return value

`true` if account is present and the library is working in full mode or `false` otherwise.

### Example

```typescript
const res = client.hasAccount()
console.log(`Library operates in ${res ? 'full' : 'account-less'} mode`);
```


# Account Configuration

Configure the customer's wallet

During client onboarding you should first request unique account credentials (mnemonic phrase, Metamask signature etc), then you can login the user. Fill the following configuration before starting:

```typescript
interface AccountConfig {
  sk: Uint8Array;
  pool: string;
  birthindex?: number;
  proverMode: ProverMode;
}
```

### **AccountConfig.sk**

**`AccountConfig.sk`** is a spending key - the top secret account credentials. Anyone who know it can get full control over the client's funds.

{% hint style="info" %}
To produce a valid spending key from the mnemonic phrase use the [`deriveSpendingKeyZkbob`](/implementation/client-library-sdk/utilities#deriving-spending-key-from-the-mnemonic) helper routine.
{% endhint %}

### **AccountConfig.pool**

**`AccountConfig.pool`** is a pool alias which should be activated on login. For example you can initialize the client to support multiple pool configurations (sepolia/goerli) and want to activate goerli pool after client login. The pool can be switched later without logout.

### **AccountConfig.birthindex**

**`AccountConfig.birthindex`** is an account "birthday" within the Merkle tree. No transactions associated with the account should exist lower than that index. If you create a *NEW* account you can pass `-1` here to create account with the birthindex which equals the current one. Always use `undefined` if you do not known the index exactly to avoid loss of any assets.

{% hint style="danger" %}
Please use this field with care because if the client account has been initialized with the incorrect `birthindex` the input transfers sent to the account before that index could be lost.
{% endhint %}

### **AccountConfig.proverMode**

**`AccountConfig.proverMode`** defines the prover behavior. The following modes are supported:

* `ProverMode.Local` - using local prover. It's the most secure way (because you won't share transaction details with 3rd-parties). But it could be slow on weak devices
* `ProverMode.Delegated` - send proving request to the designated external server (the server URL must be defined in associated `Pool.delegatedProverUrls` field). In case of any errors during external proving (e.g. in case of prover unavailable) the transaction will rejected. Please keep in mind the remote prover will have access to the transaction details
* `ProverMode.DelegatedWithFallback` - try to use external prover; in case of any error the local proving will be used

### Account  Configuration Example

```typescript
const accountConfig: AccountConfig = {
  sk: Uint8Array.from([199,146,184,107,97,216,254,23,76,10,31,212,74,35,170,141,
                       182,77,24,244,217,79,158,53,176,73,180,201,175,75,132,1]),
  pool: 'BOB-sepolia',
  birthindex: 0,
  proverMode: ProverMode.Local,
};
```


# Switching Between Pools

In the multi-pool configurations

{% hint style="info" %}
The following functions are available in both account-less and full modes
{% endhint %}

## <mark style="background-color:green;">Getting the Current Pool</mark>

The `ZkBobClient` object always operate on the concrete pool. There are no cases when the pool is undefined. All pools are named by aliases as specified in the [client configuration](/implementation/client-library-sdk/configuration/initializing-the-client/client-configuration). To obtain the name of the current pool use the following method:

```typescript
currentPool(): string
```

### Returns

String alias of the currently activated pool (as defined in the initial config).

## <mark style="background-color:green;">Getting Available Pools</mark>

You also can get the list of pools specified in the client configuration.

```typescript
availabePools(): string[]
```

### Returns

Array of the all supported pool aliases (as defined in initial config).

## <mark style="background-color:green;">Switching to Another Pool</mark>

If you initialized the `ZkBobClient` object with a multipool configuration you can simply switch between pools using the following routine:

```typescript
async switchToPool(poolAlias: string, birthindex?: number): Promise<void>
```

### Parameters

* `poolAlias`: pool's name as defined in the client configuration.
* `birthindex`: optional parameter only applicable in full mode (when account is attached). It equals the index within the Merkle tree when the account was created. There must be no entries belonging to the account below this index. Always use `undefined` if you don't know the exact index.

{% hint style="danger" %}
**Double check that birthindex is correct**. If you switch to another pool with attached account and incorrect `birthindex`, the input transfers sent to the account before that index can be lost without the ability to recover.
{% endhint %}

### Returns

No value is returned but you must wait until the switch is completed before the next library interaction.

### Example

```typescript
const curPool = zkClient.currentPool();
const allPools = zkClient.availabePools();
const newPool = allPools.find(p => p != curPool);

console.log(`Working on ${curPool} pool`);
console.log(`Available pools: ${allPools.join(',')}`);

if (newPool) {
   console.log(`Switching to ${newPool} pool...`);
   await zkClient.switchToPool(newPool);
   console.log(`Welcome to ${zkClient.currentPool()} pool!`);
}
```


# Account-less Mode Operations

You can use these methods without any user credentials

All functions described in this section belong to the `ZkBobClient` instance and are available in account-less mode as well as full mode (when user account attached).

[Converting Token Amounts](/implementation/client-library-sdk/account-less-mode-operations/converting-token-amounts)

[Transaction Fees](/implementation/client-library-sdk/account-less-mode-operations/transaction-fees)

[Transaction Constraints](/implementation/client-library-sdk/account-less-mode-operations/transaction-constraints)

[Using the Delegated Prover](/implementation/client-library-sdk/account-less-mode-operations/using-the-delegated-prover)

[Getting the State](/implementation/client-library-sdk/account-less-mode-operations/getting-the-state)

[Gift Cards](/implementation/client-library-sdk/account-less-mode-operations/gift-cards)

[Client Library Status](/implementation/client-library-sdk/account-less-mode-operations/client-library-status)

[Helpers](/implementation/client-library-sdk/account-less-mode-operations/helpers)

[Versioning](/implementation/client-library-sdk/account-less-mode-operations/versioning)


# Converting Token Amounts

Between native and pool dimensions

Each pool contains a `denominator` property which is defined during the pool contract deployment. Token amounts are always converted to the denominated form when interacting with a pool. All operations with tokens are performed in the pool denominated realm (we also call it pool or shielded dimension). The following functions perform conversions between the shielded token and the native one.

{% hint style="info" %}
The native amount is named wei, which is a misnomer. Historically pools only held the BOB token (which has 18 decimals natively). Now the library supports pools with different decimal places and `wei` isn't the correct name. This will be updated in future versions.
{% endhint %}

## <mark style="background-color:green;">Native to Shielded</mark>

Convert native tokens to the pool dimension. The result is always rounded down.

```typescript
async weiToShieldedAmount(amountWei: bigint): Promise<bigint>
```

### Parameters

`amountWei`: token amount in the native dimension (in `wei` for most ERC20 tokens)

### Returns

`Promise` returns `bigint`: amount representation in the current pool dimension (rounded down if needed).

### Example

```typescript
// ...working on BOB-sepolia pool...
const nativeAmount = 123456789123456789123n; // ~123.46 BOB
const shieldedAmount = await zkClient.weiToShieldedAmount(nativeAmount);
console.log(`${nativeAmount} native tokens -> ${shieldedAmount} shielded tokens`);
// output: 123456789123456789123 native tokens -> 123456789123 shielded tokens
```

## <mark style="background-color:green;">Shielded to Native</mark>

Convert pool tokens from the pool dimension to the native dimension.

```typescript
async shieldedAmountToWei(amountShielded: bigint): Promise<bigint>
```

### Parameters

`amountShielded`: token amount in the pool dimension (denominated value).

### Returns

`Promise` returns `bigint`: amount representation in the native token dimension (rounded down if needed).

### Example

```typescript
// ...working on BOB-sepolia pool...
const shieldedAmount = 42n * (10n ** 9n); // 42 BOB
const nativeAmount = await zkClient.shieldedAmountToWei(shieldedAmount);
console.log(`${shieldedAmount} shielded tokens -> ${nativeAmount} native tokens`);
// output: 42000000000 shielded tokens -> 42000000000000000000 native tokens
```


# Transaction Fees

Requesting and estimation

Each transaction should include a fee. This is a payment to the relayer for maintaining a request,  preparing, and sending a transaction to the pool contract. The fee is returned by the relayer and may vary for different reasons. Moreover different transactions require different fee amounts based on the calldata length. This is why the transaction fee must be calculated every time before sending a transaction in general.

The fee is always returned in the token denominated units (pool dimension). The relayer fee estimate follows the [*RelayerFee*](/implementation/client-library-sdk/common-types#relayer-raw-fee) *structure.*

In general the transaction fee estimated on the client side follows this formula ( *`relayerFee`* is the commission returned by relayer):

$$
fee(tx, relayerFee) = relayerFee.fee + relayerFee.oneByteFee \* tx.calldata.length
$$

For withdrawal transactions with token swaps the equation is as follows:

$$
fee\_{withdraw}^{swap}(tx, relayerFee) = fee(tx, relayerFee)

* relayerFee.nativeConvertFee
  $$

It is not possible to define the absolute fee value when creating a transaction, but you may provide a [relayer raw fee ](/implementation/client-library-sdk/common-types#relayer-raw-fee)object. The final fee is calculated under the hood as described above. If the raw fee field is omitted, the raw fee will be automatically requested from the relayer.

## <mark style="background-color:green;">Getting Relayer Raw Fee</mark>

Request the fee estimation from the relayer. If a previous request was performed recently (<30 secs) the cached value will be returned.

```typescript
async getRelayerFee(): Promise<RelayerFee>
```

### Returns

`Promise` returns [`RelayerFee`](/implementation/client-library-sdk/common-types#relayer-raw-fee): actual raw fee contents.

### Example

```typescript
const relayerFee = await zkClient.getRelayerFee();
console.log(`Raw fee = ${relayerFee.fee} per tx ${relayerFee.oneByteFee} per byte`);
// output: Raw fee = 100000000 per tx 429035 per byte
```

## <mark style="background-color:green;">Estimating Transaction Typical Fee</mark>

Calculate the typical fee amount for the desired transaction type. It may be helpful to display the approximate fee before entering the transaction config.

```typescript
async atomicTxFee(txType: TxType, withdrawSwap: bigint = 0n): Promise<bigint>
```

{% hint style="info" %}
Keep in mind these are *typical* fee values. In several cases the transaction fee may be different depending on the account or transaction configuration (e.g. multi-transfer transactions). To estimate the actual transaction fee please use the [feeEstimate](/implementation/client-library-sdk/full-mode-operations/fee-estimations#estimating-transaction-typical-fee) full mode method.
{% endhint %}

<details>

<summary>Related methods</summary>

* [feeEstimate( transfers, txType, withdrawSwap, updateState)](/implementation/client-library-sdk/full-mode-operations/fee-estimations#estimating-transaction-fee)
* [directDepositFee()](/implementation/client-library-sdk/full-mode-operations/direct-deposits#getting-direct-deposit-fee)

</details>

### Parameters

* `txType`: transaction type (a member of [TxType](/implementation/client-library-sdk/common-types#transaction-type) enum).
* `withdrawSwap`: An optional amount of tokens to swap in the withdrawal transaction. This may influence the typical withdraw transaction cost due to an additional fee component (ignored for other transaction types).

### Returns

`Promise` returns the absolute fee value for a typical transaction with the requested type (in tokens, pool dimension).

### Example

```typescript
const fee = await zkClient.atomicTxFee(TxType.BridgeDeposit);
console.log(`The typical deposit will cost you approx ${fee}`);
// output: The typical withdraw will cost you approx 505867110
```

## <mark style="background-color:green;">Getting Direct Deposit Fee</mark>

Direct deposit transaction should include extra cost for the relayer.

```typescript
async directDepositFee(): Promise<bigint>
```

### Returns

`Promise` returns the direct deposit fee in the pool dimension.

### Example

```typescript
console.log(`DD fee: ${await zkClient.directDepositFee()}`);
// output: DD fee: 100000000
```


# Transaction Constraints

Parameters needed to create a tx

## <mark style="background-color:green;">Getting Pool Limits</mark>

Different privacy pools have different limits for deposit and withdraw operations. Limits can also depend on the user's `0x`-address (address can be whitelisted).

```typescript
async getLimits(address?: string, directRequest: boolean = false): Promise<PoolLimits>
```

### Parameters

* `address`:  An address to be used for evaluating limits. Zero-address is used if undefined.
* `directRequest`: Request limits from the pool contract when `true` (otherwise, request from the relayer node).

### Returns

`Promise` returns [PoolLimits](/implementation/client-library-sdk/common-types#pool-limits)

### Example

```typescript
const limits = await zkClient.getLimits('0x0ECD9494F9C1F98C07E5319273C75BEF6232E86D');
console.log(`You can deposit up to ${limits.deposit.total} BOB right now`);
```

## <mark style="background-color:green;">Minimum Transaction Amount</mark>

The transaction amount cannot be less than the return value. This is applicable for each transaction type (for multi-transfer tx each note is must meet this minimum value).

```typescript
async minTxAmount(): Promise<bigint>
```

### Returns

`Promise` returns minimum transaction amount in the pool dimension.

### Example

```typescript
const minTx = await zkClient.minTxAmount();
if (requestedAmount < minTx) {
    console.log(`Requested too small amount to transact!`);
}
```

## <mark style="background-color:green;">Maximum Supported Token Swap During Withdraw</mark>

Privacy pools have the optional ability to swap an amount of tokens to native coins during a withdraw transaction (typically to seed a wallet with native tokens for operations). The following method returns the maximum number of tokens that can be swapped to native coins.

```typescript
async maxSupportedTokenSwap(): Promise<bigint>
```

{% hint style="info" %}
The swap is unavailable if `maxSupportedTokenSwap` returns 0
{% endhint %}

### Returns

`Promise` returns maximum number of tokens available to swap to native coins (in the pool dimension).

### Example

```typescript
const maxSwap = await zkClient.maxSupportedTokenSwap();
if (maxSwap > 0n) {
    console.log(`Congrats! You can swap tokens to the native coins!`);
}
```


# Using the Delegated Prover

To reduce proof time computation time

Each transaction to the privacy pool must be proven. The process is a heavy-computation process performed by the transaction sender. The library supports three proving modes:

* `Local` - The most secure case where no sensitive data sent to the 3rd parties. The hard computation is performed during the transaction proving on the user's device. This can take a long time with low performance devices. Moreover the proving parameters must be downloaded before transaction creation (\~70 Mb).
* `Delegated` - An external proving machine is used (aka Delegated Prover). In this case the unencrypted transaction is sent to the delegated prover to perform hard computations outside of the user's device. Using a properly configured heavy-duty dedicated server can significantly decrease proving time. The client shouldn't download proving parameters.&#x20;

{% hint style="danger" %}
A transaction will be rejected if the external prover is unavailable when the`Delegated` proving mode selected.
{% endhint %}

* `DelegatedWithFallback` - Combines the Delegated and Local modes (external prover is used as the primary one).

## <mark style="background-color:green;">Setting Prover Mode</mark>

The following command sets the specified prover mode. **The proving mode is set for the current pool only**. If you switch to a new pool the previously selected mode on the *new* pool will be used (if it wasn't set yet - the mode from the old pool will selected).

```typescript
async setProverMode(mode: ProverMode): Promise<void>
```

{% hint style="info" %}
`Delegated` and `DelegatedWithFallback` modes can only be set when the current pool configuration contains a correct delegated prover URL which is available. Otherwise you will receive an InternalError.
{% endhint %}

### Parameters

`mode`: proving mode (a member of [ProverMode](/implementation/client-library-sdk/common-types#prover-mode) enum)

### Returns

No return value but you must wait until the switch is completed before the next library interaction.

### Example

```typescript
try {
    await zkClient.setProverMode(ProverMode.Delegated);
    console.log('The proving system was switched to Delegated mode successfully');
} catch (err) {
    console.log(`Cannot switch to delegated prover: ${err.message}`);
}
```

## <mark style="background-color:green;">Get Current Prover Mode</mark>

The following routine gets the current prover mode:

```typescript
getProverMode(): ProverMode
```

### Returns

[ProverMode](/implementation/client-library-sdk/common-types#prover-mode) enum member.

### Example

```typescript
if (zkClient.getProverMode() == ProverMode.Local) {
    console.log('Using local prover');
}
```


# Getting the State

The state of the Merkle tree: relayer and contract

The privacy pool's state is a set of the Merkle tree root and its current index ( additional details [here](/implementation/untitled)). The state is described by the [TreeState interface](/implementation/client-library-sdk/common-types#tree-state). In normal operations the client's local state should match both the relayer and the pool contract state. However there is no local state in the accountless mode, but we can monitor relayer and pool states.

The relayer node has two different states: the regular state (which should match the pool contract) and the optimistic one. The optimistic state contain transactions which will be included into the state but are not processed yet. In other words the optimistic state is a most likely future Merkle tree state. The optimistic index should always be greater or equal to the normal state.

The client local state should be up to date with the relayer state. Use the associated full-mode method [getLocalState(index?)](/implementation/client-library-sdk/full-mode-operations/account-state#getting-the-local-state) to retrieve.

## <mark style="background-color:green;">Get Pool Contract State</mark>

This is the primary state. All subsystems (relayer, client) must be synced with the pool state.

```typescript
async getPoolState(index?: bigint): Promise<TreeState>
```

### Parameters

`index` - the index of the merkle tree root to retrieve (use the latest index if undefined)&#x20;

### Returns

`Promise` returns [TreeState](/implementation/client-library-sdk/common-types#tree-state)

### Example

```typescript
const poolState = await zkClient.getPoolState();
console.log(`The pool contract actual state: ${poolState.root} @ ${poolState.index}`);
// output: The pool contract actual state:
// 20259123668790783065614334268308370728058669663334716067122266810210516357394 @ 533760
```

## <mark style="background-color:green;">Get Relayer State</mark>

This should match the pool state. Only the latest state can be requested, there is no way to specify an index.

```typescript
async getRelayerState(): Promise<TreeState>
```

### Returns

`Promise` returns [TreeState](/implementation/client-library-sdk/common-types#tree-state)

### Example

```typescript
const relayerState = await zkClient.getRelayerState();
console.log(`The relayer state: ${relayerState.root} @ ${relayerState.index}`);
// output: The relayer state:
// 20259123668790783065614334268308370728058669663334716067122266810210516357394 @ 533760
```

## <mark style="background-color:green;">Get Relayer Optimistic State</mark>

The forcasted state by the relayer. This can be used to monitor transaction queue length. The index of the optimistic state should be greater or equal the index of the current state.

```typescript
async getRelayerOptimisticState(): Promise<TreeState>
```

### Returns

`Promise` returns [TreeState](/implementation/client-library-sdk/common-types#tree-state)

### Example

```typescript
const opState = await zkClient.getRelayerOptimisticState();
console.log(`The relayer optimistic state: ${opState.root} @ ${opState.index}`);
// output: The relayer state:
// 12637519184649269054381762592793608281145118612151270269221975452396577578313 @ 533888
```


# Gift Cards

Encoding and decoding URLs for gift cards

[Gift Cards Maintenance](/implementation/client-library-sdk/full-mode-operations/gift-cards-maintenance#getting-gift-card-actual-balance): When distributing a gift-card several parameters are provided to the user including the spending key, birth index, balance, pool address and so on. The easiest way to achieve this is to encode these parameters into the URL. The following methods are intended for creating and parsing a gift card.

{% hint style="info" %}
Use gift card codes with care. Anyone who knows the code has full access to the gift card funds.
{% endhint %}

## <mark style="background-color:green;">Encoding Gift-Card Parameters</mark>

To create gift-card URL code invoke the following method:

```typescript
async codeForGiftCard(giftCard: GiftCardProperties): Promise<string>
```

### Parameters

`giftCard` - a [GiftCardProperties](/implementation/client-library-sdk/common-types#gift-card-properties) object which contains all needed parameters to encode

### Returns

`Promise` returns Base58-encoded string which can be injected into the redeeming URL

### Example

```typescript
const gk: GiftCardProperties = {
    sk: new Uint8Array(
        [ 40, 169, 173, 13, 42, 145, 224, 222, 213, 124, 226, 142, 126, 241, 6, 1,
          42, 133, 48, 12, 96, 66, 213, 158, 112, 225, 75, 198, 172, 82, 148, 3 ]
    ),
    birthIndex: 533888,
    poolAlias: 'BOB-sepolia',
    balance: 10000000000n
};
const code = await zkClient.codeForGiftCard(gk);
const baseURL = 'https://staging--zkbob.netlify.app';
console.log(`Redeem gift-card with: ${baseURL}/?gift-code=${code}`);
// output: Redeem gift-card with: 
// https://staging--zkbob.netlify.app/?gift-code=QvHXyAF2ykRCSTCwiLJbGGJVjJX7M8Xp5vfNNLaiEvwV8BQfYBeR6ivtY7svMpPyCsL6huTHpB
```

## <mark style="background-color:green;">Decoding a Gift-Card Code</mark>

Use the following method to retrieve a gift-card from the code or URL containing the code:

```typescript
async giftCardFromCode(code: string): Promise<GiftCardProperties>
```

<details>

<summary>Related Methods</summary>

* [giftCardBalance(giftCard: GiftCardProperties)](/implementation/client-library-sdk/full-mode-operations/gift-cards-maintenance#getting-gift-card-actual-balance)
* [redeemGiftCard(giftCard:GiftCardProperties, preferredProvingMode?: ProverMode)](/implementation/client-library-sdk/full-mode-operations/gift-cards-maintenance#gift-card-redemption)

</details>

### Parameters

`code` - a gift-card code or URL contained code within the `gift-code` query parameter.

### Returns

`Promise` returns the [GiftCardProperties](/implementation/client-library-sdk/common-types#gift-card-properties) of a decoded card. Errors may return `InternalError`.

### Example

```typescript
const url = 'https://staging--zkbob.netlify.app/?gift-code=QvHXyAF2ykRCSTCwiLJbGGJVjJX7M8Xp5vfNNLaiEvwV8BQfYBeR6ivtY7svMpPyCsL6huTHpB';
try {
    const gk = await zkClient.giftCardFromCode(url);
    console.log(`This card with balance ${gk.balance} intended for ${gk.poolAlias} pool`);
} catch (err) {
    console.log(`An error was occured while parsing the gift-card: ${err.message}`);
}
// output: This card with balance 100000000000 intended for BOB-sepolia pool
```


# Client Library Status

Retrieving the client state

The `ZkBobClient` object can be in various states throughout its lifecycle. To enable the application to understand what is happening in the library and respond appropriately to changes in its state, the set of states are introduced and organized into a state machine.

<figure><img src="/files/6GUXgcJCcEbQRG95cQJO" alt=""><figcaption><p>The client library state machine</p></figcaption></figure>

You can use your own callback to get the status changes in time. You can set it on client instantination or later with the object's field `stateCallback` of type [ClientStateCallback](/implementation/client-library-sdk/common-types#client-library-state-callback). You can also remove it to stop status monitoring.

```typescript
public stateCallback?: ClientStateCallback;
```

The following routines are introduced to monitor the client's state.

## <mark style="background-color:green;">Getting the Client State</mark>

```typescript
getState(): ClientState
```

### Returns

[ClientState](/implementation/client-library-sdk/common-types#client-library-state-callback) enum member.

### Example

```typescript
const st = zkClient.getState();
if (st == ClientState.StateUpdating || st == ClientState.StateUpdatingContinuous) {
   console.log('Please wait while local state becomes up-to-dated');
}
// output: Please wait while local state becomes up-to-dated
```

## <mark style="background-color:green;">Getting the Client Continuous State Progress</mark>

```typescript
getProgress(): number | undefined;
```

### Returns

`number` which represents progress for continuous states (from 0.0 to 1.0) or `undefined` for non-continuous states.

### Example

```typescript
const st = zkClient.getState();
const pr = zkClient.getProgress();
if (st == ClientState.StateUpdating) {
   const progressStr = (pr !== undefined ? `(${(pr * 100.0).toFixed(1)}%)` : '');
   console.log(`Please wait while local state becomes up-to-dated ${progressStr}`);
}
// output: Please wait while local state becomes up-to-dated (42.0%)
```


# Helpers

## <mark style="background-color:green;">Getting the Pool Identifier</mark>

Typically each pool has a unique `poolId`. The following method retrieves the currently selected pool ID:

```typescript
async poolId(): Promise<number>
```

{% hint style="info" %}
**Exception:** Polygon BOB and Sepolia BOB pools share the same `poolID` which is 0.
{% endhint %}

### Returns

`Promise` returns `number`: pool identifier

### Example

```typescript
console.log(`Working on the pool with ID: ${await zkClient.poolId()}`);
// output: Working on the pool with ID: 0
```

## <mark style="background-color:green;">Get the Network (Chain) Name</mark>

```typescript
networkName(): string
```

### Returns

Name of the current chain (e.g. `polygon`, `sepolia` etc). The name is retrieved locally from the integrated hard-coded mapping. The default value for unknown chains is `unknown-chain`.

### Example

```typescript
console.log(`Working on ${zkClient.networkName()} chain`);
// output: Working on polygon chain
```

## <mark style="background-color:green;">Get Token Seller Contract</mark>

```typescript
async tokenSellerContract(): Promise<string>
```

### Returns

`Promise` returns the `tokenSeller` contract address.

### Example

```typescript
console.log(`${await zkClient.tokenSellerContract()}`);
// output: 0x1b9d7af7793df23782b43af2566cc614745c32cf 
```


# Versioning

Library, relayer and prover version information

## <mark style="background-color:green;">Get the Library Version</mark>

```typescript
getLibraryVersion(): string
```

### Returns

Version string as defined in the `package.json` file

### Example

```typescript
console.log(`You are using client library v${zkClient.getLibraryVersion()}`);
// output: You are using client library v5.4.0
```

## <mark style="background-color:green;">Get the Relayer Version</mark>

```typescript
async getRelayerVersion(): Promise<ServiceVersion>
```

### Returns

`Promise` returns [`ServiceVersion`](/implementation/client-library-sdk/common-types#service-version): current relayer version

### Example

```typescript
const relayerVer = await zkClient.getRelayerVersion();
console.log(`We are communicating with relayer ${relayerVer.ref}`);
// output: We are communicating with relayer v4.3.0
```

## <mark style="background-color:green;">Get the Delegated Prover Version</mark>

```typescript
async getProverVersion(): Promise<ServiceVersion>
```

{% hint style="info" %}
The method will throw an `InternalError` if the delegated prover isn't configured for the current pool during client initialization.
{% endhint %}

### Returns

`Promise` returns [`ServiceVersion`](/implementation/client-library-sdk/common-types#service-version): current delegated prover version

### Example

```typescript
try {
    const proverVer = await zkClient.getProverVersion();
    console.log(`Delegated prover ${relayerVer.ref}`);
} catch (err) {
    console.log('Delegated prover is unavailable');
}
// output: Delegated prover v0.1.0
```


# Full Mode Operations

You can use it only with an attached account

The methods described in this section are related to the customer wallet. It is necessary to [attach an account](/implementation/client-library-sdk/configuration/attaching-a-user-account) before using (an `InternalError` will throw if you invoke these methods in account-less mode).

[Balances and History](/implementation/client-library-sdk/full-mode-operations/balances-and-history)

[Shielded Addresses](/implementation/client-library-sdk/full-mode-operations/shielded-addresses)

[Account State](/implementation/client-library-sdk/full-mode-operations/account-state)

[Fee Estimations](/implementation/client-library-sdk/full-mode-operations/fee-estimations)

[Transaction Configuration](/implementation/client-library-sdk/full-mode-operations/transaction-configuration)

[Sending Transactions](/implementation/client-library-sdk/full-mode-operations/sending-transactions)

[Transaction Maintenance](/implementation/client-library-sdk/full-mode-operations/transaction-maintenance)

[Direct Deposits](/implementation/client-library-sdk/full-mode-operations/direct-deposits)

[Gift Cards Maintenance](/implementation/client-library-sdk/full-mode-operations/gift-cards-maintenance)

[Ephemeral Deposits](/implementation/client-library-sdk/full-mode-operations/ephemeral-deposits)

[Forced Exit](/implementation/client-library-sdk/full-mode-operations/forced-exit)

[Other Routines](/implementation/client-library-sdk/full-mode-operations/other-routines)


# Balances and History

Retrieve user's shielded funds and activity

## <mark style="background-color:green;">Get the Account Balance</mark>

```typescript
async getTotalBalance(updateState: boolean = true): Promise<bigint>
```

{% hint style="info" %}
Only completed (mined on the pool smart contract) transactions are used to calculate the balance. To include pending txs use the[`getOptimisticTotalBalance`](#getting-the-balance-considering-pending-transactions) method instead.
{% endhint %}

### Parameters

`updateState` - update the state before calculating the balance if `true`

{% hint style="info" %}
It's recommended to set `updateState` to `false` in case of sequential operations where we recently updated the state. So you can reduce the sync tyme and the relayer requests count
{% endhint %}

### Returns

`Promise` returns the shielded token's balance in the pool dimension (account + all unspent notes)

### Example

```typescript
const balance = await zkClient.getTotalBalance();
console.log(`Your balance in the private pool is: ${balance}`);
// output: Your balance in the private pool is: 150000000000
```

## <mark style="background-color:green;">Get the Balance Including Pending Transactions</mark>

Get the balance including transactions owned by the account that have been submitted to the pool contract but not yet mined.

```typescript
async getOptimisticTotalBalance(updateState: boolean = true): Promise<bigint>
```

### Parameters

`updateState` - update the state before calculating the balance if `true`&#x20;

### Returns

`Promise` returns the shielded token's balance (including pending transactions) in the pool dimension (account + all unspent notes).

### Example

```typescript
const confirmedBalance = await zkClient.getTotalBalance();
const optimisticBalance = await zkClient.getOptimisticTotalBalance(false);
console.log(`Your confirmed balance:  ${confirmedBalance}`);
console.log(`Your optimistic balance: ${optimisticBalance}`);
// output: Your confirmed balance:  150000000000
//         Your optimistic balance: 170000000000
```

## <mark style="background-color:green;">Get the Balance Components</mark>

The customer's balance consists of the account balance and the unspent notes. The notes are produced by incoming transfers. Each user-originated transaction collects the 3 oldest notes to the account and marks them as spent ones. The balance configuration (funds distribution between account and notes) may affect the outgoing transfers and withdrawals. The following method retrieves the total balance as well as the account and notes components.

```typescript
async getBalances(updateState: boolean = true): Promise<[bigint, bigint, bigint]>
```

### Parameters

`updateState` - update the state before calculating the balance if `true`&#x20;

### Returns

`Promise` returns the tuple `[total, account, notes]` where `total = account + notes`  without pending transactions (all balances are in the pool token dimension).

### Example

```typescript
const [total, acc, note] = await zkAccount.getBalances();
console.log(`Balance:  ${total} (${acc} on the account and ${notes} in notes)`);
// output: Balance:  170000000000 (150000000000 on the account and 20000000000 in notes)
```

## <mark style="background-color:green;">Get the Transaction History</mark>

```typescript
async getAllHistory(updateState: boolean = true): Promise<HistoryRecord[]>
```

### Parameters

`updateState` - update the state before calculating the balance if `true`&#x20;

### Returns

`Promise` returns an array of [`HistoryRecord`](/implementation/client-library-sdk/common-types#history-record) objects

### Example

```typescript
const records = await zkAccount.getAllHistory();
console.log(`The account contains ${records.count} history records`);
// output: The account contains 42 history records
```

## <mark style="background-color:green;">Get the Compliance Report</mark>

The compliance report is an extended version of the regular history. It contains transaction details including input notes, decryption keys and other fields needed to show historical integrity.

```typescript
async getComplianceReport(
    fromTimestamp: number | null,
    toTimestamp: number | null,
    updateState: boolean = true,
  ): Promise<ComplianceHistoryRecord[]>
```

### Parameters

* `fromTimestamp` - the start time for the compliance report request (seconds since Jan 01 1970)
* `fromTimestamp` - the final time for the compliance report request (seconds since Jan 01 1970)
* `updateState` - update the state before calculating the balance if `true`&#x20;

### Returns

`Promise` returns the array of [`ComplianceHistoryRecord`](/implementation/client-library-sdk/common-types#compliance-history-record) objects

### Example

```typescript
const records = await zkAccount.getComplianceReport();
console.log(`Generated ${records.count} compliance history records`);
// output: Generated 42 compliance history records
```


# Shielded Addresses

Used to transfer funds inside a privacy pool

## <mark style="background-color:green;">Generate Shielded Address (pool specific)</mark>

A regular shielded address for the currently selected privacy pool (aka pool-specific address).

```typescript
async generateAddress(): Promise<string>
```

{% hint style="info" %}
The shielded address has a human-readable prefix to indicate which privacy pool it belongs to. Do not remove this: the address without the prefix considered invalid.
{% endhint %}

### Returns

`Promise` returns the shielded address for the current pool.

### Example

```typescript
console.log(`${await zkClient.generateAddress()}`);
// output: zkbob_sepolia:MrjNRn4ja5ctEW5PVFkZDe6XG97ZNCCHtgchKGzUgbrQDYUf26PikByp61oDVD9
```

## <mark style="background-color:green;">Generate Universal Shielded Address</mark>

A universal shielded address for use with any privacy pool (aka generic address).

```typescript
async generateUniversalAddress(): Promise<string>
```

### Returns

`Promise` returns the shielded address for any pool

### Example

```typescript
console.log(`${await zkClient.generateAddress()}`);
// output: zkbob:6aSJjrJ7F6kcoEzvKwiYwFSWeGsPJ4rLyGiGVD6e1TYdRZdGffTcyKvmM4wHFse
```

## <mark style="background-color:green;">Generate Shielded Address for Another Account</mark>

You can also generate a pool-specific address for another user account. To generate, you must provide the account spending key.

```typescript
async generateAddressForSeed(seed: Uint8Array): Promise<string>
```

### Parameters

`seed` - spending key for the account generated address belongs to

{% hint style="info" %}
It's the same parameter as provided in the [`AccountConfig.sk`](/implementation/client-library-sdk/configuration/attaching-a-user-account/account-configuration) during the account attaching&#x20;
{% endhint %}

### Returns

`Promise` returns the shielded address for the current pool belonging to the provided spending key

### Example

```typescript
const mnemonic = 'magic trophy foil direct marriage glad bench wash doctor risk end cheap';
const sk = deriveSpendingKeyZkBob(mnemonic);
console.log(`${await zkClient.generateAddressForSeed(sk)}`);
// output: zkbob_sepolia:SZgsuNkez8MMZJ4tvVyNCUajQNu26agnZvqJmkxdNpcTmshdfSzA2sUW1fsPVPU
```

## <mark style="background-color:green;">Shielded Address Verification</mark>

```typescript
async verifyShieldedAddress(address: string): Promise<boolean>
```

### Parameters

`address` - pool-specific or generic shielded address

{% hint style="info" %}
Addresses without prefixes are deprecated, the only exception is the **USDC pool on Polygon where they are allowed**. Otherwise, prefix-less addresses will be marked as invalid.
{% endhint %}

### Returns

`Promise` returns `true` if address checksum is correct and can be used on the current privacy pool

### Example

```typescript
const address = 'zkbob:4NeCQJijr9GQKBUBkp61bdQiqB15sf42fvy3CKGeXjUqJR3GBrQKSRrkboJounM';
const isValid = await zkClient.verifyShieldedAddress(address)
console.log(`The checking address is ${!isValid ? 'IN' : ''}VALID`);
// output: The checking address is VALID
```

## <mark style="background-color:green;">Check if a Shielded Address Belongs to an Account</mark>

```typescript
async isMyAddress(address: string): Promise<boolean>
```

### Parameters

`address` - pool-specific or generic shielded address

{% hint style="info" %}
Addresses without prefixes are deprecated, the only exception is the **USDC pool on Polygon where they are allowed**. Otherwise, prefix-less addresses will be marked as invalid.
{% endhint %}

### Returns

`Promise` returns `true` if address checksum is correct, it can be used on the current privacy pool and it belongs to the current user account

### Example

```typescript
const address = 'zkbob_sepolia:SZgsuNkez8MMZJ4tvVyNCUajQNu26agnZvqJmkxdNpcTmshdfSzA2sUW1fsPVPU';
const isOwn = await zkClient.isMyAddress(address)
console.log(`The address is ${!isOwn ? 'not ' : ''}belongs to our account`);
// output: The address is belongs to our account
```

## <mark style="background-color:green;">Get Components of a Shielded Address</mark>

```typescript
async addressInfo(address: string): Promise<IAddressComponents>
```

### Parameters

`address` - pool-specific or generic shielded address

### Returns

`Promise` returns [`IAddressComponents`](/implementation/client-library-sdk/common-types#shielded-address-components) for the provided address

### Example

```typescript
const address = 'zkbob_sepolia:SZgsuNkez8MMZJ4tvVyNCUajQNu26agnZvqJmkxdNpcTmshdfSzA2sUW1fsPVPU';
const components = await zkClient.addressInfo(address)
console.log(`d = ${components.d}, P_d = ${components.p_d}`);
// output: d = 221091213303296764154858, P_d = 104205498421613002257722403946176839412231742105831354351312357543598985595
```




---

[Next Page](/llms-full.txt/1)

