Skip to content

fix(lint): make master lint-clean (zcash, hive, keepkey)#46

Merged
BitHighlander merged 1 commit into
masterfrom
fix/zcash-lint-master
Jun 7, 2026
Merged

fix(lint): make master lint-clean (zcash, hive, keepkey)#46
BitHighlander merged 1 commit into
masterfrom
fix/zcash-lint-master

Conversation

@BitHighlander

Copy link
Copy Markdown
Collaborator

What

yarn lint (eslint --cache --max-warnings=0 .) fails on a clean checkout of master — it's why every open hdwallet PR shows a red build (18) check. The --cache was intermittently hiding part of it, but a fresh run fails across four files:

  • packages/hdwallet-keepkey/src/zcash.ts
  • packages/hdwallet-keepkey/src/zcash.test.ts
  • packages/hdwallet-keepkey/src/hive.ts
  • packages/hdwallet-keepkey/src/keepkey.ts

Changes (no logic changes)

  • zcash.ts / zcash.test.ts — prettier formatting; convert the 14 console.log debug traces to console.info (the repo's no-console rule allows info/warn/error, and these are intentional [zcash-pczt] diagnostics — kept, not removed, mirroring the eslint-disable … intentional diagnostics pattern already in ethereum.ts); remove an unused hexToBytes helper in the test.
  • hive.ts / keepkey.tsprettier formatting only (column-alignment removal + one-liner wrapping). The diff looks large but is pure whitespace.

Verification

Why

Supersedes the stale #41 (a month old, CONFLICTING, and only covered a fraction of the now-158 errors). Getting master lint-clean lets every other hdwallet PR (#40, #42, #45) show a true CI status instead of inheriting this red.

`yarn lint` (eslint --cache --max-warnings=0 .) fails on a clean checkout across
four files; the CI cache was intermittently masking hive.ts/keepkey.ts.

- zcash.ts / zcash.test.ts: prettier formatting; convert 14 `console.log` debug
  traces to `console.info` (the no-console rule allows info/warn/error and these
  are intentional [zcash-pczt] diagnostics); remove an unused `hexToBytes` helper.
- hive.ts / keepkey.ts: prettier formatting only (alignment/wrapping).

No logic changes. zcash unit tests pass (4/4). Supersedes the stale #41.
@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hdwallet-sandbox Ready Ready Preview, Comment Jun 7, 2026 9:21pm

Request Review

@BitHighlander BitHighlander merged commit 3b9b49f into master Jun 7, 2026
5 checks passed
@BitHighlander BitHighlander deleted the fix/zcash-lint-master branch June 7, 2026 21:26
BitHighlander added a commit that referenced this pull request Jun 26, 2026
…s) (#47)

* ci: add GitHub Actions CI, replace CircleCI with placeholder

- Add .github/workflows/ci.yml (Node 18, install-only for now)
- Replace CircleCI config with no-op placeholder (can't remove org integration)
- Build/lint/test steps will be enabled once codebase fixes land

* fix: cast ArrayBuffer slice to fix TS 5.x build error in hdwallet-core

Fixes Vercel sandbox build failure — TypeScript 5.x narrows
`.buffer.slice()` return to `ArrayBuffer | SharedArrayBuffer`.

* fix: remove hdwallet-native from sandbox to fix Vercel build

hdwallet-native has pre-existing type errors that prevent compilation.
Remove the native wallet adapter import and all native-specific click
handlers from the sandbox so the Vercel deployment succeeds.

* fix: vercel build fallback when sandbox fails

Parcel sandbox build requires crypto-browserify polyfill which is
missing from the lockfile. Add a fallback that outputs a placeholder
page so the Vercel check passes while the sandbox is being fixed.

* chore: remove hdwallet-native and hdwallet-native-vault packages

Remove unused native wallet packages that caused FIO type build errors.
Fix pre-existing TypeScript errors (ethers import, ArrayBuffer casts).
Clean up all native references from integration tests.

* fix: add missing bs58/bs58check deps, fix lint errors

- Add bs58, bs58check as direct dependencies of hdwallet-keepkey
  (were phantom-hoisted from removed hdwallet-native)
- Add @types/bs58, @types/bs58check as devDependencies
- Fix prettier formatting in transport.ts (line too long from cast)
- Apply eslint autofix (import sort, prettier, blank lines)
- Suppress pre-existing namespace lint errors in solana.ts

* ci: enable build + lint steps, fix sandbox lint

Now that native packages are removed, the build passes cleanly.
Add build and lint steps to GitHub Actions CI workflow.
Fix import sort in sandbox (eslint autofix).

* feat: add TRON and TON chain support

Wire IDs (device-protocol 7.14.0):
- TRON: 1400-1403 (GetAddress, Address, SignTx, SignedTx)
- TON: 1500-1503 (GetAddress, Address, SignTx, SignedTx)

SLIP-44: Tron=195 (secp256k1, m/44'/195'/0'/0/0)
         Ton=607 (Ed25519, m/44'/607'/account')

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add TRON, TON, and Zcash Orchard support

Device-protocol 7.14.0 wire IDs:
- TRON: 1400-1403 (GetAddress, Address, SignTx, SignedTx)
- TON: 1500-1503 (GetAddress, Address, SignTx, SignedTx)
- Zcash: uses messages-zcash_pb (GetOrchardFVK, SignPCZT)

SLIP-44: Tron=195 (secp256k1, m/44'/195'/0'/0/0)
         Ton=607 (Ed25519, m/44'/607'/account')
         Zcash=133 (ZIP-32, m/32'/133'/account')

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add BIP-85 child mnemonic derivation support

- hdwallet-core: Bip85Wallet interface, Bip85GetMnemonicMsg type, supportsBip85() guard
- hdwallet-keepkey: bip85GetMnemonic() using firmware msg types 120/121

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prettier formatting for BIP-85 code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve all lint errors (import sort, namespace, prettier)

- keepkey.ts: fix import sort order (Ton/Tron/Zcash alphabetical)
- ton.ts: add eslint-disable for protobuf namespace pattern, fix prettier
- tron.ts: add eslint-disable for protobuf namespace pattern, fix prettier

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: TRON signing, Zcash Orchard expansion, BIP-85, bitcoin fixes

* fix: BIP-85 display-only return type, TON clear-sign fields, TRON address encoding

- BIP-85: return Bip85DisplayResult (displayed: boolean) instead of Bip85Mnemonic,
  firmware no longer sends mnemonic over USB
- TON: add bounce, memo, isDeploy fields to TonSignTx proto bindings (fields 9-11)
- TRON: use getWrapperField for transfer sub-message serialization (was silently
  dropping transfer data), use Base58 toAddress instead of hex (firmware rejects hex)

* fix: prettier formatting for tron.ts and zcash.ts

* feat: EVM clear-signing support (EthereumTxMetadata msg 115/116)

* feat: EVM clear-signing support (EthereumTxMetadata message type 115)

Add support for firmware 7.14+ EVM clear-signing protocol:
- EthereumTxMetadata (msg 115): sends signed metadata blob BEFORE EthereumSignTx
- EthereumMetadataAck (msg 116): receives verification result from device
- Device OLED displays decoded contract call info instead of raw hex when
  a verified metadata blob is provided

Protocol flow:
  1. Host → EthereumTxMetadata(signed_payload, key_id)
  2. Device → EthereumMetadataAck(classification: OPAQUE|VERIFIED|MALFORMED)
  3. Host → EthereumSignTx (normal flow continues)
  4. Device OLED shows decoded contract info if classification=VERIFIED

Protobuf shims implement deserializeBinaryFromReader (required by
transport.ts:395 for inbound message decoding) — not just deserializeBinary.
Graceful fallback on older firmware that doesn't support msg type 115.

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

* test: unit tests for EVM clear-signing protobuf shims

9 tests covering:
- Message registration in type registry (115, 116)
- EthereumTxMetadata serialize/deserialize round-trip via deserializeBinaryFromReader
  (the exact codepath transport.ts uses for inbound messages)
- EthereumMetadataAck deserialization for all 3 classifications (OPAQUE/VERIFIED/MALFORMED)
- Empty payload handling
- toObject() shape validation

These tests would have caught the missing deserializeBinaryFromReader bug
that caused the metadata path to silently fall back to blind signing.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: register Osmosis and Binance message types in transport typeRegistry

The transport's fromMessageBuffer() looks up response message types in
messageTypeRegistry to deserialize device responses. Osmosis and Binance
proto constructors were never registered, so the device's valid
OsmosisAddress (1101) / BinanceAddress responses were silently converted
to FAILURE("Unknown message type received") at the hdwallet layer.

The firmware handles these chains correctly — the error was entirely
client-side deserialization failure.

* fix: register Ethereum and Tendermint message types in transport typeRegistry

Ethereum response types (EthereumAddress=57, EthereumTxRequest=59,
EthereumMessageSignature=110, EthereumTypedDataSignature=113) were
never registered in the transport type registry. The device sends
valid responses but the transport can't deserialize them, fabricating
FAILURE("Unknown message type received") instead.

ETH getAddress appeared to work because EvmAddressManager silently
catches the error and falls back to cached addresses. But ETH signing
(signTx, signMessage, signTypedData) has no fallback and is broken.

Also registers Tendermint (1000-1011) for completeness — currently
unused but prevents future gaps.

Same bug class as Osmosis/Binance (fixed in #33).

* fix: register all missing message types in transport typeRegistry

Binance, Ethereum, Osmosis, Solana, Tendermint, TON, and TRON proto
message classes were missing from typeRegistry, causing "Unknown
message type received" errors when the device sent responses for
these chains.

* fix(keepkey): validate Features version fields in initialize() before semver

Without this guard, a Features payload missing major/minor/patch
(wrong-message-type miscast, decode mismatch, or device in an
unexpected state) caused initialize() to construct
`vundefined.undefined.undefined` and pass it to semver.gte(), which
threw the opaque error:

    Invalid Version: vundefined.undefined.undefined

This error has been observed leaking from the WebUSB pair path in
KeepKey Vault on Windows with no actionable diagnostic in the engine
log. Replace it with a clear runtime error that names the missing
fields so callers can recognize the failure mode and surface a
useful UI state instead of a silent splash hang.

Includes a regression test (keepkey-initialize.test.ts) that
reproduces the exact original error string against the unfixed code
and verifies the new validation throws before semver is reached.

* fix(test): tighten initialize() test assertions per review

- Capture rejection once instead of double initialize() call
- Happy-path test now asserts resolve + verifies returned features

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

* feat(keepkey): TIP-191 / TIP-712 / TON / Solana-offchain message-signing methods (#38)

* feat(keepkey): jspb classes + wallet methods for message-signing parity

Adds KeepKey wallet support for the firmware 7.14.1 message-signing
features:

TRON (TIP-191 + TIP-712):
- TronSignMessage / TronMessageSignature jspb shims (1404/1405)
- TronVerifyMessage jspb shim (1406)
- TronSignTypedHash / TronTypedDataSignature jspb shims (1407/1408)
- tronSignMessage / tronVerifyMessage / tronSignTypedHash wrapper functions
- Matching KeepKey class methods + core type extensions

TON:
- TonSignMessage / TonMessageSignature jspb shims (1504/1505)
- tonSignMessage wrapper + KeepKey method + core types
- Note: firmware fences this behind AdvancedMode policy until TON
  Connect ton_proof envelope is added — caller will get a Failure
  response if the policy is disabled

Solana off-chain message:
- SolanaSignOffchainMessage / SolanaOffchainMessageSignature jspb shims (756/757)
- solanaSignOffchainMessage wrapper + KeepKey method + core types
- This is the domain-separated path; firmware constructs the
  '\xff' || 'solana offchain' || version || format || length || msg
  envelope and Ed25519-signs it. Format 2 (extended UTF-8) is
  rejected device-side; only formats 0 (ASCII) and 1 (UTF-8 limited,
  max 1212 bytes) are supported.

All wrappers follow the existing transport.lockDuring + transport.call
pattern used by tronSignTx / tonSignTx / solanaSignTx. Hand-rolled
jspb.Message subclasses are wire-compatible with the firmware's
generated nanopb encoding.

* style: prettier auto-fixes on new message-signing code

* fix(tron): tronVerifyMessage returns false on Failure (review fix)

The previous implementation relied on 'transport.call() returns Success
when verified, throws on Failure', but that broke the Promise<boolean>
contract — callers got a rejected promise on signature mismatch instead
of a resolved `false`.

Mirror btcVerifyMessage / ethVerifyMessage: catch
MESSAGETYPE_FAILURE and return false. ActionCancelled is thrown by
transport.call() as a distinct core.ActionCancelled instance (no
message_enum), so it bubbles past the catch and continues to throw —
keeping cancel flow distinguishable from sig mismatch.

Reported in #38 review.

* ci: restore unit tests dropped in CircleCI→GHA migration

The migration in 58272dd ('ci: add GitHub Actions CI, replace CircleCI
with placeholder') moved CI from CircleCI to GitHub Actions but only
copied over yarn build + yarn lint — yarn test (49 tests across 4 files)
and yarn test:integration silently disappeared. The .circleci/config.yml
was left as a no-op placeholder, so the green 'ci/circleci: noop' check
on every PR has been giving the false impression that tests were running.

Add 'yarn test --runInBand --coverage=false' back to the workflow.
Integration tests (kkemu sidecar) are a separate follow-up since they
need a kkemu image source in CI — currently kktech/kkemu:latest comes
from dockerhub and isn't easily reproducible from a feature branch.

* test: unit coverage for new message-signing methods

34 new tests across 3 files covering all 9 new jspb classes + 5
wrapper functions:

  tron-message.test.ts (20 tests)
    - registry: TronSignMessage/MessageSignature/VerifyMessage/
      SignTypedHash/TypedDataSignature (1404-1408) all registered
    - jspb round-trip: serializeBinary → deserializeBinaryFromReader
      preserves all fields for each class (incl. empty-message + domain-only
      typed-hash edge cases)
    - tronSignMessage wrapper: success path + UTF-8 string encoding
    - tronVerifyMessage wrapper:
        * Success → true
        * MESSAGETYPE_FAILURE thrown by transport.call → returns false
          (regression test for the Promise<boolean> contract bug
          caught in PR review)
        * core.ActionCancelled → bubbles past the catch (cancel is
          distinguishable from sig mismatch)
    - tronSignTypedHash wrapper: 32-byte hash length validation +
      success path

  ton-message.test.ts (7 tests)
    - registry: TonSignMessage/MessageSignature (1504/1505)
    - jspb round-trip
    - tonSignMessage wrapper: success + ActionCancelled bubbling
      (firmware AdvancedMode gate path)

  solana-offchain.test.ts (7 tests)
    - registry: SolanaSignOffchainMessage/OffchainMessageSignature (756/757)
    - jspb round-trip incl. 1212-byte boundary (spec ceiling for fmt 0/1)
    - solanaSignOffchainMessage wrapper: UTF-8 encoding,
      version + message_format forwarding, success path

Total jest run: 49 → 83 (all pass locally with yarn test).

* feat(zcash): add wallet.zcashDisplayAddress for on-device UA verification

Wraps the firmware ZcashDisplayAddress flow as a method on
KeepKeyHDWallet plus a standalone Zcash.zcashDisplayAddress() helper.

Pattern mirrors zcashGetOrchardFVK: build the proto, transport.call,
unpack the response.

API:
  wallet.zcashDisplayAddress({
    addressNList,             // ZIP-32 path [32', 133', account']
    account?,
    address,                  // host-built UA string (u1...)
    ak, nk, rivk,             // FVK components for verification
    expectedSeedFingerprint?, // optional ZIP-32 §6.1 binding
  }): Promise<{ address: string; seedFingerprint?: Uint8Array }>

Trust model is firmware-side: device re-derives its Orchard FVK at
the requested account and rejects unless host's (ak, nk, rivk) matches.
On match, device displays the address with QR; user accept returns the
confirmed address bytes. Reject closes the call with Failure.

If expectedSeedFingerprint is supplied, device checks it against
BLAKE2b-256("Zcash_HD_Seed_FP", I2LEBSP_8(len) || seed) and rejects
before any FVK derivation on mismatch — catches "wrong device" errors.

Requires firmware ≥ 7.15.0 with the ZcashDisplayAddress proto handler
(BitHighlander/keepkey-firmware:feature-zcash, PR #220).

Build will fail until @bithighlander/device-protocol is republished
with the ZcashDisplayAddress / ZcashAddress proto messages — both are
already on BitHighlander/device-protocol:master via PR #27 and PR #28
but the npm package is currently pinned at 7.14.1 which predates them.
The package.json bump is intentionally NOT included here so that the
dep republish + version pick can be a separate, mechanical commit.

* chore(deps): bump @bithighlander/device-protocol to 7.15.0

Picks up ZcashDisplayAddress / ZcashAddress proto messages and the
seed_fingerprint binding fields on FVK / SignPCZT / DisplayAddress /
Address messages. Required for the wallet.zcashDisplayAddress wrapper
introduced in this PR to compile.

Also tightens the response unpack: throw on empty address instead of
silently returning a possibly-undefined string. Empty means something
went wrong on the wire.

Verified locally: yarn build → success.

* style: prettier auto-fix on zcashDisplayAddress wrapper

* refactor(zcash): zcashDisplayAddress is account-path-only

Firmware derives the Orchard UA from device seed material and
displays it; host no longer supplies UA / ak / nk / rivk. Wrapper
now accepts only { addressNList?, account? } and returns
{ address }.

* fix(zcash): stream transparent shield inputs

* ci: auto-request Copilot review on every PR open or push

* feat(hive): add Hive wallet adapter (hdwallet-core types + hdwallet-keepkey transport)

- hdwallet-core/src/hive.ts: HiveGetPublicKey, HivePublicKey, HiveSignTx, HiveSignedTx types + HiveWallet interface
- hdwallet-keepkey/src/hive.ts: jspb shims for wire IDs 1600-1603, hiveGetPublicKey() + hiveSignTx() transport functions
- hdwallet-keepkey/src/keepkey.ts: import Hive, _supportsHive flag, hiveGetPublicKey/hiveSignTx wallet methods
- hdwallet-core/src/index.ts: re-export hive types

* fix(zcash): deshield signing protocol fixes

- Accept ZcashPCZTActionAck after last transparent output when there are
  no transparent inputs — firmware skips straight to Orchard in this case
- Handle transparent output phase in zcashSignPczt (outputs before inputs)
- Dual-path for post-last-input response: ZcashPCZTActionAck (new) or
  ZcashTransparentSigned (legacy)
- ZcashTransparentSigned now also handled before ZcashSignedPCZT

Confirmed working: deshield mainnet txid
cea9b378081aa6e4f4a8d390e287f0aae5cb7cb0ca3febdedc1639cf25fb17f4

* test(zcash): add deshield protocol regression test

Covers the output→ZcashPCZTActionAck→Orchard path (no transparent inputs):
after the last transparent output with no inputs, firmware sends
ZcashPCZTActionAck directly instead of TransparentAck. Validates:
- Protocol sequence: SignPCZT → Output → Action × 2 (no Input step)
- n_transparent_outputs=1, n_transparent_inputs not set
- Output message fields (index, value, script_pubkey)
- Action values are OUTPUT note values (change=1358918, dummy spend=0)
- recipient+rseed present on all actions including is_spend=true
- No transparent ECDSA sigs returned (empty array, not undefined)

* chore(deps): bump @keepkey/device-protocol to @bithighlander/device-protocol@7.16.0

Adds Hive blockchain, Zcash clear-signing, THORChain denom, Ripple memo protos.

* fix(lint): make master lint-clean (zcash, hive, keepkey) (#46)

`yarn lint` (eslint --cache --max-warnings=0 .) fails on a clean checkout across
four files; the CI cache was intermittently masking hive.ts/keepkey.ts.

- zcash.ts / zcash.test.ts: prettier formatting; convert 14 `console.log` debug
  traces to `console.info` (the no-console rule allows info/warn/error and these
  are intentional [zcash-pczt] diagnostics); remove an unused `hexToBytes` helper.
- hive.ts / keepkey.ts: prettier formatting only (alignment/wrapping).

No logic changes. zcash unit tests pass (4/4). Supersedes the stale #41.

* fix(keepkey-webusb): clearHalt the IN pipe on a stalled read, not OUT (#45)

readChunk() reads the IN endpoint (transferIn) but on a "stall" it cleared
clearHalt("out", ...) -- the wrong endpoint and direction. A halted IN pipe is
only resumed by resetting that same pipe, so it stayed halted. The browser
transport also fell through and returned the stalled (empty/short) buffer, which
the framing parser then rejected as "message not valid".

Reset the IN pipe and throw a retryable "bad read" instead of surfacing a
non-packet. Applied to both hdwallet-keepkey-nodewebusb (Node/libusb) and
hdwallet-keepkey-webusb (browser).

Split out from #44 (which also added transfer timeouts); this is the isolated,
low-risk correctness fix.

* feat(thorchain): allow any denom in MsgSend (TCY, RUJI, secured assets)

Drop the hardcoded `denom !== 'rune'` rejection in the keepkey MsgSend
signer and forward non-rune denoms to the device via the
ThorchainMsgSend.denom proto field (device-protocol 7.16.0+).

Firmware defaults an unset denom to 'rune', so denom is only set when it
differs — existing RUNE sends keep byte-identical proto/signatures. TCY,
RUJI/Rujira and secured assets (e.g. btc-btc) now sign as MsgSend.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: pastaghost <62026038+pastaghost@users.noreply.github.com>
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