Skip to content

Release v4.10.0 - #960

Merged
gummy789j merged 6 commits into
masterfrom
release_v4.10.0
Jul 27, 2026
Merged

Release v4.10.0#960
gummy789j merged 6 commits into
masterfrom
release_v4.10.0

Conversation

@gummy789j

Copy link
Copy Markdown
Collaborator

New Features

Change

  1. Add standalone signing commands to the TypeScript CLI: tx sign and typed-data sign. tx sign signs a transaction built elsewhere and prints the signed payload without broadcasting, so the signed result can be handed to tx broadcast later; because it appends to an existing signature array rather than replacing it, a partially signed transaction can be passed from signer to signer until a TRON multi-sig permission threshold is met. typed-data sign signs EIP-712 / TIP-712 structured data and returns the signature, the digest that was signed, and the resolved primary type; it accommodates real-world payloads by ignoring EIP712Domain in types, accepting value as an alias for message and accepting TRON base58 addresses in address fields, while rejecting a declared primaryType that is not the message root, since a nested type can never be what gets signed. Both commands are offline for software accounts (--network is optional) and both work with Ledger accounts. (#957)

  2. Enforce TRON transaction payload integrity before any signature is produced, in the software strategy and on Ledger alike. A TRON transaction states its content three times — raw_data (what a caller reads), raw_data_hex (what the node executes) and txID (the only thing actually signed) — and nothing in the format forces the three to agree, so a payload that displays a harmless raw_data can carry the hash and bytes of a different transaction. Signing now refuses (tx_integrity) unless txID is the sha256 of raw_data_hex, the contract types encoded in raw_data_hex match those raw_data declares, and raw_data re-encodes to exactly those bytes wherever the contract type can be encoded. The checks reject nothing a correct transaction builder produces. (#957)

  3. Make Ledger failures actionable rather than opaque. APDU statuses that previously surfaced as UNKNOWN_ERROR (0x6a8c) are now mapped to typed errors that name the fix: ledger_setting_required for the TRON app's "Sign by Hash", "Transactions data" and "Custom contracts" settings, and ledger_unsupported when the app version does not implement the instruction. Device signing also honours an abort signal, closing the transport immediately instead of holding the native handle until the timeout expires. (#957)

Bug Fixes

Change

  1. Fail closed on invalid global flag values in the TypeScript CLI. An out-of-range or non-matching value for a global flag (for example --timeout 0 or an unrecognized --output) silently fell back to the flag's default and the command ran under settings the caller never asked for. Such a value is now reported as invalid_value before any command dispatch, so no RPC is attempted. (#957)

  2. Reject operations that cannot succeed instead of returning misleading results. contract info on an address with no deployed contract returned a valid-but-empty contract normalized from TronWeb's empty response, and now fails as not found. stake withdraw with nothing withdrawable passed the node's broadcast-time checks but never confirmed, leaving a phantom txid; the withdrawable amount is now queried up front and the command fails with nothing_to_withdraw. (#957)

gummy789j and others added 6 commits July 23, 2026 10:44
Two standalone signing commands so the CLI can sign payloads built elsewhere:

- `tx sign` — sign a transaction constructed outside the CLI, output the
  signed result for later `tx broadcast`. Pure signer (no owner/contract
  policy) but enforces payload integrity: txID must be sha256(raw_data_hex),
  and raw_data must re-encode to those bytes where the contract type is
  decodable. Verified end-to-end on Nile (confirmed on-chain) and offline.
- `typed-data sign` — EIP-712/TIP-712 in a new top-level `typed-data` group.
  Byte-for-byte identical output to the agent-wallet SDK for the same key and
  payload. Verified on a Ledger via Speculos (real TRON app 0.7.6).

Both support software and Ledger accounts. Integrity checking lives in a
shared tron/tx-integrity module enforced by both the software strategy and the
Ledger adapter, so a hardware account is never the weaker signer. Device
signing preliminaries (precheck, awaiting_device, abort-on-timeout) are
extracted into obtainSignature and reused by every signing path; the abort
signal now actually closes the transport.

Also in this change:
- Map Ledger app-setting APDU errors (0x6a8b/c/d) to actionable messages
  instead of an opaque UNKNOWN_ERROR.
- Remove the network "required" value: it resolved identically to "optional"
  (default-network fallback), so it only produced a false help line.
- Fix the Requires help block to state whether a command prompts, and print
  full signatures in sign-only text receipts instead of a truncated txID.
- Speed up the test suite ~13x by mocking scrypt in keystore-heavy specs
  (production crypto untouched; crypto.test.ts still uses real scrypt).
…aryType, and opaque Ledger errors

Three independent fixes to the standalone `tx sign` / `typed-data sign` paths:

- tx-integrity: add a contract-type binding (layer 1.5) between the txID hash
  check and tronweb's txCheck. It decodes raw_data_hex's outer protobuf envelope
  — which succeeds even for contract types tronweb cannot re-encode (Market,
  Shielded, legacy) — and asserts its contract type(s) equal what raw_data
  declares. Fails closed on undecodable protobuf, unknown type, or any type/count
  mismatch. Closes the disguise where a benign raw_data hides a different signed
  contract while layer 2 is skipped. Only same-type differing fields of an
  unencodable contract remain unverifiable (no encoder/decoder exists). Pinning
  tests guard the tronweb assumptions (the 6 unencodable families, the enum
  normalization).

- typed-data: reject a supplied primaryType that is not the root struct (i.e. is
  referenced as a field by another type, including via array fields). ethers
  always signs the inferred root, so a non-root primaryType meant the receipt —
  and, worse, the Ledger hashStruct — disagreed with what was actually signed.
  Pure structural check in the domain layer, so both backends inherit it; omitted
  and root primaryType are unaffected. UsageError -> exit 2.

- ledger: map APDU 0x6d00 (INS_NOT_SUPPORTED) to a generic, chain- and
  operation-agnostic ledger_unsupported error instead of a misleading
  auth_required. The app version lacks the instruction, or the wrong app is open.

All TDD'd with real-behavior tests. Full suite, typecheck, and depcruise pass.
… withdraw

Three QA-surfaced gaps where the CLI trusted a downstream default/empty
response/node acceptance instead of validating at the source:

- GLOB-006: an out-of-range global value flag (e.g. --timeout -1) was
  silently coerced to undefined and fell back to the default. coerceGlobalValue
  now returns a discriminated ok/reason result; the runner surfaces a rejected
  flag as invalid_value (exit 2) before any dispatch, uniformly for every value
  flag (--timeout, --output, --wait-timeout).
- MGT-CONTRACT-014: `contract info` on a non-contract address returned an
  empty-but-successful contract. The adapter now treats an empty getContract
  response as not_found at the anti-corruption boundary.
- MGT-STAKE-014: `stake withdraw` with nothing withdrawable broadcast a tx that
  never confirmed, leaving a phantom txid. A pre-flight check on the withdrawable
  amount now rejects with nothing_to_withdraw before build/broadcast (dry-run too).
feat(ts): add tx sign and typed-data sign commands
@gummy789j
gummy789j merged commit db60105 into master Jul 27, 2026
1 check 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.

3 participants