Skip to content

Correct the ZIP 317 transaction fee calculator - #763

Open
DanielDerefaka wants to merge 1 commit into
ZecHub:mainfrom
DanielDerefaka:zip317-fee
Open

Correct the ZIP 317 transaction fee calculator#763
DanielDerefaka wants to merge 1 commit into
ZecHub:mainfrom
DanielDerefaka:zip317-fee

Conversation

@DanielDerefaka

Copy link
Copy Markdown
Contributor

Fixes the ZIP 317 fee calculation in the Build a Shielded Transaction visualizer.

The bug

calculateZip317Fee in BuildShieldedTransactionContent.tsx was:

const baseFee = 10000;
const marginalFee = 10000;
const logicalActions = 1 + transparentInputs + transparentOutputs + shieldedActions;
return baseFee + marginalFee * Math.max(0, logicalActions - 1);

Three things are off against ZIP 317:

  1. There is no base fee in the spec, and marginal_fee is 5,000 zats, not 10,000.
  2. The formula is marginal_fee × max(grace_actions, logical_actions) with grace_actions = 2, not a base plus a per extra action charge.
  3. Logical actions are not a flat sum of everything a transaction touches. Each pool contributes separately, and inside a pool spends and outputs pair up so only the busier side counts.

The visible symptom: an Orchard to Orchard send was quoted at 0.0002 ZEC, while the Output Description stage of the same visualizer already shows the correct 0.0001 ZEC on the change note.

The fix

Moved the calculation into src/lib/zip317.ts, following the spec directly:

conventional_fee = marginal_fee * max(grace_actions, logical_actions)

Logical actions are now built one contribution per pool, with the ZIP's own field names so the code can be read next to the spec:

Pool Contribution
Transparent max(ceil(tx_in_total_size / 150), ceil(tx_out_total_size / 34))
Sprout 2 × nJoinSplit
Sapling max(nSpendsSapling, nOutputsSapling)
Orchard nActionsOrchard
Ironwood nActionsIronwood (revision 1)

simpleTransfer(from, to) builds the shape the visualizer actually models: one note or UTXO spent in the sender's pool, the recipient paid in the target pool, change back to the sender. Every pool pair comes out at 2 logical actions, so the builder now quotes 0.0001 ZEC across the board, which is what a wallet charges for a simple send and is the point ZIP 317 is making.

The fee panel also shows the formula it just applied, e.g.

Fee = 5,000 zats × max(2 grace actions, 2 logical actions) · 1 transparent + 1 Sapling

so the number is no longer unexplained.

Testing

  • src/lib/__tests__/zip317.test.ts, 21 cases covering the spec parameters, each pool contribution, the grace floor, byte rounding, and all nine pool pairs. These live under src/lib so the existing lib-tests job runs them.
  • yarn jest src/lib passes, 6 suites / 57 tests.
  • yarn build clean, tsc --noEmit clean, no new eslint findings.
  • Walked all nine From/To pool combinations in Chromium against yarn build && yarn start and checked the fee, the action count and the breakdown line each time.

The builder used a base fee of 10,000 zats plus 10,000 per extra
action, and counted logical actions by adding up every component the
transaction touched. Neither matches ZIP 317, so a shielded send was
quoted at 0.0002 ZEC while the Output Description stage of the same
visualizer shows the real 0.0001 ZEC.

Move the calculation into src/lib/zip317.ts and follow the spec:

  conventional_fee = marginal_fee * max(grace_actions, logical_actions)

with marginal_fee 5,000 zats and grace_actions 2. Logical actions are
now one contribution per pool, so transparent inputs and outputs are
measured in bytes against the standard P2PKH sizes, Sapling spends
pair with outputs, and Orchard and Ironwood Actions count directly.

The builder's fee panel also shows the formula it just applied and the
actions each pool contributed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant