# What is NEBRA UPA?

## Submit a proof at [demo-app.nebra.one](http://demo-app.nebra.one)!

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

NEBRA's Universal Proof Aggregator (UPA) is our answer to the growing cost of on-chain ZK proof verification.

| Proof System | Gas Cost  | <p>FIAT Cost </p><p>(30 Gwei, 3000 USD ETH)</p> |
| ------------ | --------- | ----------------------------------------------- |
| Groth16      | 250,000   | 22.5 USD                                        |
| Halo2-KZG    | 400,000   | 36 USD                                          |
| STARK        | 1,500,000 | 105 USD                                         |

UPA *aggregates* proofs into batches, passing on the savings to you. The current release of UPA targets the popular proof system Groth16. Aggregating your proofs with UPA can save you up to 90% in verification costs!<br>

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

## Throughput

We have used UPA to aggregate 1 million proofs in 36 hours. Check out the [contract here. ](https://sepolia.etherscan.io/address/0x98D2c8A4FDDC1bdCbC8A059081Bf1918316CAB6B)


# Using UPA in your ZK app

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

### ZK apps before UPA <a href="#zk-apps-before-saturn" id="zk-apps-before-saturn"></a>

In a typical ZK app:

* The client makes requests of the app contract. Each request contains public inputs`PIs` along with a proof that those public inputs were valid.
* The app contract calls a `verify` function that checks whether a request's `(PIs, proof)` pair is valid or not. If the request was valid, then the app contract executes the corresponding business logic.

### ZK apps after UPA <a href="#zk-apps-after-saturn" id="zk-apps-after-saturn"></a>

In a UPA-integrated ZK app, the UPA contract takes over the responsibility of proof verification. The client now makes a `AppRequest` in two stages.

* First, they submit `(PIs, Proof)` to the UPA contract. If the proof is valid, then after some time the `PIs` are marked as verified in the UPA contract.
* Second, they make an `AppRequest(PIs)`. The app contract checks whether `PIs` has been marked as verified on the UPA contract. If so, the app goes on to execute the request.

The client saves gas because UPA verifies batches of proofs rather than individual proofs.

### What the UPA contract does <a href="#what-the-saturn-contract-does" id="what-the-saturn-contract-does"></a>

The UPA contract verifies batches of proofs as follows.

* The UPA contract receives a `submit(PIs, Proof)` call.
* An off-chain proof aggregator adds the `(PIs, Proof)` pair to a batch. Then it produces a ZK proof `aggregatedProof` that all of the `(PIs, proof)` pairs in that batch are valid. It then sends the `aggregatedProof` to the UPA contract.
* The UPA contract only needs to verify a single proof: `aggregatedProof`. If correct, then the contract marks all of the proofs in the corresponding batch as valid.​


# Demo app: Before and after UPA

## Demo app using UPA

Clients submit solutions $$(c, d, e, f)$$ for the equation:

$$a \* b = c \* d + e + f$$

to a smart contract, which counts the number of solutions it has seen. Elements $$a$$ and $$b$$ are not published on-chain, and instead a ZK proof is used to show know knowledge of them.

## Client changes

**Before UPA:** The client directly submits their solution `publicInputs`, along with a `proof`:

```typescript
await demoApp.submitSolution(proof, publicInputs);
```

**After UPA:** The client first submits their solution and proof to the UPA contract. Then the client waits for UPA to verify the proof before submitting the solution to the simple-app contract:

```typescript
const submissionHandle = await upaClient.submitProofs(circuitId, proof, publicInputs);

await upaClient.waitForSubmissionVerified(submissionHandle);

await demoApp.submitSolution(publicInputs);
```

## App smart contract changes

**Before UPA:** The contract checks that the solution is valid by using  `this.verifyProof` to directly verify the proof on-chain.

```solidity
function submitSolution(
    bytes calldata proof,
    uint256[4] calldata solution
) public returns (bool r) {
    bool isProofCorrect = this.verifyProof(proof, solution);
    require(isProofCorrect, "Proof was not correct");
```

\
**After UPA:** `this.verifyProof` is replaced by `upaVerifier.isVerified`. We don't need to send the proof, only the solution:

```typescript
function submitSolution(
    uint256[4] calldata solution
) public returns (bool r) {
    bool isProofCorrect = upaVerifier.isVerified(circuitId, solution);
    require(isProofCorrect, "Proof was not correct");
```

Here, the client saves on gas costs as this proof is not verified individually on-chain.

## Demo repository

<https://github.com/NebraZKP/demo-app>


# Try submitting a proof to UPA!

## [Option 1: Sending a demo app proof via website](https://demo-app.nebra.one)

### Pre-requisites

A browser wallet containing Sepolia ETH. We recommend using a Metamask wallet. You may fund the wallet from a faucet from <https://faucetlink.to/sepolia>.<br>

<figure><img src="/files/rDfens7xemzP0hgrKNQe" alt="" width="375"><figcaption></figcaption></figure>

### Submit a proof

Click the "Submit Proof" button to submit a proof.

* First a demo app solution and proof is generated in your browser.&#x20;
* Then the proof is submitted to UPA. You will be prompted by your wallet software to confirm this transaction. Once submission is complete, you may click the Etherscan link to view the transaction.
* While the proof is being aggregated you may click "View on nebrascan.io" to view its status in our proof explorer.<br>

  <figure><img src="/files/MA6aVkTUNnafdCok6Hfd" alt="" width="364"><figcaption></figcaption></figure>
* Finally, you can submit the solution to the demo app. You will be prompted by your wallet software again to confirm this transaction. Once the transaction is confirmed you may click  the link to see it on Etherscan. At this point you have completed the demo. Well done!

<figure><img src="/files/7aoaNb2Wat9MPomCPFcd" alt="" width="364"><figcaption></figcaption></figure>

## Option 2: Sending a demo app proof via CLI

### Pre-requisites

* yarn and Node 20+. These may be installed as follows:

```bash
# nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc # Update bash terminal so we can use the command `nvm`.

# npm
nvm install 20

# yarn
corepack enable
yarn set version stable
yarn install
```

### Submit a proof

<pre class="language-bash"><code class="lang-bash"><strong>git clone git@github.com:NebraZKP/demo-app.git &#x26;&#x26; cd demo-app
</strong>
<strong># Build demo-app
</strong>yarn
yarn build
# Make sure binary is placed in .bin
yarn

# Set up demo-app commands in shell
source scripts/shell_setup.sh

# Use upa local to generate a key
upa local ethkeygen --keyfile keyfilename.key
</code></pre>

Use a faucet to fund your address with sepolia ETH. Or send a message to our Telegram:\
![](/files/dxd95g28jlw3RG06AiWq)<br>

<pre class="language-bash"><code class="lang-bash"># Submit a solution along with a proof
<strong>demo-app submit --keyfile keyfilename.key
</strong></code></pre>

The last command will print a link to the proof's status on [NEBRA's proof explorer](https://www.nebrascan.io/). Your proof should be either verified or pending verification.

## Bonus

Try integrating your own ZK app with NEBRA UPA!


