Skip to content

feat(ts): add tx sign and typed-data sign commands - #957

Merged
gummy789j merged 3 commits into
release_v4.10.0from
feat/ts-signing-commands
Jul 27, 2026
Merged

feat(ts): add tx sign and typed-data sign commands#957
gummy789j merged 3 commits into
release_v4.10.0from
feat/ts-signing-commands

Conversation

@gummy789j

Copy link
Copy Markdown
Collaborator

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).

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).
@Mohammedsapry

Copy link
Copy Markdown

Ok

@matrix-agent116 matrix-agent116 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Summary

This PR adds valuable standalone transaction and typed-data signing flows with shared device handling and transaction-integrity checks. The overall direction is strong, but I’m requesting changes because an explicitly supplied primaryType does not consistently determine the typed data being signed, and the Ledger path can consequently return a digest that differs from the device-signed payload.

Required improvements

  • Blocker: Make primaryType drive one canonical rooted type set used consistently for the software signature, returned digest, and Ledger struct hash. Please add regression coverage where the supplied primaryType differs from the type TronWeb would infer, verifying signature recovery plus the Ledger hashes and returned digest.

Comment thread ts/src/adapters/outbound/chain/tron/signing-strategy.ts
Comment thread ts/src/adapters/outbound/ledger/index.ts
…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).
@gummy789j
gummy789j merged commit 5cd44d3 into release_v4.10.0 Jul 27, 2026
@gummy789j gummy789j mentioned this pull request Jul 27, 2026
Comment thread ts/README.md
- **Encrypted local storage** — software keystores are encrypted on disk; secrets are never passed via argv or environment variables.
- **Software and Ledger signing** — sign in software, or on a Ledger device (the private key never leaves the device).
- **Covers the main TRON capabilities** — HD wallets, TRX and TRC20/TRC10 transfers, staking / resource delegation, voting / rewards, smart-contract calls and deployment, message signing, and on-chain queries.
- **Covers the main TRON capabilities** — HD wallets, TRX and TRC20/TRC10 transfers, staking / resource delegation, voting / rewards, smart-contract calls and deployment, message and EIP-712/TIP-712 signing, and on-chain queries.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good

Comment thread ts/README.md
@@ -112,6 +112,7 @@ Every command — including every subcommand — has a reference page; run `wall
| Command | Description |
|---|---|
| [`tx send`](docs/commands/tx/send.md) | Send native TRX or TRC20/TRC10 tokens |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

C l1

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.

6 participants