Gas costs on L1s

End-to-end gas costs of using UPA on L1 chains (e.g. Ethereum Mainnet)

Let's walk through the factors determining the end-to-end gas costs for a ZK app using NEBRA UPA. There are three on-chain steps that can consume gas:

  1. Submitting proofs

    • on-chain (~13-100k gas per proof- depends on submission size)

    • off-chain (0 gas per proof)

  2. Verifying the aggregated proof (~18k gas per proof)

  3. Querying the verification result (~22k gas per proof or per submission)

The total per-proof gas cost, in both the on-chain submission (UPA v1) and off-chain submission (UPA v2) case is shown below:

Submission Size
Per-Proof Gas w/o Aggregation
Per-Proof Gas (v1)
Per-Proof Gas (v2)

1

250k

150k

40k

2

250k

100k

40k

4

250k

75k

40k

8

250k

62.5k

40k

16

250k

56k

40k

32

250k

53k

40k

Summing up these costs, we find that in total:

  • Applications submitting their proofs on-chain can save as much as 197k gas (~80%) per proof.

  • Applications submitting their proofs off-chain can save upwards of 210k gas (~85%) per proof.

You can estimate your application's gas savings using our gas calculator at gas.nebra.one. For simplicity we show gas estimates for an application with four public inputs, but the actual costs for each step will vary a bit depending on the number of public inputs per proof.

Step 1: Proof Submissions

NEBRA UPA collects submissions of one or more proofs and places them in a queue to be aggregated. Note that UPA does not aggregate submission-by-submission. Instead, aggregated batches are chosen independently of the way the proofs were submitted.

Applications will have two options for proof submissions: on-chain and off-chain. On-chain submissions cost gas in exchange for censorship-resistance (an aggregator that skips verifying a valid proof can be slashed). Off-chain submissions have no gas cost, but offer weaker censorship resistance.

The current UPA release (v1.3) supports on-chain submission. Off-chain submission will be available soon.

Cost of on-chain submission

Due to per-transaction storage costs, using a whole Ethereum transaction to submit a single proof is relatively expensive. Instead, we recommend that apps take advantage of multi-proof submissions.

The contracts currently deployed to the Sepolia testnet are initial implementations with large scope for gas optimization, but they serve to illustrate the approximate cost model. Currently, the fixed cost per submission is about 100k gas. The marginal cost of each additional proof in a submission is about 10k gas (for processing the additional proof and public input data).

The table below shows the measured cost of submissions of different sizes.

Submission Batch Size
Per-proof Submission Gas

4

33,127

8

22,013

16

16,451

32

13,679

Cost of off-chain submission

Off-chain submissions have no associated gas cost, but a weaker censorship resistance mechanism: In response to a submission, the aggregator sends back a confirmation that they will aggregate the proof before a certain deadline. If this aggregator misses this deadline then they are subject to slashing.

Step 2: Verifying the aggregated proof

The current version of NEBRA UPA aggregates proofs into a Halo2-KZG proof. The cost of verifying such a proof in isolation is about 350k gas, and is roughly independent of batch size. In addition, the UPA contract emits an event and updates its storage to mark each application proof in the batch as verified. This incurs a marginal per-proof cost of about 7k gas.

Step 3: Query of the verification result

Once a proof has been verified by the UPA contract, the app contract may query the UPA contract to confirm that the associated public inputs are valid. This typically looks like:

require(upaVerifier.isProofVerified(circuitId, publicInputs, "Not verified"));

The gas cost of this external contract call is about 22k gas per proof (assuming four public inputs).

Applications may also query the verification status of an entire submission as follows:

bytes32 submissionId = UpaLib.computeSubmissionId(
    circuitId,
    publicInputsArray
);

require(
    upaVerifier.isSubmissionVerified(submissionId),
    "Sequence not verified"
);

This costs about the same amount of gas as calling isProofVerified.

Calculation of end-to-end gas savings

Without UPA

The total cost for an app to verify an individual Groth16 proof is about 250-270k gas, which can be broken down into:

  1. Submitting and verifying the proof (~250k gas)

  2. Retrieving the verification result

    • from an external contract (~10-20k gas)

    • from within the app contract (0 gas)

Using UPA with on-chain submission

which represents a savings of about 197k gas per proof (~80%).

Using UPA with off-chain submission

The end-to-end gas cost with off-chain submissions will be approximately:

which represents a savings of about 210k gas per proof (~85%).

Last updated