|
| 1 | +# wallet-cli tx sign |
| 2 | + |
| 3 | +Sign a transaction built elsewhere, without broadcasting. |
| 4 | + |
| 5 | +## Synopsis |
| 6 | + |
| 7 | +``` |
| 8 | +wallet-cli tx sign --transaction <json> [options] |
| 9 | +``` |
| 10 | + |
| 11 | +## Description |
| 12 | + |
| 13 | +Signs a transaction that was constructed outside this CLI with the active account's key (or |
| 14 | +`--account`) and prints the signed result. Nothing is broadcast — pass the signed payload to |
| 15 | +[`tx broadcast`](broadcast.md) when you are ready. `--network` is optional: signing itself is |
| 16 | +offline for software accounts. |
| 17 | + |
| 18 | +This is a **pure signer**. It does not check who owns the transaction, what contract it calls, or |
| 19 | +how much it moves — the caller decides what to sign; the wallet holds the key, not the policy. |
| 20 | + |
| 21 | +What it does check is **payload integrity**, because a TRON transaction states its content three |
| 22 | +times and nothing in the format forces the three to agree: |
| 23 | + |
| 24 | +| Field | Who reads it | |
| 25 | +|---|---| |
| 26 | +| `raw_data` | you / your agent, when deciding whether to sign | |
| 27 | +| `raw_data_hex` | the node, when executing the transaction | |
| 28 | +| `txID` | the signature — this hash, and only this hash, is what gets signed | |
| 29 | + |
| 30 | +So a transaction whose `raw_data` reads "1 TRX" can carry the `txID` and `raw_data_hex` of a |
| 31 | +1000 TRX transfer, and the signature would be perfectly valid for what actually executes. |
| 32 | +`tx sign` therefore refuses (`tx_integrity`) unless: |
| 33 | + |
| 34 | +1. `txID` is the sha256 of `raw_data_hex` — always enforced, for every contract type; and |
| 35 | +2. `raw_data` re-encodes to exactly those bytes — enforced wherever the contract type can be |
| 36 | + decoded. A handful of types (`MarketSellAssetContract`, `MarketCancelOrderContract`, |
| 37 | + `ShieldedTransferContract`) cannot be re-encoded; those are still bound by check 1. |
| 38 | + |
| 39 | +Neither check rejects anything a correct transaction builder produces. |
| 40 | + |
| 41 | +Watch-only accounts cannot sign (`watch_only_no_signer`). Ledger accounts sign on the device. |
| 42 | + |
| 43 | +### Multi-sig co-signing |
| 44 | + |
| 45 | +If the transaction you pass already carries a `signature` array, this command **appends** its |
| 46 | +signature rather than replacing what is there. That is what TRON multi-sig requires — each |
| 47 | +permitted key signs the same transaction in turn — so a partially signed transaction can be passed |
| 48 | +from signer to signer and broadcast once the permission threshold is met. |
| 49 | + |
| 50 | +The consequence worth knowing: `data.signed.signature` may contain signatures this wallet did not |
| 51 | +produce. `data.address` always tells you which key *this* invocation used, and the text receipt |
| 52 | +numbers them so you can tell them apart: |
| 53 | + |
| 54 | +```console |
| 55 | +✅ Signed transaction |
| 56 | + Address TU9Z8Ha6Xj9oLLhamrT8MC77dxsj65VYMC |
| 57 | + TxID d0157b08eb6a5ce9482d4429a481f3bca4a95914f92a6b8f2fb73fb905ff7de0 |
| 58 | + Signature 1 ffffffff… ← already on the transaction |
| 59 | + Signature 2 16a2ec10… ← added by this invocation |
| 60 | +``` |
| 61 | + |
| 62 | +## Options |
| 63 | + |
| 64 | +| Option | Description | |
| 65 | +|---|---| |
| 66 | +| `--transaction <string>` | Unsigned transaction JSON (`raw_data`, `raw_data_hex` and `txID` must agree) | |
| 67 | +| `--password-stdin` | Master password from stdin (software accounts) | |
| 68 | + |
| 69 | +Plus the [global options](../index.md#global-options-every-command). |
| 70 | + |
| 71 | +The payload is passed on argv, not stdin: it is not a secret, and this leaves fd 0 free for |
| 72 | +`--password-stdin`. |
| 73 | + |
| 74 | +## Examples |
| 75 | + |
| 76 | +In the examples, `$PW` is your master password (fed on stdin via `--password-stdin`) and `$TX` |
| 77 | +holds the unsigned transaction JSON. |
| 78 | + |
| 79 | +```bash |
| 80 | +echo "$PW" | wallet-cli tx sign --transaction "$TX" --password-stdin |
| 81 | +``` |
| 82 | + |
| 83 | +```console |
| 84 | +✅ Signed transaction |
| 85 | + Address TU9Z8Ha6Xj9oLLhamrT8MC77dxsj65VYMC |
| 86 | + TxID d0157b08eb6a5ce9482d4429a481f3bca4a95914f92a6b8f2fb73fb905ff7de0 |
| 87 | + Signature 16a2ec107591827046bcd77e9ae71a5fc415e2f69b9eebe5ad0fe6e3e39cfed16e7dad3e35bc36031ddd5bc47e22de63a925bf65a82e0e1e69508735bc3fd6271C |
| 88 | +``` |
| 89 | + |
| 90 | +The signature is printed in full — it is the product of the command and you have to copy it |
| 91 | +somewhere. `Fee` is omitted because nothing was estimated: the transaction was built elsewhere. |
| 92 | + |
| 93 | +Sign now, broadcast later: |
| 94 | + |
| 95 | +```bash |
| 96 | +echo "$PW" | wallet-cli tx sign --transaction "$TX" --password-stdin -o json \ |
| 97 | + | jq -c .data.signed > signed.json |
| 98 | +wallet-cli tx broadcast --network tron:nile --tx-stdin < signed.json |
| 99 | +``` |
| 100 | + |
| 101 | +A transaction whose fields disagree is refused rather than signed: |
| 102 | + |
| 103 | +```console |
| 104 | +{"success":false,"command":"tx.sign","error":{"code":"tx_integrity","message":"TRON transaction raw_data does not match its raw_data_hex; refusing to sign"}} |
| 105 | +``` |
| 106 | + |
| 107 | +## Output |
| 108 | + |
| 109 | +| Field | Type | Meaning | |
| 110 | +|---|---|---| |
| 111 | +| `kind` | string | `"sign"` | |
| 112 | +| `mode` | string | `"sign-only"` — same shape `tx send --sign-only` emits, so consumers need no branch | |
| 113 | +| `address` | string | Address that produced the signature | |
| 114 | +| `txId` | string | Transaction id (TRON: `txID`) | |
| 115 | +| `signed` | object | The signed transaction — exactly what `tx broadcast` accepts | |
| 116 | + |
| 117 | +No `fee` is reported: nothing was estimated, because the transaction was not built here. |
| 118 | + |
| 119 | +## Exit status |
| 120 | + |
| 121 | +`0` signed · `1` execution failure (`tx_integrity`, `watch_only_no_signer`, `auth_failed`, |
| 122 | +`signing_rejected`) · `2` usage error (missing or malformed `--transaction` → `missing_option` / |
| 123 | +`invalid_value`). |
| 124 | + |
| 125 | +## See also |
| 126 | + |
| 127 | +[`tx broadcast`](broadcast.md) · [`typed-data sign`](../typed-data/sign.md) · |
| 128 | +[Security model](../../concepts/security.md) |
0 commit comments