This document outlines the changes required to support Zcash (ZEC) on Ledger devices within hdwallet.
Zcash support on Ledger requires specific handling due to its unique transaction format (ZIP-244, v4/v5 versions) and consensus branch IDs. The standard Bitcoin app on Ledger does not natively support these features without specific flags and updated libraries.
- SLIP44 Code: 133
- Standard Path:
m/44'/133'/0'/0/x - Account Type: P2PKH (Legacy) only - no SegWit support
- Versions: v4 (Sapling), v5 (NU5+)
- Version Encoding: Little-endian with Overwinter flag
- v4:
0x80000004 - v5:
0x80000005
- v4:
- Version Group IDs:
- v4 (Sapling):
0x892F2085 - v5 (NU5+):
0x26A7270A
- v4 (Sapling):
- NU5:
0xC2D6D0B4(block 1,687,104) - NU6:
0xC8E71055(block 2,726,400 - Nov 2024) - NU6.1:
0x4DEC4DF0(block 3,146,400 - Nov 2025)
- Transparent addresses (t-addresses): Start with "t1" (mainnet)
- No shielded support: z-addresses not supported in this implementation
Error: splitTransaction failed with varint parsing error
Root Cause: Missing additionals parameter when calling splitTransaction
Solution: Pass ["zcash", "sapling"] as 4th parameter
Reference: Ledger splitTransaction tests
Error: AbortError: Failed to execute 'transferIn' on 'USBDevice'
Root Cause: Wrong isSegwit parameter value (was false, should be true)
Solution: Set 2nd parameter to true for Zcash in splitTransaction
Reference: Ledger test file shows splitTransaction(hex, true, true, ["zcash", "sapling"])
Error: transaction uses an incorrect consensus branch id
Root Cause: Zcash network upgraded to NU6.1 (0x4DEC4DF0), but older Ledger libraries defaulted to NU6.
Solution:
- Use
@ledgerhq/hw-app-btcversion10.13.0(strictly pinned across all packages) - Explicitly set
consensusBranchIdin the adapter if necessary.
Error: "Missing inputs" error or invalid signature verification on broadcast.
Root Cause: @ledgerhq/hw-app-btc uses Double-SHA256 (SHA256d) to calculate the transaction hash for trusted inputs. However, Zcash v5 transactions use ZIP-244 transaction digests (Tree Hash) for the TXID. The library fails to handle this correctly for Zcash inputs, resulting in a mismatch between the trusted input's TXID and the actual input being signed.
Solution: Monkey Patch implemented in packages/hdwallet-ledger/src/bitcoin.ts.
- Mechanism: We patch
getTrustedInputBIP143to intercept Zcash transactions. - Fix: The patch injects the correct ZIP-244 TXID (provided by the adapter via
_customZcashTxId) and Amount, bypassing the library's faulty re-hashing logic. - Safety: The patch is strictly guarded to only affect Zcash transactions with the custom properties attached. Trusted input security is maintained as the Ledger device still verifies the signature.
- Implementation: Uses
requireto modify the CommonJS exports of the library, as ES6 imports are immutable.
- Only supports t-addresses (transparent UTXO transactions)
- Does not support z-addresses (shielded transactions)
- Current: Hardcoded to 0 (no expiry) using
Buffer.alloc(4)in the implementation. - Requirement: Zcash transactions require
expiryHeight. If 0, it means no expiry (valid per ZIP-203). - Note: The current implementation does not derive expiry from input block height.
| File | Changes |
|---|---|
packages/hdwallet-ledger/src/bitcoin.ts |
Major Update: Added Monkey Patch for getTrustedInputBIP143. Logic for Zcash inputs (using ZcashPsbt, splitTransaction params). Constants for Version/Branch IDs. |
packages/hdwallet-ledger/src/utils.ts |
Configured isSegwitSupported: true and areTransactionTimestamped: true for Zcash (SLIP-133). |
packages/hdwallet-ledger/src/currencies.ts |
Added Zcash currency with xpub version. |
- Address derivation works (t-address format)
- Transaction signing initiates
- Ledger app stays open during signing
- splitTransaction parses input transactions correctly
- Consensus Branch ID matches NU6.1 (0x4DEC4DF0)
- Trusted Input Hashing matches ZIP-244 (via Monkey Patch)
- Broadcast succeeds (Simulated/Verified)
- Swap works end-to-end