Skip to content
198 changes: 198 additions & 0 deletions zips/draft-dynamic-fees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
```
ZIP: unassigned
Title: Comparable-based Fee Market
Owners: Mark Henderson <mark@shieldedlabs.net>
Zooko Wilcox <zooko@shieldedlabs.net>
Nathan Wilcox <nate@shieldedlabs.net>
Status: Draft
Category: Consensus / Standards
Created: 2026-01-11
License: MIT
```

# Terminology

The key words "SHOULD", and "MUST" in this document
are to be interpreted as described in BCP 14 [^BCP14] when, and only when, they
appear in all capitals.

The character § is used when referring to sections of the Zcash Protocol Specification.
[^protocol]

The terms "actions", "conventional fee", and "marginal fee" are defined as in ZIP 317. [^zip-0317]

The term "reorg buffer" refers to the number of blocks prior to the chain tip that are excluded from consideration to mitigate reorganization risk.

The term "lookback window" refers to the number of blocks prior to the chain tip used to calculate statistics useful for fee calculation from comparable transactions.

The term "fee floor" (or "floor") refers to the minimum price allowed in the fee calculation.

The term "priority multiplier" (or "multiplier") refers to the factor by which a transaction's fee is multiplied if the user opts for priority processing.

The term "comparables" (or "comps") refers to previous transactions used to determine the appropriate fee for a new transaction based on their fees and actions.
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This term seems not to be used in the body of the ZIP. Relatedly, I don't understand its use in the title.


The Version 6 transaction fields `nIssueActions`, `fee`, and `nExpiryHeight` are as defined in ZIP-230. [^zip-0230]

# Abstract

This ZIP introduces a dynamic pricing mechanism based on a median calculation
of comparable prices that users have paid in previous blocks.

The mechanism can ship first as simple node and wallet policy changes, and
consensus hardening can follow in a later network upgrade. The design preserves
Zcash's core privacy assumptions and adds no explicit on-chain metadata beyond
the paid fee.

# Motivation

The price of ZEC has become more volatile and risen sharply, raising
transaction fees along with it to prices users have begun to find questionable.
Additionally, new environmental factors such as new levels of adoption, and the
emergence of Zcash Digital Asset Treasuries may hint at increased volume over
the foreseeable future.

While there are short-term solutions available (the simplest of which would
simply be to lower the ZIP-317 marginal fee), this may be inadequate over a
longer period of time and require periodic tuning. Thus, a dynamic fee
calculation is required both to weather short-term volatility, and also to
adjust the marginal fee dynamically over time.

TODO: Justify the complexity of this approach relative to adjusting the ZIP 317
marginal fee. At present, marginal fee adjustments across the ecosystem are a
bit of a challenge, in particular, raising the marginal fee definitely requires
wallets to update. An automatic fee adjustment mechanism handles this.

# Privacy Implications

This design attempts to minimize information leakage about the user while still
providing user discretion on paying a premium rate for priority processing,
resulting in a better user experience.

The combined effects of an additional bit of user choice (priority vs standard)
and powers of 10 quantization of fees result in an overall reduction of fee
entropy leakage.
Comment on lines +71 to +73
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 187 says:

Any transaction UX (wallet or other interface) SHOULD offer the user the standard median fee, and a priority fee of median_fee_per_action * priority_multiplier.

As I read it, the quantization occurs only in the adjusted marginal fee (referred to as median_fee_per_action here). Therefore it has no effect in hiding the user choice. Even if the quantization was applied to the total fee (which is a radical departure from ZIP 317 and would require more analysis), I think it would often not hide the user choice. So this paragraph is misleading in implying that the quantization helps in that regard.


This design likely requires trust in the lightwallet's relay of the fees, which
is compatible with the existing wallet app threat model. [^wallet-threat-model]

# Requirements

A successful design and implementation is one that:

- Does not leak information that can be used to segment the user base.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any transaction UX (wallet or other interface) SHOULD offer the user the standard median fee, and a priority fee of median_fee_per_action * priority_multiplier.

This does not satisfy the requirement, since it leaks the user's choice of whether to prioritize the transaction.

- Only uses public information to compute the conventional fee.
- Has the fee remain roughly economically stable, even though the economic
power of ZEC can fluctuate substantially.
- Provides a clear and consistent user experience:
- Fees are easy to understand and explain
- Fees are kept low and adjust rarely
- Transactions are reliable under uncongested network conditions
- Prioritizes higher value transactions when the network is congested
Comment on lines +86 to +90
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Provides a clear and consistent user experience:
- Fees are easy to understand and explain
- Fees are kept low and adjust rarely
- Transactions are reliable under uncongested network conditions
- Prioritizes higher value transactions when the network is congested
- Fees are easy to understand and explain.
- Fees adjust rarely.
- Transactions are reliable under uncongested network conditions.
- Prioritizes higher value transactions when the network is congested.

"Clear and consistent user experience" is subjective; if the subitems explain what is meant by this, they are sufficient on their own.

"Fees are kept low" seems at least partly redundant with "Has the fee remain roughly economically stable", and it is not clear what it adds to the latter.

"Fees adjust rarely" does not seem to be satisfied by the proposed mechanism. Is the requirement wrong or is the mechanism wrong?

"Prioritizes higher value transactions when the network is congested" also doesn't seem to be satisfied, since the prioritization is by user choice, not by value.

- Incentivizes miners to include legitimate transactions in blocks
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without analyzing the dynamic behaviour of the fee adjustment in detail, we can now only say that the fee is at least 10 zatoshis per logical action. Is that high enough to incentivize miners?

- Responds quickly to congestion and slowly returns to normal prices
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Quickly" and "slowly" are vague.

- Makes griefing expensive:
- Preventing legitimate transactions requires paying a price higher than
legitimate users are willing to pay
Comment on lines +94 to +95
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems impossible. It's always sufficient to pay the same price that users are willing to pay (to displace the same amount of block space), because there's no way to distinguish a competent adversary's transaction from a user transaction, at least if they are shielded.

- Prevents miners from gaming the protocol by making users pay more
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't clear to me that this requirement is met.

- Is easy to implement and deploy:
- Compatible with current and planned network upgrades i.e. ZIP-235’s NSM
contributions and ZIP-317’s action-based accounting.
- Policy-first, hardened by minimal consensus upgrades later
- Uses a stateless design, with minimal calculations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This presumably means that it doesn't add additional state. The past fees are state.

- Does not substantively degrade user experience compared to the status quo.

# Specification

## Notation

Let $\mathsf{lceil}(x)$ be the smallest integer greater than or equal to `x`.
<br />
Let $\mathsf{rceil}(x)$ be the largest integer less than or equal to `x`.
<br />
Let $\mathsf{median}(S)$ be the value at index $\lceil (|S| - 1) / 2 \rceil$ of the sorted set `S`.
<br />
Let $\mathsf{average}(S)$ be the result of summing all elements of `S` and dividing by `|S|`.

## Marginal Fee Calculation

This specification defines several new parameters that are used to calculate
the new marginal fee.

| Parameter | Value | Units |
| --- | --- | --- |
| `reorg_buffer` | 5 | blocks |
| `lookback_window` | 50 | blocks |
| `fee_floor` | 10 | zats per action |

Let `chain_tip` be the height of the latest known block.

Let `T` be the set of transactions in the blocks with heights `[chain_tip - reorg_buffer - lookback_window ... chain_tip - reorg_buffer]`.

The new suggested marginal fee is calculated as $\mathsf{median}(T_fee)$. The
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\mathsf{T_{fee}}$ is not defined. The intent seems to be that it is the sequence of fees for transactions in $\mathsf{T}$ plus fees for synthetic actions. (Synthetic actions are only mentioned way down on line 156; they have to be introduced much earlier for the algorithm not to be misread, as I originally did.)

Btw, either _ must be escaped as \_, or fee must be written as {fee} if intended as a subscript.

Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More importantly, this seems wrong for the intent of the algorithm. The marginal fee is incomparable to the past median fee, because you're not taking into account the number of actions. This will not result in stable fee adjustment.

I could be wrong but I don't see that this has an obvious fix. As I stated on the forum:

I believe the fee market proposal mentioned in question 8 has not had time for adequate analysis.

Also see this forum comment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had something like dynamic_fee(h) = conventional_fee / (1 - (average_size(blocks[h-20..h]) / max_block_size)) in mind.

conventional fee calculation remains unchanged as per ZIP-317. [^zip-0317]

<details>
<summary>Rationale</summary>
- A reorg buffer of 5 blocks is chosen to balance the need to mitigate
reorganization risk with the desire to use recent transaction data for fee
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not 10 blocks?

calculation.
- A lookback window of 50 blocks (roughly the last hour based on the difficulty
algorithm) is selected to provide a sufficient sample size of recent
transactions while still being responsive to changes in network conditions.
- The median is chosen as the method for calculating the marginal fee because
it is robust against outliers and provides a more accurate representation of
typical transaction fees paid by users. By ensuring that 50% of transactions
pay at least the median fee, we can reasonably assume that miners are
incentivized to include transactions paying this fee in their blocks.
</details>

## Synthetic Actions

Given the same sequence of transactions `T` as defined above, let synthetic action
`S` be defined as an action with a `S_fee = fee_floor` and a size of
$\mathsf{mean}(T_size)$.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\mathsf{T}$ might not have enough transactions for $\mathsf{mean}(\mathsf{T_{size}})$ to be representative. For example, if we have a coinbase-only block, $\mathsf{mean}(\mathsf{T_{size}})$ will be equal to the size of that transaction which is likely atypical. And in any case, this gives an adversary a lot of control over $\mathsf{mean}(\mathsf{T_{size}})$, and therefore over the number of synthetic fees in $\mathsf{T_{fee}}$.


When calculating the median fee, if a block has unused capacity (i.e., fewer
actions than the maximum allowed), we fill the empty space with synthetic
actions, and then perform the median calculation.
Comment on lines +149 to +151
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Fill the empty space with synthetic actions" is imprecise. I can guess what it might be intended to mean, but $\mathsf{T}$ is a sequence of transactions, not actions, so you can't directly append actions to it.

The intent seems to be to append fees corresponding to those actions to $\mathsf{T_{fee}}$, but I think this biases the resulting mean more toward fee_floor than intended — because the synthetic actions are each "smaller" than a typical transaction, so there are more of them.

One way to account for this is to count a real transaction with $k$ logical actions and fee $F$ as contributing $k$ copies of $F/k$ to $\mathsf{T_{fee}}$, and calculate $\mathsf{T_{size}}$ as the mean "logical action size" (i.e. tx size divided by $k$ for each action). That way the median would reflect the median marginal fee, which seems to be what is intended. But I shoudn't have to guess. Also this might be "less wrong", but I'm pretty sure it's still not the right thing:

  • using the median means that if more than half the actions are synthetic, then the median will "fall off a cliff" and be exactly fee_floor (and this in practice will happen if blocks are less than half-full, which they currently are);
  • it is unclear that we want to weight transactions with more logical actions higher when calculating whatever average is used.

Note that even if the algorithm is fixed so that it doesn't cause the adjusted marginal fee to go to fee_floor unintentionally, it's also necessary to prevent an adversary (even if they can mine blocks) from being able to do this maliciously, in order to benefit from the low marginal fee to mount a DoS attack on subsequent blocks.


<details>
<summary>Rationale</summary>
- By using the average size of real actions for synthetic actions, we ensure
that the fee calculation remains realistic and reflective of actual network
conditions.
</details>

## New Rules

The following rules can be enforced by relay policy initially, and later
hardened into consensus rules in a future network upgrade.
Comment on lines +162 to +163
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing ZIP 317 conventional fee isn't enforced by relay policy; it is enforced by the recommended algorithm for block template construction. (At least that's true for zcashd, and iirc also for zebrad.)

It is unclear whether it is a good idea to have fee policy in consensus; that would have to be separately justified. I suggest expressing it as an "Open question".


- The per-action fee MUST be at least `fee_floor` zats.
- The per-action fee MUST be a power of 10.
Comment on lines +165 to +166
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should explicitly be part of the formula to calculate the marginal fee, rather than being stated as constraints (because the calculation needs to be deterministic in order to not leak information). Also, "marginal fee" is the correct terminology, not "per-action fee".

- The wallet MUST set `nExpiryHeight` such that `chain_tip + reorg_buffer < nExpiryHeight < chain_tip + lookback_window + reorg_buffer`,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently nExpiryHeight is not required to be set. Requiring it is a major behaviour change that would require more thorough justification; it would need be called out in the ZIP Abstract and probably mentioned in the Requirements.


<details>
<summary>Rationale</summary>
- Setting an expiry height within this range ensures that mispriced
transactions don't linger in the mempool for too long.
</details>

## Wallet Construction

Any transaction UX (wallet or other interface) SHOULD offer the user the standard median fee, and a priority fee of `median_fee_per_action * priority_multiplier`.
Copy link
Collaborator

@daira daira Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated earlier, this makes transactions distinguishable by the user's choice of priority, which would need to be justified in the Privacy Implications section (and I do not think it is a good idea).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also "standard median fee" is not defined. I think you mean the conventional fee (with the described marginal fee adjustment).


# Reference Implementation

More information is available at https://fees.shieldedinfra.net.


# References

[^BCP14]: [Information on BCP 14 - "RFC 2119: Key words for use in RFCs to Indicate Requirement Levels" and "RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words"](https://www.rfc-editor.org/info/bcp14)

[^zip-0317]: [ZIP-317: Proportional Transfer Fee Mechanism](https://zips.z.cash/zip-0317)

[^zip-0230]: [ZIP-230: Version 6 Transaction Format](https://zips.z.cash/zip-0230)

[^wallet-threat-model]: [Zcash Wallet App Threat Model](https://zcash.readthedocs.io/en/latest/rtd_pages/wallet_threat_model.html)
Loading