DOC# THEFEE SLUG the_fee_paradox PRINTED 2026-05-06 03:47 UTC

The fee paradox: why every smart-contract privacy mixer needs a relayer

On account-model chains the very act of paying a transaction fee deanonymises the recipient. This post formalises the paradox, walks through three resolutions, and sets up the SPST construction that resolves it inside the ZK proof itself.

FROM
Dax the Dev <[email protected]>
SOURCE
https://blog.skill-issue.dev/blog/the_fee_paradox/
FILED
2026-04-28 16:00 UTC
REVISED
2026-04-28 16:00 UTC
TIME
7 min read
SERIES
relayerless-privacy
TAGS
#zk #cryptography #privacy #tornado-cash #railgun #pedersen #phd

The first time I read the Tornado Cash whitepaper I missed the fee paragraph. I noticed the Merkle inclusion, the nullifier hash, the snarkjs circuit. I did not notice the part where, to actually withdraw to a fresh address, you need somebody else to broadcast the transaction. It was buried under “operator/relayer” — a word I generously read as “convenience”. It is not a convenience. It is the load-bearing wall of every smart-contract privacy mixer in production.

This post is post 2 of 11 in the relayerless-privacy series. Post 1 introduced the framework FRP\mathcal{F}_{\text{RP}} and outlined what the rest of the series builds. Here we slow down on the fee paradox itself — what it is, why every system using fees in a public token suffers it, and the three approaches that resolve it.

Definition: the fee paradox

On any blockchain B\mathcal{B} with a fee-based inclusion mechanism:

  1. To submit a transaction, the submitter’s address must pay a gas fee f>0f > 0.
  2. To hold gas, the address must have been previously funded.
  3. Funding an address creates an on-chain link to the funding source.

Therefore, any address that submits a transaction has a traceable funding history. Any privacy-preserving withdrawal to a fresh (unfunded) address requires an external party to pay the gas fee.

Formally, in the fee paradox game:

  1. User U\mathcal{U} deposits value vv into a shielded pool.
  2. U\mathcal{U} wishes to withdraw to a fresh addrrecv\mathsf{addr}_{\mathsf{recv}} with no prior on-chain history.
  3. Adversary A\mathcal{A} observes all transactions.
  4. If U\mathcal{U} funds addrrecv\mathsf{addr}_{\mathsf{recv}} to pay gas, A\mathcal{A} traces the funding source.
  5. A\mathcal{A} wins by linking the withdrawal to a prior deposit with non-negligible advantage.
AdvAFeeParadox(λ)    1negl(λ)in standard blockchain models.\mathsf{Adv}^{\mathsf{FeeParadox}}_{\mathcal{A}}(\lambda) \;\geq\; 1 - \mathsf{negl}(\lambda) \quad \text{in standard blockchain models.}

The advantage is overwhelming. The deck is stacked because the chain literally requires an attestation from a funded account before it will include any state transition. Privacy at the cryptographic layer collides with funding at the consensus layer, and the consensus layer wins.

Why UTXO chains don’t have this problem

Bitcoin and Zcash inherit a different model. A UTXO transaction’s “fee” is the difference between input value and output value:

f  =  iviin    jvjout.f \;=\; \sum_i v^{\mathrm{in}}_i \;-\; \sum_j v^{\mathrm{out}}_j.

The miner takes ff as the implicit fee. There is no separate “gas account” that needs to exist beforehand. In Zcash specifically, a Sapling or Orchard transaction’s valueBalance field — the net flow from the shielded pool to the transparent pool — is the fee. The binding signature proves the value commitment balance. The miner is paid out of value the prover is already moving, signed by the prover, with the prover’s identity hidden by the ZK proof.

Result: Zcash is relayer-free by construction. Penumbra, Aleo, Namada, and Monero are too — for the same reason. They all run on chains whose native fee model is fee-from-balance, not gas-from-account.

The fee paradox is specific to account-model chains like Ethereum and Solana, where transactions require an explicit fee payer signature and the fee is debited from a known account.

Three approaches to resolution

The paper section §2.3 enumerates three approaches to resolving the paradox without a relayer:

Approach A — Protocol-Native Fee Abstraction via ZK Fee Proofs

The fee is extracted from the shielded pool inside the ZK proof itself. The proof attests that

i=1ninvi  =  j=1noutvj  +  f\sum_{i=1}^{n_{\mathsf{in}}} v_i \;=\; \sum_{j=1}^{n_{\mathsf{out}}} v'_j \;+\; f

where ff is a public input to the proof and vi,vjv_i, v'_j are private inputs. Pedersen commitments make this clean: with Ci=viG+riHC_i = v_i \cdot G + r_i \cdot H for input notes and Cj=vjG+rjHC'_j = v'_j \cdot G + r'_j \cdot H for output notes,

iCi  =  jCj  +  fG  +  rΔH,\sum_i C_i \;=\; \sum_j C'_j \;+\; f \cdot G \;+\; r_\Delta \cdot H,

where rΔ=irijrjr_\Delta = \sum_i r_i - \sum_j r'_j is a blinding-factor residual that the prover demonstrates equals the right thing.

The validator extracts ff as inclusion compensation directly. The submitter does not need a public balance. This is what SPST does, and it’s the path the whole series builds toward.

Approach B — Nullifier-Derived Fee Authorization

A second derivation from the spending key, parallel to the nullifier:

nullifier=PRFk(ρ),fee_auth=PRFk(ρ“fee”f).\mathsf{nullifier} = \mathsf{PRF}_k(\rho), \qquad \mathsf{fee\_auth} = \mathsf{PRF}_k(\rho \,\|\, \text{``fee''} \,\|\, f).

The ZK proof proves both come from the same (k,ρ)(k, \rho), that fee_auth\mathsf{fee\_auth} encodes ff, and that the underlying note has sufficient balance. The fee is bound to the nullifier cryptographically — no party can alter the fee post-proof-generation.

This is more invasive than Approach A (changes the nullifier scheme) but gives a stronger non-malleability property. Most production designs use Approach A; Approach B becomes interesting when the protocol wants stricter binding for compliance audits.

Approach C — Recursive Fee Amortization via Batch Proofs

For high-frequency private transactions, fold nn proofs into a single Nova-style accumulator:

FoldedProofn  =  Fold(FoldedProofn1,(txn,wn)).\mathsf{FoldedProof}_n \;=\; \mathsf{Fold}(\mathsf{FoldedProof}_{n-1}, \, (\mathsf{tx}_n, w_n)).

The folded proof attests that all nn transactions are individually valid and that the cumulative fee Fn=k=1nfkF_n = \sum_{k=1}^n f_k has been correctly accumulated. A single on-chain verification covers all nn transactions.

On Solana, with ~200,000 CU per Groth16 verification and a per-transaction limit of ~1,400,000 CU, batches of n7n \leq 7 fit within a single transaction’s compute budget. The amortised per-transaction CU cost drops by an order of magnitude.

Tradeoff summary

AspectProsCons
Approach A (ZK fee proof) Smallest circuit overhead, no extra nullifier, clean Pedersen homomorphism Fee amount $f$ is public (necessary for validator compensation)
Approach B (Nullifier-derived) Cryptographic binding of fee to spending key; non-malleable Doubles the PRF calls; fee changes mean new nullifier scheme
Approach C (Folded batch) Amortises verification cost across $n$ transactions Requires Nova/SuperNova folding; off-chain coordination overhead

What the relayer-dependent protocols do instead

Tornado Cash, RAILGUN, and Light Protocol’s older privacy phase all chose none of the above. They use a relayer who pays the gas in the host chain’s native asset, takes a fee from the withdrawn amount, and broadcasts the transaction. The architecture is roughly:

The proof is binding — the relayer cannot redirect funds, cannot change the fee — but the relayer can refuse to broadcast. They can also log the user’s IP, timing, and metadata. In RAILGUN this is mitigated by routing over the Waku P2P network; in Tornado Cash it was just an HTTPS endpoint. Either way: the relayer is a third party, and that third party is a regulatory and operational single point of failure.

What changes when fees are folded into the proof

Once the fee comes from inside the proof, three things become different:

  1. The submitter does not need a balance. The transaction can be broadcast by the user themselves from a fresh address that has zero of the host chain’s native asset. The chain’s transaction-broadcasting interface accepts transactions from any party with a valid signature; that signature now binds nothing to the user’s identity.

  2. The validator gets paid out of the shielded pool’s escrow. On Solana, this is realised by having the privacy program’s PDA hold a lamport reserve. The fee ff — proven inside the SPST proof — authorises a transfer from this reserve to the validator. The shielded pool’s internal accounting decrements by ff. Every deposit replenishes the reserve.

  3. Censorship surface collapses. There is no “approved relayer list” for an adversary to attack. There is no operator to subpoena. The user’s only dependency is chain liveness — and that’s what Solana’s PoS consensus guarantees.

This is the Self-Sovereignty Theorem in informal form. The next post (SPST) makes it formal.

Bibliography

Previous: Series intro ← · Next: SPST: self-paying shielded transactions →

← Back to article