Skip to content

fix(zcash): update ZIP-317 fee calculation for NU6.1 compatibility#1671

Merged
Thorian1te merged 2 commits intomasterfrom
fix/zcash-zip317-nu6.1-fees
Mar 31, 2026
Merged

fix(zcash): update ZIP-317 fee calculation for NU6.1 compatibility#1671
Thorian1te merged 2 commits intomasterfrom
fix/zcash-zip317-nu6.1-fees

Conversation

@Thorian1te
Copy link
Copy Markdown
Collaborator

@Thorian1te Thorian1te commented Mar 31, 2026

NU6.1 (activated Nov 24 2025 at block 3146400) changed fee rules:

  • MARGINAL_FEE: 5000 → 10000 zatoshi (doubled)
  • GRACE_ACTIONS: 2 → 0 (removed, all actions must be paid)
  • Logical actions: inCount + outCount → max(inCount, outCount)

Without this fix, transactions are rejected with:
"-26: 66: tx unpaid action limit exceeded"

Summary by CodeRabbit

  • Updates
    • Revised Zcash fee computation to improve how fees scale with inputs/outputs and memo inclusion.
    • Increased default estimated fees for certain Zcash transactions, updating expected fee amounts and affecting max-send calculations.
    • Adjusted client fee estimation so memo impact is handled centrally, yielding more consistent fee expectations across transaction types.

NU6.1 (activated Nov 24 2025 at block 3146400) changed fee rules:
- MARGINAL_FEE: 5000 → 10000 zatoshi (doubled)
- GRACE_ACTIONS: 2 → 0 (removed, all actions must be paid)
- Logical actions: inCount + outCount → max(inCount, outCount)

Without this fix, transactions are rejected with:
"-26: 66: tx unpaid action limit exceeded"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

Zcash fee calculation and fee-usage sites were changed: MARGINAL_FEE increased and calculateFee/getFee logic altered to base fees on max(inputCount, outputCount) with memo handling centralized in getFee; tests and client prepareMaxTx updated to match new fee values and call signatures.

Changes

Cohort / File(s) Summary
Builder / Fee model
packages/zcash-js/src/builder.ts
Increased MARGINAL_FEE (5000 → 10000); removed GRACE_ACTIONS multiplier; changed calculateFee to use Math.max(inCount, outCount) and ensure minimum multiplier of 1; switched calls to use getFee(utxos.length, 1, memo) / getFee(inputs.length, 2, memo) so memo-slot sizing is handled inside getFee.
Client / max-tx fee usage
packages/xchain-zcash/src/client.ts
Updated prepareMaxTx to call getFee(utxos.length, 1, memo) (fixed outCount=1) so memo effect is delegated to getFee, changing computed fee and maxAmount for memo vs non-memo cases.
Tests / expectations
packages/xchain-zcash/__tests__/client.test.ts
Adjusted FeeType.FlatFee expectations: for getFees({ memo: 'test' }) flat fees changed 25000 → 30000; for getFees({ sender: ... }) flat fees changed 30000 → 40000 across average/fast/fastest tiers.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • underthesun49
  • Naq302
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: updating Zcash's ZIP-317 fee calculation for NU6.1 compatibility, which aligns with the PR's core objective of addressing fee rule changes from the NU6.1 network upgrade.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/zcash-zip317-nu6.1-fees

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/zcash-js/src/builder.ts`:
- Around line 21-25: The memo fee calculation is inconsistent across codepaths:
standardize on one output-count convention and ensure all fee computations use
the same helper; update calculateFee (and/or getFee) to define whether memo
consumes an extra output slot (use max(inCount, outCount) or max(inCount,
outCount + memoSlots) consistently), then change buildTx, buildMaxTx and any
callers (e.g., getFee usage in client code) to call that single helper
(calculateFee/getFee) rather than re-implementing logic so memo transfers,
selections, and builds all compute identical fees.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6cdd3e3c-fc1a-4baf-8a8e-93f7e36aac43

📥 Commits

Reviewing files that changed from the base of the PR and between 9069989 and 1aafbc4.

📒 Files selected for processing (2)
  • packages/xchain-zcash/__tests__/client.test.ts
  • packages/zcash-js/src/builder.ts

Comment on lines +21 to +25
const MARGINAL_FEE = 10000

function calculateFee(inCount: number, outCount: number): number {
const logicalActions = inCount + outCount
return MARGINAL_FEE * Math.max(GRACE_ACTIONS, logicalActions)
const logicalActions = Math.max(inCount, outCount)
return MARGINAL_FEE * Math.max(1, logicalActions)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Memo fee counting still diverges across estimate, selection, and build paths.

After switching to max(inCount, outCount), this becomes a user-visible mismatch: Line 56 and packages/xchain-zcash/src/client.ts, Lines 195-208, use getFee(..., 2, memo), buildTx() uses getFee(..., 3, memo) on Lines 154-155, and buildMaxTx() on Lines 105-107 bypasses getFee() entirely. That means a memo transfer can be quoted/selected against one fee and finalized with another, while long-memo sweeps still ignore extra memo slots and can remain underfunded. Please normalize the output-count convention and route every memo path through the same helper before merging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/zcash-js/src/builder.ts` around lines 21 - 25, The memo fee
calculation is inconsistent across codepaths: standardize on one output-count
convention and ensure all fee computations use the same helper; update
calculateFee (and/or getFee) to define whether memo consumes an extra output
slot (use max(inCount, outCount) or max(inCount, outCount + memoSlots)
consistently), then change buildTx, buildMaxTx and any callers (e.g., getFee
usage in client code) to call that single helper (calculateFee/getFee) rather
than re-implementing logic so memo transfers, selections, and builds all compute
identical fees.

getFee is now the single source of truth for memo output slot counting.
Callers pass only non-memo outputs; getFee adds memo slots internally.
Fixes double-counting in buildTx/prepareMaxTx and bypass in buildMaxTx.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/xchain-zcash/src/client.ts (1)

111-116: ⚠️ Potential issue | 🟡 Minor

The placeholder 'memo' string in getFeeFromUtxos doesn't affect Zcash's current fee calculation, but is still incorrect.

While the method receives _data (compiled memo Buffer) and should derive memo length from it, it instead passes the literal string 'memo' (4 chars) to getFee, which calculates slots based on string length.

However, this doesn't cause issues in practice for Zcash because:

  1. getFeesWithRates() is overridden to throw (line 187-189), so the parent class's call to getFeeFromUtxos never executes
  2. Zcash uses its own getFees() method (line 195-208) which correctly passes actual memo strings (options?.memo)

That said, the method should still be fixed to avoid confusion and support potential future use cases. Since the compiled memo Buffer is provided but its original string length is lost, you could estimate memo length from the Buffer size or refactor to pass the original memo string alongside the compiled version.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/xchain-zcash/src/client.ts` around lines 111 - 116, getFeeFromUtxos
currently passes the literal string 'memo' to getFee which is incorrect; instead
derive the memo length from the compiled memo Buffer _data and pass a string of
that length (or otherwise adapt to getFee's expected input) so slot calculation
is correct. Update the getFeeFromUtxos implementation (function name:
getFeeFromUtxos, and call site getFee) to check _data, compute memoLength =
_data.length (or an estimated character length based on the Buffer size), and
pass a string constructed to that length (e.g., repeated char) to getFee, or
refactor to pass the memo length if getFee can accept it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/xchain-zcash/src/client.ts`:
- Around line 111-116: getFeeFromUtxos currently passes the literal string
'memo' to getFee which is incorrect; instead derive the memo length from the
compiled memo Buffer _data and pass a string of that length (or otherwise adapt
to getFee's expected input) so slot calculation is correct. Update the
getFeeFromUtxos implementation (function name: getFeeFromUtxos, and call site
getFee) to check _data, compute memoLength = _data.length (or an estimated
character length based on the Buffer size), and pass a string constructed to
that length (e.g., repeated char) to getFee, or refactor to pass the memo length
if getFee can accept it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7af5da1b-924b-4beb-b3ae-e1cd72ef1a10

📥 Commits

Reviewing files that changed from the base of the PR and between 1aafbc4 and fd1b287.

📒 Files selected for processing (2)
  • packages/xchain-zcash/src/client.ts
  • packages/zcash-js/src/builder.ts

@Thorian1te Thorian1te merged commit 5877107 into master Mar 31, 2026
3 checks passed
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.

2 participants