TypeScript CLI signing and security¶
The TypeScript CLI supports software keystores, Ledger accounts, and watch-only accounts. Software private keys stay encrypted locally, Ledger private keys never leave the device, and watch-only accounts can query state but cannot sign.
Sign an existing transaction¶
tx sign signs a TRON transaction constructed outside wallet-cli without broadcasting it:
printf '%s\n' "$WALLET_PASSWORD" |
wallet-cli tx sign --transaction "$TX_JSON" --password-stdin --output json
For a software account, signing is offline and --network is optional. The command is a pure
signer: it does not decide whether the signer owns the transaction, whether a contract call is
desirable, or how much value should move. The caller remains responsible for policy checks.
Transaction integrity¶
A TRON transaction represents its content in three related fields:
| Field | Purpose |
|---|---|
raw_data |
Human- and application-readable transaction content. |
raw_data_hex |
Bytes executed by the node. |
txID |
Hash covered by the signature. |
Before signing, wallet-cli requires txID to equal the SHA-256 hash of raw_data_hex. It also
decodes the outer transaction envelope and requires the contract types declared in raw_data to
match those encoded in raw_data_hex. For contract types that can be re-encoded, it additionally
requires the field-level contents of raw_data to produce the same bytes. A mismatch returns
tx_integrity and no signature is produced.
MarketSellAssetContract, MarketCancelOrderContract, and ShieldedTransferContract cannot be
re-encoded by the underlying library. Their transaction hash and contract type are still checked,
but their raw_data field values cannot be verified against raw_data_hex; callers must treat those
displayed field values as unverified.
Multi-signature transactions¶
When the input already contains a signature array, tx sign appends the new signature rather than
replacing existing signatures. The same partially signed transaction can therefore move from one
authorized signer to the next until its permission threshold is met.
Extract the signed payload and broadcast it later:
printf '%s\n' "$WALLET_PASSWORD" |
wallet-cli tx sign --transaction "$TX_JSON" --password-stdin --output json |
jq -c '.data.signed' > signed.json
wallet-cli tx broadcast --network tron:nile --tx-stdin < signed.json
Text output prints the complete signature rather than an abbreviated transaction identifier.
Sign typed data¶
typed-data sign signs EIP-712/TIP-712 structured data and returns the signer address, inferred or
declared primary type, digest, and signature:
printf '%s\n' "$WALLET_PASSWORD" |
wallet-cli typed-data sign \
--typed-data "$TYPED_DATA_JSON" \
--password-stdin \
--output json
The payload follows the usual domain, types, primaryType, and message structure. The CLI:
- ignores
EIP712Domainwhen it appears insidetypes; - accepts
valueas an alias formessage; - accepts TRON Base58 addresses in
addressfields; - infers
primaryTypewhen omitted and reports the resolved type; - rejects a declared
primaryTypethat is not the root message type.
domain.chainId is signed exactly as supplied and is not compared with --network. Review the
domain and message before signing.
Ledger behavior¶
Both tx sign and typed-data sign support Ledger accounts. Transaction-integrity checks run
before software and Ledger signing alike.
Typed-data signing uses the TRON application's hash-signing capability. Enable
Settings > Sign by Hash > Allowed on the device. Otherwise the CLI returns
ledger_setting_required. A TRON application version that does not support the instruction returns
ledger_unsupported.
Other Ledger application settings, such as transaction data or custom-contract signing, are also
reported as actionable ledger_setting_required errors instead of an opaque APDU error. A device
timeout or cancellation closes the transport so a later attempt can reconnect cleanly.
The Ledger screen cannot render every typed-data field and may show only hashes. Verify the payload on the host before approving it on the device.
Secret input policy¶
Secrets are never accepted as command-line arguments or environment configuration.
The supported stdin channels are:
| Flag | Input |
|---|---|
--password-stdin |
Master password used to unlock a software keystore. |
--tx-stdin |
Signed transaction JSON consumed by tx broadcast. |
--message-stdin |
Message consumed by message sign. |
Only one *-stdin flag can consume stdin in a single invocation.
The following high-value setup operations are interactive-only and require hidden input from a real TTY:
import mnemonicimport private-keychange-password
They do not accept --mnemonic-stdin, --private-key-stdin, or --password-stdin. Without a TTY,
they fail with tty_required.
Failure behavior¶
- Watch-only accounts return
watch_only_no_signerbefore a signing or write operation starts. - Invalid global values return
invalid_valuerather than falling back to defaults. - Ledger setting and version problems use
ledger_setting_requiredandledger_unsupported. - A transaction whose representations disagree returns
tx_integrity. - User or device refusal returns
signing_rejected.
JSON mode returns these codes in a single wallet-cli.result.v1 envelope and uses exit code 1 for
execution failures and exit code 2 for invalid usage.
For complete field-level command references, see
tx sign and
typed-data sign.