From 0da92187f16646157ad0a832902a1e5a082b64c7 Mon Sep 17 00:00:00 2001 From: "Leon.Zhang" Date: Fri, 28 Aug 2026 15:59:50 +0800 Subject: [PATCH 1/8] docs: align v4.13 transaction safety guidance --- README.md | 2 +- ts/README.md | 4 +-- ts/docs/commands/tx/broadcast.md | 2 +- ts/docs/commands/tx/send.md | 17 ++++++---- ts/docs/commands/tx/status.md | 12 ++++--- ts/docs/concepts/security.md | 4 +-- ts/docs/guide/scripting.md | 18 +++++++---- ts/docs/machine-interface.md | 55 ++++++++++++++++++++++++++------ ts/docs/troubleshooting.md | 2 +- 9 files changed, 84 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index e074bb382..b992cb920 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Both manage the same kind of wallet on the same networks — your address is ide | **Command style** | PascalCase verbs: `RegisterWallet`, `SendCoin`, `GetBalance`. Amounts in **SUN** (1 TRX = 1,000,000 SUN). | Noun-verb subcommands: `create`, `tx send`, `account balance`, with `--flags`. | | **Output for scripts** | Human-readable text. | Stable JSON via `-o json` ([`wallet-cli.result.v1`](ts/docs/machine-interface.md)) + fixed exit codes (`0`/`1`/`2`). | | **Config / networks** | `config.conf` (net type + full node), or `SwitchNetwork` at runtime. Mainnet · Nile · Shasta · custom. | `--network` flag / `config` command. `tron:mainnet` · `tron:nile` · `tron:shasta`. | -| **Signing** | Software keystore · Ledger. | Encrypted local keystore · Ledger. Secrets never via argv/env. | +| **Signing** | Software keystore · Ledger. | Encrypted local keystore · Ledger. Secrets enter via stdin/TTY, never argv or dedicated secret env vars. | | **Feature scope** | **The full surface** — wallets and transfers, staking, voting and rewards, governance, contracts, TRC10, and the on-chain exchange. | **The full surface** — HD wallets, TRX/TRC20/TRC10 transfers, staking & delegation, voting & rewards, governance proposals & super-representative operation, contract call/deploy/governance, TRC10 issuance, the on-chain Bancor exchange, multi-sig, GasFree transfers, message signing, and on-chain queries. | | **Best for** | People at a terminal who want every TRON capability. | Scripting, CI pipelines, and AI agents. | | **Full docs** | [java/README.md](java/README.md) | [ts/README.md](ts/README.md) | diff --git a/ts/README.md b/ts/README.md index 990e47ea7..ebf8e1a18 100644 --- a/ts/README.md +++ b/ts/README.md @@ -5,7 +5,7 @@ The agent-first implementation of wallet-cli, built for automation: every comman ## Key features - **Agent-first** — stable JSON output, deterministic exit codes, and discoverable schemas, built for scripts, CI, and AI agents (details in [The contract, in one paragraph](#the-contract-in-one-paragraph)). -- **Encrypted local storage** — software keystores are encrypted on disk; secrets are never passed via argv or environment variables. +- **Encrypted local storage** — software keystores are encrypted on disk; secrets enter via stdin/TTY, never argv or dedicated secret environment variables. - **Software and Ledger signing** — sign in software, or on a Ledger device (the private key never leaves the device). - **Covers the full TRON feature surface** — HD wallets, TRX and TRC20/TRC10 transfers, staking / resource delegation, voting / rewards, governance proposals and super-representative operation, smart-contract calls, deployment and governance, TRC10 issuance, the on-chain Bancor exchange, multi-sig, GasFree transfers, message signing, and on-chain queries. @@ -174,7 +174,7 @@ Offline local commands and configuration. ## The contract, in one paragraph -Every command supports `-o json` and then prints **exactly one** terminal JSON frame on stdout, schema [`wallet-cli.result.v1`](docs/machine-interface.md#the-result-envelope). Exit codes are fixed: `0` success, `1` execution failure, `2` usage error. Secrets (passwords, mnemonics, private keys) are never accepted via argv or environment variables — only via stdin flags or interactive TTY prompts; mnemonic/private-key import and `change-password` are interactive-only (no stdin path at all). Full spec: [machine-interface.md](docs/machine-interface.md); for calling from an AI agent, see the [Agent skill](skills/wallet-cli/SKILL.md). +Every command supports `-o json` and then prints **exactly one** terminal JSON frame on stdout, schema [`wallet-cli.result.v1`](docs/machine-interface.md#the-result-envelope). Exit codes are fixed: `0` success, `1` execution failure, `2` usage error. Secrets (passwords, mnemonics, private keys) are never accepted via argv and are not read from dedicated secret environment variables. Passwords can enter through stdin flags or interactive TTY prompts; mnemonic/private-key import and `change-password` are interactive-only (no stdin path at all). Full spec: [machine-interface.md](docs/machine-interface.md); for calling from an AI agent, see the [Agent skill](skills/wallet-cli/SKILL.md). ## Understanding TRON mechanics diff --git a/ts/docs/commands/tx/broadcast.md b/ts/docs/commands/tx/broadcast.md index 20b61ea68..f386d1caa 100644 --- a/ts/docs/commands/tx/broadcast.md +++ b/ts/docs/commands/tx/broadcast.md @@ -6,7 +6,7 @@ Broadcast a presigned transaction. ``` wallet-cli tx broadcast (--hex | --file | --transaction | --tx-stdin) - [--dry-run] --network [options] + [--dry-run] [--network ] [options] ``` ## Description diff --git a/ts/docs/commands/tx/send.md b/ts/docs/commands/tx/send.md index 054c7fe4f..18706c1d1 100644 --- a/ts/docs/commands/tx/send.md +++ b/ts/docs/commands/tx/send.md @@ -32,7 +32,7 @@ a different id is refused outright), but a wrong value *inside* that range canno locally — there is nothing to compare it against. When the exact base-unit quantity matters, pass `--raw-amount`, which is used verbatim and never rescaled. -Early exits: `--dry-run` builds and estimates only — no signature, no broadcast, nothing leaves your machine; `--sign-only` signs and prints the signed transaction **hex**; `--build-only` builds but does **not** sign, printing the **unsigned** hex. The hex is protobuf on TRON and RLP (`0x02…`) on EVM; either feeds [`tx sign`](sign.md) and [`tx broadcast`](broadcast.md). +Early exits still build through the selected network first. `--dry-run` builds and estimates, then returns the plan with no signature and no broadcast; `--sign-only` builds, estimates, signs, and prints the signed transaction **hex** without broadcasting; `--build-only` builds and estimates but does **not** unlock or sign, printing the **unsigned** hex. The hex is protobuf on TRON and RLP (`0x02…`) on EVM; either feeds [`tx sign`](sign.md) and [`tx broadcast`](broadcast.md). **Fees are family-specific.** TRON burns bandwidth/energy and caps the energy spend with `--fee-limit`; EVM pays gas, so `--gas-limit`, `--max-fee`, `--priority-fee` and `--nonce` apply instead. Help tags each set `(tron only)` / `(evm only)`, and using one on the other family is refused with `invalid_option` — as are `--max-fee` / `--priority-fee` on an EVM chain that still prices in `gasPrice`. @@ -42,7 +42,12 @@ TRON multi-sig uses `--permission-id` to select the signing group and `--expirat **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed, or poll [`tx status`](status.md). -Requires an account and the master password via `--password-stdin` — signing commands do not show an interactive prompt, so without it the command fails with `auth_required`. +Requires: + +```text + the master password only when the selected mode signs — pass --password-stdin then; other modes need no password + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) +``` ## Options @@ -53,9 +58,9 @@ Requires an account and the master password via `--password-stdin` — signing c | `--raw-amount ` | Raw integer amount in native base units (SUN / wei) or token base units | | `--token ` | Token symbol from the address book; excludes `--contract`, `--asset-id` | | `--contract ` | Token contract address; omit for a native-coin transfer | -| `--dry-run` | Build and estimate only; excludes `--sign-only` / `--build-only` | -| `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only` | +| `--dry-run` | Build and estimate through the selected network; no signing or broadcast; excludes `--sign-only` / `--build-only` | +| `--sign-only` | Build, estimate, sign, and output the signed hex without broadcasting; excludes `--dry-run` / `--build-only` | +| `--build-only` | Build and estimate, output the **unsigned** hex without unlocking; excludes `--dry-run` / `--sign-only` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default 60000; on cap returns the submitted receipt) | | `--password-stdin` | Master password from stdin | @@ -81,7 +86,7 @@ Plus the [global options](../index.md#global-options-every-command). ## Examples -> **Password**: except for `--dry-run`, the examples below omit the password to keep the focus on the selector flags. A real send needs the master password on stdin — prefix with `printf '%s' "$PW" |` and append `--password-stdin` (see the description above). +> **Password**: the examples below omit the password to keep the focus on selector flags. Software signing modes need the master password on stdin — prefix with `printf '%s' "$PW" |` and append `--password-stdin`; `--dry-run`, `--build-only`, and Ledger signing do not. ```bash # 1 TRX on Nile; 1 ETH-denominated amount on Sepolia diff --git a/ts/docs/commands/tx/status.md b/ts/docs/commands/tx/status.md index 59aba9863..cd5f3a576 100644 --- a/ts/docs/commands/tx/status.md +++ b/ts/docs/commands/tx/status.md @@ -14,10 +14,12 @@ Reports which step a transaction is at, using **four states**, on TRON and EVM n | `data.state` | Meaning | Terminal? | |---|---|---| -| `confirmed` | On chain — solidified on TRON, receipted on EVM; `blockNumber` present | yes | +| `confirmed` | Included in a block and an execution result/receipt is available; `blockNumber` present | yes | | `failed` | Included and reverted / rejected | yes | -| `pending` | Seen by the node, not yet solidified | no — keep polling | -| `not_found` | Unknown to the queried node (wrong network? not propagated yet?) | no — poll within your own deadline | +| `pending` | Seen by the node, with no execution result/receipt yet | no — keep polling | +| `not_found` | Unknown to the queried endpoint (wrong network, not propagated, dropped, or pruned); outcome unknown | no — keep polling/reconcile; do not assume failure | + +> `confirmed` is an inclusion-and-receipt state, not a finality guarantee. If a workflow needs finality, verify it separately with a TRON SolidityNode view or an EVM finalized block. ## Options @@ -54,7 +56,7 @@ wallet-cli tx status --txid 0x55b0068ef31bce39bbf5b06d456eaef307fd77f96d85ea291f {"schema":"wallet-cli.result.v1","success":true,"command":"tx.status","data":{"txid":"0x55b0068ef31bce39bbf5b06d456eaef307fd77f96d85ea291f48c1ae4b900d80","state":"confirmed","confirmed":true,"failed":false,"blockNumber":11576586,"confirmations":0},"meta":{"durationMs":408,"warnings":[]},"chain":{"family":"evm","network":"evm:11155111","chainId":"11155111"}} ``` -An unknown txid is a **success** with `state: "not_found"` (exit 0) — the query worked; the answer is "not there": +An unknown txid is a **success** with `state: "not_found"` (exit 0) — the query worked; this endpoint has no record of that hash: ```json {"schema":"wallet-cli.result.v1","success":true,"command":"tx.status","data":{"txid":"0000…0000","state":"not_found","confirmed":false,"failed":false},"meta":{"durationMs":1022,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} @@ -66,6 +68,8 @@ On EVM, `not_found` also carries a `meta.warnings` entry, because a public endpo {"…":"…","data":{"txid":"0x0000…0000","state":"not_found","confirmed":false,"failed":false},"meta":{"durationMs":407,"warnings":["0x0000…0000 is unknown to this endpoint. Public nodes often prune history, so this may mean the node has no record of it rather than that it never existed; try an archival endpoint."]}} ``` +> A polling deadline that ends in `pending` or `not_found` is still an unknown outcome. Do not treat it as failure or use it as an automatic resend trigger; reconcile the txid against the intended network and endpoint history first. + ## Output | Field | Type | Meaning | diff --git a/ts/docs/concepts/security.md b/ts/docs/concepts/security.md index 56d77ee39..8dad1ebd7 100644 --- a/ts/docs/concepts/security.md +++ b/ts/docs/concepts/security.md @@ -14,7 +14,7 @@ One seed covers **every chain family** — the same phrase re-derives your TRON ## Secrets in transit: stdin or TTY, never argv/env -Anything in a command's arguments or environment leaks into shell history, `ps` output, and CI logs. wallet-cli therefore refuses secrets there — they enter only via: +Anything in a command's arguments leaks into shell history and `ps` output. Exported environment variables are also easy to leak through shells and CI logs. wallet-cli therefore refuses passwords, mnemonics, and private keys in argv and does not read any dedicated secret environment variables for them — they enter only via: - interactive TTY prompts, or - explicit stdin flags: `--password-stdin`, `--tx-stdin` — **one `*-stdin` flag per run**, so a pipeline can never silently feed the wrong secret to the wrong prompt. The highest-value secrets go further: mnemonics and private keys are accepted **only** via hidden TTY input (`import mnemonic` / `import private-key` / `change-password` have no stdin path at all). @@ -46,7 +46,7 @@ Unexpected internal exceptions are collapsed to a generic `internal_error` messa | Software key | `create` / `import` | Convenient; host compromise = key compromise | | Ledger | `import ledger` | Key never on host; every send confirmed on-device. `--app` fixes the account to one chain family — import once per app to cover both — see [Ledger guide](../guide/ledger.md) | | Watch-only | `import watch` | No signing at all; safe for monitoring balances of cold storage. Bound to the pasted address's family | -| Split sign/broadcast | `--sign-only` + `tx broadcast` | Signing machine needs no network — see [Scripting](../guide/scripting.md#sign-here-broadcast-there) | +| Split sign/broadcast | `tx send --build-only` → `tx sign --offline` → `tx broadcast` | Signing machine can stay offline; `--sign-only` still builds and estimates online — see [Scripting](../guide/scripting.md#sign-here-broadcast-there) | ## What wallet-cli cannot do for you diff --git a/ts/docs/guide/scripting.md b/ts/docs/guide/scripting.md index 98c737737..7c2cc80fd 100644 --- a/ts/docs/guide/scripting.md +++ b/ts/docs/guide/scripting.md @@ -35,14 +35,14 @@ else fi ``` -**3. Secrets via stdin, never argv.** Passwords/mnemonics/keys in arguments would end up in shell history and `ps` output: +**3. Secrets via stdin, never argv.** Passwords/mnemonics/keys in arguments would end up in shell history and `ps` output. wallet-cli does not read dedicated secret environment variables either: ```bash printf '%s' "$PW" | wallet-cli tx send --to T... --amount 1 \ --network tron:nile --password-stdin -o json ``` -(`$PW` should come from your secret store, not from a file in the repo. Only one `*-stdin` flag per run.) +(`$PW` should come from your secret store as a short-lived shell variable for this pipe, not from a file in the repo and not from a long-lived `export`. Only one `*-stdin` flag per run.) ## Waiting for confirmation @@ -57,18 +57,24 @@ Or decouple: capture `data.txId`, then poll [`tx status`](../commands/tx/status. ## Sign here, broadcast there -`--sign-only` and `tx broadcast` split signing from submission, so the machine holding keys never needs chain access: +`--sign-only` separates signing from broadcast, but it still builds and estimates through the selected RPC endpoint before signing. For a signing machine with no chain access, build unsigned hex online, sign that artifact offline, then broadcast from an online machine: ```bash -# on the signing machine +# on the connected build machine wallet-cli tx send --to T... --amount 1 --network tron:nile \ - --password-stdin --sign-only -o json | jq -r '.data.hex' > signed.hex + --build-only -o json | jq -r '.data.hex' > unsigned.hex + +# on the offline signing machine +printf '%s' "$PW" | wallet-cli tx sign --file unsigned.hex --network tron:nile \ + --offline --password-stdin --out signed.hex # on the connected machine wallet-cli tx broadcast --file signed.hex --network tron:nile -o json ``` -The **hex** form above works on both chain families — protobuf on TRON, RLP on EVM. The JSON form is TRON-only: +The **hex** form above works on both chain families — protobuf on TRON, RLP on EVM. If the signing machine does have RPC access and you only want to withhold broadcast, `tx send --sign-only` emits signed hex directly. + +TRON also accepts signed transaction JSON, but JSON must go through `--transaction` or `--tx-stdin`; `--file` and `--hex` are hex-only: ```bash wallet-cli tx send ... --sign-only -o json | jq -c '.data.signed' > signed.json diff --git a/ts/docs/machine-interface.md b/ts/docs/machine-interface.md index 48dd97cfd..3875aa443 100644 --- a/ts/docs/machine-interface.md +++ b/ts/docs/machine-interface.md @@ -248,11 +248,13 @@ Alongside it, an error may carry a scalar list of just the identifiers to retry ## Secret handling -Secrets never travel via argv or environment variables — they would leak into shell history and process listings. Two channels only: +wallet-cli never reads passwords, mnemonics, or private keys from argv or from dedicated secret environment variables. Arguments and exported environment values leak into shell history, process listings, and CI logs. For secrets, use these CLI channels: -1. **stdin flags** — `--password-stdin`, `--tx-stdin`, `--message-stdin`. **Only one `*-stdin` flag can consume stdin per run.** (Mnemonics and private keys have no stdin path — `import mnemonic` / `import private-key` / `change-password` are interactive-only, hidden TTY input.) +1. **stdin flags** — `--password-stdin` for the master password; `--tx-stdin` / `--message-stdin` for large payloads. **Only one `*-stdin` flag can consume stdin per run.** (Mnemonics and private keys have no stdin path — `import mnemonic` / `import private-key` / `change-password` are interactive-only, hidden TTY input.) 2. **Interactive TTY prompt** — when running with a terminal attached. +Shell variables in examples are only a shell-side source for a pipe; wallet-cli does not read them. Keep them process-local and short-lived, and do not export them long term. + ```bash # non-interactive unlock printf '%s' "$MASTER_PASSWORD_FROM_YOUR_VAULT" | wallet-cli tx send \ @@ -280,21 +282,56 @@ This is a wallet; a wrong success check loses money. The rules: | `data.state` | Meaning | Terminal? | |---|---|---| - | `confirmed` | Solidified on chain (`blockNumber` present) | yes | + | `confirmed` | Included in a block and an execution result/receipt is available (`blockNumber` present) | yes | | `failed` | Included and reverted / rejected | yes | - | `pending` | Seen but not yet solidified | no — keep polling | - | `not_found` | Unknown to the queried node | no — keep polling until your own deadline, then treat as failed | + | `pending` | Seen by the node, with no execution result/receipt yet | no — keep polling | + | `not_found` | Unknown to the queried endpoint | no — keep polling/reconcile; do not assume failure | `data.confirmed` and `data.failed` are provided as booleans for direct branching. + > `confirmed` means included and receipted, not finalized. Use a TRON SolidityNode view or an EVM finalized block check when that distinction matters. + + > A deadline that ends in `pending` or `not_found` is an unknown outcome. Do not record it as failed, and do not resend automatically without external reconciliation. + **GasFree transfers are the exception.** `gasfree transfer` submits to a provider, not directly to a node: the submitted receipt carries a `traceId` (not a `txId`), and progress follows the provider's states — `WAITING` → `INPROGRESS` → `CONFIRMING` → `SUCCEED` / `FAILED`. Follow it with `--wait` or [`gasfree trace `](commands/gasfree/trace.md) rather than `tx status`; a `txId` appears only once the provider puts it on-chain. ```bash -txid=$(wallet-cli tx send --to T... --amount 1 --network tron:nile --password-stdin -o json \ - < pw.fifo | jq -r '.data.txId') || exit 1 -until [ "$(wallet-cli tx status --txid "$txid" --network tron:nile -o json | jq -r '.data.state')" = confirmed ]; do - sleep 3 # add your own deadline; 'failed' should abort, not loop +#!/usr/bin/env bash +set -euo pipefail + +deadline=$((SECONDS + 90)) +txid=$( + printf '%s' "$PW" | + wallet-cli tx send --to T... --amount 1 --network tron:nile --password-stdin -o json | + jq -er '.data.txId' +) + +while (( SECONDS < deadline )); do + state=$( + wallet-cli tx status --txid "$txid" --network tron:nile -o json | + jq -er '.data.state' + ) + + case "$state" in + confirmed) + exit 0 + ;; + failed) + echo "transaction failed: $txid" >&2 + exit 1 + ;; + pending|not_found) + sleep 3 + ;; + *) + echo "unexpected transaction state: $state" >&2 + exit 1 + ;; + esac done + +echo "transaction outcome unknown after deadline: $txid" >&2 +exit 1 ``` 4. **Batch operations**: each command is one transaction with one exit code. Stop-on-first-failure is the default safe posture; if you continue, track per-item txids and reconcile with `tx status` before reporting success. diff --git a/ts/docs/troubleshooting.md b/ts/docs/troubleshooting.md index 011ef5d01..f4b6f2210 100644 --- a/ts/docs/troubleshooting.md +++ b/ts/docs/troubleshooting.md @@ -70,7 +70,7 @@ An unexpected failure. The message is intentionally generic (secret-redaction). ## Not an error code, but frequently asked -- **`tx status` says `pending` for a long time** — the tx is seen but not solidified; keep polling. If it never leaves `pending`/`not_found` past your deadline, treat it as failed and investigate on a block explorer before resending. +- **`tx status` says `pending` for a long time** — the tx is seen, but no execution result/receipt is available yet; keep polling. If it never leaves `pending`/`not_found` past your deadline, the outcome is unknown, not failed. Reconcile it on the intended network, preferably with an explorer or archival endpoint, before any resend. - **"only one *-stdin flag can consume stdin per run"** — pipe one secret per invocation; for send-with-password use `--password-stdin` and let the mnemonic/key live in the encrypted store. - **Forgot the master password** — there is no recovery; restore from your BIP39 mnemonic (`import mnemonic`) into a fresh wallet and set a new password. - **`account history` fails while other queries work** — history requires a TronGrid endpoint; plain node RPC is not enough. It is also TRON-only: on an EVM network it fails with `family_mismatch`. From c21eacf2ab9d728104f129b83e6c2ad2ce4f23a9 Mon Sep 17 00:00:00 2001 From: "Leon.Zhang" Date: Fri, 28 Aug 2026 18:55:15 +0800 Subject: [PATCH 2/8] docs: align transaction mode guidance with CLI behavior --- ts/docs/commands/account/activate.md | 4 ++-- ts/docs/commands/account/set.md | 4 ++-- ts/docs/commands/asset/issue.md | 2 +- ts/docs/commands/asset/participate.md | 2 +- ts/docs/commands/asset/unfreeze.md | 2 +- ts/docs/commands/asset/update.md | 2 +- ts/docs/commands/contract/clear-abi.md | 2 +- ts/docs/commands/contract/deploy.md | 2 +- ts/docs/commands/contract/send.md | 2 +- ts/docs/commands/contract/set-origin-energy-limit.md | 2 +- ts/docs/commands/contract/set-user-resource-percent.md | 2 +- ts/docs/commands/exchange/create.md | 2 +- ts/docs/commands/exchange/inject.md | 2 +- ts/docs/commands/exchange/trade.md | 2 +- ts/docs/commands/exchange/withdraw.md | 2 +- ts/docs/commands/gasfree/transfer.md | 2 +- ts/docs/commands/permission/update.md | 4 ++-- ts/docs/commands/proposal/approve.md | 2 +- ts/docs/commands/proposal/create.md | 2 +- ts/docs/commands/proposal/delete.md | 2 +- ts/docs/commands/reward/withdraw.md | 2 +- ts/docs/commands/stake/cancel-unfreeze.md | 2 +- ts/docs/commands/stake/delegate.md | 2 +- ts/docs/commands/stake/freeze.md | 2 +- ts/docs/commands/stake/undelegate.md | 2 +- ts/docs/commands/stake/unfreeze.md | 2 +- ts/docs/commands/stake/withdraw.md | 2 +- ts/docs/commands/tx/index.md | 4 ++-- ts/docs/commands/vote/cast.md | 2 +- ts/docs/commands/witness/create.md | 2 +- ts/docs/commands/witness/set-brokerage.md | 2 +- ts/docs/commands/witness/update.md | 2 +- ts/docs/concepts/security.md | 2 +- ts/docs/guide/ledger.md | 2 +- ts/docs/guide/send-tokens.md | 4 ++-- ts/docs/guide/stake-and-resources.md | 4 ++-- ts/src/adapters/inbound/cli/commands/contract.ts | 2 +- ts/src/adapters/inbound/cli/commands/proposal.ts | 2 +- ts/src/adapters/inbound/cli/commands/shared.ts | 4 ++-- ts/src/adapters/inbound/cli/commands/witness.ts | 2 +- 40 files changed, 47 insertions(+), 47 deletions(-) diff --git a/ts/docs/commands/account/activate.md b/ts/docs/commands/account/activate.md index cf0bb92da..f9d74e980 100644 --- a/ts/docs/commands/account/activate.md +++ b/ts/docs/commands/account/activate.md @@ -16,7 +16,7 @@ A TRON address doesn't exist on-chain until it receives its first asset or is ex Use it only when an address needs to *exist* on its own — to be queryable, or able to initiate its own transactions. If you're sending it funds anyway, [`tx send`](../tx/send.md) activates the recipient automatically in one step; and adding an address to a multi-sig permission does **not** require activation. -Requires the payer account and the master password via `--password-stdin`; watch-only accounts fail with `watch_only_no_signer`. +Requires the payer account. The master password via `--password-stdin` is needed only when the selected mode signs — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. ## Options @@ -25,7 +25,7 @@ Requires the payer account and the master password via `--password-stdin`; watch | `--address ` | **Required.** The address to activate (a valid, not-yet-activated TRON address) | | `--dry-run` | Build and estimate only; no signature/broadcast, no password. Excludes `--sign-only` / `--build-only` | | `--sign-only` | Build and sign, output the signed hex (feed [`tx broadcast`](../tx/broadcast.md)). Excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md)). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md)). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/account/set.md b/ts/docs/commands/account/set.md index 22ef0f84f..524606f90 100644 --- a/ts/docs/commands/account/set.md +++ b/ts/docs/commands/account/set.md @@ -16,7 +16,7 @@ Sets the account's on-chain **name** (a display alias, up to 32 bytes) or its ** ⚠️ **On mainnet each can be set only once and can never be changed** — the value is permanent, and there is no confirmation prompt. This is different from [`rename`](../rename.md), which changes the local label and can be redone anytime. -Requires the account and the master password via `--password-stdin`; watch-only accounts fail with `watch_only_no_signer`. The account id's uniqueness is enforced on-chain — a taken id fails with `id_taken`. +Requires the account. The master password via `--password-stdin` is needed only when the selected mode signs — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. The account id's uniqueness is enforced on-chain — a taken id fails with `id_taken`. ## Options @@ -26,7 +26,7 @@ Requires the account and the master password via `--password-stdin`; watch-only | `--id ` | **Required** (one of). Account id, 8–32 bytes, globally unique; can be set once | | `--dry-run` | Build and estimate only; no signature/broadcast, no password. Excludes `--sign-only` / `--build-only` | | `--sign-only` | Build and sign, output the signed hex (feed [`tx broadcast`](../tx/broadcast.md)). Excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md)). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md)). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/asset/issue.md b/ts/docs/commands/asset/issue.md index 27e1e960e..6c1228b1f 100644 --- a/ts/docs/commands/asset/issue.md +++ b/ts/docs/commands/asset/issue.md @@ -48,7 +48,7 @@ Constraints are checked locally before broadcast: `--name` and `--abbr` are 1– | `--freeze :` | **Repeatable.** Frozen tranche; amount in whole tokens, e.g. `100000000:30` | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/asset/participate.md b/ts/docs/commands/asset/participate.md index 3041d4e60..0967c1c5c 100644 --- a/ts/docs/commands/asset/participate.md +++ b/ts/docs/commands/asset/participate.md @@ -28,7 +28,7 @@ The acting account cannot be the token's own issuer. | `--pay ` | **Required.** TRX to spend (not a token count), > 0 | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/asset/unfreeze.md b/ts/docs/commands/asset/unfreeze.md index b318945e6..a2ee1cff8 100644 --- a/ts/docs/commands/asset/unfreeze.md +++ b/ts/docs/commands/asset/unfreeze.md @@ -30,7 +30,7 @@ This command has no options of its own. |---|---| | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/asset/update.md b/ts/docs/commands/asset/update.md index 243bc60c6..379451e04 100644 --- a/ts/docs/commands/asset/update.md +++ b/ts/docs/commands/asset/update.md @@ -31,7 +31,7 @@ Pass only the fields you are changing. The others are read from chain and writte | `--public-free-net ` | Shared free-bandwidth pool for holders (unchanged if omitted) | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/contract/clear-abi.md b/ts/docs/commands/contract/clear-abi.md index a6ffc6092..1a081f116 100644 --- a/ts/docs/commands/contract/clear-abi.md +++ b/ts/docs/commands/contract/clear-abi.md @@ -29,7 +29,7 @@ Only the contract's deployer can do this — the address the chain records as th | `
` | **Required.** Contract whose ABI to clear | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/contract/deploy.md b/ts/docs/commands/contract/deploy.md index cf5f59595..65bd48418 100644 --- a/ts/docs/commands/contract/deploy.md +++ b/ts/docs/commands/contract/deploy.md @@ -50,7 +50,7 @@ Requires an account. The master password (via `--password-stdin`) is needed only | `--constructor-signature ` | The constructor's types when there is no ABI, e.g. `constructor(uint256,string)`; excludes `--artifact`, and not accepted on TRON | | `--dry-run` | Estimate only; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | | `--password-stdin` | Master password from stdin | diff --git a/ts/docs/commands/contract/send.md b/ts/docs/commands/contract/send.md index 5df66830b..e65f033fe 100644 --- a/ts/docs/commands/contract/send.md +++ b/ts/docs/commands/contract/send.md @@ -36,7 +36,7 @@ Requires an account. The master password (via `--password-stdin`) is needed only | `--value ` | Native coin sent with the call, in whole coins | | `--dry-run` | Estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | | `--password-stdin` | Master password from stdin | diff --git a/ts/docs/commands/contract/set-origin-energy-limit.md b/ts/docs/commands/contract/set-origin-energy-limit.md index 54b711fd7..1a04f6eb2 100644 --- a/ts/docs/commands/contract/set-origin-energy-limit.md +++ b/ts/docs/commands/contract/set-origin-energy-limit.md @@ -32,7 +32,7 @@ Only the contract's deployer can do this; the current value is in [`contract inf | `` | **Required.** Per-call energy the deployer will cover, integer > 0 | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/contract/set-user-resource-percent.md b/ts/docs/commands/contract/set-user-resource-percent.md index ab3104684..99a40eb2b 100644 --- a/ts/docs/commands/contract/set-user-resource-percent.md +++ b/ts/docs/commands/contract/set-user-resource-percent.md @@ -32,7 +32,7 @@ Only the contract's deployer can do this; the current value is in [`contract inf | `` | **Required.** Share of energy paid by the caller, integer 0–100 | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/exchange/create.md b/ts/docs/commands/exchange/create.md index 3615a8a90..147ee641c 100644 --- a/ts/docs/commands/exchange/create.md +++ b/ts/docs/commands/exchange/create.md @@ -36,7 +36,7 @@ The creation fee is **burned** — the chain parameter `getExchangeCreateFee`, c | `--raw-amounts :` | The same two amounts in minimal units. One of `--amounts` / `--raw-amounts` | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/exchange/inject.md b/ts/docs/commands/exchange/inject.md index 580878ba2..fec12ce95 100644 --- a/ts/docs/commands/exchange/inject.md +++ b/ts/docs/commands/exchange/inject.md @@ -33,7 +33,7 @@ If the amount is so small that the computed other side rounds to zero, the chain | `--raw-amount ` | The same amount in minimal units. One of `--amount` / `--raw-amount` | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/exchange/trade.md b/ts/docs/commands/exchange/trade.md index d3ba4121a..09793771c 100644 --- a/ts/docs/commands/exchange/trade.md +++ b/ts/docs/commands/exchange/trade.md @@ -45,7 +45,7 @@ Slippage grows with trade size relative to the reserves — that is the curve, n | `--slippage ` | Derive the floor from current reserves, minus this percentage; > 0 and < 100 | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/exchange/withdraw.md b/ts/docs/commands/exchange/withdraw.md index edb359982..7089140d4 100644 --- a/ts/docs/commands/exchange/withdraw.md +++ b/ts/docs/commands/exchange/withdraw.md @@ -31,7 +31,7 @@ The mirror of [`exchange inject`](inject.md): you name one side and its amount, | `--raw-amount ` | The same amount in minimal units. One of `--amount` / `--raw-amount` | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/gasfree/transfer.md b/ts/docs/commands/gasfree/transfer.md index 11cc9e9b6..fb1ade637 100644 --- a/ts/docs/commands/gasfree/transfer.md +++ b/ts/docs/commands/gasfree/transfer.md @@ -15,7 +15,7 @@ Signs a transfer with EIP-712 structured-data signing and submits it to the GasF Submission returns a **`traceId`** (the provider's acceptance id); at that point the transfer is accepted but **not yet on-chain**. Add `--wait` to poll the provider to a terminal state (`SUCCEED` / `FAILED`), or follow it later with [`gasfree trace`](trace.md). On the first transfer, when the GasFree address isn't activated yet, this transfer carries the activation automatically and the total deducted is amount + service fee + activation fee (itemised in the receipt and in `--dry-run`). -There is no `--sign-only` / `--build-only`: the signed payload is bound to the provider's submission protocol, so offline distribution has no meaning. Requires an account, the master password via `--password-stdin`, and the provider credentials (`gasfreeApiKey` / `gasfreeApiSecret`, set with [`config`](../config.md)); watch-only accounts fail with `watch_only_no_signer`. +There is no `--sign-only` / `--build-only`: the signed payload is bound to the provider's submission protocol, so offline distribution has no meaning. Requires an account and the provider credentials (`gasfreeApiKey` / `gasfreeApiSecret`, set with [`config`](../config.md)). The master password via `--password-stdin` is needed only when submitting the transfer; `--dry-run` does not unlock or sign. Watch-only accounts fail with `watch_only_no_signer` when submitting. ## Options diff --git a/ts/docs/commands/permission/update.md b/ts/docs/commands/permission/update.md index 39285de00..64d6260bd 100644 --- a/ts/docs/commands/permission/update.md +++ b/ts/docs/commands/permission/update.md @@ -14,7 +14,7 @@ wallet-cli permission update (--file | --json ) Replaces the account's **entire** permission structure with the new one given by `--file` (a JSON file) or `--json` (an inline JSON string) — TRON's `UpdateAccountPermission` has replace semantics, so the JSON you supply becomes the whole structure. The chain burns **100 TRX** for the change. -The command runs without a confirmation prompt. It requires an account and the master password via `--password-stdin`; watch-only accounts fail with `watch_only_no_signer`. +The command runs without a confirmation prompt. It requires an account. The master password via `--password-stdin` is needed only when the selected mode signs — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. **Input format.** The permission JSON is the same shape as [`permission show -o json`](show.md)'s `data` (`owner` / `witness` / `actives`; a key's `local` field may be omitted). You write the **contract-type names** for each active group's `operations`, not the raw bitmap — the CLI encodes it. A convenient way to produce a valid input is to export the current structure, edit it, and submit the file. @@ -42,7 +42,7 @@ Changing only `keys`, `threshold` or `name` needs no such deletion. | `--json ` | **Required** (one of). Inline JSON string with the new structure (same shape) | | `--dry-run` | Mock receipt — fee, resulting-structure card, and warnings — matching a real submission; no signature, no broadcast, no password. Excludes `--sign-only` / `--build-only` | | `--sign-only` | Build and sign, output the signed hex without broadcasting (feed [`tx broadcast`](../tx/broadcast.md) for on-chain co-signing). Excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md) for service-relayed multi-sig). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md) for service-relayed multi-sig). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active) — changing permissions is owner-level, so normally `0` (default `0`) | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/proposal/approve.md b/ts/docs/commands/proposal/approve.md index 4acbb58ab..3bf38c916 100644 --- a/ts/docs/commands/proposal/approve.md +++ b/ts/docs/commands/proposal/approve.md @@ -26,7 +26,7 @@ Only a registered witness can approve; other accounts fail with `not_a_witness`. | `--cancel` | Withdraw an approval you cast earlier instead of adding one | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/proposal/create.md b/ts/docs/commands/proposal/create.md index c1cf2b7dd..21c07ddb8 100644 --- a/ts/docs/commands/proposal/create.md +++ b/ts/docs/commands/proposal/create.md @@ -27,7 +27,7 @@ Pass `--set` once per parameter. The receipt and `data.changes[]` order changes | `--set =` | **Required, repeatable.** One parameter change, e.g. `--set getTransactionFee=15`; `name` is a `chain params` key, a raw parameter id is also accepted | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/proposal/delete.md b/ts/docs/commands/proposal/delete.md index 2cbb069a2..4802c9302 100644 --- a/ts/docs/commands/proposal/delete.md +++ b/ts/docs/commands/proposal/delete.md @@ -27,7 +27,7 @@ The chain records the result under its own name — after a successful delete, [ | `` | **Required.** Proposal id | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/reward/withdraw.md b/ts/docs/commands/reward/withdraw.md index cbc940746..19b59ec57 100644 --- a/ts/docs/commands/reward/withdraw.md +++ b/ts/docs/commands/reward/withdraw.md @@ -23,7 +23,7 @@ Moves your accumulated voting rewards (plus block rewards if you are an SR) into |---|---| | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/stake/cancel-unfreeze.md b/ts/docs/commands/stake/cancel-unfreeze.md index 475ef2b72..246547e08 100644 --- a/ts/docs/commands/stake/cancel-unfreeze.md +++ b/ts/docs/commands/stake/cancel-unfreeze.md @@ -21,7 +21,7 @@ Cancels **every** unstake still in its waiting period and rolls those amounts ba |---|---| | `--dry-run` | Estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/stake/delegate.md b/ts/docs/commands/stake/delegate.md index 33d3b59c5..f0fecbcc6 100644 --- a/ts/docs/commands/stake/delegate.md +++ b/ts/docs/commands/stake/delegate.md @@ -31,7 +31,7 @@ Check how much you can still delegate with [`stake delegated`](delegated.md) (`M | `--lock-period ` | Lock duration in blocks (~3 s/block); requires `--lock` | | `--dry-run` | Estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/stake/freeze.md b/ts/docs/commands/stake/freeze.md index 1118ba65e..85eb46702 100644 --- a/ts/docs/commands/stake/freeze.md +++ b/ts/docs/commands/stake/freeze.md @@ -25,7 +25,7 @@ Amount is in SUN (1 TRX = 1,000,000 SUN). Staked TRX stays yours; to get it back | `--resource ` | Resource type to obtain (default `bandwidth`) | | `--dry-run` | Estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/stake/undelegate.md b/ts/docs/commands/stake/undelegate.md index 5e313addb..b6ac8a171 100644 --- a/ts/docs/commands/stake/undelegate.md +++ b/ts/docs/commands/stake/undelegate.md @@ -27,7 +27,7 @@ Reclaiming is immediate (no waiting period — the TRX was staked all along, onl | `--resource ` | Resource type to reclaim (default `bandwidth`) | | `--dry-run` | Estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/stake/unfreeze.md b/ts/docs/commands/stake/unfreeze.md index 43ad8cfc5..9b42c73f3 100644 --- a/ts/docs/commands/stake/unfreeze.md +++ b/ts/docs/commands/stake/unfreeze.md @@ -25,7 +25,7 @@ Stake 2.0 allows at most **32 pending unstakes** per account at a time; check re | `--resource ` | Resource type to release (default `bandwidth`) | | `--dry-run` | Estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/stake/withdraw.md b/ts/docs/commands/stake/withdraw.md index 3cff26c4a..d4b2e985b 100644 --- a/ts/docs/commands/stake/withdraw.md +++ b/ts/docs/commands/stake/withdraw.md @@ -23,7 +23,7 @@ Withdrawing also frees up unstake slots (max 32 pending unstakes per account). |---|---| | `--dry-run` | Estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/tx/index.md b/ts/docs/commands/tx/index.md index e8e4ad85e..aeac4a4a6 100644 --- a/ts/docs/commands/tx/index.md +++ b/ts/docs/commands/tx/index.md @@ -25,13 +25,13 @@ The transaction **hex** these commands exchange is `protocol.Transaction` protob ## The transaction lifecycle ``` -build ──sign──> submit ──solidify──> confirmed +build ──sign──> submit ──receipt──> confirmed │ │ │ └ --dry-run └ default return └ tx status: confirmed/failed stops here point ("submitted") (pending/not_found while in flight) ``` -`tx send` covers build+sign+submit in one step (with `--dry-run` / `--sign-only` stopping earlier); `tx broadcast` submits what was signed elsewhere; `tx status` / `tx info` observe the outcome. **Submission is not confirmation** — scripts must follow [machine-interface → Script safety](../../machine-interface.md#script-safety-never-mistake-submitted-for-confirmed). +`tx send` covers build+sign+submit in one step (with `--dry-run` / `--sign-only` stopping earlier); `tx broadcast` submits what was signed elsewhere; `tx status` / `tx info` observe the outcome. `confirmed` means included and receipted, not finalized. **Submission is not confirmation** — scripts must follow [machine-interface → Script safety](../../machine-interface.md#script-safety-never-mistake-submitted-for-confirmed). ## Multi-sig co-signing diff --git a/ts/docs/commands/vote/cast.md b/ts/docs/commands/vote/cast.md index 5c33138f9..b0ba849d9 100644 --- a/ts/docs/commands/vote/cast.md +++ b/ts/docs/commands/vote/cast.md @@ -29,7 +29,7 @@ Votes take effect at the next maintenance cycle (~6 h). Each vote uses 1 TP (it | `--for ` | **Required, repeatable.** SR address = vote count (positive integer); the whole set becomes your full allocation (1–30 entries) | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/witness/create.md b/ts/docs/commands/witness/create.md index b9ddb39fa..658308aff 100644 --- a/ts/docs/commands/witness/create.md +++ b/ts/docs/commands/witness/create.md @@ -27,7 +27,7 @@ The account must already be activated and hold at least the registration fee. `- | `--url ` | **Required.** Candidate info page | | `--dry-run` | Build and estimate only, no signature/broadcast; reports the registration fee; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/witness/set-brokerage.md b/ts/docs/commands/witness/set-brokerage.md index 2b0c05dbe..74e9d5af5 100644 --- a/ts/docs/commands/witness/set-brokerage.md +++ b/ts/docs/commands/witness/set-brokerage.md @@ -27,7 +27,7 @@ Any registered witness can set it, elected or not. The acting account must be a | `` | **Required.** Share the SR keeps, integer 0–100 | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/commands/witness/update.md b/ts/docs/commands/witness/update.md index 688856ec6..647fad391 100644 --- a/ts/docs/commands/witness/update.md +++ b/ts/docs/commands/witness/update.md @@ -25,7 +25,7 @@ The acting account must already be a candidate; otherwise the command fails with | `--url ` | **Required.** New candidate info page | | `--dry-run` | Build and estimate only, no signature/broadcast; excludes `--sign-only` / `--build-only` | | `--sign-only` | Sign without broadcasting, output the signed hex; excludes `--dry-run` / `--build-only`; pairs with `--expiration` | -| `--build-only` | Build only, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | +| `--build-only` | Build and estimate, output the **unsigned** hex; excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | | `--expiration ` | Transaction expiration in ms, up to `86400000` (24h); only with `--sign-only` or `--build-only`; omitted = node default (~60s) | | `--permission-id ` | Permission group to sign with (0=owner, 1=witness, 2-9=active); default `0` | | `--wait` / `--wait-timeout ` | Poll after broadcast until confirmed/failed (cap default: config `waitTimeoutMs`, built-in 60000) | diff --git a/ts/docs/concepts/security.md b/ts/docs/concepts/security.md index 8dad1ebd7..79254f4b3 100644 --- a/ts/docs/concepts/security.md +++ b/ts/docs/concepts/security.md @@ -4,7 +4,7 @@ What wallet-cli protects, how, and what remains your job. ## Local storage -All secrets (seeds, private keys) are stored **encrypted under your master password**; nothing usable is on disk in the clear. Metadata (labels, addresses) is readable without unlock — that's why `list` needs no password but `tx send` does. +All secrets (seeds, private keys) are stored **encrypted under your master password**; nothing usable is on disk in the clear. Metadata (labels, addresses) is readable without unlock — that's why `list` needs no password but a software-signed `tx send` does. The master password is local protection only: it is never sent anywhere and **cannot be recovered**. It must be at least 8 characters with an uppercase letter, a lowercase letter, a digit, and a special character. diff --git a/ts/docs/guide/ledger.md b/ts/docs/guide/ledger.md index a11ca9fd4..2591dad7b 100644 --- a/ts/docs/guide/ledger.md +++ b/ts/docs/guide/ledger.md @@ -50,7 +50,7 @@ More remedies: [Troubleshooting](../troubleshooting.md#timeout-exit-1). ## Offline pattern -Ledger already isolates keys, but you can combine it with the split flow — `--sign-only` on the machine with the device, [`tx broadcast`](../commands/tx/broadcast.md) on a connected one. See [Scripting → Sign here, broadcast there](scripting.md#sign-here-broadcast-there). +Ledger already isolates keys, but you can still split build/sign/broadcast. For a device machine with no chain access, build unsigned hex with `--build-only` on a connected machine, sign it with `tx sign --offline` where the Ledger is attached, then broadcast the signed hex from a connected machine. See [Scripting → Sign here, broadcast there](scripting.md#sign-here-broadcast-there). ## See also diff --git a/ts/docs/guide/send-tokens.md b/ts/docs/guide/send-tokens.md index 5d70cc4a5..8ff272754 100644 --- a/ts/docs/guide/send-tokens.md +++ b/ts/docs/guide/send-tokens.md @@ -2,7 +2,7 @@ One command sends every asset kind — the network's native coin, TRC20/ERC20 contract tokens, and TRC10 assets — the selector flags decide which. Command examples run on Nile; the same commands work on an EVM network by swapping `--network`. -> **Password**: every `tx send` needs your master password on stdin, and signing shows no prompt. The examples below omit it to keep the token flags in focus — prepend `printf '%s' "$PW" |` and append `--password-stdin`, or pipe from a password manager (see [Getting started](getting-started.md#3-send-your-first-transaction)). +> **Password**: software-signed sends need your master password on stdin, and signing shows no prompt. The examples below omit it to keep the token flags in focus — prepend `printf '%s' "$PW" |` and append `--password-stdin`, or pipe from a password manager. `--dry-run`, `--build-only`, and Ledger signing do not use the master password. ## The native coin @@ -76,7 +76,7 @@ wallet-cli tx send --to T... --asset-id 1002000 --raw-amount 1000000 --network t ## Rehearse, then send -`--dry-run` builds the transaction and estimates fees without signing or broadcasting — nothing can leave your wallet: +`--dry-run` builds the transaction and estimates fees through the selected network, then returns without signing or broadcasting: ```bash wallet-cli tx send --to T... --token USDT --amount 5 --network tron:nile --dry-run -o json diff --git a/ts/docs/guide/stake-and-resources.md b/ts/docs/guide/stake-and-resources.md index af09581e7..88b6c1f74 100644 --- a/ts/docs/guide/stake-and-resources.md +++ b/ts/docs/guide/stake-and-resources.md @@ -2,7 +2,7 @@ Stake TRX to earn **resources** — energy and bandwidth — instead of burning TRX on every transaction. This walkthrough uses the `stake` commands on Nile. **TRON only**: EVM networks price transactions in gas and have nothing to stake, so every command here fails there with `family_mismatch`. Background: [Energy & bandwidth](../concepts/energy-bandwidth.md). -> **Password**: every `stake` command signs a transaction, so it needs your master password on stdin (`--password-stdin`), and signing shows no prompt. The examples below omit it to keep the resource flags in focus — prepend `printf '%s' "$PW" |` and append `--password-stdin`, or pipe from a password manager (see [Getting started](getting-started.md#3-send-your-first-transaction)). Step 1 is a read-only query and needs no password. +> **Password**: stake write commands need your master password only when the selected mode signs. The examples below omit it to keep the resource flags in focus — prepend `printf '%s' "$PW" |` and append `--password-stdin` for software signing, or pipe from a password manager. `--dry-run`, `--build-only`, `stake info`, and `stake delegated` need no password. ## 1. See what you have @@ -33,7 +33,7 @@ Plain TRX transfers consume **bandwidth**; smart-contract calls (TRC20 transfers wallet-cli stake freeze --amount-sun 100000000 --resource energy --network tron:nile ``` -`--resource` chooses which resource the stake produces. It defaults to `bandwidth`; stake for `energy` when you plan to send TRC20 tokens or call contracts, since those spend energy (as in step 1). The TRX stays yours — it is locked, not spent — and staking also grants TRON Power (governance votes). Like every state-changing command, `stake freeze` supports `--dry-run`, `--sign-only`, `--wait`, and returns at submission by default. +`--resource` chooses which resource the stake produces. It defaults to `bandwidth`; stake for `energy` when you plan to send TRC20 tokens or call contracts, since those spend energy (as in step 1). The TRX stays yours — it is locked, not spent — and staking also grants TRON Power (governance votes). Like every state-changing command, `stake freeze` supports `--dry-run`, `--sign-only`, `--build-only`, `--wait`, and returns at submission by default. Verify the effect by running `account info` again — the `Energy` limit now reflects the TRX you staked: diff --git a/ts/src/adapters/inbound/cli/commands/contract.ts b/ts/src/adapters/inbound/cli/commands/contract.ts index 8529633fd..b3f6622f2 100644 --- a/ts/src/adapters/inbound/cli/commands/contract.ts +++ b/ts/src/adapters/inbound/cli/commands/contract.ts @@ -690,7 +690,7 @@ export const contractInfoTronBinding = (svc: TronContractService): FamilyBinding const contractGovernanceBase = { network: "optional" as const, wallet: "optional" as const, - auth: "required" as const, + auth: "conditional" as const, broadcasts: true, capability: "contract.governance", baseRefine: governanceTxRefine, diff --git a/ts/src/adapters/inbound/cli/commands/proposal.ts b/ts/src/adapters/inbound/cli/commands/proposal.ts index 07b49e097..1dbe14800 100644 --- a/ts/src/adapters/inbound/cli/commands/proposal.ts +++ b/ts/src/adapters/inbound/cli/commands/proposal.ts @@ -65,7 +65,7 @@ export const proposalShowTronBinding = (service: TronProposalService): FamilyBin const proposalWriteBase = { network: "optional" as const, wallet: "optional" as const, - auth: "required" as const, + auth: "conditional" as const, broadcasts: true, capability: "proposal.write", baseRefine: governanceTxRefine, diff --git a/ts/src/adapters/inbound/cli/commands/shared.ts b/ts/src/adapters/inbound/cli/commands/shared.ts index 98087ea96..e44adebbe 100644 --- a/ts/src/adapters/inbound/cli/commands/shared.ts +++ b/ts/src/adapters/inbound/cli/commands/shared.ts @@ -50,7 +50,7 @@ export const txModeFields = { .boolean() .default(false) .describe( - "build and output unsigned complete transaction hex without unlocking; the entry point for multi-party signing (relay it with `tx sign`, or open a queue with `tx multisig --create`)", + "build and estimate, then output unsigned complete transaction hex without unlocking; the entry point for multi-party signing (relay it with `tx sign`, or open a queue with `tx multisig --create`)", ), }; @@ -62,7 +62,7 @@ export const governanceTxModeFields = { .boolean() .default(false) .describe( - "build an unsigned transaction without signing or broadcasting; mutually exclusive with --dry-run/--sign-only", + "build and estimate an unsigned transaction without unlocking, signing, or broadcasting; mutually exclusive with --dry-run/--sign-only", ), }; diff --git a/ts/src/adapters/inbound/cli/commands/witness.ts b/ts/src/adapters/inbound/cli/commands/witness.ts index 27410c41a..4208cc664 100644 --- a/ts/src/adapters/inbound/cli/commands/witness.ts +++ b/ts/src/adapters/inbound/cli/commands/witness.ts @@ -14,7 +14,7 @@ const witnessUrl = z const witnessWriteBase = { network: "optional" as const, wallet: "optional" as const, - auth: "required" as const, + auth: "conditional" as const, broadcasts: true, capability: "witness.manage", baseRefine: governanceTxRefine, From 44fae254a9e0907c1a0fcc75b502631d019c309c Mon Sep 17 00:00:00 2001 From: "Leon.Zhang" Date: Fri, 28 Aug 2026 19:38:17 +0800 Subject: [PATCH 3/8] docs: align CLI behavior references --- ts/docs/commands/account/index.md | 2 +- ts/docs/commands/contract/index.md | 2 +- ts/docs/commands/index.md | 8 ++++---- ts/docs/commands/tx/multisig.md | 8 ++++---- ts/docs/commands/tx/sign.md | 8 ++++---- ts/docs/guide/stake-and-resources.md | 2 +- ts/docs/machine-interface.md | 10 +++++----- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ts/docs/commands/account/index.md b/ts/docs/commands/account/index.md index 9ba23041a..7580fc474 100644 --- a/ts/docs/commands/account/index.md +++ b/ts/docs/commands/account/index.md @@ -8,7 +8,7 @@ Query on-chain account state, and activate & name accounts. wallet-cli account COMMAND ``` -Subcommands act on the **active account** by default; override with `--account ` or change the default with `wallet-cli use `. Which address is queried follows the selected network's family — the same account has a TRON base58 address and an EVM `0x` address. The first four are read-only queries; `activate` and `set` change on-chain state and need the master password. +Subcommands act on the **active account** by default; override with `--account ` or change the default with `wallet-cli use `. Which address is queried follows the selected network's family — the same account has a TRON base58 address and an EVM `0x` address. The first four are read-only queries; `activate` and `set` change on-chain state. Software signing needs the master password, Ledger signing confirms on device, and `--dry-run` / `--build-only` do not unlock the wallet. ## Subcommands diff --git a/ts/docs/commands/contract/index.md b/ts/docs/commands/contract/index.md index 97dcb9d22..018bcf68a 100644 --- a/ts/docs/commands/contract/index.md +++ b/ts/docs/commands/contract/index.md @@ -23,7 +23,7 @@ wallet-cli contract COMMAND | `contract set-user-resource-percent` | [set-user-resource-percent.md](set-user-resource-percent.md) | Share of a call's energy paid by the caller | TRON only | | `contract create2` | [create2.md](create2.md) | Precompute a CREATE2 address | TRON only | -The three portable commands share one flag vocabulary and differ only in fees: `--fee-limit` / `--permission-id` / `--expiration` on TRON, `--gas-limit` / `--max-fee` / `--priority-fee` / `--nonce` on EVM, each refused on the other family with `invalid_option`. The **TRON only** commands are the ones with no EVM counterpart — an on-chain ABI registry and the deployer-pays energy model are TRON protocol features, and TRON's CREATE2 derivation is not Ethereum's. Running one against an EVM network fails with `family_mismatch`. +The portable commands share the same chain families, not the same flags. `contract call` is read-only and takes call inputs; `contract send` and `contract deploy` are write transactions and carry the fee/signing vocabulary: `--fee-limit` / `--permission-id` / `--expiration` on TRON, `--gas-limit` / `--max-fee` / `--priority-fee` / `--nonce` on EVM, each refused on the other family with `invalid_option`. The **TRON only** commands are the ones with no EVM counterpart — an on-chain ABI registry and the deployer-pays energy model are TRON protocol features, and TRON's CREATE2 derivation is not Ethereum's. Running one against an EVM network fails with `family_mismatch`. ## See also diff --git a/ts/docs/commands/index.md b/ts/docs/commands/index.md index b92dc0a33..4aafee713 100644 --- a/ts/docs/commands/index.md +++ b/ts/docs/commands/index.md @@ -185,16 +185,16 @@ Individual flags are family-scoped the same way. `--help` tags them `(tron only) -h, --help / -V, --version ``` -Broadcast (✍️) commands additionally take `--wait` / `--wait-timeout ` (cap default: config `waitTimeoutMs`, built-in 60000) and the early-exit modes `--dry-run` / `--sign-only` / `--build-only`. +Broadcast (✍️) commands additionally take `--wait` / `--wait-timeout ` (cap default: config `waitTimeoutMs`, built-in 60000). Early-exit modes are command-specific: transaction-building commands expose `--dry-run` / `--sign-only` / `--build-only`, while submit-only commands such as `tx broadcast` do not rebuild or sign and therefore omit `--sign-only` / `--build-only`. Fee and multi-sig flags are **family-scoped**, so they are not global: | Flags | Family | Where | |---|---|---| -| Permission group and expiry — see below | TRON | every TRON broadcast command | +| Permission group and expiry — see below | TRON | TRON transaction-building commands that sign or can emit unsigned hex; not `tx broadcast` or GasFree | | `--fee-limit ` | TRON | the commands that spend energy: `tx send`, `contract send` / `deploy` | | `--gas-limit ` / `--max-fee ` / `--priority-fee ` / `--nonce ` | EVM | `tx send`, `contract send` / `deploy` | -Every TRON broadcast command takes the multi-signature pair: the permission group to sign under (0=owner, 1=witness, 2-9=active) and the transaction's expiry, which extends the window for collecting co-signatures. On a multi-family command they are tagged `(tron only)` and refused on EVM with `invalid_option`; an EVM transaction carries exactly one signature, so neither has a counterpart there. +Those TRON transaction-building commands take the multi-signature pair: the permission group to sign under (0=owner, 1=witness, 2-9=active) and the transaction's expiry, which extends the window for collecting co-signatures when building or signing offline. On a multi-family command they are tagged `(tron only)` and refused on EVM with `invalid_option`; an EVM transaction carries exactly one signature, so neither has a counterpart there. -The three early-exit modes are mutually exclusive, and `--expiration` is accepted only alongside `--sign-only` or `--build-only`. Breaking either rule is a usage error at exit `2`. The code depends on where the check runs: on the governance writes it is `invalid_value`, and the message names the field as `--input` rather than the flags you passed — for example `invalid --input: choose at most one of --dry-run, --sign-only, --build-only`. Elsewhere the same conflict reports `invalid_option`. Branch on the exit code, not on the code string; see [machine interface](../machine-interface.md#error-codes). +Where all three early-exit modes are present, they are mutually exclusive, and `--expiration` is accepted only alongside `--sign-only` or `--build-only`. Breaking either rule is a usage error at exit `2`. The code depends on where the check runs: on the governance writes it is `invalid_value`, and the message names the field as `--input` rather than the flags you passed — for example `invalid --input: choose at most one of --dry-run, --sign-only, --build-only`. Elsewhere the same conflict reports `invalid_option`. Branch on the exit code, not on the code string; see [machine interface](../machine-interface.md#error-codes). diff --git a/ts/docs/commands/tx/multisig.md b/ts/docs/commands/tx/multisig.md index 615590037..2e399ddf2 100644 --- a/ts/docs/commands/tx/multisig.md +++ b/ts/docs/commands/tx/multisig.md @@ -16,8 +16,8 @@ wallet-cli tx multisig [--create (--hex | --file ) | --sign Where the on-chain path passes a hex from person to person, the service path has the TronLink service **hold** a transaction, **accumulate** signatures one by one, and **push** notifications to co-signers over a WebSocket. The command has four mutually exclusive modes: - **default (no mode flag)** — list the service's multi-sig transactions involving this account, with their progress. This is the everyday way to find what's awaiting you. -- **`--create`** — sign an **unsigned** transaction locally and submit it, which opens the collection. The input is unsigned hex, produced by any broadcast command in `--build-only` mode (e.g. `tx send … --build-only`). Requires the master password. -- **`--sign `** — co-sign one: fetch it with the signatures gathered so far, sign locally, and submit the whole transaction back for the service to accumulate. Requires the master password. +- **`--create`** — sign an **unsigned** transaction locally and submit it, which opens the collection. The input is unsigned hex, produced by a transaction-building command that supports `--build-only` (e.g. `tx send … --build-only`). Software accounts require the master password; Ledger accounts confirm on device. +- **`--sign `** — co-sign one: fetch it with the signatures gathered so far, sign locally, and submit the whole transaction back for the service to accumulate. Software accounts require the master password; Ledger accounts confirm on device. - **`--watch`** — keep a WebSocket open and nudge you with the **count** of transactions awaiting your signature (no details); list them with the default mode to act. ### Opening a collection is your first signature @@ -43,11 +43,11 @@ The credentials are per-environment (mainnet / testnet); set them with [`config` | `--sign ` | Co-sign a pending transaction by 32-byte hex txId: fetch → sign locally → submit back; excludes `--create` / `--watch` | | `--watch` | Keep a WebSocket open; nudge with the count awaiting your signature (no details); excludes `--create` / `--sign` | -Plus the [global options](../index.md#global-options-every-command) and `--password-stdin` (with `--create` and `--sign`). +Plus the [global options](../index.md#global-options-every-command) and `--password-stdin` for software accounts (with `--create` and `--sign`). ## Examples -In the examples, `$PW` is your master password, fed on stdin via `--password-stdin`. +In the examples, `$PW` is a software account's master password, fed on stdin via `--password-stdin`. The initiator builds an **unsigned** transaction (`--build-only`, expiry extended to allow collection), then signs and submits it to open a collection: diff --git a/ts/docs/commands/tx/sign.md b/ts/docs/commands/tx/sign.md index 7bb3b427d..133047ac1 100644 --- a/ts/docs/commands/tx/sign.md +++ b/ts/docs/commands/tx/sign.md @@ -16,9 +16,9 @@ Two input modes: with `--hex` / `--file` it signs the transaction hex (protobuf **On EVM there is no co-signing.** An EVM transaction carries exactly one signature, so a hex that already has one is refused with `invalid_transaction`, and there is no threshold, weight or permission group to report. The chain id inside the transaction is checked against `--network` **before** signing (`chain_id_mismatch`) — a mainnet transaction handed to `--network sepolia` would otherwise come back validly signed for mainnet, and nothing downstream could catch it. -On TRON it is the on-chain co-signing path: an initiator produces a partially signed hex with `tx send --sign-only` (or any broadcast command in `--sign-only` mode), each co-signer runs `tx sign` in turn — passing the hex from person to person — and once the weight reaches the threshold, anyone broadcasts the final hex with [`tx broadcast --hex`](broadcast.md). All signatures must be collected before the transaction expires (default ~60s, up to 24h via `--expiration`). +On TRON it is the on-chain co-signing path: an initiator produces a partially signed hex with `tx send --sign-only` (or another transaction-building command that supports `--sign-only`), each co-signer runs `tx sign` in turn — passing the hex from person to person — and once the weight reaches the threshold, anyone broadcasts the final hex with [`tx broadcast --hex`](broadcast.md). All signatures must be collected before the transaction expires (default ~60s, up to 24h via `--expiration`). -Signing endorses the transaction with your key: the command shows no preview or confirmation, reads the master password from `--password-stdin`, and signs directly — to inspect a transaction without signing it, use [`tx approvals`](approvals.md). It does **not** broadcast, and has **no `--permission-id`** (the group is fixed in the transaction body; it's shown on the `Permission` line). Watch-only accounts fail with `watch_only_no_signer`. +Signing endorses the transaction with your key: software accounts read the master password from `--password-stdin` and sign without a CLI preview, while Ledger accounts do not read a master password and confirm on device. To inspect a transaction without signing it, use [`tx approvals`](approvals.md). It does **not** broadcast, and has **no `--permission-id`** (the group is fixed in the transaction body; it's shown on the `Permission` line). Watch-only accounts fail with `watch_only_no_signer`. ### What is verified before signing @@ -44,7 +44,7 @@ Four contract types cannot be re-encoded by the bundled decoder — `UnfreezeAss | `--offline` | Sign locally without contacting a node; skips the signer-permission and approval-weight checks. Only meaningful on TRON — EVM signing contacts no node either way | | `--out ` | Write the resulting hex to a file (mode 0644, written atomically) instead of stdout | -Plus the [global options](../index.md#global-options-every-command) and `--password-stdin`. +Plus the [global options](../index.md#global-options-every-command) and `--password-stdin` for software accounts. The transaction is passed on argv, not stdin: it is not a secret, and this leaves fd 0 free for `--password-stdin`. @@ -58,7 +58,7 @@ An initiator first produces a partially signed `tx.hex` with `tx send --sign-onl echo "$PW" | wallet-cli tx send --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --amount 1000 --sign-only --permission-id 2 --expiration 86400000 --network tron:nile --password-stdin > tx.hex ``` -A second signer appends their signature — no preview, no confirmation; the receipt carries the transaction content and progress blocks: +A second software signer appends their signature; the receipt carries the transaction content and progress blocks: ```bash echo "$PW" | wallet-cli tx sign --file tx.hex --account cosigner --out tx.signed.hex --network tron:nile --password-stdin diff --git a/ts/docs/guide/stake-and-resources.md b/ts/docs/guide/stake-and-resources.md index 88b6c1f74..178226a43 100644 --- a/ts/docs/guide/stake-and-resources.md +++ b/ts/docs/guide/stake-and-resources.md @@ -33,7 +33,7 @@ Plain TRX transfers consume **bandwidth**; smart-contract calls (TRC20 transfers wallet-cli stake freeze --amount-sun 100000000 --resource energy --network tron:nile ``` -`--resource` chooses which resource the stake produces. It defaults to `bandwidth`; stake for `energy` when you plan to send TRC20 tokens or call contracts, since those spend energy (as in step 1). The TRX stays yours — it is locked, not spent — and staking also grants TRON Power (governance votes). Like every state-changing command, `stake freeze` supports `--dry-run`, `--sign-only`, `--build-only`, `--wait`, and returns at submission by default. +`--resource` chooses which resource the stake produces. It defaults to `bandwidth`; stake for `energy` when you plan to send TRC20 tokens or call contracts, since those spend energy (as in step 1). The TRX stays yours — it is locked, not spent — and staking also grants TRON Power (governance votes). Like the other stake write commands, `stake freeze` supports `--dry-run`, `--sign-only`, `--build-only`, `--wait`, and returns at submission by default. Verify the effect by running `account info` again — the `Energy` limit now reflects the TRX you staked: diff --git a/ts/docs/machine-interface.md b/ts/docs/machine-interface.md index 3875aa443..8c8f13a63 100644 --- a/ts/docs/machine-interface.md +++ b/ts/docs/machine-interface.md @@ -158,13 +158,13 @@ Text mode titles the same window (`Assets (limit 50, offset 0)`, `Proposals (sho The **exit code is the hard contract**: `2` means the call was malformed (it will still be wrong on retry), `1` means execution failed (network / device / chain / wallet). `error.code` is a machine-readable string that refines the exit code — branch on the exit code first, then optionally on `error.code`. -**The complete code index is published**, one line per code, under `errorCodes` in the discovery catalog: +**The maintained code index is published**, one line per code, under `errorCodes` in the discovery catalog: ```bash wallet-cli --json-schema | jq '.errorCodes' ``` -That index is the authority and is enforced by the build: a code cannot be raised without an entry, and an entry cannot outlive its code. The tables below are the frequently-hit subset, kept for reading. New codes may still be added within v1, and a few strings (e.g. `invalid_value`, `aborted`) can appear under either exit code depending on where they are raised — so always tolerate an unknown code by falling back to its exit-code class. +That index is the machine-readable catalog exposed by this build. Treat it as a discovery aid, not a closed enum: a few code paths choose among error-code strings dynamically, so a runtime envelope can still carry a code not present in `errorCodes`. The tables below are the frequently-hit subset, kept for reading. New codes may still be added within v1, and a few strings (e.g. `invalid_value`, `aborted`) can appear under either exit code depending on where they are raised — so always tolerate an unknown code by falling back to its exit-code class. Common codes at exit **2** (usage — fix the call): @@ -176,7 +176,7 @@ Common codes at exit **2** (usage — fix the call): | `invalid_option` | A flag was used in an invalid combination, or is scoped to the other chain family | | `invalid_value` | A flag value failed validation (e.g. `config defaultOutput xml`) | | `invalid_amount` | An amount is malformed or out of range | -| `invalid_secret` | A supplied mnemonic / private key is malformed | +| `invalid_mnemonic` / `invalid_private_key` | A supplied mnemonic or private key is malformed | | `weak_password` | Master password below policy (≥8 chars; upper + lower + digit + special) | | `tty_required` | An interactive prompt is needed but no TTY is attached — pass the matching `*-stdin` flag | | `missing_network` / `unsupported_network` | `--network` absent, or not a known canonical id or alias | @@ -220,7 +220,7 @@ Common codes at exit **1** (execution — runtime failure): | `ledger_unsupported` | The Ledger TRON app cannot sign this contract type — refused before the device is touched (`asset` writes, `witness` writes) | | `not_a_witness` / `already_witness` / `not_proposal_owner` | Governance identity does not meet the operation's rule | | `already_approved` / `not_approved` / `proposal_expired` / `already_canceled` | Proposal voting conditions | -| `account_not_active` / `chain_parameter_unavailable` | `witness create`: the account is not activated on chain, or the node did not return `getAccountUpgradeCost` | +| `account_not_active` / `account_already_active` / `name_already_set` / `id_already_set` / `chain_parameter_unavailable` | Account activation/name/id conditions, or `witness create` could not read `getAccountUpgradeCost` | | `not_contract_deployer` | The account did not deploy that contract | | `already_issued_asset` / `not_an_issuer` | The account has already issued a TRC10, or has never issued one | | `not_in_ico_window` / `self_participation` | TRC10 ICO participation conditions | @@ -232,7 +232,7 @@ Common codes at exit **1** (execution — runtime failure): | `account_exists` / `wrong_keystore_password` | `import keystore`: the address is already in the wallet, or the file's own password is wrong (distinct from `auth_failed`, which is the master password). A file whose `mac` is missing or not hex is `invalid_keystore`, not a wrong password — hex case is not significant | | `internal_error` | Unexpected internal failure; message is intentionally generic | -Unexpected exceptions are **redacted** to `internal_error` with a generic message, so a library error that happens to echo secret material can never reach the envelope. The two tables above are a reading aid; `--json-schema`'s `errorCodes` is the complete index. +Unexpected exceptions are **redacted** to `internal_error` with a generic message, so a library error that happens to echo secret material can never reach the envelope. The two tables above are a reading aid; `--json-schema`'s `errorCodes` is the maintained discovery index, not a parser exhaustiveness guarantee. ### `error.details.matches` From 6b30d87803133572428ca45b9578d2e4fd14c3c9 Mon Sep 17 00:00:00 2001 From: "Leon.Zhang" Date: Sun, 30 Aug 2026 22:30:44 +0800 Subject: [PATCH 4/8] docs: align behavior docs with implementation --- ts/docs/commands/contract/deploy.md | 2 +- ts/docs/commands/tx/broadcast.md | 10 ++++++---- ts/docs/guide/scripting.md | 2 +- ts/docs/machine-interface.md | 2 +- ts/docs/troubleshooting.md | 6 +++--- ts/src/domain/errors/codes.ts | 2 +- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ts/docs/commands/contract/deploy.md b/ts/docs/commands/contract/deploy.md index 65bd48418..b07f8e6e6 100644 --- a/ts/docs/commands/contract/deploy.md +++ b/ts/docs/commands/contract/deploy.md @@ -32,7 +32,7 @@ An artifact is read for `.bytecode.object`, `.bytecode`, or `.evm.bytecode.objec **TRON needs an ABI.** Pass `--artifact` or `--abi`; passing both is an error. `--abi` is TRON-only, and the ABI's `constructor` entry needs a string `stateMutability` (`"nonpayable"` / `"payable"`) — `solc` emits it, but a hand-trimmed ABI or one from `solc` older than 0.5 may not. EVM deploys need no ABI when the types come from `--constructor-signature` or the arguments are self-describing. -Same execution model as other broadcast commands: `--dry-run` previews, `--sign-only` outputs a signed transaction for [`tx broadcast`](../tx/broadcast.md), `--build-only` an unsigned one, default returns at submission, `--wait` blocks until confirmed/failed. +Execution modes match the transaction-building write commands: `--dry-run` previews, `--sign-only` outputs a signed transaction for [`tx broadcast`](../tx/broadcast.md), `--build-only` an unsigned one, default returns at submission, `--wait` blocks until confirmed/failed. Fee flags follow the family — `--fee-limit` (TRON, default `100000000` SUN) or `--gas-limit` / `--max-fee` / `--priority-fee` / `--nonce` (EVM). Help tags each set, and using one on the other family is refused with `invalid_option`. diff --git a/ts/docs/commands/tx/broadcast.md b/ts/docs/commands/tx/broadcast.md index f386d1caa..67abcb01b 100644 --- a/ts/docs/commands/tx/broadcast.md +++ b/ts/docs/commands/tx/broadcast.md @@ -13,7 +13,7 @@ wallet-cli tx broadcast (--hex | --file | --transaction | -- Submits a transaction that was signed elsewhere, on TRON or EVM networks alike. No wallet unlock is needed; the transaction is already signed. The signed input can be **hex** — `--hex` inline or `--file` from a file (the format emitted by `--sign-only` and `tx sign`; protobuf on TRON, RLP `0x02…` on EVM) — or **JSON** — `--transaction` inline or `--tx-stdin` from stdin, both **TRON only**. Exactly one of the four; prefer `--file` for long hex. -A presigned transaction carries no network of its own, so pass `--network` to say which network to broadcast to (falls back to the config default network when omitted). +TRON signed transactions do not carry a network id. EVM signed transactions do carry an EIP-155 chain id, but `--network` still selects the endpoint and the CLI rejects the transaction if that chain id does not match. When omitted, `--network` falls back to the config default. ### Validation before submission @@ -78,7 +78,7 @@ wallet-cli tx broadcast --hex 0a02...9f31 --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"tx.broadcast","data":{"kind":"broadcast","stage":"submitted","txId":"72a315303323125708f426c77b94c5215afd8964ed27d67e49c29b56e29078f5"},"meta":{"durationMs":926,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"tx.broadcast","data":{"kind":"broadcast","stage":"submitted","txId":"72a315303323125708f426c77b94c5215afd8964ed27d67e49c29b56e29078f5","transaction":{"txId":"72a315303323125708f426c77b94c5215afd8964ed27d67e49c29b56e29078f5","contractType":"TransferContract","operation":"Transfer TRX","from":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ","to":"TVjsyZ7fYF3qLF6BQgPmTEZy1xrNNyVAAA","rawAmount":"1000000","permission":{"id":0,"name":"owner","threshold":1},"currentWeight":1,"missingWeight":0,"thresholdReached":true,"approved":[{"address":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ","weight":1}],"expiration":1784388720000,"expired":false,"signatures":1},"multiSignFeeSun":0},"meta":{"durationMs":926,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -87,8 +87,10 @@ wallet-cli tx broadcast --hex 0a02...9f31 --network tron:nile -o json | Stage | Fields | |---|---| -| default (submit) | `kind`, `stage: "submitted"`, `txId` | -| `--wait` (confirmed/failed) | above, plus `confirmed`, `blockNumber`, `failed`, and result fields — `netUsed` / `feeSun` on TRON, `gasUsed` / `feeWei` / `effectiveGasPriceWei` on EVM | +| default (submit, TRON) | `kind`, `stage: "submitted"`, `txId`, `transaction` (approval view), `multiSignFeeSun` | +| default (submit, EVM) | `kind`, `stage: "submitted"`, `txId`, and `alreadyKnown: true` when the node had already seen the transaction | +| `--wait` (confirmed/failed) | submit fields, plus `confirmed`, `blockNumber`, `failed`, and result fields — `netUsed` / `feeSun` on TRON, `gasUsed` / `feeWei` / `effectiveGasPriceWei` on EVM | +| `--dry-run` (TRON) | `kind`, `mode: "dry-run"`, `transaction` (approval view), `multiSignFeeSun` | | `--dry-run` (EVM) | `kind`, `mode: "dry-run"`, `txId`, `hash`, `address` (recovered signer), `to`, `rawAmount`, `fee` (`feeModel`, `maxCostWei`, `gasLimit`, `maxPerGasWei`), `tx`, and `checks[]` (`name`, `status` — `ok` / `warning` / `skipped` — and `detail`) | On EVM a node that already knows the transaction sets `alreadyKnown: true` on the submitted receipt rather than failing. diff --git a/ts/docs/guide/scripting.md b/ts/docs/guide/scripting.md index 7c2cc80fd..c2e5035f5 100644 --- a/ts/docs/guide/scripting.md +++ b/ts/docs/guide/scripting.md @@ -4,7 +4,7 @@ How to call wallet-cli from shell scripts and CI. This is the gentle version; th ## Discovering the surface -Before hard-coding anything, ask the CLI what it supports. One call returns every command, its flags as JSON Schema, which chain families it serves, and the complete error-code index: +Before hard-coding anything, ask the CLI what it supports. One call returns every command, its flags as JSON Schema, which chain families it serves, and the maintained error-code discovery index: ```bash wallet-cli --json-schema | jq '.commands[] | select(.id == "tx.send") | {families, examples}' diff --git a/ts/docs/machine-interface.md b/ts/docs/machine-interface.md index 8c8f13a63..d554026c5 100644 --- a/ts/docs/machine-interface.md +++ b/ts/docs/machine-interface.md @@ -200,7 +200,7 @@ Common codes at exit **1** (execution — runtime failure): | `rpc_error` | The node rejected or failed the request — a TRON API call, or a JSON-RPC method such as `eth_estimateGas` | | `invalid_node_response` | The node's answer contradicts the request or the protocol: a TRC10/exchange record whose id is not the one asked for, a `precision` outside 0..6, or a rate pair that is not a positive int32. These decide signed amounts, so the command stops rather than acting on them. List reads drop the offending record and keep the page | | `timeout` | Aborted waiting for network or device (`--timeout` exceeded) | -| `auth_required` | Master password required but not supplied | +| `auth_required` | Required credential was unavailable — a software master password, or Ledger app/device readiness | | `auth_failed` | Wrong master password (decryption failed) | | `signing_rejected` / `transaction_rejected` | Signing or broadcast rejected (device or chain) | | `watch_only_no_signer` | The account is watch-only and cannot sign | diff --git a/ts/docs/troubleshooting.md b/ts/docs/troubleshooting.md index f4b6f2210..913c4a7cc 100644 --- a/ts/docs/troubleshooting.md +++ b/ts/docs/troubleshooting.md @@ -1,6 +1,6 @@ # Troubleshooting -Remedies for humans, keyed by the [error codes](machine-interface.md#error-codes) defined in the machine interface (the single authority on what each code *is* — this page only covers what to *do*). For a code not covered here, the complete one-line index is `wallet-cli --json-schema | jq '.errorCodes'`. +Remedies for humans, keyed by the [error codes](machine-interface.md#error-codes) defined in the machine interface (the single authority on what each code *is* — this page only covers what to *do*). For a code not covered here, the maintained discovery index is `wallet-cli --json-schema | jq '.errorCodes'`; still fall back to the exit-code class if a runtime envelope carries a code outside that catalog. ## `usage_error` / `invalid_value` (exit 2) @@ -40,10 +40,10 @@ A nonce that is *ahead* of the account's next one is not an error: it is a `meta ## `tty_required` / `auth_required` (exit 2 / exit 1) -A secret was needed but none could be read. +A credential, secret, or signing-device approval was needed but none was available. - `tty_required` — no terminal is attached (CI, pipes). For commands with a stdin path, provide the matching `*-stdin` flag (`--password-stdin`, `--tx-stdin`). `import mnemonic`, `import private-key`, and `change-password` are interactive-only — they must run in a real TTY; there is no non-interactive alternative. -- `auth_required` — the command needs the master password; pass `--password-stdin` or run it interactively. +- `auth_required` — software signing needs the master password, or Ledger signing needs the right app/device state. For software accounts, pass `--password-stdin` or run interactively; for Ledger, unlock the device and open the TRON or Ethereum app that matches the account family. - `auth_failed` — the password was wrong (decryption failed); re-enter it. ## `timeout` (exit 1) diff --git a/ts/src/domain/errors/codes.ts b/ts/src/domain/errors/codes.ts index 57d5338f3..99695c385 100644 --- a/ts/src/domain/errors/codes.ts +++ b/ts/src/domain/errors/codes.ts @@ -37,7 +37,7 @@ export const ERROR_CODES = { watch_only_no_signer: "the selected account can be watched but cannot sign", // ── secrets, keystore, local files ──────────────────────────────────────── - auth_required: "the master password is needed and was not available", + auth_required: "a required credential or Ledger app/device approval was not available", auth_failed: "the master password was wrong", weak_password: "the proposed master password does not meet the strength rule", wrong_keystore_password: "the keystore file's own password was wrong", From 19716cd097df20fa024d88d9919067c500f5b758 Mon Sep 17 00:00:00 2001 From: "Leon.Zhang" Date: Mon, 31 Aug 2026 17:37:50 +0800 Subject: [PATCH 5/8] docs: correct remaining implementation mismatches --- README.md | 15 +++++++------ java/README.md | 13 ++++++++--- java/docs/guide/command-flow.md | 2 +- java/docs/guide/getting-started.md | 15 +++++++++++-- java/docs/guide/index.md | 4 ++-- ts/README.md | 22 +++++++++++-------- ts/docs/commands/account/activate.md | 4 +++- ts/docs/commands/account/set.md | 4 +++- ts/docs/commands/address/generate.md | 2 +- ts/docs/commands/asset/issue.md | 4 +++- ts/docs/commands/asset/participate.md | 4 +++- ts/docs/commands/asset/unfreeze.md | 4 +++- ts/docs/commands/asset/update.md | 4 +++- ts/docs/commands/backup.md | 4 +--- ts/docs/commands/chain/params.md | 2 +- ts/docs/commands/change-password.md | 6 ++--- ts/docs/commands/config.md | 2 +- ts/docs/commands/contact/add.md | 2 +- ts/docs/commands/contact/remove.md | 2 +- ts/docs/commands/contract/clear-abi.md | 4 +++- ts/docs/commands/contract/deploy.md | 4 +++- .../contract/set-origin-energy-limit.md | 4 +++- .../contract/set-user-resource-percent.md | 4 +++- ts/docs/commands/create.md | 2 +- ts/docs/commands/derive.md | 4 ++-- ts/docs/commands/exchange/index.md | 2 +- ts/docs/commands/gasfree/info.md | 2 +- ts/docs/commands/gasfree/trace.md | 2 +- ts/docs/commands/gasfree/transfer.md | 16 +++++++------- ts/docs/commands/import/keystore.md | 2 +- ts/docs/commands/import/mnemonic.md | 2 +- ts/docs/commands/import/private-key.md | 2 +- ts/docs/commands/permission/show.md | 2 +- ts/docs/commands/permission/update.md | 2 +- ts/docs/commands/rename.md | 2 +- ts/docs/commands/stake/cancel-unfreeze.md | 4 +++- ts/docs/commands/token/add.md | 2 +- ts/docs/commands/token/remove.md | 2 +- ts/docs/commands/tx/multisig.md | 2 +- ts/docs/commands/use.md | 2 +- ts/docs/commands/vote/cast.md | 2 +- ts/docs/commands/vote/list.md | 14 ++++++------ ts/docs/commands/vote/status.md | 12 +++++----- ts/docs/commands/witness/create.md | 4 +++- ts/docs/commands/witness/set-brokerage.md | 4 +++- ts/docs/commands/witness/update.md | 4 +++- ts/docs/concepts/accounts-and-hd.md | 2 +- ts/docs/concepts/networks.md | 4 +++- ts/docs/machine-interface.md | 21 +++++++++++------- 49 files changed, 153 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index b992cb920..363833d29 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

wallet-cli

@@ -14,10 +14,10 @@ This repository holds **two independent implementations** that share the same purpose but target different users: -- **[Java](java/README.md)** — the original, full-featured reference CLI. An interactive prompt (REPL) you drive by hand. +- **[Java](java/README.md)** — the original, full-featured reference CLI. Run one-shot standard commands or start the interactive prompt (REPL). - **[TypeScript](ts/README.md)** — an agent-first rewrite for automation. Standard subcommands with a stable JSON envelope, built for scripts, CI, and AI agents. -Both manage the same kind of wallet on the same networks — your address is identical regardless of which you use. They cover the same TRON feature surface and differ in how you install and drive them. Pick one and read its own README for depth; this page gives you the basics of each so you can choose. +Both manage TRON wallets, but they are independent implementations rather than interchangeable account stores. Do not assume every derived account has the same address across implementations: check the recorded BIP44 path when migrating. The TypeScript implementation additionally supports selected EVM networks. ## At a glance @@ -26,10 +26,10 @@ Both manage the same kind of wallet on the same networks — your address is ide | **What it is** | The mature, full-feature reference CLI. | A newer rewrite focused on programmatic integration. | | **Runtime** | JVM — built with Gradle, run as a `.jar`. Uses the [Trident](https://github.com/tronprotocol/trident) SDK. | [Node.js](https://nodejs.org) **20+**. | | **Install** | `git clone` + `./gradlew build` (see [Setup](java/README.md#setup)) | `npm install -g @tron-walletcli/wallet-cli` | -| **How you drive it** | An **interactive prompt only** — start it, then type commands at `>`. | **One-shot subcommands** — `wallet-cli ` from your shell. Interactive prompts only for secret input. | +| **How you drive it** | One-shot standard commands, or an interactive prompt when run without a command / with `--interactive`. | **One-shot subcommands** — `wallet-cli ` from your shell. Interactive prompts only for secret input. | | **Command style** | PascalCase verbs: `RegisterWallet`, `SendCoin`, `GetBalance`. Amounts in **SUN** (1 TRX = 1,000,000 SUN). | Noun-verb subcommands: `create`, `tx send`, `account balance`, with `--flags`. | -| **Output for scripts** | Human-readable text. | Stable JSON via `-o json` ([`wallet-cli.result.v1`](ts/docs/machine-interface.md)) + fixed exit codes (`0`/`1`/`2`). | -| **Config / networks** | `config.conf` (net type + full node), or `SwitchNetwork` at runtime. Mainnet · Nile · Shasta · custom. | `--network` flag / `config` command. `tron:mainnet` · `tron:nile` · `tron:shasta`. | +| **Output for scripts** | Text by default; standard mode supports `--output json` and structured success/error envelopes. | Stable JSON via `-o json` ([`wallet-cli.result.v1`](ts/docs/machine-interface.md)) + fixed exit codes (`0`/`1`/`2`). | +| **Config / networks** | `config.conf` (net type + full node), or `SwitchNetwork` at runtime. Mainnet · Nile · Shasta · custom. | `--network` flag / `config` command. Three TRON networks plus Ethereum, Sepolia, BNB Smart Chain, and its testnet. | | **Signing** | Software keystore · Ledger. | Encrypted local keystore · Ledger. Secrets enter via stdin/TTY, never argv or dedicated secret env vars. | | **Feature scope** | **The full surface** — wallets and transfers, staking, voting and rewards, governance, contracts, TRC10, and the on-chain exchange. | **The full surface** — HD wallets, TRX/TRC20/TRC10 transfers, staking & delegation, voting & rewards, governance proposals & super-representative operation, contract call/deploy/governance, TRC10 issuance, the on-chain Bancor exchange, multi-sig, GasFree transfers, message signing, and on-chain queries. | | **Best for** | People at a terminal who want every TRON capability. | Scripting, CI pipelines, and AI agents. | @@ -37,11 +37,12 @@ Both manage the same kind of wallet on the same networks — your address is ide ## Java — get a taste -Interactive only. Build it, start the prompt, then type commands: +Build it, then either run a standard command or start the prompt: ```console $ git clone https://github.com/tronprotocol/wallet-cli.git $ cd wallet-cli && ./gradlew build && cd build/libs +$ java -jar wallet-cli.jar --output json --network nile get-balance --address T... $ java -jar wallet-cli.jar # opens the interactive prompt > RegisterWallet 123456 # create a keystore (password 123456) > Login # unlock it diff --git a/java/README.md b/java/README.md index 46462fe76..72ec9f94f 100644 --- a/java/README.md +++ b/java/README.md @@ -1,6 +1,6 @@ # wallet-cli — Java implementation -The original, full-featured implementation of wallet-cli: an interactive prompt (REPL) covering the complete TRON feature surface — managing accounts and keystores, TRX / TRC10 / TRC20 transfers, staking resources, voting for super representatives, deploying and calling smart contracts, Ledger hardware signing, and [GasFree](https://gasfree.io) gas-less transfers. All gRPC calls run on the [Trident SDK](https://github.com/tronprotocol/trident). +The original, full-featured implementation of wallet-cli. It supports both one-shot standard commands for scripts and an interactive prompt (REPL), covering the complete TRON feature surface — managing accounts and keystores, TRX / TRC10 / TRC20 transfers, staking resources, voting for super representatives, deploying and calling smart contracts, Ledger hardware signing, and [GasFree](https://gasfree.io) gas-less transfers. All gRPC calls run on the [Trident SDK](https://github.com/tronprotocol/trident). > For what wallet-cli is and how this compares to the scriptable, JSON-first [TypeScript implementation](../ts/README.md), see the [repository overview](../README.md). @@ -43,14 +43,21 @@ You can also switch networks at runtime with the [`SwitchNetwork`](docs/commands $ cd wallet-cli $ ./gradlew build $ cd build/libs - $ java -jar wallet-cli.jar + $ java -jar wallet-cli.jar --help ``` +With no command, wallet-cli opens the legacy interactive prompt. With a command, it uses the standard CLI; `--interactive` selects the prompt explicitly. Standard mode accepts global options such as `--network `, `--wallet`, `--grpc-endpoint`, and `--output `: + +```console +$ java -jar wallet-cli.jar --output json --network nile get-balance --address T... +$ java -jar wallet-cli.jar --interactive +``` + wallet-cli connects to java-tron via the gRPC protocol, which can be deployed locally or remotely. Configure the java-tron node IP and port in `src/main/resources/config.conf`, or use `SwitchNetwork` to switch among mainnet, testnets (Nile and Shasta), and custom networks. ## Quickstart -Build, create an account, and send your first transfer — all from the interactive prompt: +This quickstart uses the interactive prompt. For automation, pass a standard command to the jar and add `--output json`; see the [standard CLI contract](docs/standard-cli-contract-spec.md). ```console # 1. Build diff --git a/java/docs/guide/command-flow.md b/java/docs/guide/command-flow.md index 8fa03a444..8fbbf2ae3 100644 --- a/java/docs/guide/command-flow.md +++ b/java/docs/guide/command-flow.md @@ -1,6 +1,6 @@ # Command-line operation flow -A worked end-to-end example of an interactive session: build and run, register, back up, inspect, issue an asset, and transfer it. +A worked end-to-end example of the legacy interactive session: build and run, register, back up, inspect, issue an asset, and transfer it. For one-shot commands and JSON output, see [Getting started](getting-started.md#standard-cli). ```console $ cd wallet-cli diff --git a/java/docs/guide/getting-started.md b/java/docs/guide/getting-started.md index c1866ed0e..4448b4f1d 100644 --- a/java/docs/guide/getting-started.md +++ b/java/docs/guide/getting-started.md @@ -1,10 +1,10 @@ # Getting started -The first-run flow: build, create an account, unlock it, inspect it, and send your first TRX — all from the interactive prompt. +wallet-cli has two entry modes: a standard one-shot CLI for scripts and a legacy interactive prompt. The first-run flow below uses the prompt because it keeps account creation, unlock, inspection, and transfer in one session. ## Quickstart -Build, create an account, and send your first transfer — all from the interactive prompt: +Build, create an account, and send your first transfer from the interactive prompt: ```console # 1. Build @@ -26,6 +26,17 @@ $ java -jar wallet-cli.jar > On mainnet these commands move **real funds**. While learning, switch to a testnet with `SwitchNetwork` (Nile or Shasta) and top up from that network's faucet. +## Standard CLI + +Passing a command selects the standard CLI instead of the prompt. It supports text or JSON output and global network, wallet, and endpoint overrides: + +```console +$ java -jar wallet-cli.jar --output json --network nile get-balance --address T... +$ java -jar wallet-cli.jar --network nile send-coin --to T... --amount 1000000 --password-stdin +``` + +Run `java -jar wallet-cli.jar --help` for the command catalog and ` --help` for command options. The parsing, authentication, JSON envelope, and exit behavior are defined in the [standard CLI contract](../standard-cli-contract-spec.md). + ## How to create account You can create accounts by transferring funds to non-existing accounts, or by initiating a transaction to create an account using the **CreateAccount** command. Transferring to a non-existent account has a minimum restriction amount of **1 TRX**. Creating an account through the `CreateAccount` command still burns **1 TRX**. diff --git a/java/docs/guide/index.md b/java/docs/guide/index.md index bc3bfd7f4..77c6ff100 100644 --- a/java/docs/guide/index.md +++ b/java/docs/guide/index.md @@ -4,7 +4,7 @@ Task-oriented walkthroughs for wallet-cli (Java). | Guide | What it covers | |---|---| -| [Getting started](getting-started.md) | Build, create an account, unlock, and send your first TRX | -| [Command-line operation flow](command-flow.md) | A worked end-to-end interactive session | +| [Getting started](getting-started.md) | Standard CLI and interactive entry modes; build, create an account, and send TRX | +| [Command-line operation flow](command-flow.md) | A worked end-to-end legacy interactive session | For per-command reference, see the [command index](../commands/index.md); for TRON mechanics, see [concepts](../concepts/index.md). diff --git a/ts/README.md b/ts/README.md index ebf8e1a18..2e3b16948 100644 --- a/ts/README.md +++ b/ts/README.md @@ -27,19 +27,23 @@ The agent-first implementation of wallet-cli, built for automation: every comman ## Supported chains -Three TRON networks are supported today. Networks are identified by a canonical `family:chain` id (all `tron` today): +Seven built-in networks are supported. Networks use a canonical `family:chain` id: -| Network id | What it is | TRX value | -|---|---|---| -| `tron:mainnet` | Production mainnet | **Real funds** | -| `tron:nile` | Primary testnet (faucet at nileex.io) | None — use freely | -| `tron:shasta` | Alternate testnet | None | +| Network id | Family | Native coin | Environment | +|---|---|---|---| +| `tron:mainnet` | TRON | TRX | Mainnet — **real funds** | +| `tron:nile` | TRON | TRX | Testnet | +| `tron:shasta` | TRON | TRX | Testnet | +| `evm:1` | EVM | ETH | Ethereum mainnet — **real funds** | +| `evm:11155111` | EVM | ETH | Sepolia testnet | +| `evm:56` | EVM | BNB | BNB Smart Chain mainnet — **real funds** | +| `evm:97` | EVM | BNB | BNB Smart Chain testnet | -Your address is the same on every network, but balances, tokens, and transactions are isolated per network. Fees use TRON's `tron-resource` model (bandwidth + energy) rather than EVM gas — see [networks](docs/concepts/networks.md) and [energy & bandwidth](docs/concepts/energy-bandwidth.md). +One seed produces a TRON address and a different EVM address. Each address is reused within its family, while balances, tokens, and transactions remain isolated per network. TRON uses the `tron-resource` fee model (bandwidth + energy); EVM networks use gas. See [networks](docs/concepts/networks.md) and [energy & bandwidth](docs/concepts/energy-bandwidth.md). ## Install -**Prerequisites**: [Node.js](https://nodejs.org) **20 or later** (`node --version` to check). Ledger signing additionally needs a supported Ledger device with the TRON app installed — see the [Ledger guide](docs/guide/ledger.md). +**Prerequisites**: [Node.js](https://nodejs.org) **20 or later** (`node --version` to check). Ledger signing additionally needs a supported Ledger device with the app for the selected family installed — TRON for TRON accounts, Ethereum for EVM accounts. See the [Ledger guide](docs/guide/ledger.md). ```bash npm install -g @tron-walletcli/wallet-cli @@ -180,7 +184,7 @@ Every command supports `-o json` and then prints **exactly one** terminal JSON f TRON differs a lot from EVM chains in fees, accounts, and key permissions — these are worth understanding up front to avoid surprises: -- [Networks](docs/concepts/networks.md) — the three networks and the `family:chain` id +- [Networks](docs/concepts/networks.md) — built-in TRON/EVM networks and the `family:chain` id - [Accounts & HD](docs/concepts/accounts-and-hd.md) — mnemonics, derivation paths, account activation - [Energy & bandwidth](docs/concepts/energy-bandwidth.md) — TRON's resource-based fee model (in place of EVM gas) - [Security](docs/concepts/security.md) — keystore encryption, secret handling, multi-sig permissions diff --git a/ts/docs/commands/account/activate.md b/ts/docs/commands/account/activate.md index f9d74e980..0810db993 100644 --- a/ts/docs/commands/account/activate.md +++ b/ts/docs/commands/account/activate.md @@ -18,6 +18,8 @@ Use it only when an address needs to *exist* on its own — to be queryable, or Requires the payer account. The master password via `--password-stdin` is needed only when the selected mode signs — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign `AccountCreateContract`: Ledger accounts may use `--dry-run` or `--build-only`, but `--sign-only`, default submission, and `--wait` fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -88,7 +90,7 @@ echo "$PW" | wallet-cli account activate --address TNewAddr9k2fP7cW4bXm1sV8dRj6e ## Exit status -`0` submitted (or built/signed/dry-run in early-exit modes) · `1` execution failure (`account_already_active`, `watch_only_no_signer`, `auth_failed`, `insufficient_balance`, `rpc_error`, `timeout`) · `2` usage error (`invalid_value` — malformed address). +`0` submitted (or built/signed/dry-run in early-exit modes) · `1` execution failure (`account_already_active`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`, `insufficient_balance`, `rpc_error`, `timeout`) · `2` usage error (`invalid_value` — malformed address). After a **confirmed** transaction the command reads the account back to verify the change took effect. That follow-up never turns an already-paid transaction into a command failure: a mismatch or an unreadable read is reported as a `meta.warnings` entry (`account_activate_postcheck_mismatch` / `account_activate_postcheck_unavailable`) with `success` still `true` and exit `0`. diff --git a/ts/docs/commands/account/set.md b/ts/docs/commands/account/set.md index 524606f90..d10ee2b4c 100644 --- a/ts/docs/commands/account/set.md +++ b/ts/docs/commands/account/set.md @@ -18,6 +18,8 @@ Sets the account's on-chain **name** (a display alias, up to 32 bytes) or its ** Requires the account. The master password via `--password-stdin` is needed only when the selected mode signs — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. The account id's uniqueness is enforced on-chain — a taken id fails with `id_taken`. +Ledger support differs by field: the TRON app can sign `--name`, but cannot sign `--id` (`SetAccountIdContract`). A Ledger account may still build or dry-run either field; a signing mode with `--id` fails with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -90,7 +92,7 @@ echo "$PW" | wallet-cli account set --id acme-treasury-01 --network tron:nile -- ## Exit status -`0` submitted (or built/signed/dry-run in early-exit modes) · `1` execution failure (`name_already_set`, `id_already_set`, `id_taken`, `watch_only_no_signer`, `auth_failed`, `rpc_error`, `timeout`) · `2` usage error (`invalid_value`, `invalid_option` — malformed or missing name/id). +`0` submitted (or built/signed/dry-run in early-exit modes) · `1` execution failure (`name_already_set`, `id_already_set`, `id_taken`, `watch_only_no_signer`, `ledger_unsupported` — Ledger signing with `--id`, `auth_failed`, `rpc_error`, `timeout`) · `2` usage error (`invalid_value`, `invalid_option` — malformed or missing name/id). After a **confirmed** transaction the command reads the account back to verify the change took effect. That follow-up never turns an already-paid transaction into a command failure: a mismatch or an unreadable read is reported as a `meta.warnings` entry (`account_set_postcheck_mismatch` / `account_set_postcheck_unavailable`) with `success` still `true` and exit `0`. diff --git a/ts/docs/commands/address/generate.md b/ts/docs/commands/address/generate.md index 1c447b25b..62c925a63 100644 --- a/ts/docs/commands/address/generate.md +++ b/ts/docs/commands/address/generate.md @@ -60,7 +60,7 @@ wallet-cli address generate -o json ## Exit status -`0` success · `1` execution failure (`io_error`, `output_exists` — the `--out` target already exists and is never overwritten, `entropy_failure` — the system CSPRNG was unavailable) · `2` usage error (`invalid_value`). +`0` success · `1` execution failure (`io_error`, `entropy_failure` — the system CSPRNG was unavailable) · `2` usage error (`output_exists` — the `--out` target already exists and is never overwritten; `invalid_value`). ## See also diff --git a/ts/docs/commands/asset/issue.md b/ts/docs/commands/asset/issue.md index 6c1228b1f..55be9419d 100644 --- a/ts/docs/commands/asset/issue.md +++ b/ts/docs/commands/asset/issue.md @@ -30,6 +30,8 @@ Constraints are checked locally before broadcast: `--name` and `--abbr` are 1– **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign TRC10 issuance contract types. Ledger accounts may dry-run or build unsigned hex, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -110,7 +112,7 @@ Definition fields: `name`, `abbr`, `totalSupply` (raw), `precision`, `price` (th ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`already_issued_asset` — this account already issued one, `insufficient_balance` — below the issuance fee, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`missing_option` — a required flag is absent; `invalid_asset_name` — name or abbreviation outside 1–32 visible ASCII; `invalid_value` — rate, precision, dates, bandwidth limits, or frozen tranches out of range, or the rate exceeding int32 after conversion). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`already_issued_asset` — this account already issued one, `insufficient_balance` — below the issuance fee, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — a required flag is absent; `invalid_asset_name` — name or abbreviation outside 1–32 visible ASCII; `invalid_value` — rate, precision, dates, bandwidth limits, or frozen tranches out of range, or the rate exceeding int32 after conversion). ## See also diff --git a/ts/docs/commands/asset/participate.md b/ts/docs/commands/asset/participate.md index 0967c1c5c..7b82ab30e 100644 --- a/ts/docs/commands/asset/participate.md +++ b/ts/docs/commands/asset/participate.md @@ -20,6 +20,8 @@ The acting account cannot be the token's own issuer. **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign TRC10 issuance contract types. Ledger accounts may dry-run or build unsigned hex, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -80,7 +82,7 @@ echo "$PW" | wallet-cli asset participate 1000124 --pay 100 --network tron:nile ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`asset_not_found` — no such token, `not_in_ico_window` — outside the funding window, `self_participation` — you issued this token, `insufficient_balance`, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`missing_option` — no `--pay`; `invalid_amount` — `--pay` is not a decimal number, or has more than 6 decimal places; `invalid_value` — `--pay` ≤ 0, or too small to buy one unit). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`asset_not_found` — no such token, `not_in_ico_window` — outside the funding window, `self_participation` — you issued this token, `insufficient_balance`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — no `--pay`; `invalid_amount` — `--pay` is not a decimal number, or has more than 6 decimal places; `invalid_value` — `--pay` ≤ 0, or too small to buy one unit). ## See also diff --git a/ts/docs/commands/asset/unfreeze.md b/ts/docs/commands/asset/unfreeze.md index a2ee1cff8..25a2cc4eb 100644 --- a/ts/docs/commands/asset/unfreeze.md +++ b/ts/docs/commands/asset/unfreeze.md @@ -22,6 +22,8 @@ This is unrelated to [`stake unfreeze`](../stake/unfreeze.md), which releases st **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign TRC10 issuance contract types. Ledger accounts may dry-run or build unsigned hex, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options This command has no options of its own. @@ -79,7 +81,7 @@ echo "$PW" | wallet-cli asset unfreeze --network tron:nile --wait --password-std ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_an_issuer` — this account has not issued a TRC10, `no_frozen_supply`, `not_yet_unfreezable` — nothing has matured yet, `watch_only_no_signer`, `auth_failed`) · `2` usage error. +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_an_issuer` — this account has not issued a TRC10, `no_frozen_supply`, `not_yet_unfreezable` — nothing has matured yet, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error. ## See also diff --git a/ts/docs/commands/asset/update.md b/ts/docs/commands/asset/update.md index 379451e04..61f3dc9ba 100644 --- a/ts/docs/commands/asset/update.md +++ b/ts/docs/commands/asset/update.md @@ -21,6 +21,8 @@ Pass only the fields you are changing. The others are read from chain and writte **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign TRC10 issuance contract types. Ledger accounts may dry-run or build unsigned hex, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -82,7 +84,7 @@ The four fields are `url`, `description`, `freeAssetNetLimit`, and `publicFreeAs ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_an_issuer` — this account has not issued a TRC10, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`missing_option` — no field given; `invalid_value` — URL or description too long, bandwidth limits out of range). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_an_issuer` — this account has not issued a TRC10, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — no field given; `invalid_value` — URL or description too long, bandwidth limits out of range). ## See also diff --git a/ts/docs/commands/backup.md b/ts/docs/commands/backup.md index 2310fa7fb..bbdc48b70 100644 --- a/ts/docs/commands/backup.md +++ b/ts/docs/commands/backup.md @@ -162,9 +162,7 @@ Both forms are local commands — no `chain` block — and they carry different ## Exit status -`0` success · `1` execution failure (`not_exportable` — watch-only or Ledger, `invalid_value` — no such account, `auth_failed`, `io_error` — path not writable) · `2` usage error (`output_exists` — the target file already exists and is never overwritten; `invalid_value` — a record filter without `--records`, `--keystore` / `--out` with `--records`, or a bad time / limit / offset). - -`invalid_value` appears under both exit codes here: an unresolvable account reference is exit `1`, a malformed call is exit `2`. Branch on the exit code first. +`0` success · `1` execution failure (`account_not_found` — no such account; `not_exportable` — watch-only or Ledger; `auth_failed`; `io_error` — path not writable) · `2` usage error (`output_exists` — the target file already exists and is never overwritten; `invalid_value` — a record filter without `--records`, `--keystore` / `--out` with `--records`, or a bad time / limit / offset). ## See also diff --git a/ts/docs/commands/chain/params.md b/ts/docs/commands/chain/params.md index 7cdf31b07..32db6f256 100644 --- a/ts/docs/commands/chain/params.md +++ b/ts/docs/commands/chain/params.md @@ -80,7 +80,7 @@ wallet-cli chain params --network tron:nile -o json ## Exit status -`0` success · `1` execution failure (`rpc_error`; `not_found` — `--key` doesn't exist) · `2` usage error (`invalid_value`). +`0` success · `1` execution failure (`rpc_error`) · `2` usage error (`not_found` — `--key` doesn't exist; `invalid_value`). ## See also diff --git a/ts/docs/commands/change-password.md b/ts/docs/commands/change-password.md index 320b10e97..dd63cc452 100644 --- a/ts/docs/commands/change-password.md +++ b/ts/docs/commands/change-password.md @@ -17,7 +17,7 @@ The master password decrypts **every software wallet's** keystore, so changing i The flow: 1. **Verify** — enter the current master password; it must decrypt an existing keystore (`auth_failed` otherwise, nothing touched). -2. **Set** — enter the new password twice (mismatch → retry; policy failure → `weak_password`). +2. **Set** — enter the new password twice. A mismatch or policy failure is rejected at the prompt and asks again. 3. **Confirm** — the command lists how many software wallets will be re-encrypted; `[y/N]` (skipped with `--yes`). Declining aborts with no changes. 4. **Re-encrypt atomically** — each keystore: decrypt with old → encrypt with new → write temp file → fsync; only after *all* succeed are files renamed into place. Any failure rolls everything back and reports `io_error` — the old keystores stay valid. @@ -48,11 +48,11 @@ wallet-cli change-password ## Output -This command is interactive: the receipt is printed to the terminal (listing the re-encrypted software wallets, never any secret), and even with `-o json` it produces no structured machine-readable output. Local command — no `chain` block. +This command is interactive. In text mode, the receipt lists the re-encrypted software wallets and never includes secret material. In JSON mode, `data.wallets` contains those wallet labels/ids and `data.count` contains their count. Local command — no `chain` block. ## Exit status -`0` changed · `1` execution failure (`tty_required` — no TTY for interactive input; `auth_failed`; `weak_password`; `no_software_wallet` — nothing to re-encrypt; `io_error` — write failed, rolled back) · `2` usage error. +`0` changed · `1` execution failure (`auth_failed`; `no_software_wallet` — nothing to re-encrypt; `invalid_value` — a referenced encrypted wallet blob is missing; `io_error` — write failed, rolled back) · `2` usage error (`tty_required` — no TTY for interactive input; `invalid_value` — the new password equals the current one; `aborted` — confirmation declined). ## See also diff --git a/ts/docs/commands/config.md b/ts/docs/commands/config.md index cae815413..6ac78bde2 100644 --- a/ts/docs/commands/config.md +++ b/ts/docs/commands/config.md @@ -149,7 +149,7 @@ An unset network field is **absent** from the view rather than present and empty ## Exit status -`0` success · `1` execution failure (`invalid_config` — `config.yaml` is unreadable or not valid YAML; `insecure_config` — it holds service credentials but is a symlink or group/world-readable, so `chmod 600` it) · `2` usage error (`invalid_value` — unknown key, a read-only key given a value, or an unsupported `networks..`). See [machine-interface](../machine-interface.md). +`0` success · `1` execution failure (`io_error` — an atomic config write failed) · `2` usage error (`invalid_config` — `config.yaml` is unreadable or not valid YAML; `insecure_config` — it holds service credentials but is a symlink or group/world-readable, so `chmod 600` it; `invalid_value` — unknown key, a read-only key given a value, or an unsupported `networks..`). See [machine-interface](../machine-interface.md). ## See also diff --git a/ts/docs/commands/contact/add.md b/ts/docs/commands/contact/add.md index 89bc21521..d6bded037 100644 --- a/ts/docs/commands/contact/add.md +++ b/ts/docs/commands/contact/add.md @@ -62,7 +62,7 @@ wallet-cli contact add alice TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --note "Alice ma ## Exit status -`0` success · `1` execution failure (`already_exists` — the name is taken, `limit_exceeded` — the address book is full). The address book is a local file: `encoding_error` if it cannot be decoded, `insecure_permissions` if it is a symlink or group/world-readable (`chmod 600` it). · `2` usage error (`invalid_address` — the address is not valid for its family; `invalid_value` — an invalid name or note). +`0` success · `1` execution failure (`encoding_error` — the local address book cannot be decoded; `insecure_permissions` — it is a symlink or group/world-readable, so run `chmod 600`) · `2` usage error (`already_exists` — the name or address is taken; `limit_exceeded` — the address book is full; `invalid_address` — the address is not valid for a supported family; `invalid_value` — invalid name or note). ## See also diff --git a/ts/docs/commands/contact/remove.md b/ts/docs/commands/contact/remove.md index b970005f1..f5bc26f92 100644 --- a/ts/docs/commands/contact/remove.md +++ b/ts/docs/commands/contact/remove.md @@ -45,7 +45,7 @@ wallet-cli contact remove bob -o json ## Exit status -`0` success · `1` execution failure (`not_found` — no such contact, `encoding_error`, `insecure_permissions`) · `2` usage error. +`0` success · `1` execution failure (`encoding_error`, `insecure_permissions`) · `2` usage error (`contact_not_found` — no contact by that name; `invalid_value`). ## See also diff --git a/ts/docs/commands/contract/clear-abi.md b/ts/docs/commands/contract/clear-abi.md index 1a081f116..fc92f5781 100644 --- a/ts/docs/commands/contract/clear-abi.md +++ b/ts/docs/commands/contract/clear-abi.md @@ -22,6 +22,8 @@ Only the contract's deployer can do this — the address the chain records as th **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot parse this governance contract type. Ledger accounts may dry-run or build, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -74,7 +76,7 @@ echo "$PW" | wallet-cli contract clear-abi TQ5nJ8mV...4wRe --network tron:nile - ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`contract_not_found` — no such contract, `not_contract_deployer`, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`invalid_value` — malformed address). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`contract_not_found` — no such contract, `not_contract_deployer`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`invalid_value` — malformed address). ## See also diff --git a/ts/docs/commands/contract/deploy.md b/ts/docs/commands/contract/deploy.md index b07f8e6e6..bc26cfef9 100644 --- a/ts/docs/commands/contract/deploy.md +++ b/ts/docs/commands/contract/deploy.md @@ -38,6 +38,8 @@ Fee flags follow the family — `--fee-limit` (TRON, default `100000000` SUN) or Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +On TRON, the Ledger app cannot sign `CreateSmartContract`; Ledger accounts may dry-run or build, but signing modes fail with `ledger_unsupported`. This restriction does not apply to EVM deployment through the Ethereum app. + ## Options | Option | Description | @@ -139,7 +141,7 @@ echo "$PW" | wallet-cli contract deploy --artifact ./build/contracts/Token.json ## Exit status -`0` submitted (or built/signed/dry-run in early-exit modes) · `1` execution failure (`watch_only_no_signer`, `auth_failed`, `rpc_error`, `timeout`) · `2` usage error — `file_not_found` (no artifact/bytecode file at that path), or `invalid_value` for: none or more than one of `--artifact` / `--code` / `--code-file`; an artifact that is not JSON, has no creation bytecode, or holds only `"0x"`; `--constructor-args` with no type source; `--constructor-params` or `--constructor-signature` alongside `--artifact`; a TRON deploy with neither `--abi` nor `--artifact`, or with both; `--constructor-signature` on TRON; an ABI constructor without a string `stateMutability`. +`0` submitted (or built/signed/dry-run in early-exit modes) · `1` execution failure (`watch_only_no_signer`, `ledger_unsupported` — TRON signing only, `auth_failed`, `rpc_error`, `timeout`) · `2` usage error — `file_not_found` (no artifact/bytecode file at that path), or `invalid_value` for: none or more than one of `--artifact` / `--code` / `--code-file`; an artifact that is not JSON, has no creation bytecode, or holds only `"0x"`; `--constructor-args` with no type source; `--constructor-params` or `--constructor-signature` alongside `--artifact`; a TRON deploy with neither `--abi` nor `--artifact`, or with both; `--constructor-signature` on TRON; an ABI constructor without a string `stateMutability`. ## See also diff --git a/ts/docs/commands/contract/set-origin-energy-limit.md b/ts/docs/commands/contract/set-origin-energy-limit.md index 1a04f6eb2..0c96fd486 100644 --- a/ts/docs/commands/contract/set-origin-energy-limit.md +++ b/ts/docs/commands/contract/set-origin-energy-limit.md @@ -24,6 +24,8 @@ Only the contract's deployer can do this; the current value is in [`contract inf **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot parse this governance contract type. Ledger accounts may dry-run or build, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -80,7 +82,7 @@ echo "$PW" | wallet-cli contract set-origin-energy-limit TQ5nJ8mV...4wRe 5000000 ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`contract_not_found` — no such contract, `not_contract_deployer`, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`invalid_value` — malformed address, or energy not an integer > 0). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`contract_not_found` — no such contract, `not_contract_deployer`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`invalid_value` — malformed address, or energy not an integer > 0). ## See also diff --git a/ts/docs/commands/contract/set-user-resource-percent.md b/ts/docs/commands/contract/set-user-resource-percent.md index 99a40eb2b..ea5e503d4 100644 --- a/ts/docs/commands/contract/set-user-resource-percent.md +++ b/ts/docs/commands/contract/set-user-resource-percent.md @@ -24,6 +24,8 @@ Only the contract's deployer can do this; the current value is in [`contract inf **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot parse this governance contract type. Ledger accounts may dry-run or build, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -82,7 +84,7 @@ echo "$PW" | wallet-cli contract set-user-resource-percent TQ5nJ8mV...4wRe 100 - ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`contract_not_found` — no such contract, `not_contract_deployer`, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`invalid_value` — malformed address, or percent outside 0–100). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`contract_not_found` — no such contract, `not_contract_deployer`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`invalid_value` — malformed address, or percent outside 0–100). ## See also diff --git a/ts/docs/commands/create.md b/ts/docs/commands/create.md index 61ca9e159..e76d7691c 100644 --- a/ts/docs/commands/create.md +++ b/ts/docs/commands/create.md @@ -70,7 +70,7 @@ printf '%s' "$PW" | wallet-cli create --label main --password-stdin -o json | `index` | number | HD derivation index (0 for the first account) | | `active` | boolean | Whether it became the active account | | `addresses` | object | One address per family the account can produce: `tron` (base58) and `evm` (`0x`, EIP-55 checksummed) | -| `derivationPath` | object | The BIP44 template each address came from: `{"tron":"m/44'/195'/0'/0/","evm":"m/44'/60'/0'/0/"}` | +| `derivationPath` | object | The BIP44 path each address came from: `{"tron":"m/44'/195'/'/0/0","evm":"m/44'/60'/0'/0/"}` | | `seedId` | string | Owning seed wallet id | ## Exit status diff --git a/ts/docs/commands/derive.md b/ts/docs/commands/derive.md index c4644c32a..5e6224452 100644 --- a/ts/docs/commands/derive.md +++ b/ts/docs/commands/derive.md @@ -46,7 +46,7 @@ printf '%s' "$PW" | wallet-cli derive --seed-id wlt_y8cz6xda --password-stdin -o ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"derive","data":{"status":"created","accountId":"wlt_y8cz6xda.1","label":"main-1","type":"seed","index":1,"active":true,"addresses":{"tron":"TWCa1W6BkcXZnRGxeZZw9jh8eNgULDVGzj","evm":"0x2395227A93465175c6D6EAF2B9d37c2cC0BaB60c"},"seedId":"wlt_y8cz6xda","derivationPath":{"tron":"m/44'/195'/0'/0/1","evm":"m/44'/60'/0'/0/1"}},"meta":{"durationMs":1013,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"derive","data":{"status":"created","accountId":"wlt_y8cz6xda.1","label":"main-1","type":"seed","index":1,"active":true,"addresses":{"tron":"TWCa1W6BkcXZnRGxeZZw9jh8eNgULDVGzj","evm":"0x2395227A93465175c6D6EAF2B9d37c2cC0BaB60c"},"seedId":"wlt_y8cz6xda","derivationPath":{"tron":"m/44'/195'/1'/0/0","evm":"m/44'/60'/0'/0/1"}},"meta":{"durationMs":1013,"warnings":[]}} ``` ## Output @@ -62,7 +62,7 @@ printf '%s' "$PW" | wallet-cli derive --seed-id wlt_y8cz6xda --password-stdin -o | `index` | number | HD derivation index | | `active` | boolean | Always `true` (the new account is made active) | | `addresses` | object | One address per family the account can produce: `tron` (base58) and `evm` (`0x`, EIP-55 checksummed) | -| `derivationPath` | object | The BIP44 template each address came from: `{"tron":"m/44'/195'/0'/0/","evm":"m/44'/60'/0'/0/"}` | +| `derivationPath` | object | The BIP44 path each address came from: `{"tron":"m/44'/195'/'/0/0","evm":"m/44'/60'/0'/0/"}` | | `seedId` | string | Owning seed wallet id | ## Exit status diff --git a/ts/docs/commands/exchange/index.md b/ts/docs/commands/exchange/index.md index 4087233fc..902eb2f5e 100644 --- a/ts/docs/commands/exchange/index.md +++ b/ts/docs/commands/exchange/index.md @@ -14,7 +14,7 @@ Pairs trade **TRX against TRC10** — never TRC20 — and settle instantly again `--raw-*` variants when the exact base-unit quantity matters — they are used verbatim. - **TRX's token id on chain is the underscore `_`.** Write `TRX` (any case) or an asset id; `_` is accepted too. json shows what actually went on chain, so TRX appears there as `"_"`. -**Pricing follows the curve, not the ratio.** The ratio of the two reserves is a marginal quote — true only for a trade of size zero. A real trade moves along the curve, and the larger it is relative to the reserves, the worse the price it gets. That gap is the slippage, which is why [`exchange trade`](trade.md) always requires a floor (`--min-received` or `--slippage`), and why no command here prints a "price". To price a specific amount, run `exchange trade --dry-run` against the current reserves. Reserves are also capped by the chain parameter `getExchangeBalanceLimit`. +**Pricing follows the curve, not the ratio.** The ratio of the two reserves is a marginal quote — true only for a trade of size zero. A real trade moves along the curve, and the larger it is relative to the reserves, the worse the price it gets. That gap is the slippage. [`exchange trade`](trade.md) accepts one optional floor (`--min-received`, `--raw-min-received`, or `--slippage`); omitting all three is allowed but emits a warning and submits the protocol minimum `expected = 1`, which provides no practical slippage protection. No command here prints a "price". To price a specific amount, run `exchange trade --dry-run` against the current reserves. Reserves are also capped by the chain parameter `getExchangeBalanceLimit`. **Tokens are named by id only in this group** — `TRX` or a numeric asset id, never a token name. Pairs are written with a colon (`--pair TRX:1000123`, `--amounts 10000:500000`), and TRC10 names may legally contain colons, so allowing names would make `--pair` ambiguous. Resolve a name to its id with [`asset info `](../asset/info.md). diff --git a/ts/docs/commands/gasfree/info.md b/ts/docs/commands/gasfree/info.md index 64de9c8cf..33aeed700 100644 --- a/ts/docs/commands/gasfree/info.md +++ b/ts/docs/commands/gasfree/info.md @@ -57,7 +57,7 @@ wallet-cli gasfree info --account main --network tron:nile -o json ## Exit status -`0` success · `1` execution failure (`gasfree_credentials_missing`, `gasfree_integrity` — the provider's fee metadata disagreed between the token list and the address response, `provider_error` — service error / rate limit, `unsupported_network`) · `2` usage error (`invalid_value`). +`0` success · `1` execution failure (`gasfree_integrity` — the provider's fee metadata disagreed between the token list and the address response, `provider_error` — service error / rate limit) · `2` usage error (`gasfree_credentials_missing`, `unsupported_network`, `invalid_value`). ## See also diff --git a/ts/docs/commands/gasfree/trace.md b/ts/docs/commands/gasfree/trace.md index 571028ad4..3aa688581 100644 --- a/ts/docs/commands/gasfree/trace.md +++ b/ts/docs/commands/gasfree/trace.md @@ -58,7 +58,7 @@ wallet-cli gasfree trace 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 --network tron:nil ## Exit status -`0` success · `1` execution failure (`gasfree_credentials_missing`, `not_found` — no such trace id, `gasfree_integrity`, `provider_error`, `unsupported_network`) · `2` usage error (`invalid_value`). +`0` success · `1` execution failure (`not_found` — no such trace id, `gasfree_integrity`, `provider_error`) · `2` usage error (`gasfree_credentials_missing`, `unsupported_network`, `invalid_value`). A `FAILED` transfer is a successful query: the envelope stays `success: true` at exit `0`, and `data.failureReason` carries the provider's explanation. diff --git a/ts/docs/commands/gasfree/transfer.md b/ts/docs/commands/gasfree/transfer.md index fb1ade637..cf5da6e07 100644 --- a/ts/docs/commands/gasfree/transfer.md +++ b/ts/docs/commands/gasfree/transfer.md @@ -43,7 +43,7 @@ echo "$PW" | wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub ```console ⏳ Submitted to GasFree — send 25 USDT Trace ID 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 - From TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw (GasFree address) + From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER (GasFree address) To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub Fee 0.5 USDT Total 25.5 USDT @@ -52,7 +52,7 @@ echo "$PW" | wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.transfer","data":{"kind":"gasfree-transfer","stage":"submitted","traceId":"7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527","token":"USDT","amount":"25000000","serviceFee":"500000","activateFee":"0","totalDeducted":"25500000","from":"TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","nonce":4},"meta":{"durationMs":650,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.transfer","data":{"kind":"gasfree-transfer","stage":"submitted","traceId":"7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527","token":"USDT","tokenAddress":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","decimals":6,"amount":"25000000","serviceFee":"500000","activateFee":"0","authorizedMaxFee":"500000","totalDeducted":"25500000","owner":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC","from":"TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","serviceProvider":"TKtWbdzEq5ss9vTS9kwRhBp5mXmBfBns3E","nonce":"8","deadline":"1700000060"},"meta":{"durationMs":650,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` Add `--wait` to poll to a terminal state, with the on-chain txid and actual deduction: @@ -65,7 +65,7 @@ echo "$PW" | wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub ✅ Sent 25 USDT via GasFree Trace ID a41b6c88-0d2f-4e73-9a05-3c7d81f2b964 TxID d2e... - From TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw (GasFree address) + From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER (GasFree address) To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub Fee 0.5 USDT Total 25.5 USDT @@ -80,14 +80,14 @@ wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --amount 25 ```console ⏳ Dry run — GasFree transfer 25 USDT (not submitted) - From TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw (GasFree address, not activated) + From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER (GasFree address, not activated) To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub Fee 1.5 USDT (0.5 service + 1.0 activation) Total 26.5 USDT ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.transfer","data":{"kind":"gasfree-transfer","mode":"dry-run","token":"USDT","amount":"25000000","serviceFee":"500000","activateFee":"1000000","totalDeducted":"26500000","from":"TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","nonce":0},"meta":{"durationMs":210,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.transfer","data":{"kind":"gasfree-transfer","stage":"dry-run","token":"USDT","tokenAddress":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","decimals":6,"amount":"25000000","serviceFee":"500000","activateFee":"1000000","authorizedMaxFee":"1500000","totalDeducted":"26500000","owner":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC","from":"TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","serviceProvider":"TKtWbdzEq5ss9vTS9kwRhBp5mXmBfBns3E","nonce":"8","deadline":"1700000060"},"meta":{"durationMs":210,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -96,16 +96,16 @@ wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --amount 25 | Mode | Fields | |---|---| -| default (submit) | `kind: "gasfree-transfer"`, `stage: "submitted"`, `traceId`, `token`, `tokenAddress`, `amount`, `serviceFee`, `activateFee`, `authorizedMaxFee`, `totalDeducted`, `from`, `to`, `nonce`, `deadline`, `serviceProvider`, plus `toContact` when `--to` was a contact name | +| default (submit) | `kind: "gasfree-transfer"`, `stage: "submitted"`, `traceId`, `token`, `tokenAddress`, `decimals`, `amount`, `serviceFee`, `activateFee`, `authorizedMaxFee`, `totalDeducted`, `owner`, `from`, `to`, `nonce`, `deadline`, `serviceProvider`, plus `toContact` when `--to` was a contact name | | `--wait` (confirmed) | the above, but `stage: "confirmed"`, plus `confirmed`, `state` (`SUCCEED` / `FAILED`), `failed`, and `txId` | | `--wait` (failed) | the same fields, but `stage: "failed"`, `failed: true`, `state: "FAILED"`, and `failureReason` carrying the provider's explanation | -| `--dry-run` | `kind`, `mode: "dry-run"`, `token`, `amount`, `serviceFee`, `activateFee`, `totalDeducted`, `from`, `to`; no `traceId` | +| `--dry-run` | the default fields except `traceId`, with `stage: "dry-run"`; no signature or submission | A provider-side failure still leaves the envelope at `success: true` and exit `0` — the command completed; the transfer did not. Branch on `data.stage` / `data.state`, not on the exit code. See [script safety](../../machine-interface.md#script-safety-never-mistake-submitted-for-confirmed). ## Exit status -`0` submitted (or dry-run) · `1` execution failure (`gasfree_credentials_missing`, `insufficient_token_balance` — token balance < amount + service fee [+ activation fee], `unsupported_token`, `gasfree_rejected` — the provider declined the authorization, `gasfree_integrity` — the provider's fee metadata disagreed with itself, `watch_only_no_signer`, `auth_failed`, `signing_rejected`, `provider_error`) · `2` usage error (`invalid_value`, `invalid_amount`). +`0` submitted (or dry-run) · `1` execution failure (`insufficient_token_balance` — token balance < amount + service fee [+ activation fee], `gasfree_rejected` — the provider declined the authorization, `gasfree_integrity` — the provider's fee metadata disagreed with itself, `watch_only_no_signer`, `auth_failed`, `signing_rejected`, `provider_error`) · `2` usage error (`gasfree_credentials_missing`, `unsupported_network`, `unsupported_token`, `invalid_value`, `invalid_amount`). ## See also diff --git a/ts/docs/commands/import/keystore.md b/ts/docs/commands/import/keystore.md index f7765a878..96a8e7f9c 100644 --- a/ts/docs/commands/import/keystore.md +++ b/ts/docs/commands/import/keystore.md @@ -75,7 +75,7 @@ wallet-cli import keystore ./tronlink-export.json --label imported -o json ## Exit status -`0` imported · `1` execution failure (`keystore_not_found` — no such file; `invalid_keystore` — not a valid keystore JSON; `wrong_keystore_password`; `account_exists` — this address is already in the wallet; `auth_failed`; `io_error`) · `2` usage error (`tty_required` — no TTY for interactive input, checked before anything else; duplicate label). +`0` imported · `1` execution failure (`wrong_keystore_password`; `account_exists` — this address is already in the wallet; `auth_failed`; `io_error`) · `2` usage error (`tty_required` — no TTY for interactive input, checked before anything else; `keystore_not_found` — no such file; `invalid_keystore` — not a valid keystore JSON; `invalid_value` — duplicate or invalid label). ## See also diff --git a/ts/docs/commands/import/mnemonic.md b/ts/docs/commands/import/mnemonic.md index 84edb2df6..60e4b57dc 100644 --- a/ts/docs/commands/import/mnemonic.md +++ b/ts/docs/commands/import/mnemonic.md @@ -78,7 +78,7 @@ wallet-cli import mnemonic --label restored -o json ## Exit status -`0` imported · `1` execution failure (`tty_required` — no TTY for interactive input; `auth_failed`; `password_mismatch`; `io_error`) · `2` usage error (invalid mnemonic, duplicate label). +`0` imported · `1` execution failure (`auth_failed` — the entered master password does not match an existing keystore; `invalid_mnemonic` — storage validation rejected the phrase; `io_error`) · `2` usage error (`tty_required` — no TTY for the hidden prompts; `invalid_value` — invalid or duplicate label). An invalid phrase or weak new password entered at a TTY prompt is rejected there and re-prompted rather than returned as a terminal error. ## See also diff --git a/ts/docs/commands/import/private-key.md b/ts/docs/commands/import/private-key.md index 8e9840a91..3337f5b97 100644 --- a/ts/docs/commands/import/private-key.md +++ b/ts/docs/commands/import/private-key.md @@ -70,7 +70,7 @@ wallet-cli import private-key --label hot -o json ## Exit status -`0` imported · `1` execution failure (`tty_required` — no TTY for interactive input; `auth_failed`; `password_mismatch`; `io_error`) · `2` usage error (invalid private key, duplicate label). +`0` imported · `1` execution failure (`auth_failed` — the entered master password does not match an existing keystore; `invalid_private_key` — storage validation rejected the key; `io_error`) · `2` usage error (`tty_required` — no TTY for the hidden prompts; `invalid_value` — invalid or duplicate label). An invalid key or weak new password entered at a TTY prompt is rejected there and re-prompted rather than returned as a terminal error. ## See also diff --git a/ts/docs/commands/permission/show.md b/ts/docs/commands/permission/show.md index 2fb185c75..cb155f06a 100644 --- a/ts/docs/commands/permission/show.md +++ b/ts/docs/commands/permission/show.md @@ -102,7 +102,7 @@ wallet-cli permission show --account main --network tron:nile -o json ## Exit status -`0` success · `1` execution failure (`rpc_error`) · `2` usage error (`invalid_value`, or `not_found` when the address is unactivated / absent on chain). +`0` success · `1` execution failure (`not_found` — the address is unactivated / absent on chain; `rpc_error`) · `2` usage error (`invalid_value`). ## See also diff --git a/ts/docs/commands/permission/update.md b/ts/docs/commands/permission/update.md index 64d6260bd..ecfc09498 100644 --- a/ts/docs/commands/permission/update.md +++ b/ts/docs/commands/permission/update.md @@ -124,7 +124,7 @@ Local warnings (`owner_lockout`, `owner_lockout_partial`, `active_can_update_per ## Exit status -`0` submitted (or built/signed/dry-run in early-exit modes) · `1` execution failure (`invalid_permission`, `not_authorized`, `watch_only_no_signer`, `auth_failed`, `insufficient_balance`, `rpc_error`, `timeout`) · `2` usage error (`invalid_value`). +`0` submitted (or built/signed/dry-run in early-exit modes) · `1` execution failure (`not_authorized`, `watch_only_no_signer`, `auth_failed`, `insufficient_balance`, `rpc_error`, `timeout`) · `2` usage error (`invalid_permission` — malformed JSON or an invalid permission structure; `invalid_value`). On a multi-sig account, a submission whose accumulated signature weight is below the permission threshold is refused **after signing and before broadcasting** with `not_authorized` (`signature threshold is not reached; missing N weight`) — nothing is sent and no fee is burned. Collect the remaining signatures through `--sign-only` + [`tx sign`](../tx/sign.md) and submit with [`tx broadcast`](../tx/broadcast.md) instead. `--sign-only` and `--build-only` still return a partial signature, which is how a co-signing flow starts. diff --git a/ts/docs/commands/rename.md b/ts/docs/commands/rename.md index a0f83e52e..76635226a 100644 --- a/ts/docs/commands/rename.md +++ b/ts/docs/commands/rename.md @@ -41,7 +41,7 @@ wallet-cli rename main-1 --label hot-hd -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"rename","data":{"previousLabel":"main-1","accountId":"wlt_0y2z0gvr.1","label":"hot-hd","type":"seed","index":1,"active":true,"addresses":{"tron":"TRzaAZWRvPCcmqNETTWvmMLDi6cKwM3gbR","evm":"0x94f2e5cbb4BcA39A3F6c252217a0F30A0D23660b"},"seedId":"wlt_0y2z0gvr","derivationPath":{"tron":"m/44'/195'/0'/0/1","evm":"m/44'/60'/0'/0/1"}},"meta":{"durationMs":14,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"rename","data":{"previousLabel":"main-1","accountId":"wlt_0y2z0gvr.1","label":"hot-hd","type":"seed","index":1,"active":true,"addresses":{"tron":"TRzaAZWRvPCcmqNETTWvmMLDi6cKwM3gbR","evm":"0x94f2e5cbb4BcA39A3F6c252217a0F30A0D23660b"},"seedId":"wlt_0y2z0gvr","derivationPath":{"tron":"m/44'/195'/1'/0/0","evm":"m/44'/60'/0'/0/1"}},"meta":{"durationMs":14,"warnings":[]}} ``` ## Output diff --git a/ts/docs/commands/stake/cancel-unfreeze.md b/ts/docs/commands/stake/cancel-unfreeze.md index 246547e08..ab597c5ad 100644 --- a/ts/docs/commands/stake/cancel-unfreeze.md +++ b/ts/docs/commands/stake/cancel-unfreeze.md @@ -15,6 +15,8 @@ Cancels **every** unstake still in its waiting period and rolls those amounts ba **By default the command returns at submission**; `--wait` blocks until confirmed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign `CancelAllUnfreezeV2`. Ledger accounts may dry-run or build, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -79,7 +81,7 @@ echo "$PW" | wallet-cli stake cancel-unfreeze --network tron:nile --wait --passw ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`watch_only_no_signer`, `auth_failed`, `rpc_error`, `timeout`) · `2` usage error. +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`watch_only_no_signer`, `ledger_unsupported`, `auth_failed`, `rpc_error`, `timeout`) · `2` usage error. ## See also diff --git a/ts/docs/commands/token/add.md b/ts/docs/commands/token/add.md index 458f3bb29..3268f8ec3 100644 --- a/ts/docs/commands/token/add.md +++ b/ts/docs/commands/token/add.md @@ -67,7 +67,7 @@ wallet-cli token add --contract 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 --net ## Exit status -`0` added · `1` execution failure (`token_metadata_unavailable` — metadata could not be fetched, nothing is stored; `token_already_listed` — already in the official layer) · `2` usage error (`invalid_value`; `invalid_option` — `--asset-id` on an EVM network). +`0` added · `1` execution failure (`token_metadata_unavailable` — metadata could not be fetched, nothing is stored) · `2` usage error (`token_already_listed` — already in the official layer; `invalid_value`; `invalid_option` — `--asset-id` on an EVM network). ## See also diff --git a/ts/docs/commands/token/remove.md b/ts/docs/commands/token/remove.md index 45e7cde76..648ce60d9 100644 --- a/ts/docs/commands/token/remove.md +++ b/ts/docs/commands/token/remove.md @@ -53,7 +53,7 @@ wallet-cli token remove --contract TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf --network ## Exit status -`0` removed · `1` execution failure (`token_is_official` — official-layer tokens can't be removed; `token_not_in_book` — not in the book) · `2` usage error (`invalid_value`; `invalid_option` — `--asset-id` on an EVM network). +`0` removed · `1` execution failure (`encoding_error`, `insecure_permissions`, `io_error`) · `2` usage error (`token_is_official` — official-layer tokens can't be removed; `token_not_in_book` — not in the user layer; `invalid_value`; `invalid_option` — `--asset-id` on an EVM network). ## See also diff --git a/ts/docs/commands/tx/multisig.md b/ts/docs/commands/tx/multisig.md index 2e399ddf2..aa404e5ef 100644 --- a/ts/docs/commands/tx/multisig.md +++ b/ts/docs/commands/tx/multisig.md @@ -174,7 +174,7 @@ A record the client cannot reconcile with the chain stays visible and is labelle ## Exit status -`0` success · `1` execution failure (`tronlink_credentials_missing`, `not_found` — txId not on the service, `not_authorized`, `already_signed`, `tx_expired`, `auth_failed`, `provider_error` — service error / rate limit) · `2` usage error (`invalid_value` — including an already-signed transaction passed to `--create`, conflicting modes). +`0` success · `1` execution failure (`not_found` — txId not on the service, `not_authorized`, `already_signed`, `tx_expired`, `auth_failed`, `provider_error` — service error / rate limit) · `2` usage error (`tronlink_credentials_missing`, `unsupported_network`, `invalid_value` — including an already-signed transaction passed to `--create`, conflicting modes). ## See also diff --git a/ts/docs/commands/use.md b/ts/docs/commands/use.md index bceb0aa6f..12a58b644 100644 --- a/ts/docs/commands/use.md +++ b/ts/docs/commands/use.md @@ -35,7 +35,7 @@ wallet-cli use main-1 -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"use","data":{"previous":"wlt_758891fa.0","accountId":"wlt_758891fa.1","label":"main-1","type":"seed","index":1,"active":true,"addresses":{"tron":"TRs9HgTuY3dT3yDasdFdP9WQHqL37891Ax","evm":"0xf3ec542047Fe61E0b753a7EBca95B27a672F9cbe"},"seedId":"wlt_758891fa","derivationPath":{"tron":"m/44'/195'/0'/0/1","evm":"m/44'/60'/0'/0/1"}},"meta":{"durationMs":14,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"use","data":{"previous":"wlt_758891fa.0","accountId":"wlt_758891fa.1","label":"main-1","type":"seed","index":1,"active":true,"addresses":{"tron":"TRs9HgTuY3dT3yDasdFdP9WQHqL37891Ax","evm":"0xf3ec542047Fe61E0b753a7EBca95B27a672F9cbe"},"seedId":"wlt_758891fa","derivationPath":{"tron":"m/44'/195'/1'/0/0","evm":"m/44'/60'/0'/0/1"}},"meta":{"durationMs":14,"warnings":[]}} ``` ## Output diff --git a/ts/docs/commands/vote/cast.md b/ts/docs/commands/vote/cast.md index b0ba849d9..3f0cd0f25 100644 --- a/ts/docs/commands/vote/cast.md +++ b/ts/docs/commands/vote/cast.md @@ -89,7 +89,7 @@ echo "$PW" | wallet-cli vote cast --for TZ4...=600 --for TT5...=400 --network tr ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`watch_only_no_signer`, `auth_failed`, `insufficient_voting_power` — total exceeds available TP) · `2` usage error (`invalid_value` — bad SR address, non-positive count, > 30 entries). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`watch_only_no_signer`, `auth_failed`) · `2` usage error (`insufficient_voting_power` — total exceeds available TP; `invalid_value` — bad SR address, non-positive count, > 30 entries). ## See also diff --git a/ts/docs/commands/vote/list.md b/ts/docs/commands/vote/list.md index 37254d550..933b0bc6a 100644 --- a/ts/docs/commands/vote/list.md +++ b/ts/docs/commands/vote/list.md @@ -10,11 +10,11 @@ wallet-cli vote list [--limit ] [--candidates] [options] ## Description -Lists SRs (the 27 elected, by default) with votes, estimated APR, and reward ratio — the numbers you need before a [`vote cast`](cast.md). Read-only, no account needed. +Lists SRs (the 27 elected, by default) with votes and reward ratio — the on-chain data available before a [`vote cast`](cast.md). Read-only, no account needed. Column semantics: -- **APR** — the voter's estimated annual return, already adjusted for the SR's reward ratio. **Best-effort**: not from chain RPC but from explorer/TronGrid data; when unavailable the column shows `—` (json `null`). +- **APR** — reserved for a future estimate source. The current implementation does not query one, so the column always shows `—` and json always returns `null`. - **Reward ratio** — the share of rewards the SR passes to voters (on-chain, reliable). 80% means voters split 80% of the rewards; **0% means your votes earn nothing**. json also carries the chain-native `brokeragePct` (= 100 − rewardRatioPct). - **Ranks and eligibility** — ranks 1–27 are elected SRs (block + vote rewards); 28–127 are partners (vote rewards only); beyond 127 candidates earn nothing, so `--limit` caps at 127. @@ -36,9 +36,9 @@ wallet-cli vote list --limit 3 --network tron:nile ```console | Rank | Name | Votes | APR | Reward ratio | Address | | ---- | --------------- | ------------- | ---- | ------------ | ---------------------------------- | -| 1 | TRONSCAN | 1,203,456,789 | 4.8% | 80% | TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g | -| 2 | Binance Staking | 998,765,432 | 0% | 0% | TT5W8MPbYJih9R586kTszb4LoybzUvCYm2 | -| 3 | JustLend | 876,543,210 | 4.9% | 80% | TWxkzUeAiKcFvzXvJEcaTQCQqCuMednAtN | +| 1 | TRONSCAN | 1,203,456,789 | — | 80% | TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g | +| 2 | Binance Staking | 998,765,432 | — | 0% | TT5W8MPbYJih9R586kTszb4LoybzUvCYm2 | +| 3 | JustLend | 876,543,210 | — | 80% | TWxkzUeAiKcFvzXvJEcaTQCQqCuMednAtN | ``` ```bash @@ -46,7 +46,7 @@ wallet-cli vote list --limit 3 --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"vote.list","data":{"witnesses":[{"rank":1,"name":"TRONSCAN","address":"TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g","voteCount":"1203456789","rewardRatioPct":80,"brokeragePct":20,"aprPct":4.8}]},"meta":{"durationMs":40,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"vote.list","data":{"witnesses":[{"rank":1,"name":"TRONSCAN","address":"TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g","voteCount":"1203456789","rewardRatioPct":80,"brokeragePct":20,"aprPct":null}]},"meta":{"durationMs":40,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -61,7 +61,7 @@ wallet-cli vote list --limit 3 --network tron:nile -o json | `voteCount` | string | Total votes, raw integer | | `rewardRatioPct` | number | % of rewards passed to voters (on-chain) | | `brokeragePct` | number | SR's cut (= 100 − `rewardRatioPct`) | -| `aprPct` | number \| null | Estimated voter APR; `null` when the estimate source is unavailable | +| `aprPct` | null | Reserved field; always `null` in the current implementation | ## Exit status diff --git a/ts/docs/commands/vote/status.md b/ts/docs/commands/vote/status.md index b1e0b1350..b06d8e41b 100644 --- a/ts/docs/commands/vote/status.md +++ b/ts/docs/commands/vote/status.md @@ -10,10 +10,10 @@ wallet-cli vote status [options] ## Description -One read-only screen for the stake → vote → reward loop: your current vote distribution (with each SR's APR and reward ratio), your voting power (total / used / available TP), and the currently claimable reward. +One read-only screen for the stake → vote → reward loop: your current vote distribution and each SR's reward ratio, your voting power (total / used / available TP), and the currently claimable reward. - **Voting power (TP)** — total = staked TRX; used = votes placed; available = total − used. -- **APR / Reward ratio** — same semantics and sources as [`vote list`](list.md). Worth re-checking: an SR can change its ratio at any time (on-chain UpdateBrokerage) — votes placed at 80% silently stop earning if it drops to 0%. +- **APR / Reward ratio** — reward ratio is read on-chain. `aprPct` is reserved and always `null` because the current implementation has no APR provider. An SR can change its ratio at any time (on-chain UpdateBrokerage) — votes placed at 80% silently stop earning if it drops to 0%. - **0% warning** — if any votes sit on an SR with a 0% reward ratio, text output appends a `!` line and json adds a plain-string entry to `meta.warnings`, one per affected SR. - **Claimable** — same source as [`reward balance`](../reward/balance.md); claim with [`reward withdraw`](../reward/withdraw.md). @@ -35,8 +35,8 @@ Claimable 12.345678 TRX Current votes (2) | Name | Votes | APR | Reward ratio | Address | | --------------- | ----- | ---- | ------------ | ---------------------------------- | -| TRONSCAN | 600 | 4.8% | 80% | TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g | -| Binance Staking | 400 | 0% | 0% | TT5W8MPbYJih9R586kTszb4LoybzUvCYm2 | +| TRONSCAN | 600 | — | 80% | TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g | +| Binance Staking | 400 | — | 0% | TT5W8MPbYJih9R586kTszb4LoybzUvCYm2 | ! 400 votes on Binance Staking earn nothing — 0% reward ratio ``` @@ -45,7 +45,7 @@ wallet-cli vote status --account main --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"vote.status","data":{"address":"TQk...","votingPower":{"total":1500,"used":1000,"available":500},"claimableRewardSun":"12345678","votes":[{"witness":"TZ4...","name":"TRONSCAN","count":600,"rewardRatioPct":80,"brokeragePct":20,"aprPct":4.8},{"witness":"TT5...","name":"Binance Staking","count":400,"rewardRatioPct":0,"brokeragePct":100,"aprPct":0}]},"meta":{"durationMs":16,"warnings":["400 votes on TT5... (Binance Staking) earn nothing: reward ratio is 0%"]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"vote.status","data":{"address":"TQk...","votingPower":{"total":1500,"used":1000,"available":500},"claimableRewardSun":"12345678","votes":[{"witness":"TZ4...","name":"TRONSCAN","count":600,"rewardRatioPct":80,"brokeragePct":20,"aprPct":null},{"witness":"TT5...","name":"Binance Staking","count":400,"rewardRatioPct":0,"brokeragePct":100,"aprPct":null}]},"meta":{"durationMs":16,"warnings":["400 votes on TT5... (Binance Staking) earn nothing: reward ratio is 0%"]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -55,7 +55,7 @@ wallet-cli vote status --account main --network tron:nile -o json | `address` | string | Queried account | | `votingPower.total` / `.used` / `.available` | number | TP total / spent / spendable | | `claimableRewardSun` | string | Currently claimable reward, in SUN | -| `votes[]` | array | Current distribution: `witness`, `name`, `count`, `rewardRatioPct`, `brokeragePct`, `aprPct` | +| `votes[]` | array | Current distribution: `witness`, `name`, `count`, `rewardRatioPct`, `brokeragePct`, and reserved `aprPct` (always `null`) | Zero-reward-ratio warnings appear in `meta.warnings` as plain strings — see [reading `meta.warnings`](../../machine-interface.md#reading-metawarnings). diff --git a/ts/docs/commands/witness/create.md b/ts/docs/commands/witness/create.md index 658308aff..2dc6865dc 100644 --- a/ts/docs/commands/witness/create.md +++ b/ts/docs/commands/witness/create.md @@ -20,6 +20,8 @@ The account must already be activated and hold at least the registration fee. `- **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign witness contract types. Ledger accounts may dry-run or build, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -74,7 +76,7 @@ echo "$PW" | wallet-cli witness create --url https://sr.acme.io --network tron:n ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`already_witness`, `account_not_active`, `insufficient_balance` — below the registration fee, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`missing_option` — no `--url`). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`already_witness`, `account_not_active`, `insufficient_balance` — below the registration fee, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — no `--url`). ## See also diff --git a/ts/docs/commands/witness/set-brokerage.md b/ts/docs/commands/witness/set-brokerage.md index 74e9d5af5..40f247eab 100644 --- a/ts/docs/commands/witness/set-brokerage.md +++ b/ts/docs/commands/witness/set-brokerage.md @@ -20,6 +20,8 @@ Any registered witness can set it, elected or not. The acting account must be a **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign witness contract types. Ledger accounts may dry-run or build, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -76,7 +78,7 @@ echo "$PW" | wallet-cli witness set-brokerage 20 --network tron:nile --wait --pa ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_a_witness`, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`invalid_value` — percent missing, not an integer, or outside 0–100). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_a_witness`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`invalid_value` — percent missing, not an integer, or outside 0–100). ## See also diff --git a/ts/docs/commands/witness/update.md b/ts/docs/commands/witness/update.md index 647fad391..f50a01b0b 100644 --- a/ts/docs/commands/witness/update.md +++ b/ts/docs/commands/witness/update.md @@ -18,6 +18,8 @@ The acting account must already be a candidate; otherwise the command fails with **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. +The Ledger TRON app cannot sign witness contract types. Ledger accounts may dry-run or build, but signing modes fail with `ledger_unsupported` before device interaction. + ## Options | Option | Description | @@ -70,7 +72,7 @@ echo "$PW" | wallet-cli witness update --url https://sr.acme.io/v2 --network tro ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_a_witness`, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`missing_option` — no `--url`). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_a_witness`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — no `--url`). ## See also diff --git a/ts/docs/concepts/accounts-and-hd.md b/ts/docs/concepts/accounts-and-hd.md index 381fe076c..35c33fe3f 100644 --- a/ts/docs/concepts/accounts-and-hd.md +++ b/ts/docs/concepts/accounts-and-hd.md @@ -19,7 +19,7 @@ wlt_4473p34m.1 A key is not tied to a chain, so **an account holds one address per [family](networks.md)** — a TRON base58 address and an EVM `0x` address — derived from the same seed at different BIP44 coin types: ``` -m/44'/195'/0'/0/ TRON +m/44'/195'/'/0/0 TRON m/44'/60'/0'/0/ EVM ``` diff --git a/ts/docs/concepts/networks.md b/ts/docs/concepts/networks.md index 589439173..432bae135 100644 --- a/ts/docs/concepts/networks.md +++ b/ts/docs/concepts/networks.md @@ -46,7 +46,9 @@ Listings (`networks`, `config`) print an endpoint's **host only**, because a com 1. Explicit `--network ` on the command; 2. otherwise `config.defaultNetwork` (`wallet-cli config defaultNetwork tron:nile`); -3. chain commands with neither will tell you a network is required. +3. if the config file does not override it, the built-in default is `tron:mainnet`. + +Omitting `--network` therefore does **not** stop a chain command. For any operation that can move funds, pass `--network` explicitly so the target is visible in shell history and review logs. Balances, tokens, and transactions are entirely separate per network. A txid from Nile does not exist on mainnet — querying it there returns `not_found`/`rpc_error`. diff --git a/ts/docs/machine-interface.md b/ts/docs/machine-interface.md index d554026c5..f0633bdd3 100644 --- a/ts/docs/machine-interface.md +++ b/ts/docs/machine-interface.md @@ -164,7 +164,7 @@ The **exit code is the hard contract**: `2` means the call was malformed (it wil wallet-cli --json-schema | jq '.errorCodes' ``` -That index is the machine-readable catalog exposed by this build. Treat it as a discovery aid, not a closed enum: a few code paths choose among error-code strings dynamically, so a runtime envelope can still carry a code not present in `errorCodes`. The tables below are the frequently-hit subset, kept for reading. New codes may still be added within v1, and a few strings (e.g. `invalid_value`, `aborted`) can appear under either exit code depending on where they are raised — so always tolerate an unknown code by falling back to its exit-code class. +That index is the machine-readable catalog exposed by this build. Treat it as a discovery aid, not a closed enum: a few code paths choose among error-code strings dynamically, so a runtime envelope can still carry a code not present in `errorCodes`. The tables below are the frequently-hit subset, kept for reading. New codes may still be added within v1, and a few strings (e.g. `invalid_value`, `aborted`, `not_found`) can appear under either exit code depending on where they are raised — so always tolerate an unknown code by falling back to its exit-code class. Common codes at exit **2** (usage — fix the call): @@ -174,12 +174,12 @@ Common codes at exit **2** (usage — fix the call): | `family_mismatch` | The command, the account, the recipient, or the raw transaction does not belong to the selected network's chain family | | `missing_option` | A required flag was not provided | | `invalid_option` | A flag was used in an invalid combination, or is scoped to the other chain family | +| `invalid_permission` | A permission document or selected permission group is invalid for the operation | | `invalid_value` | A flag value failed validation (e.g. `config defaultOutput xml`) | | `invalid_amount` | An amount is malformed or out of range | -| `invalid_mnemonic` / `invalid_private_key` | A supplied mnemonic or private key is malformed | | `weak_password` | Master password below policy (≥8 chars; upper + lower + digit + special) | -| `tty_required` | An interactive prompt is needed but no TTY is attached — pass the matching `*-stdin` flag | -| `missing_network` / `unsupported_network` | `--network` absent, or not a known canonical id or alias | +| `tty_required` | An interactive prompt is needed but no TTY is attached — run in a TTY, or use the matching stdin flag when that command exposes one | +| `missing_network` / `unsupported_network` | A caller explicitly asked the registry to resolve an empty network id, or the supplied canonical id / alias is unknown. Normal chain commands use `config.defaultNetwork`, whose built-in value is `tron:mainnet`, when `--network` is omitted | | `unsupported_network_capability` | The selected network does not offer what this command needs | | `limit_exceeded` | A bounded input (file size, list length, page size) was over its limit | | `unknown_command` | No such command | @@ -189,7 +189,11 @@ Common codes at exit **2** (usage — fix the call): | `invalid_keystore` | `import keystore`: not a valid Web3 V3 keystore — bad JSON, `version` ≠ 3, an unsupported cipher/KDF, or a payload that is not a 32-byte private key | | `invalid_config` | `config.yaml` cannot be read or is not valid YAML — fix or remove the file. The parser detail is withheld: it quotes the offending line, which may carry a credential | | `insecure_config` | `config.yaml` holds service credentials but is a symlink or is group/world-readable — run `chmod 600` on it (POSIX only; not enforced on Windows) | -| `token_not_in_book` / `token_is_official` / `token_metadata_unavailable` | Token address-book conditions | +| `contact_not_found` / `already_exists` | No contact by that name, or a contact name/address is already stored | +| `token_not_in_book` / `token_is_official` / `token_already_listed` | Token address-book conditions | +| `unsupported_token` | The selected provider or command does not support that token | +| `insufficient_voting_power` | The requested votes exceed the account's available voting power | +| `gasfree_credentials_missing` / `tronlink_credentials_missing` | Required service credentials are not configured (set them with `config`) | | `unknown_parameter` | No chain parameter by that name or id (`proposal create --set`) | | `invalid_asset_name` | A TRC10 name or abbreviation outside 1–32 visible ASCII characters | @@ -204,20 +208,21 @@ Common codes at exit **1** (execution — runtime failure): | `auth_failed` | Wrong master password (decryption failed) | | `signing_rejected` / `transaction_rejected` | Signing or broadcast rejected (device or chain) | | `watch_only_no_signer` | The account is watch-only and cannot sign | +| `invalid_mnemonic` / `invalid_private_key` | Storage validation rejected a malformed mnemonic or private key; interactive import normally catches it at the prompt and asks again | +| `token_metadata_unavailable` | Required token metadata could not be read from the selected network | | `wrong_device_seed` | Connected Ledger does not match the registered account | | `tx_integrity` / `invalid_transaction` | A presigned transaction failed integrity / validity checks | | `insufficient_balance` / `insufficient_token_balance` | Not enough TRX / token to cover the amount plus fees | | `provider_error` | An external service (GasFree, TronLink multi-sig) returned an error or rate-limited | -| `gasfree_credentials_missing` / `tronlink_credentials_missing` | Required service credentials are not configured (set them with `config`) | | `tx_expired` | The transaction's expiration passed before signatures were collected (TRON) | | `chain_id_mismatch` | An EVM transaction was built for a different chain than the selected network | | `nonce_too_low` | The EVM transaction's nonce is already used by a mined transaction | | `migration_required` | Persisted wallet data needs an upgrade that this invocation cannot perform — see [startup wallet-data upgrades](#startup-wallet-data-upgrades) | | `history_not_supported` | The endpoint lacks TronGrid history support (`account history`, TRON) | -| `not_found` | The addressed thing does not exist — an unactivated account, a contact, a chain parameter, a GasFree or TronLink resource. Lookups that have a group of their own use the specific code below | +| `not_found` | The addressed thing does not exist — for example an unactivated account, transaction, block, or GasFree / TronLink resource. Some command-level lookups raise the same string as a usage error instead; branch on exit code first | | `proposal_not_found` / `contract_not_found` / `asset_not_found` / `exchange_not_found` | Nothing on chain under that proposal id, contract address, TRC10 reference, or exchange pair id | | `ambiguous_asset_name` | A TRC10 name matches more than one token; `error.details` carries the candidates — see [`error.details.matches`](#errordetailsmatches) | -| `ledger_unsupported` | The Ledger TRON app cannot sign this contract type — refused before the device is touched (`asset` writes, `witness` writes) | +| `ledger_unsupported` | The selected Ledger app cannot sign this transaction type — refused before the device is touched (TRON account activation, account id, asset writes, contract deploy/governance, witness writes, and cancel-unfreeze) | | `not_a_witness` / `already_witness` / `not_proposal_owner` | Governance identity does not meet the operation's rule | | `already_approved` / `not_approved` / `proposal_expired` / `already_canceled` | Proposal voting conditions | | `account_not_active` / `account_already_active` / `name_already_set` / `id_already_set` / `chain_parameter_unavailable` | Account activation/name/id conditions, or `witness create` could not read `getAccountUpgradeCost` | From c6bb5a930471a033507cf4cd194c24a054a3696b Mon Sep 17 00:00:00 2001 From: Steven Lin Date: Mon, 31 Aug 2026 18:09:31 +0800 Subject: [PATCH 6/8] refactor(config): drop the EVM legacy aliases, keep TRON's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `evm:1` / `evm:11155111` / `evm:56` / `evm:97` were added as compatibility aliases alongside TRON's, but they protect nobody: the EVM networks were never part of a published release. `develop`'s builtins carry no `evm:` id at all, and 4.10.1 through 4.12.0 shipped TRON alone — no config.yaml or script can be holding one of those spellings. An alias is a promise to resolve something forever and it shows up in `config aliases`, so an entry nobody could have written is only clutter. The TRON ids stay: they did ship, and people's files hold them. tokens.json's rename map keeps its EVM rows deliberately. The two surfaces fail differently — an unresolvable id in config.yaml is an error the user reads and fixes, while an unmatched scope key silently yields an empty token list. Four lines is a cheap hedge against someone on a branch build losing their token book. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Vw3cw2uPxtDMZScdkmQpFx --- ts/docs/commands/networks.md | 2 +- ts/docs/machine-interface.md | 2 +- ...02\346\226\207\346\241\243-v4.13.0 (4).md" | 3983 +++++++++++++++++ ...02\346\226\207\346\241\243-v4.13.0 (5).md" | 3915 ++++++++++++++++ ts/src/adapters/outbound/config/builtins.ts | 21 +- ts/src/domain/migration/tokens-v2.ts | 12 +- 6 files changed, 7922 insertions(+), 13 deletions(-) create mode 100644 "ts/prd-\345\221\275\344\273\244\351\234\200\346\261\202\346\226\207\346\241\243-v4.13.0 (4).md" create mode 100644 "ts/prd-\345\221\275\344\273\244\351\234\200\346\261\202\346\226\207\346\241\243-v4.13.0 (5).md" diff --git a/ts/docs/commands/networks.md b/ts/docs/commands/networks.md index aac85aec1..e8dda6b2a 100644 --- a/ts/docs/commands/networks.md +++ b/ts/docs/commands/networks.md @@ -12,7 +12,7 @@ wallet-cli networks [options] Lists every network wallet-cli knows, with the short alias `--network` also accepts. Purely local — no node is contacted. -**Network** is the canonical CAIP-2 id, `namespace:reference`; **Alias** is the short name you can type instead. Both resolve to the same network, and nothing downstream ever sees the alias. The pre-CAIP-2 ids (`tron:nile`, `evm:56`, …) also still resolve, as permanent aliases. +**Network** is the canonical CAIP-2 id, `namespace:reference`; **Alias** is the short name you can type instead. Both resolve to the same network, and nothing downstream ever sees the alias. The TRON ids used before CAIP-2 (`tron:mainnet`, `tron:nile`, `tron:shasta`) also still resolve, as permanent aliases. Endpoints are shown as **hosts only**. A commercial RPC endpoint can carry its API key in the URL path, and this listing is output people paste into issues and CI logs; read the full URL with `config networks..httpEndpoint`, which is a deliberate named read rather than a listing. diff --git a/ts/docs/machine-interface.md b/ts/docs/machine-interface.md index 91d505e06..e4759912b 100644 --- a/ts/docs/machine-interface.md +++ b/ts/docs/machine-interface.md @@ -12,7 +12,7 @@ wallet-cli -o json [--network ] [--timeout ] [--account - In JSON mode, stdout carries **exactly one terminal frame** — the result envelope. Nothing else is ever written to stdout. Diagnostics go to stderr. - Every RPC / device call is bounded by `--timeout` (milliseconds, default `config.timeoutMs`, built-in 60000). - `--network` takes a canonical **CAIP-2** id (`tron:3448148188`, `eip155:11155111`) or a short alias (`nile`, `sepolia`, `bsc`). The namespace is not the chain family: `eip155` addresses the `evm` family. Aliases resolve once at selection; nothing downstream ever sees one, and the envelope's `chain.network` always reports the canonical id. Prefer canonical ids in scripts — an alias is a local config entry and can be re-pointed. -- The pre-CAIP-2 ids (`tron:nile`, `evm:56`, …) remain permanent aliases, so existing invocations keep working. **Output is a different matter**: `chain.network`, the `networks` listing's `id` and the `config` keys now report the CAIP-2 id, so a consumer that string-matches or keys a map by `evm:56` must be updated. An alias exists only at selection and can never appear in a result. +- The **TRON** ids this CLI used before CAIP-2 (`tron:mainnet`, `tron:nile`, `tron:shasta`) remain permanent aliases, so existing invocations keep working. **Output is a different matter**: `chain.network`, the `networks` listing's `id` and the `config` keys now report the CAIP-2 id, so a consumer that string-matches or keys a map by `tron:nile` must be updated. An alias exists only at selection and can never appear in a result. ### Discovery diff --git "a/ts/prd-\345\221\275\344\273\244\351\234\200\346\261\202\346\226\207\346\241\243-v4.13.0 (4).md" "b/ts/prd-\345\221\275\344\273\244\351\234\200\346\261\202\346\226\207\346\241\243-v4.13.0 (4).md" new file mode 100644 index 000000000..7957b19c2 --- /dev/null +++ "b/ts/prd-\345\221\275\344\273\244\351\234\200\346\261\202\346\226\207\346\241\243-v4.13.0 (4).md" @@ -0,0 +1,3983 @@ +# wallet-cli 命令需求文档 v4.13.0 + +`wallet-cli` 是一个多链 CLI 钱包,架构覆盖 TRON 与 EVM。**本版起 EVM 从「架构预留」变为「实际可用」**——同一套助记词、同一批命令,靠 `--network` 选链。 + +本文档为 **v4.13.0**,承接 [v4.12.0](../v4.12.0/prd-命令需求文档-v4.12.0.md)(90 条命令,治理 / SR 竞选 / 合约治理 / TRC10 / Bancor / keystore 互导已闭合)。本版两个主题: + +1. **EVM 落地**——账户地址层 + 只读、转账与部署。**不新增任何命令**,而是给既有命令补 EVM family。 +2. **Java 版 Standard CLI 移除**——TS 版已完整接管非交互命令行场景,**本版一次性删除**,不留弃用过渡版本(§12)。 + +**本版另有一次强制的启动迁移(§0)**:`ChainAddresses` 因加入 `evm` 而变为不兼容既有 `wallets.json`,注册文件必须一次迁移到齐。这是本版**唯一一处在任何命令执行之前就可能阻断**的机制,故单列为 §0。 + +本版新增的每一处 help 输出都必须满足统一的文案规范(§10.1)。 + +> **阅读方式**:先看「范围与命令一览」(本版主题 / 能力矩阵 / 命令树 / root help / 横切约定),再看 **§0(启动前置:强制迁移)** 与 §1–§2(账户模型、网络配置),然后是 §3–§9 的命令逐条规格,最后 §10–§12。 +> +> **通用约定**(沿用 v4.12.0,此处只列与本版相关的): +> - **命令文法**:`<必填>` | `[可选]` | `a | b`(互斥二选一)| `(… | …)`(互斥组必选其一)。 +> - **图标**:🔒 需主密码 | ✍️ 改链上状态(会广播交易)| ⚠️ 高风险 / 不可逆 | 无图标 = 纯读 / 仅本地。 +> - **输出**:text(人读,字段独占一行)与 json(envelope `wallet-cli.result.v1`)两种;text/json 对称,**输出字段必须是数据、静态说明进 help**。 +> - **数量单位**:命令行与 text 用**人话单位**(TRX / ETH / gwei),json 给**链上原始值**(sun / wei)。**单位与小数位**由网络所属的 family 决定(TRX 6 位 / ETH 18 位);**币种名称**(TRX / ETH / BNB)由**网络**决定,不由 family 决定(§2.2)。 +> - **时间与时区**:一律 **UTC**、精确到秒(`YYYY-MM-DD HH:MM:SS UTC`);键值块标签含 `time` 字样、值带 `UTC`,表格把 `(UTC)` 挂在列名上。 +> - **stdout / stderr 分流**:stdout 只放结果(text 回执或 json envelope),提示、诊断、警告走 stderr。示例块中出现的 `? …` 提示行与 `password ✓ via pipe` 均来自 stderr,为还原真实终端观感而并列展示,**机器只读 stdout 即可**。 +> - **「相对现状」注解**:每个 Help 输出块上方一行,说明该 help 相对现状改了什么、没改什么,便于逐条核对。 +> - **示例省略**:地址、TxID、区块哈希写成 `TSRmq8kP...9dEf` / `0x7a3f...c19b` 只是排版省略,实际输出为完整值,json 亦然。 + +--- + +## 修订记录 + +| # | 日期 | 修订 | 依据 | +| :---: | --- | --- | --- | +| 1 | 2026-08-27 | **按实作同步全文**:§0 新增;§1–§2 / §3.2 / §3.6–§3.11 / §4.2 / §5 / §6 / §7 / §9.2–§9.3 / §10.1–§10.2 / §11 改写 | `spec-deviations-全量-v4.13.0.md` 的 A 档 26 项 + B 档 8 项(B 档均取推荐选项) | +| 2 | 2026-08-27 | **§12 改回一次性移除**:本版直接删除 Java Standard CLI,不设弃用过渡版本;头部主题与范围表同步 | PM 决策 | +| 3 | 2026-08-27 | **family 标注词表改为 `(TRON only)` / `(EVM only)`**:全文 132 处,词表规则写进 §10.1;原 v4.13.1 主题 1 整体折叠进本版 | PM 决策 | +| 4 | 2026-08-28 | **按 `e206c00a` 重新核实**:核实基准前移;修订 3 的标注改造与 §12 的 Java 移除**均已在实作落地**,两处 ⚠️ 注记删除;全局旗标文案回贴;命令数 90→91、§10 待办 46→38、§12.2 测试 2→24 / 文档 5→1 四个数字改正;help 区块示例的主网网络改回测试网 | `doc-verify-全量-v4.13.0-20260828.md` | + +> **核实基准**:PR [#990](https://github.com/tronprotocol/wallet-cli/pull/990) head `feat/v4.13.0` @ `e206c00a`(2026-08-27 18:49 +0800)。 +> +> **示例真实性**:§2.3 / §2.4 / §3.9 / §3.10 / §3.11 / §7.1 / §9.2 / §9.3 的示例与 help 区块为**实测输出**(`backup` 的绝对路径目录部分省略为 ``);§7.3 的 `Address` / `TxID` / `Fee` 已标注为设计稿;其余示例沿用原文。 +> **family 标注列已追平**——修订 3 的 `(TRON only)` / `(EVM only)` 全大写词表实作已于 `e206c00a` 落地,全量 help 扫描小写残留为 0,该列现已是实测值。 +> +> **本轮未做**:§10 的**命令层描述 / Args / Examples 重贴**(38 个命令层区块),见 §10 开头的待办说明。全局旗标文案(`--network` / `--timeout`)已于修订 4 回贴完毕。 + +--- + +## 范围与命令一览 + +### 本版主题与非目标 + +| 主题 | 范围 | 非目标(本版明确不做) | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **EVM 第一档:账户地址层** | EVM 地址编解码、family 派生、`create` / `import` 五路 / `list` / `current` / `derive` / `backup` 的 EVM 适配、**`contact` 的 family 感知改造**、EVM 网络与端点配置(§1–§3) | —— | +| **EVM 第二档:只读 + 转账 + 部署** | `account balance` / `portfolio` / `info`、`token` 全组、`tx send` / `sign` / `broadcast` / `status` / `info`、`contract call` / `send` / `deploy`、`message sign`、`typed-data sign`、`block`、`chain node` / `prices`(§4–§9) | `contract info`、`account history`、**ENS 域名解析**(后续版本支持,§1.3)、交易替换(同 nonce 提速 / 取消)、**GasFree 的 EVM 端**(后续跟进)、EVM 合约多签(Safe)、**Base / Arbitrum 等二层链**(费用模型不同,后续版本支持,§2.2)、NFT | +| **Java Standard CLI 移除** | 本版**一次性删除** 39 文件 / 6,773 行 + 入口挂钩 + 24 个测试文件 + 1 篇文档;配套命令映射表、Java 版 major 号跳跃、交互式 shell 的迁移提示、社区通知(§12) | 不动 Java 交互式 shell 本身 | +| **强制启动迁移** | `wallets.json` v1 → v2 的一次性阻断式迁移,在任何命令派发之前执行(§0) | **不做降版保护**;**未知 `source.type` 视为无秘密**(§0.4) | + +### 能力矩阵(命令 × family) + +**本版不新增命令,命令总数仍为 91**,变化的是「命令 × 网络」这个维度,故不再有「命令总数对照」表。 + +> **91 而非 90**:v4.12.0 起沿用的「90 条」是一处 off-by-one。实测 `e206c00a`:逐条抓 help 得 **91** 个命令层 help,`--json-schema` 的 `commands` 长度亦为 **91**,且**下方命令树枚举的正是这 91 条**(91 条全部能在树里找到,树里也无多余项)。本版据实改为 91,`v4.12.0` 侧的同一数字与根 `CLAUDE.md` 待 `baseline-merge` 时一并订正。矩阵与运行时的命令注册同源、不是手工维护的清单,但 **help 恒为全量**——命令树与 root help 是静态的,family 专属项靠 `(TRON only)` / `(EVM only)` 标注区分(§10.1)。 + +#### 本版交付(EVM) + +| 档 | 组 | 命令 | EVM 侧要点 | +| :---: | --- | --- | --- | +| 一 | 本地钱包 | `create` / `import` 五路 / `list` / `current` / `derive` / `backup` | 一次产出两族地址(keystore 本就是 EVM 原生格式);`import watch` 自动识别 `0x…`、`import ledger` 加 `--app ethereum`;地址列按网络 family;`backup --keystore` 导出私钥时按 `--network` 选族(§3.1–3.10) | +| 一 | 本地工具 | `contact add` / `list` / `remove` | **必须改造**:条目按地址格式识别 family 并持久化;**名称与地址均全局唯一,family 不对外呈现**,`--to ` 跨族报错(§3.11) | +| 一 | 本地工具 | `config` / `networks` | 端点可读可写;`networks` 新增 `Alias` 列、`Network` 列改放规范 id;新增 header 型 RPC 凭证(`apiKeyHeader` / `apiKey`)与只读的 `aliases` 键;`config` 展示改为完整树(§2.2–2.4) | +| 一 | —— | `use` / `rename` / `delete` / `change-password` / `encoding convert` / `address generate` / `backup --records` | **无改造**:与 family 无关,或已同时输出两族地址;仅在 `encoding convert` / `address generate` 的 help 补一句「这是编码工具、与账户模型无关」的边界说明(§3.12 给出两句原文与完整 help) | +| 二 | account | `balance` / `portfolio` / `info` | `info` 给 Balance / Nonce / Type(§4.1–4.3) | +| 二 | token | `balance` / `info` / `add` / `list` / `remove` | ERC20;`kind` 扩 `erc20`;探测兼容 bytes32(§5) | +| 二 | tx | `send` / `sign` / `broadcast` / `status` / `info` | gas 四选项、nonce、RLP raw tx(§6) | +| 二 | contract | `call` / `send` / `deploy` | 不依赖链上 ABI;deploy 地址确定性算出(§7) | +| 二 | 签名 · 链信息 | `message sign` / `typed-data sign` · `block` / `chain node` / `chain prices` | EIP-191 / EIP-712;`prices` 给 base / priority / gas price + 转账折算(§8–§9) | + +#### 本版不做(EVM) + +| 命令 | 结论 | 为什么 | +| --- | :---: | --- | +| `account history` | 后续 | JSON-RPC **没有**按账户查历史的接口;可用的三条路子(Etherscan 兼容 API / Blockscout / 服务商增强方法)**互不兼容**,且能力取决于用户配了哪个端点;前置=新增 `explorer` port + `networks..explorerUrl`(key 可选)+ Requires 行(§4.4) | +| `contract info` | 后续 | 链上**不存 ABI**,只有字节码;同样依赖 explorer,或要求用户自带 ABI 文件 | +| ENS 域名解析(`--to .eth`) | 后续 | 不是一条命令,是 `--to` / `--account` 的收款人形态;本版只接受 `0x` 地址。后续版本支持,解析结果必须回显、不静默替换(§1.3) | +| `gasfree` 3 条 | 后续 | **GasFree 本身正从 TRON 扩展到 Ethereum 及 EVM 兼容链**,届时它就不再是 TRON 专属服务,`info` / `transfer` / `trace` 三条的命令形状两族通用,我方跟进接入即可。增量在:开放平台端点与鉴权(是否与 TRON 端同一套 API Key)、签名结构由 TIP-712 换成 EIP-712、费用口径(TRON 端是一次性激活费 + 每笔服务费从 USDT 扣)。**前置=GasFree 的 EVM 端正式可用**,具体档位待其上线时间明确后再定 | +| `permission` 2、`tx approvals` / `multisig`(4) | 未定档 | TRON 多签是**协议层**权限;EVM 多签是 **Safe 等合约**,属应用层,形态是 Safe 交易构造与协同签名,与现有多签命令不共用模型 | +| `account activate` / `set`(2) | 不做 | EVM 账户无需激活、链上无账户名 | +| `stake` 8、`chain params`(9) | 不做 | EVM 无质押换资源;协议参数由硬分叉决定,不可查询 | +| `proposal` 5、`witness` 3、`vote` 3、`reward` 2(13) | 不做 | EVM 无链上提案 / SR 选举 / 出块分红 | +| `asset` 6、`exchange` 6(12) | 不做 | TRC10 与 Bancor 池是 TRON 协议原生;EVM 对应物全在合约层(ERC20 已由 `token` 组覆盖,DEX 属应用) | +| `contract` 治理 4 条 | 不做 | EVM 无 origin energy limit / user resource percent / 链上 ABI 这些概念 | + +### 命令树(v4.13.0) + +命令集与 v4.12.0 **完全相同**(本版不增删命令),变的是标注口径:`(TRON only)` = TRON 专属,**无标注 = 两族通用**。因此 `account info`、`contract deploy`、`chain prices` 从 `(TRON only)` 行移到无标注行(本版 EVM 交付,§4.3 / §7.3 / §9.3)。 + +``` +wallet-cli 多链 CLI 钱包(TRON + EVM,--network 选链) +│ +├─ Common Commands ── 高频入口 +│ ├── create 新建 HD 钱包(BIP39,一次产出两族地址) +│ ├── import 导入钱包(mnemonic/private-key/ledger/watch/keystore) +│ └── list 列出钱包 / 账号 +│ +├─ Management Commands ── 链上资源(--network 选链;(TRON only)=TRON 专属) +│ ├── account balance | portfolio | info +│ │ history | activate | set (TRON only) +│ ├── permission show | update (TRON only) +│ ├── token balance | info | add | list | remove +│ ├── tx send | broadcast | status | info | sign +│ │ approvals | multisig (TRON only) +│ ├── gasfree info | transfer | trace (TRON only) +│ ├── contract call | send | deploy +│ │ info | clear-abi | set-origin-energy-limit +│ │ set-user-resource-percent | create2 (TRON only) +│ ├── proposal list | show | create | approve | delete (TRON only) +│ ├── witness create | update | set-brokerage (TRON only) +│ ├── asset issue | update | participate | unfreeze | info | list (TRON only) +│ ├── exchange create | inject | withdraw | trade | show | list (TRON only) +│ ├── stake freeze | unfreeze | withdraw | cancel-unfreeze (TRON only) +│ │ delegate | undelegate | info | delegated +│ ├── vote cast | list | status (TRON only) +│ ├── reward balance | withdraw (TRON only) +│ ├── chain node | prices +│ │ params (TRON only) +│ ├── message sign +│ ├── typed-data sign +│ └── block +│ +└─ Commands ── 其余本机命令 + ├── use / current(--qr) / rename / derive / delete / config / networks + ├── backup (--keystore) | --records (本地) + ├── change-password + ├── encoding convert 编码/地址互转(纯本地) + ├── address generate 随机密钥对(纯本地) + └── contact add | list | remove 收款人通讯录(纯本地) +``` + +### 横切约定(本版新增,全文有效) + +- **一个账户、多链地址**:账户是链无关的身份。`--account` 选谁、`--network` 选哪条链,两者正交。 +- **原生币单位由 family 决定,币种名称由网络决定**:**单位与小数位**归 family——TRX/sun(6 位)、ETH/wei(18 位);**币种名称**(TRX / ETH / BNB)归**网络**——`eip155:1` 与 `eip155:56` 同族但币种是 ETH 与 BNB,族级 symbol 对其中一条链必然是错的。分界线是:**族拥有编码与算术规则,网络拥有那条链的身份**(§2.2 的内置网络表因此有「原生币」列)。json 字段随单位命名:TRON 侧 `feeSun` 不变,EVM 侧 `feeWei`。 +- **gas 价格单位一律 gwei**:命令行接受 `--max-fee 25` 与 `--max-fee 25gwei` 两种写法(后缀大小写不敏感);**`wei` / `ether` 等其他单位点名拒绝**并报 `invalid_value`,不静默改读——`--max-fee 0.01ether` 与 `--max-fee 25` 差十亿倍,打错的代价就是实付费用差十亿倍。text `21.0 gwei`,json 给 wei 整数字符串。**本条同时列入 §1.4 的规则表**,因为各命令引用的是那张表。 +- **family 不匹配显式报错**:账户与网络不符、raw tx 与网络不符,统一 `family_mismatch`。 +- **签名能力按账户类型分档,全部 ✍️ / 🔒 命令通用**:软件账户(seed / private-key / keystore)本地解密后签名;**观察账户没有私钥,一律拒绝签名,报 `watch_only_no_signer`**;Ledger 账户在设备上签名,需设备连接并解锁,少数交易类型 Ledger app 不支持时报 `ledger_unsupported`。各命令小节不再重复这条。 +- **EVM 侧不为既有命令新增 family 专属字段**:EVM 沿用该命令在 TRON 下已有的字段集,只换值与单位;某个字段两族语义不同才按 family 取舍(如 `account info` 的 `Nonce` / `Type`)。**全文新增的输出字段限于下列各处,除此之外不得新增**:`list` 的 `derivationPath`(§3.7)、`networks` 的 `Alias` 与 `Endpoint` 列(§2.3)、`config` 的 `networks.` 对象形状与 `aliases`(§2.4)、`tx status` / `tx info` 的 `Confirmations`(§6.4–6.5)、`portfolio` 代币条目的 `id` 与两个价格状态字段 `priceUnavailable` / `balanceUnavailable`(§4.2)、`account info` 的 `decimals`(§4.3)、`chain prices` 的 `feeModel`(§9.3)、`tx info` 透传的 `transaction` / `receipt` 两个原始对象(§6.5)、`tx broadcast --dry-run` 的 `checks`(§6.3)。**其中两族同时生效的是**:`derivationPath`、`Confirmations`、`id`、价格状态字段;`feeModel` / `decimals` / 透传对象 / `checks` 按各族既有形状对齐(TRON 侧 `tx info` 本就透传 `transaction` / `info` 两个原始对象)。既有的字段级不一致(如 `token info` 的 `totalSupply` 在 json 与 help 里有、text 没有)本版不处理,见 §5.2。 +- **text 输出只有四种形状**:无标题的 `<字段> <值>` 块 · `<标题>: <值>` + 缩进字段 · `<标记> <动词摘要>` + 缩进字段(标记 ✅/❌/⏳/⚠️/❓)· **Markdown 管道表格**(含 `| --- |` 分隔行)。本版全部 EVM 示例按此书写。 +- **EVM 写命令继承既有横切**:`--dry-run` / `--sign-only` / `--build-only` / `--wait` / `--wait-timeout` 语义不变。 +- **family 专属 flag 在 help 里全量展示、按族标注,不按网络裁剪**:help 是**静态**的——`--network` 不影响它,渲染层把各 family 的 flag 合并后一次列全。故 TRON 的 `--asset-id` / `--fee-limit` / `--permission-id` 与 EVM 的 `--gas-limit` / `--max-fee` / `--priority-fee` / `--nonce` **会同时出现在 `tx send --help` 里**,各自行尾标 `(TRON only)` / `(EVM only)`——沿用 root help 已在用的组级标注体例(`stake … (TRON only)`)。**运行时仍按 family 严格校验**:EVM 网络上传 `--fee-limit` 会被该网络拒绝,报 `invalid_option`。 + +--- + +## 0. 启动前置:强制迁移 + +> **本版新增的机制,先于一切命令发生。** 位置反映执行顺序:读者读到任何命令规格之前,就该知道有这道闸门。 + +### 0.1 为什么需要 + +`ChainAddresses` 是**完整类型**,加入 `evm` 之后,既有的 `wallets.json` 对自己的类型失效。两条路: + +| 方案 | 代价 | +| --- | --- | +| 类型改成 `Partial` | **每一处读取**都要处理「这一族可能没有」 | +| **一次迁移到齐**(采用) | 一次阻断式启动迁移 | + +选后者,也是 §1.2 拒绝 `derive --path` 的同一个理由——迁移之所以能保持 `ChainAddresses` 完整,正是因为每个账户都两族齐备。 + +### 0.2 闸门的位置与阻断范围 + +- 在 **`--help` / `--version` 短路之后**、**任何命令派发之前**执行。 +- 只要有注册文件落后版本,**任何命令都不跑**。 +- 升级完成后重跑为 **no-op**。 + +> ❌ **实作与本节相反,需改实作**(`e206c00a` 实测):闸门跑在 help/meta **之前**——stale v1 文件下 `wallet-cli --help` **不打印 help**、`wallet-cli -V` **不打印版本**,两者都被闸门接管。源码自述亦然(`src/bootstrap/migration-gate.ts` 开头:"Runs on every invocation **before help/meta handling**, argument validation, or command dispatch")。 +> +> **建议改实作、而非改本节**:文件落后时连 `--help` 都不给,等于把用户在故障现场唯一的自助工具也关掉;且 §0.5 只承诺「连 `list` 都不能跑」,实际比承诺的更狠。`--help` / `--version` 不读也不写钱包状态,没有必须挡的理由。 + +### 0.2.1 机器可读输出(`-o json`) + +闸门在 json 模式下**不打印散文,而是产出一个正规信封**——这是 agent 侧唯一能可靠判读迁移发生过的途径: + +```json +{ "schema":"wallet-cli.result.v1","success":true,"command":"migration","data":{ "upgraded":true,"files":[{ "path":"…/wallets.json","from":1,"to":2,"backup":"…/wallets.json.v1.bak" }],"originalCommandExecuted":false },"meta":{ "durationMs":20,"warnings":[] } } +``` + +> 以上为实测输出(`e206c00a`)。**退出码 0**,且 `success: true`——迁移本身办成了,故不是错误;**`originalCommandExecuted: false` 是关键字段**:它告诉调用方「你原本那条命令没跑,请重发」。text 模式下对应的是末行 `Upgrade complete. Please run your command again.`。 +> +> 本信封的 `command:"migration"` 与 `data` 四个字段是本版新增的机器契约面,**不受「横切约定」那条输出字段封闭清单的约束**(该清单列的是既有命令的字段增量,闸门不是命令)。无法取得主密码时不走本信封,而是 `migration_required` + **退出码 2**(§0.4)——text 与 json 两模式的退出码一致,均实测。 + +> ❌ **text 形态需改实作**:闸门当前的 text 输出用的是 `==> …` 前缀段、`✓`、以及 `🎉 Upgrade complete. Please run your command again.`,**三者都不在「横切约定」允许的四种 text 形状之内**,`🎉` / `✓` 也不在标记词表(✅/❌/⏳/⚠️/❓)之内。 +> +> 应改为既有的「**`<标记> <动词摘要>` + 缩进字段**」形状——完成回执用 `✅`,告知段(stderr)用无标题键值块。**这不是排版洁癖**:四种形状是 text 渲染层的封闭集合,多一种就多一处解析器与后续命令都对不上的地方,而闸门恰恰是**每个用户升级后见到的第一屏**。 + +### 0.3 成本不对称是设计的核心 + +| source 类型 | 是否持有本机秘密 | 迁移行为 | +| --- | :---: | --- | +| `seed` / `privateKey` | 是 | **需要主密码**,走同意流程 | +| `ledger` / `watch` | 否 | **不问主密码,自动升级**(仍照常打印告知段与完成回执) | + +**只有 watch / ledger 的用户从未设过主密码**——若此处误问,他将无解。这条不对称不是优化,是可用性的下限。 + +> **「不提示」指的是不问主密码,不是无输出**(2026-08-28 PM 拍板,按实作)。原文「完全静默升级,不提示」有歧义,已改写。`e206c00a` 实测:watch-only 的 v1 文件迁移**跳过主密码那一步**,但仍在 stderr 打完整告知段(检测到旧格式 / 文件路径 / v1→v2 / 备份路径 / 只跑一次)、在 stdout 打完成回执。 +> +> **告知段该留**——迁移会改写钱包文件并留下一个永不自动清除的 `.bak`,这件事对 watch / ledger 用户同样成立;省掉主密码是因为他没有秘密可解,不是因为这件事不值得告诉他。 + +### 0.4 同意流程与其余规则 + +**需要主密码时,闸门先说明、再要求答复,答完才问密码**:说清「哪个文件、v几到v几、备份留在哪、只跑一次」。 + +> 旧行为是直接跳一个没有前因后果的 `Master password (hidden):`——没有理由、没说要改写文件、除了 Ctrl+C 没有拒绝的方式。**说明走 stderr**,stdout 保留给命令输出。 + +| 规则 | 内容 | +| --- | --- | +| 原子性 | **全成或全不成**(同一个事务) | +| 备份 | 迁移前留 `.v.bak`,**永不自动清除**——既有的事务机制只防崩溃,不防「成功但写错」 | +| 无 TTY | 报 `migration_required`(**退出码 2**,text / json 两模式一致),但**接受 `--password-stdin`**,CI 可自愈 | +| 密码错误 | TTY 下最多三次然后 `auth_failed`;**失败不留 `.bak`** | +| 全新安装 | 文件不存在 → 回报为当前版本,闸门放行 | +| `version` 缺失或非法 | `encoding_error`,**绝不当成第 0 版**——对一个装着钱包状态的文件,跑一个针对未知结构的迁移比挡下来更危险 | +| 迁移产出 | **== 新建产出**:重跑 `create` / `import` 用的同一组 derive 函数,不由既有 TRON 地址反推。已实测迁移后的 `wallets.json` 地址表与本版全新建立的**逐字节相同** | +| 其余注册文件 | `contacts.json` 与 `tokens.json` **不需要迁移**——前者落盘格式本来就是 family 分键、每笔自带 `family`(只需放宽校验),后者以 network id 为键,EVM 只是多几个键 | + +**两个「决定不做」的边界**(如实反映,不是待办): + +| 边界 | 内容 | +| --- | --- | +| **无降版保护** | 版本高于本体的文件不算落后,直接放行 | +| **未知 `source.type` 视为无秘密** | 不当成需要主密码的类型 | + +两者是同一个形状:**未知的东西被当成安全的东西放过去**。决定不挡的理由是今日皆无实害(只有四种 source type,且 v2 是最新版),而挡下来要付出的是**把用户锁在自己文件外面**的风险。 + +### 0.5 锁死后果(必须写进 release note) + +**忘记主密码且钥匙圈内有本机秘密者,连 `list` 都不能跑,且每次执行都会再挡一次。** + +这是**刻意接受**的——该用户本来就已无法签名/备份/导出,闸门没有新增损失,只是让它更早、更明显。 + +> **用 `--password-stdin` 的用户看不到屏幕上的说明,release note 是唯一告知管道**;同时应点明迁移会留下 `wallets.json.v1.bak` 且永不自动清除。 + +### 0.6 验证 + +47 项非交互情境 + 6 项真实 TTY 情境(pty 驱动)全数通过。 + +--- + +## 1. EVM 账户与密钥模型 + +### 1.1 账户模型 + +**账户是链无关的身份,同一个账户在每条链上按该链的 BIP44 coin type 各派生一把 key。** 与 OKX、Trust Wallet 等主流多链钱包一致,也与既有的账户存储结构一致。 + +| 账户来源 | TRON 地址 | EVM 地址 | 私钥关系 | +| ---------------------------------- | ------------------- | ------------------ | ---------------- | +| `create` / `import mnemonic`(seed) | `m/44'/195'/N'/0/0` | `m/44'/60'/0'/0/N` | 两族各一把,**不同** | +| `import private-key` | 该 key 的 TRON 编码 | 该 key 的 EVM 编码 | **同一把** | +| `import watch` | 仅当地址是 `T...` | 仅当地址是 `0x...` | 无(单 family) | +| `import ledger` | `--app tron` | `--app ethereum` | device(单 family) | + +### 1.2 派生路径 + +**每族跟随各自生态惯例,账户序号挂的层级不同。** + +``` +TRON m/44'/195'/'/0/0 序号在 account 层(保持现状,不动存量) +EVM m/44'/60'/0'/0/ 序号在 address_index 层 +``` + +以太坊标准路径为 `m/44'/60'/0'/0/x`,MetaMask、Trezor、Rabby 及绝大多数 dApp 钱包递增 address_index;走 account 层的只有 Ledger Live 一支。**跟随生态优先于跨族形状对称**——同 §3.6 Ledger EVM 用 Live 模板。 + +**互导手段**(覆盖从 Ledger Live / Legacy 等别家钱包迁入): + +| 手段 | 命令 | 说明 | +| --- | --- | --- | +| 硬件账户显式路径 | `import ledger --path ` | 指定完整路径注册硬件账户,绕开默认模板(§3.6) | +| 事后核对 | `list -o json` 的 `derivationPath` | 看出账户用的哪套模板 | + +> **软件账户本版只支持默认模板**:`derive` 不提供 `--path`。原因是显式路径会产生「单 family 的 seed 账户」——`Source.seed.addresses` 的 `ChainAddresses` 是完整类型,单族槽位表达不了,只能改成 `Partial` 或加槽位判别式;两者都会反噬本版的强制启动迁移(§0),而该迁移能保持 `ChainAddresses` 完整,正是因为每个账户都两族齐备。同时 `derivationPath` 会从「由 index 算出」变成「必须落盘」,等于在本版**第一次**强制迁移的同时再加一项 schema 变更。 +> +> 被挡住的只有「只有助记词、且资产在 Ledger Live / MEW 模板上」的用户——属功能缺口,不是资产风险;硬件用户走 `import ledger --path` 不受影响。绕行手段是用外部工具按目标路径导出私钥后 `import private-key`。 + +### 1.3 地址表示 + +| 项 | 规则 | +| --- | --- | +| 输出 | EVM 地址一律按 **EIP-55 校验和大小写**输出(text 与 json 一致) | +| 输入·全小写 / 全大写 | 视为**未带校验和**,接受 | +| 输入·混合大小写 | **必须通过 EIP-55 校验**,不匹配一律报 `invalid_address`、拒绝执行 | +| 输入·其它 | 必须带 `0x`、长度与十六进制合法性校验失败报 `invalid_address` | +| family 识别 | 由地址编解码器自动识别(`T...` → tron、`0x...` → evm),`--account 0x...` 可直接定位账户 | + +> **混合大小写必须校验**:协议层地址不区分大小写,但一个带校验和的地址被改动一位后校验必然失败——放行等于把「打错一位」和「剪贴板被替换」这两类事故直接变成资金损失。MetaMask、Trust Wallet 与硬件钱包均拒绝校验和不匹配的地址,ethers 的 `getAddress()` 同样抛错。我方对齐这一行为。 +> +> **ENS 本版不解析,后续版本支持**:`--to vitalik.eth` 在本版报 `invalid_address`;需要的用户自行解析后传入 `0x` 地址。**这是排期问题、不是拒绝**——ENS 是 EVM 生态的默认收款人形态,长期缺席不合理。 + +### 1.4 金额与精度显示 + +原生币 18 位小数(TRON 6 位),全部 18 位铺在 text 里既不可读也无意义,故定: + +| 场景 | 规则 | +| --- | --- | +| text 原生币 / 代币 | 最多保留 **6 位小数**,尾随零去除(`12.3456 ETH`、`0.25 ETH`) | +| text 非零但小于显示精度 | 显示 `<0.000001`,**绝不显示 `0`** | +| json | 恒给**最小单位整数字符串**(wei / sun / 代币基本单位),不做任何截断 | +| 命令行输入 | 按人话单位接受完整精度(`--amount 0.000000000000000001` 合法),超出该代币 decimals 才报 `invalid_amount` | +| USD 价格与估值 | **不适用上面的规则**:估值固定 2 位小数、单价 4 位,按 USD 惯例补零(`$2,500.00`、`$0.9998`) | +| gas 价格(`--max-fee` / `--priority-fee`) | 命令行**一律按 gwei 读**:`25` 与 `25gwei` 等价(后缀大小写不敏感);**`wei` / `ether` 等其他单位点名拒绝**并报 `invalid_value`。text 按 gwei 显示,json 给 wei 整数字符串 | +| 千分位 | **text 里的整数部分一律加千分位**——金额(`$41,004.35`)、区块号(`#11,204,113`)、gas(`1,204,551 gas`)、字节数(`3,124 bytes`)同此一条规则。**json 一律不加**(`"valueUsd":"41004.35"`),那是给机器解析的 | + +本规则适用于全文所有出现金额的输出:`account balance` / `portfolio`(§4.1–4.2)、`token balance`(§5.1)、`tx send` 的转账额与 Fee 行(§6.1)、`contract send` 的 `Allowance`(§7.2)、`chain prices` 的 `Transfer cost`(§9.3)。 + +> 「非零不显示 0」是关键:余额 1 wei 若显示成 `0 ETH`,用户会认定账户空了。截断只发生在 text,json 与实际转账金额始终是精确整数。 +--- + +## 2. 网络与配置 + +### 2.1 网络 ID 与别名 + +**规范 id = `<命名空间>:<链自身的 id>`,命名空间取 CAIP-2 的写法。** EVM 侧命名空间为 `eip155`,冒号后那一段就是 EIP-155 的数字 chain id(`eip155:1`、`eip155:11155111`、`eip155:56`、`eip155:97`);TRON 侧同样取 CAIP-2 的写法——**本版将 TRON 规范 id 由网络名改为十进制 chain id**: + +| 新规范 id | 旧形式(v4.12.0 及以前) | 别名 | +| --- | --- | --- | +| `tron:728126428` | `tron:mainnet` | `tron` | +| `tron:3448148188` | `tron:nile` | `nile` | +| `tron:2494104990` | `tron:shasta` | `shasta` | + +chain id 取**创世块哈希末 4 字节**(TIP-474),十进制渲染——与 `eip155` 用十进制、与 TRON 在 `ethereum-lists/chains` / ChainList 的既有登记一致。 + +> **为什么改**:原口径「TRON 没有数字 chain id」是**事实错误**——它有(TIP-474)。规范 id 取网络名是历史遗留,而 `tron` 命名空间的 CAIP-2 规范已在立项,定十进制为规范形式并明确 **`0x` 十六进制不是合法 CAIP-2 引用**。两侧都用 CAIP-2 写法之后,「规范 id = CAIP-2」这条规则才对全族成立,`--network` 的取值集合也不再分族记忆。 + +> **命名空间不是 family。** `eip155` 是链标识体系里的命名空间,而 family 是我方的适配层分组,值仍为 `evm`——json 的 `chain.family` 恒为 `evm`、`config.yaml` 自配网络的 `family` 字段也填 `evm`,只有 `chain.network` 这类**网络 id** 用 `eip155:` 前缀。两者不同名是有意的:将来若同一个 family 要覆盖非 EIP-155 的链,不必再改一次 id 形式。 + +这样定的理由是**可寻址性**:chain id 由链自己定义、全网唯一且永不变,于是**任何 EVM 链无需我方先起名字就能被指定**——用户配一个 Polygon 端点,直接 `--network eip155:137` 即可,不必等我方在代码里登记一个 `eip155:polygon`。**前缀直接取 `eip155` 而不是我方的 family 名,是为了让这个 id 与业内既有写法逐字相同**——CAIP-2 的 `eip155:1`、WalletConnect / SIWE 的链标识都是这个形式,用户与 agent 从别处拿到的 id 可以原样贴进 `--network`,我方不做一层翻译。EIP-3085 的 `chainId` 同样以数字为准。 + +**每条内置网络另给一个别名**,因为 `eip155:1` 不可读,而人要在命令行里天天敲它。**别名是不带命名空间前缀的简写**,与 hardhat(`--network sepolia`)、foundry(`--chain sepolia`)的习惯一致: + +| 规范 id | 别名 | 兼容别名(历史 id) | +| --- | --- | --- | +| `tron:728126428` | `tron` | `tron:mainnet` | +| `tron:3448148188` | `nile` | `tron:nile` | +| `tron:2494104990` | `shasta` | `tron:shasta` | +| `eip155:1` | `ethereum` | —— | +| `eip155:11155111` | `sepolia` | —— | +| `eip155:56` | `bsc` | —— | +| `eip155:97` | `bsc-testnet` | —— | + +> **「兼容别名」列不是新机制**,就是别名簿里的普通记录——上表内置全量因此为 **10 条**(7 条短别名 + 3 条历史 id)。 + +#### 历史 id 的兼容 + +破坏面只在**输出侧**,输入侧零成本: + +| 面 | 处置 | +| --- | --- | +| **`--network` 输入** | `tron:mainnet` / `tron:nile` / `tron:shasta` **永久保留为别名**,与 `tron` / `nile` / `shasta` 并列进别名簿。老脚本一个字不用改 | +| **`config.yaml`** | `networks..*` 的键与 `defaultNetwork` 的值由 **§0 的强制启动迁移**一并改写;`aliases` 里指向旧 id 的用户自定义别名同步重定向 | +| **json `chain.network`** | ⚠️ **这是唯一的破坏**——值由 `tron:nile` 变为 `tron:3448148188`。按旧值做分支的 agent 脚本必须改 | +| **触达** | 强制迁移是阻断式的、且在 json 模式产出正规信封(§0.2.1),是**唯一能保证被看见**的渠道。迁移信封的 `data` 须列出 `networkIdsRemapped: [{from, to}]`,让 agent 能程序化得知这次改名 | + +> **别名簿容得下这三条**是因为它本就是 `别名 → 规范 id` 的扁平表(见下):旧 id 降级为别名不需要新机制,只是多三条记录。§2.1 的「解析顺序先查规范 id、后查别名簿」不变,`tron:nile` 走别名簿命中同一张网络描述符。 + +**`--network` 接受两种写法**,运行时一律归一到规范 id:规范 id(`eip155:11155111`)与别名(`sepolia`)。**解析顺序固定为「先查规范 id、后查别名簿」**,由此得到一条比消歧更重要的保证——**别名永远不能遮蔽规范 id**:`--network eip155:1` 恒为以太坊主网,无论用户在 `config.yaml` 的别名簿里写了什么。 + +不设 **带命名空间前缀的别名**(`eip155:sepolia`):它存在的理由是「别名重名时消歧」,而下面的别名簿让重名在结构上不可能发生。`eip155:sepolia` 两次查找都不中,报 `unsupported_network`。 + +别名是可读性糖、可能随生态改名而调整(如 BSC 官方已更名 BNB Smart Chain),**规范 id 永不变**——所以机器面(json 的 `network` 字段、`config` 的 `networks..*` 键)只认规范 id,agent 与脚本不要拿别名做匹配。 + +**别名以「别名簿」这一张扁平表实现**——`config.aliases` 是 `别名 → 规范 id` 的一层 map,别名**不是**挂在网络描述符上的字段。上表七条即其内置全量。 + +这个形状让三条原本要写死并校验的规则**结构上自动成立**,不需要任何校验代码: + +| 原规则 | 在扁平表下为何自动成立 | +| --- | --- | +| 别名在全部 family 范围内唯一 | 一张 map 不可能有重复的键 | +| family 名是保留字(不设 `evm` 别名) | 表里没有 `evm` 这个键。`tron` 作为 `tron:728126428` 的别名是表里的一条普通记录 | +| 用户自配网络不自动获得别名 | 没写进表就没有别名 | + +配套两点: + +- **匹配只发生在 `--network` 解析这一步**,之后全流程只见规范 id。 +- **别名指向未知网络时,错误同时点名别名与它的目标**——`alias "polygon" points at unknown network eip155:99999`,而不是只说 `unknown network: polygon`(后者会让用户去检查自己敲的字,而问题在别名簿里)。 + +### 2.2 内置网络与 RPC 端点 + +| 规范 id | 别名 | family | 原生币 | 测试网 | feeModel | 端点主机 | +| --- | --- | --- | --- | :---: | --- | --- | +| `tron:728126428` | `tron` | tron | TRX | | `tron-resource` | `api.trongrid.io` | +| `tron:3448148188` | `nile` | tron | TRX | ✅ | `tron-resource` | `nile.trongrid.io` | +| `tron:2494104990` | `shasta` | tron | TRX | ✅ | `tron-resource` | `api.shasta.trongrid.io` | +| `eip155:1` | `ethereum` | evm | ETH | | `evm-gas` | `ethereum-rpc.publicnode.com` | +| `eip155:11155111` | `sepolia` | evm | ETH | ✅ | `evm-gas` | `ethereum-sepolia-rpc.publicnode.com` | +| `eip155:56` | `bsc` | evm | BNB | | `evm-gas` | `bsc-dataseed.bnbchain.org` | +| `eip155:97` | `bsc-testnet` | evm | BNB | ✅ | `evm-gas` | `bsc-testnet-dataseed.bnbchain.org` | + +> **「原生币」是网络级字段,不是 family 级**(§横切约定):`eip155:1` 与 `eip155:56` 同族而币种是 ETH 与 BNB,从 family 表读会把 BSC 上的 0.5 BNB 显示成 `0.5 ETH`。本列的存在也让将来接入 Polygon 时类型会强制填写,不会默默继承 ETH。 +> +> **「测试网」标记决定估值行为**(§4.2):标记为测试网的四条网络,币价与代币价一律为 **0**,且**不发任何外部请求**。**未申报为测试网的用户自配网络维持 `null`**——不知道 ≠ 不值钱。 +> +> **端点主机名随官方域名迁移更新**:BSC 的 dataseed 已由 `binance.org` 迁至 `bnbchain.org`,表中为迁移后的值。 + +> 本文档 §3–§9 的示例一律用**别名**书写(`--network sepolia`),与用户实际会敲的形式一致;json 示例里的 `network` 字段则一律是规范 id。 + +**本版内置的 EVM 网络限于一层链:Ethereum 与 BNB Smart Chain,各带一条测试网。** 每条主网都配测试网是硬要求——签名、nonce、gas 估算这些东西不该拿主网真钱去试,`bsc` 与 `bsc-testnet` 的关系等同 `ethereum` 与 `sepolia`。 + +**Base、Arbitrum、Optimism 等二层链后续版本支持**,本版不内置。原因是**费用模型不同,不是加个端点的事**:L2 上一笔交易的成本 = L2 执行费 + **把数据写回 L1 的 data fee**,后者由 L1 的 blob / calldata 价格决定,且各家 L2 的取值方式不一样(OP Stack 有 `GasPriceOracle` 预编译,Arbitrum 把它折进 gas 用量)。现有的 `evm-gas` 费用模型只算 `gasLimit × gasPrice`,**在 L2 上会系统性低估**——`tx send --dry-run` 报的费用比实际扣的少,这比不支持更糟。后续版本要新增 `evm-l2-gas` 费用模型并逐条对齐各 L2 的取数方式。 + +> **未内置的 EVM 链仍可指定,但费用估算不保证**:规范 id 的形式让任何 EVM 链开箱可寻址(`--network eip155:8453` + 自配端点即可查询与转账)。查询类命令与转账本身没有问题,**只有费用估算在 L2 上会偏低**。本版不阻止这种用法,也不为它背书。 + +**自配网络的必填字段**(写在 `config.yaml` 的 `networks.` 下): + +| 字段 | 必填 | 说明 | +| --- | :---: | --- | +| `family` | 是 | 必须是受支持的 family(本版为 `tron` / `evm`) | +| `chainId` | 是 | EVM 侧为 EIP-155 数字 chain id,与规范 id 后半段一致 | +| `nativeSymbol` | 是 | 该链原生币名称;缺了没有可回退的正确值(见上表说明) | +| `httpEndpoint` | 实务上必填 | 未内置的网络没有默认端点 | +| `capabilities` | 否 | 缺则视为空——没有额外特性是正常情况,不是错误 | +| `testnet` | 否 | 缺则视为主网,估值走真实价格源 | + +**校验发生在载入 `config.yaml` 的当下**:缺 `family` / `chainId` / `nativeSymbol`,或 family 不受支持,一律报 `invalid_value` 并**点名是哪条网络的哪个字段**。这条规则的意义在于错误的形态——config 的错误必须以 config 错误的形式、在读文件的当下报出;先前写错的后果是 bootstrap 崩溃,任何命令都回一个没有线索的 `internal_error`。 + +**四条 EVM 网络都内置可用端点,装完即可查询与转账**,不必先做配置。与 TRON 的差别不在有没有默认,而在谁运营:TronGrid 是链方第一方端点,EVM 侧没有单一权威运营方,内置的是第三方公共 RPC——**有限流、无 SLA、可能下线**,且默认会把查询地址暴露给该服务商。因此生产环境与高频调用建议换成自建节点或商用网关: + +```bash +wallet-cli config set networks.ethereum.httpEndpoint https:/// +``` + +`sepolia`(`eip155:11155111`)是本版冒烟测试网(等同 TRON 侧 Nile 地位),示例一律用它。 + +### 2.3 `networks` + +```bash +$ wallet-cli networks +| Network | Alias | Family | Chain id | Fee model | Endpoint | +| --------------- | ----------- | ------ | -------- | ------------- | ----------------------------------- | +| tron:728126428 | tron | tron | 728126428 | tron-resource | api.trongrid.io | +| tron:3448148188 | nile | tron | 3448148188 | tron-resource | nile.trongrid.io | +| tron:2494104990 | shasta | tron | 2494104990 | tron-resource | api.shasta.trongrid.io | +| eip155:1 | ethereum | evm | 1 | evm-gas | ethereum-rpc.publicnode.com | +| eip155:11155111 | sepolia | evm | 11155111 | evm-gas | ethereum-sepolia-rpc.publicnode.com | +| eip155:56 | bsc | evm | 56 | evm-gas | bsc-dataseed.bnbchain.org | +| eip155:97 | bsc-testnet | evm | 97 | evm-gas | bsc-testnet-dataseed.bnbchain.org | +``` + +> **六列,`Network` 放规范 id、`Alias` 单列一列。** 取舍是「一列还是两列」:只显示别名的话,用户看得到自己要敲什么,却无从得知机器面该用什么;而**规范 id 是稳定值,别名是可读性糖、会随生态改名而调整**(§2.1)。两列则两者都看得到,原本「用户要看到自己该敲什么」的顾虑没有损失。没有别名的用户自配网络,`Alias` 列为空。 +> +> **`Chain id` 是本版由 `Chain` 改名**,以对应规范 id 的后半段——那个值就是规范 id 冒号后的部分。 +> +> **`Endpoint` 是本版新增列,且只输出主机名,不输出完整 URL。** 理由是**端点路径常夹带 API key**(`…/v2/`、`…?apikey=`),而 `networks` 是列表输出、不是机密接口——它的结果会被贴进 issue 与 CI log。裁到主机名是唯一不需要猜「哪一段是密钥」的切法。要看完整 URL 走指名读取:`config networks..httpEndpoint`(§2.4)。 + +**Help 输出** + +> **相对现状**:描述行由 `List known networks` 扩写为含 family / chain id / fee model / endpoint host,并说明 `Network` / `Alias` 两列的分工与「端点只给主机名」。 + +```text +$ wallet-cli networks --help + +Usage: + wallet-cli networks [options] + +List known networks with their family, chain id, fee model and endpoint host. +Network is the canonical id (family:chain-id); Alias is the short name --network +also accepts. Endpoints are shown as hosts only. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli networks +``` + +### 2.4 `config` 新增项 + +| key | 读 | 写 | 默认 | 说明 | +| --- | :---: | :---: | --- | --- | +| `networks..httpEndpoint` | **本版新增** | **本版新增** | 内置值 | 该网络的 RPC 端点;此前只能手工编辑 `config.yaml`。`` **别名与规范 id 都接受**,写入时归一为规范 id。**指名读取回完整 URL**(列表输出裁成主机名,见下) | +| `networks..apiKeyHeader` | **本版新增** | **本版新增** | 无 | header 型 RPC 凭证的 **header 名称**(如 `TRON-PRO-API-KEY`)。写入时校验必须是 RFC 9110 的 header token——不含空白、`:`、CR/LF | +| `networks..apiKey` | **本版新增**(**只写不读**) | **本版新增** | 无 | 该 header 的凭证值,限 1–256 字符且不含控制字符。**任何读取面一律回 `********`** | +| `networks.` | **本版新增** | —— | —— | 整条读取该网络的可配置字段(中间粒度);未配置的字段**不出现**,不是空行 | +| `aliases` | **本版新增** | ❌ **无写入通道** | 内置 7 条 | 别名簿(§2.1)。可读,维护靠手工编辑 `config.yaml` | +| `defaultNetwork` | 既有 | 既有 | `tron:728126428` | 可设为任一 EVM 网络,无需改动;别名与规范 id 都接受,存储时归一为规范 id | + +**`aliases` 为什么只读不可写**:别名簿在 CLI 上**没有其他可见面**——不给读,用户要知道 `sepolia` 指向哪里就只能打开 `config.yaml`,而那正是我们希望他不必做的事。不给写,是因为别名簿的维护频率极低(七条内置值),而一个写入通道要处理的校验(目标存在吗、会不会遮蔽规范 id、会不会撞名)比它省下的手工编辑多得多。**别名目标的合法性改为在解析时报错**(§2.1 的 `alias "polygon" points at unknown network eip155:99999`),不在写入时拦。 + +#### `apiKeyHeader` / `apiKey`:走 header 的商用 RPC 凭证 + +`Endpoint` 只印主机名那条规则(§2.3)隐含一个假设:**API key 夹在端点 URL 里**。**这个假设对主流商用 RPC 不成立**——TronGrid 用 `TRON-PRO-API-KEY` header,其他家也多半走 header。没有这两个字段,用了配额端点的用户**根本无法在 CLI 里配置**,只能退回未认证的公共端点吃限流。 + +```bash +wallet-cli config networks.tron:3448148188.apiKeyHeader TRON-PRO-API-KEY +wallet-cli config networks.tron:3448148188.apiKey +``` + +- **拆成两个字段而不是一个 `apiKey`**:header 名称各家不同,写死任何一个名字都只服务一家。 +- **两者都挂在网络上而非全局**:一把 key 只对一家 provider 的一条链有效,全局字段在多网络下必然是错的。 +- **两者要成对配置才生效**,缺一则不带 header。 + +`apiKey` 从一开始就按**秘密**处理,三道约束: + +| 约束 | 内容 | +| --- | --- | +| **只写不读** | 任何 config 读取面(整份 config、`config networks`、`config networks.`、指名读 leaf、`-o json`)一律回 `********`;连 `config set` 的回执与回显的 `input` 都是遮蔽值 | +| **落盘即受 0600 检核** | `config.yaml` 只要有任一网络带着非空 `apiKey`,就与 `tronlinkSecretKey` / `gasfreeApiSecret` 同级,权限不合就拒绝载入。它**嵌套在 `networks` 底下**,只看顶层键的旧 gate 发不出这个检核 | +| **带 header 的请求禁止跟随转址** | 否则节点回一个 302,fetch 会把凭证原封不动送到转址目的地 | + +> `apiKeyHeader` 的 header token 校验不是形式主义:这个值会被逐字写进请求的 header 列表,允许换行等于让一个手工编辑的 `config.yaml` 夹带第二个 header,是 header injection。 +> +> **两种 key 的保护方式对照**:URL 型的 key 靠**裁剪**保护(§2.3 的 `Endpoint` 只印主机名),header 型的 key 靠**遮蔽**保护(一律 `********`)。 + +**`` 段接受别名**,与 `--network` 同一套解析:`config set networks.sepolia.httpEndpoint ` 与 `config set networks.eip155:11155111.httpEndpoint ` 等价。三条规则配套: + +| 规则 | 说明 | +| --- | --- | +| **写入归一** | 无论用户敲的是别名还是规范 id,落到 `config.yaml` 的键**一律是规范 id**。否则同一条网络可能同时存在 `networks.sepolia` 与 `networks.eip155:11155111` 两个键,合并顺序决定谁生效——用户改了端点却不生效,且看不出原因 | +| **读取也归一** | 手工编辑 `config.yaml` 写成别名(TRON 时代就是这么改端点的)同样生效。**不认别名就等于静默失效**:配了跟没配一样,是最难排查的一类故障 | +| **重复键报错** | 若 `config.yaml` 里同一条网络既有别名键又有规范 id 键,**启动即报 `invalid_value` 并点名这两个键**,不静默取其一 | + +#### `config` 的展示形状 + +`config get networks` 现状只返回网络 id 列表,看不到各网络的端点——用户配完无从确认生效没有。本版两处改动: + +**① `networks` 的值由字符串变成对象**:`{ httpEndpoint, apiKeyHeader, apiKey }`,未配置的字段**不出现**(不是空行)。列表输出(整份 config、`config networks`)的 `httpEndpoint` 仍**裁成主机名**,`apiKey` 仍是 `********`。 + +这不是独立的美化,是 `apiKey` 两个字段的直接后果:一条网络现在有三个可配置字段,而旧的 `id → 端点字符串` 形状**只装得下一个**。要在旧形状下呈现另外两个,就得在整份 config、`config networks`、单条读取、json 四个展示面各加一段代码,下一个字段再重复一次。改成「网络的值就是它的可配置字段」之后,字段清单是唯一的一份,新增一个字段同时出现在四个面上。 + +**② `config networks.` 可整条读**,补上先前缺失的**中间粒度**——此前只有「整份」与「单一 leaf」,要确认一条网络配好了没(端点、header 名、key 有没有设)得敲三次。该读法给**完整端点 URL**(与 leaf 读取同一条分界线:**指名即意图**)。别名照样解析为规范 id。 + +**text 渲染同步改为树状**:纯量 `key value`;嵌套 map 印**裸键**后缩进一层,对齐只在同一层内计算。**不加 `key:` 的冒号**——这一层的键本身就含冒号(`tron:mainnet`),加了分隔符反而看不出 id 到哪里结束,而 id 正是用户要原样复制到 `--network` / `config networks.` 的那个字符串;缩进已经表明层级。旧版把 map 值摘要成「键的列表」,于是 `config` 告诉用户 `networks.tron:3448148188` 存在、却从不说它装了什么。 + +```bash +$ wallet-cli config +defaultNetwork tron:728126428 +defaultOutput text +timeoutMs 60000 +waitTimeoutMs 60000 +networks + tron:728126428 + httpEndpoint api.trongrid.io + tron:3448148188 + httpEndpoint nile.trongrid.io + tron:2494104990 + httpEndpoint api.shasta.trongrid.io + eip155:1 + httpEndpoint ethereum-rpc.publicnode.com + eip155:11155111 + httpEndpoint ethereum-sepolia-rpc.publicnode.com + eip155:56 + httpEndpoint bsc-dataseed.bnbchain.org + eip155:97 + httpEndpoint bsc-testnet-dataseed.bnbchain.org +aliases + tron tron:728126428 + nile tron:3448148188 + shasta tron:2494104990 + ethereum eip155:1 + sepolia eip155:11155111 + bsc eip155:56 + bsc-testnet eip155:97 +``` + +```bash +# 指名读取:给完整 URL(列表输出里是 nile.trongrid.io) +$ wallet-cli config networks.tron:3448148188 +networks.tron:3448148188 + httpEndpoint https://nile.trongrid.io +``` + +```bash +$ wallet-cli config aliases -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"config","data":{ "key":"aliases","value":{ "tron":"tron:728126428","nile":"tron:3448148188","shasta":"tron:2494104990","ethereum":"eip155:1","sepolia":"eip155:11155111","bsc":"eip155:56","bsc-testnet":"eip155:97" } },"meta":{ "durationMs":14,"warnings":[] } } +``` + +**Help 输出** + +> **相对现状**:`key` 的 Args 文案**列出全部合法键名**(agent 读得到,散文里读不到);Examples 换为「整份 / 读 leaf / 写 leaf / 读整条网络 / 写 header 名」五例。 + +```text +$ wallet-cli config --help + +Usage: + wallet-cli config [] [] [options] + +Show / get / set configuration values + +Args: + key config key to read or set (defaultNetwork, defaultOutput, timeoutMs, waitTimeoutMs, networks, aliases, tronlinkSecretId, tronlinkSecretKey, tronlinkChannel, gasfreeApiKey, gasfreeApiSecret, or networks. / networks..{httpEndpoint | apiKeyHeader | apiKey}); omit to show the whole effective config + value new value; omit to read the key + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli config + wallet-cli config defaultNetwork + wallet-cli config defaultNetwork nile + wallet-cli config networks.tron:728126428 + wallet-cli config networks.tron:728126428.apiKeyHeader TRON-PRO-API-KEY +``` + +--- + +## 3. 本地钱包组(EVM 适配) + +### 3.1 `create` —— 新建 HD 钱包 🔒 + +> **本版改动**:回执多一行 EVM 地址;助记词一次产出两族地址。 + +**用法** + +``` +wallet-cli create [--label ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 生成 BIP39 助记词并落库,**一次产出两族地址** | +| EVM 增量 | 回执地址行由一行变两行(TRON / EVM) | +| 错误 | `account_exists`、`tty_required` | + +**示例与输出** + +```bash +$ wallet-cli create --label main +# 首次创建:设置主密码两步(keystore 已存在时只提示一行 Master password);助记词加密落库、不打印到任何输出 +? Set master password (hidden): +? Confirm master password: +✅ Created wallet "main" + Account ID wlt_ab12cd34.0 + Type HD + TRON address TSRmq8kP...9dEf + EVM address 0x7a3f...c19b + Active yes + +⚠️ Recovery phrase is encrypted locally and was not printed. +⚠️ Run `backup` soon and store the file offline. +``` + +```bash +$ wallet-cli create --label main --password-stdin -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"create","data":{ "status":"created","accountId":"wlt_ab12cd34.0","label":"main","type":"seed","index":0,"active":true,"addresses":{ "tron":"TSRmq8kP...9dEf","evm":"0x7a3f...c19b" },"seedId":"wlt_ab12cd34" },"meta":{ "durationMs":1088,"warnings":[] } } +``` + +> **EVM 增量只有 `addresses.evm` 一个键**——`status` / `accountId`(带 `.0` 后缀)/ `type`(`seed`,非 text 里的 `HD`)/ `seedId` 全部沿用既有结构。 + +**Help 输出** + +> **相对现状**:描述补两句(每族各派生一个地址、助记词本地加密不打印);Requires 主密码文案按 §10.1 统一。 + +```text +$ wallet-cli create --help + +Usage: + wallet-cli create [options] + +Create a new HD wallet (BIP39 seed). Derives one address per chain family +from the same seed; the recovery phrase is encrypted locally and never printed. + +Requires: + the master password — pass --password-stdin, or enter it interactively in a TTY + +Options: + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli create --label main +``` + +### 3.2 `import mnemonic` —— 导入助记词 🔒 + +> **本版改动**:一次导入产出两族地址。 + +**用法** + +``` +wallet-cli import mnemonic [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 导入 BIP39 助记词;助记词与主密码经隐藏 TTY 读取 | +| EVM 增量 | 两族地址(一次导入两族齐备) | +| 错误 | `invalid_mnemonic`、`account_exists`、`tty_required` | + +**Options** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--label ` | 否 | 自动 | 账户标签 | + +**示例与输出** + +```bash +$ wallet-cli import mnemonic --label cold +# 提示顺序固定:先主密码、后助记词(密码在 dispatch 阶段 prime) +? Master password (hidden): +? Paste recovery phrase (hidden): +✅ Imported wallet "cold" + Account ID wlt_9f7e21aa.0 + Type HD + TRON address TKq3xW7v...2bNc + EVM address 0x91b2...4d0e + Active yes + +⚠️ Recovery phrase was read from hidden input and was not printed. +``` + +> 一次导入两族地址齐备,无需为 EVM 再导一次。 +> +> **软件账户本版只支持默认模板**(§1.2):`derive` 不提供 `--path`。迁自 Ledger Live / MEW 等非默认模板的用户,硬件账户走 `import ledger --path`(§3.6);纯助记词用户需用外部工具按目标路径导出私钥后 `import private-key`。 + +**Help 输出** + +> **相对现状**:仅 `--label` 去掉重复的「助记词交互输入」尾注(该信息已在描述段)。 + +```text +$ wallet-cli import mnemonic --help + +Usage: + wallet-cli import mnemonic [options] + +Import a BIP39 mnemonic phrase. The recovery phrase and master password are read +interactively from the TTY (hidden input); they never touch argv or stdin. + +Requires: + the master password — entered interactively in a TTY + +Options: + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import mnemonic --label main +``` + +### 3.3 `import private-key` —— 导入裸私钥 🔒 + +> **本版改动**:同一把 key 输出两族地址(与 seed 账户不同,私钥相同)。 + +**用法** + +``` +wallet-cli import private-key [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 导入一把 secp256k1 私钥;私钥与主密码经隐藏 TTY 读取,**不接受 argv / stdin** | +| EVM 增量 | 同一把 key 输出两族地址(**私钥相同**,与 seed 账户不同) | +| 错误 | `invalid_private_key`、`account_exists`、`tty_required` | + +**示例与输出** + +```bash +$ wallet-cli import private-key --label hot +? Master password (hidden): +? Paste private key (hidden): +✅ Imported wallet "hot" + Account ID wlt_5c0d88b1 + Type private key + TRON address TBhCfAyt...3TCUp + EVM address 0x12E9...6D29 + Active yes + +⚠️ Private key was read from hidden input and was not printed. +``` + +> 两个地址是同一把 key 的两种编码——与 `encoding convert` 的输出一致。 +> +> 导入即设为活跃账户,与 `create` / `import mnemonic` 一致,故有 `Active yes` 行。`import watch` 是例外(观察账户不自动激活)。 + +**Help 输出** + +> **相对现状**:描述补一句「一把 key 每族各一个地址」;`--label` 去掉重复尾注。 + +```text +$ wallet-cli import private-key --help + +Usage: + wallet-cli import private-key [options] + +Import a raw private key. The private key and master password are read +interactively from the TTY (hidden input); they never touch argv or stdin. +One key yields an address on every chain family. + +Requires: + the master password — entered interactively in a TTY + +Options: + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import private-key --label hot +``` + +### 3.4 `import keystore` —— 导入 keystore 文件 🔒 + +> **本版改动**:keystore 本就是 EVM 原生格式;导入后为 private-key 类型、不可再派生。 + +**用法** + +``` +wallet-cli import keystore [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 导入 Web3 标准 keystore JSON(scrypt/aes),装的是**一把私钥**、非种子 | +| EVM 增量 | 两族地址(同 `import private-key`:一把 key 两种编码);keystore 本就是 EVM 原生格式,TRON 侧属借用 | +| 秘密输入 | keystore 文件密码经隐藏 TTY 读取,**仅交互式**,无 TTY 报 `tty_required` | +| 错误 | `invalid_keystore`、`wrong_keystore_password`、`account_exists`、`tty_required` | + +**示例与输出** + +```bash +$ wallet-cli import keystore ./UTC--2026-08-06--0x7a3f.json --label from-mm +? Master password (hidden): +? Keystore password (hidden): +✅ Imported wallet "from-mm" + Account ID wlt_3d81f0aa + Type private key + TRON address TDq7mW4x...8sVnP + EVM address 0x6Ae4...b1F7 + Active yes + +⚠️ Private key was read from the keystore file and was not printed. +``` + +> keystore 装单条私钥,**不可再派生**——导入后是 private-key 类型账户,没有 `index`,`derive` 对它不适用。这与 MetaMask / Geth 导出的 keystore 语义一致。 +> +> 同地址重复导入报 `account_exists`(不静默覆盖,先 `delete`)。 + +**Help 输出** + +> **相对现状**:描述精简改写,并补一句「一把 key 每族各一个地址」(与 `import private-key` 同一句);**flag 集合无变化**。 + +```text +$ wallet-cli import keystore --help + +Usage: + wallet-cli import keystore [options] + +Import a Web3 keystore JSON file. It holds a single private key, not a seed: +the account cannot be derived from. One key yields an address on every chain +family. The keystore password is read interactively from the TTY (hidden input). + +Args: + path path to the keystore JSON file + +Requires: + the master password — entered interactively in a TTY + +Options: + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import keystore ./keystore.json --label from-mm +``` + +### 3.5 `import watch` —— 注册观察地址 + +> **本版改动**:接受 `0x…`,建出 EVM 单 family 账户;地址行标签改为 family 标签。 + +**用法** + +``` +wallet-cli import watch --address [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 注册只读地址(无秘密),family 由地址格式自动识别 | +| EVM 增量 | 接受 `0x…`;建出的账户为 **EVM 单 family** | +| 错误 | `invalid_address`、`account_exists` | + +**Options** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--address ` | 是 | —— | TRON base58 `T…` 或 EVM `0x…`;family 自动识别。混合大小写的 EVM 地址必须通过 EIP-55 校验(§1.3) | +| `--label ` | 否 | 自动 | 账户标签 | + +**示例与输出** + +```bash +$ wallet-cli import watch --address 0xC4d9...30ab --label team-vault +✅ Added watch-only account "team-vault" + EVM address 0xC4d9...30ab + Note read-only; signing operations will be rejected +``` + +> **本版把地址行标签从通用的 `Address` 改为 family 标签**(`TRON address` / `EVM address`,与其它 import 回执一致):两族并存后,`Address` 不告诉用户这是哪条链的地址。单 family 账户在另一族网络下使用报 `family_mismatch`(§11)。 + +**Help 输出** + +> **相对现状**:描述补 family 自动识别与单族可用;`--address` 由「TRON base58」改为两族;Examples 补 EVM 一条。 + +```text +$ wallet-cli import watch --help + +Usage: + wallet-cli import watch [options] + +Register a watch-only address (no secret). The chain family is detected from the +address format; the account is usable only on networks of that family. + +Options: + --address address to track: TRON base58 T... or EVM 0x... [required] + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import watch --address TKq3xW7v...2bNc --label team-vault + wallet-cli import watch --address 0xC4d9...30ab --label team-evm +``` + +### 3.6 `import ledger` —— 注册 Ledger 账户 + +> **本版改动**:新增 `--app ethereum`,EVM 路径默认跟随 Ledger Live 模板。 + +**用法** + +``` +wallet-cli import ledger --app (tron | ethereum) [--index | --path | --address [--scan-limit ]] + [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 注册硬件账户(本地不存私钥,签名在设备上完成) | +| EVM 增量 | `--app ethereum`;EVM 路径**默认用 Ledger Live 模板** `m/44'/60'/N'/0/0` | +| app 取值 | 只有 `tron` 与 `ethereum` 两个。**Ethereum app 覆盖全部 EVM 网络**(`ethereum` / `sepolia` / `bsc` / `bsc-testnet` 四条网络共用一个账户),不按链单列 `--app` | +| 错误 | `device_not_found`、`device_locked`、`ledger_unsupported`(**规格里的 `app_not_open` 并入此码**,§11.1)、`ledger_setting_required`、`invalid_path`、`account_exists` | + +> Ledger 的 EVM 默认模板与 §1.2 软件账户的默认模板不同——**各自跟随所属生态的默认**:软件账户跟随 MetaMask,硬件账户跟随 Ledger Live。Legacy / MEW 用户走 `--path`。 +> +> **EVM 侧本版不做 clear-signing——设备屏幕上是盲签。** 签名时传给 `hw-app-eth` 的 resolution 为 `null`:传入 resolution 会让它在**签名过程中**向 Ledger 的 CDN 抓取 clear-signing 描述子,好让设备显示「转 100 USDT 给 0xabc」而非一串原始哈希。 +> +> **由项目负责人决定采用 `null`:wallet-cli 在签名时不对任何第三方发出请求。** +> +> | | | +> | --- | --- | +> | **得到** | 签名流程无网络请求,不外泄合约地址与交易意图;离线 / 受限环境可用 | +> | **失去** | **设备上显示的是原始哈希**,用户无法在硬件上核对收款人与金额 | +> +> 这是**用户可见**的行为差异,而硬件钱包用户尤其在意——clear-signing 正是他们买硬件钱包的理由之一。后续是否开放待定。 +> +> **`--app` 不按链细分**:设备上的 Ethereum app 能为任意 EVM 链签名(chain id 在交易里,由 app 读取),且各 EVM 链共用 coinType 60 的同一把 key,所以一次 `--app ethereum` 注册出的账户在四条 EVM 网络上通用。Ledger 的 clone app(BSC、Polygon 等)是给想要自有品牌界面的链做的可选项,不是签名前提;新 EVM 网络接入 Ledger 走的是 Crypto Asset List 登记,不是新增一个 app。`--app` 的取值是**设备上要打开的 app 名**(故为 `ethereum` 而非 `evm`),账户 family 仍记为 `evm`。 + +**示例与输出** + +```bash +$ wallet-cli import ledger --app ethereum --index 0 --label cold-evm +✅ Registered Ledger account "cold-evm" + Account ID wlt_e18b45c0 + App evm + Path m/44'/60'/0'/0/0 + EVM address 0x3c8d...77a1 + +⚠️ No private key is stored locally. Signing requires device confirmation. +``` + +> `App` 行的值取自账户 family(`evm`),不是 `--app` 的输入值(`ethereum`)——现状如此,本版不改:family 才是后续所有命令的匹配依据。 + +**Help 输出** + +> **相对现状**:描述与 Requires 与现状一致,只动 Options 与 Examples——`--app` 由 `` 扩为 ``;**`--scan-limit` 的默认值由描述移入 `[optional, default: …]` tag**;`--path` 去掉 TRON 专属的路径举例;`--app` 描述去掉「address-derivation scheme」改为「选定 chain family」;Examples 补 ethereum 一条。 +> +> **`--index` 是这条规则的例外,默认值留在描述文字里。** 那个 tag 由 schema 的 `.default()` 推导——要显示 `default: 0`,字段就必须真的有默认值。而 `--index` 参与「`--index` / `--path` / `--address` 三个定位器只能给一个」的互斥规则,**该规则数的是「有没有给」**:加上默认值后 `index` 恒为已给,`--path` 单独使用会被判成两个定位器而被拒(实测确认会发生)。 +> +> (`--scan-limit` 之所以能做,是因为它不参与互斥;实作直接引用服务层的 `DEFAULT_SCAN_LIMIT` 作 `.default()`,既消掉重复的默认值副本,也让 `--json-schema` 的 `inputSchema` 有 `"default": 20`——散文里的默认值 agent 读不到。) + +```text +$ wallet-cli import ledger --help + +Usage: + wallet-cli import ledger [options] + +Register a Ledger account (watch-only; signs on device) + +Requires: + a connected, unlocked Ledger with the selected app (--app) open + +Options: + --app Ledger app to open on the device; selects the chain family [required] + --index account index under the app's default path; mutually exclusive with --path and --address [optional, default: 0] + --path explicit derivation path; mutually exclusive with --index and --address [optional] + --address locate this address by scanning indexes; mutually exclusive with --index and --path [optional] + --scan-limit how many indexes to scan when using --address [optional, default: 20] + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import ledger --app tron --index 0 --label cold + wallet-cli import ledger --app ethereum --index 0 --label cold-evm +``` + +### 3.7 `list` —— 列出钱包 / 账户 + +> **本版改动**:地址列按当前网络 family 显示;json 恒给两族全量 + 新增 `derivationPath`。 + +**用法** + +``` +wallet-cli list [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 列出全部本地账户,按 HD 种子 / 类型分组 | +| EVM 增量 | 地址列**按当前网络的 family 显示**;json 恒给两族全量 | +| 网络 | 可选,仅用于决定显示哪族地址(不访问节点) | + +**示例与输出** + +```bash +$ wallet-cli list --network sepolia +HD wlt_ab12cd34 +├─ [0] main 0x7a3f...c19b (active) +└─ [1] main-1 0x91b2...4d0e + +private key +└─ hot 0x12E9...6D29 + +watch +└─ team-vault 0xC4d9...30ab +``` + +```bash +$ wallet-cli list --network nile +# 同一批账户,地址列切到 TRON 族;watch 账户因是 EVM 单 family,不在此网络下展示 +HD wlt_ab12cd34 +├─ [0] main TSRmq8kP...9dEf (active) +└─ [1] main-1 TKq3xW7v...2bNc + +private key +└─ hot TBhCfAyt...3TCUp +``` + +```bash +$ wallet-cli list -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"list","data":[ { "accountId":"wlt_ab12cd34.0","label":"main","type":"seed","index":0,"active":true,"addresses":{ "tron":"TSRmq8kP...9dEf","evm":"0x7a3f...c19b" },"seedId":"wlt_ab12cd34","derivationPath":{ "tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0" } },{ "accountId":"wlt_c4a70e93","label":"team-vault","type":"watch","index":null,"active":false,"family":"evm","addresses":{ "evm":"0xC4d9...30ab" },"derivationPath":null } ],"meta":{ "durationMs":13,"warnings":[] } } +``` + +> **被网络过滤掉的账户会在 stderr 补一行提示**(不进 stdout): +> +> ```text +> warning: 2 account(s) have no tron address and are not shown; use --network to switch, or --output json to see every family +> ``` +> +> 理由是 **Ledger 账户与 watch 一样是单族,而 Ledger 是能签名的真实账户**:一个只有 EVM Ledger 的用户,在默认 TRON 网络下跑 `list` 会**什么硬件账户都看不到**,且没有任何线索告诉他 `--network` 的存在。走 stderr 而不是 stdout,是为了让 stdout 保持干净(机器只读 stdout),json 不受影响。 +> +> text 不并排两族地址:表会宽一倍,且用户当下只关心在用的链。json 给全量。**`derivationPath` 是本版新增字段**(按 family 的 map,watch / private-key 账户为 `null`)——现状 json 只有 `accountId` / `label` / `type` / `index` / `active` / `addresses` / `seedId`,没有路径,用户无从判断账户用的哪套派生模板(§1.2)。 +> +> **两个按账户类型出现/消失的字段,规则本版写死**:`seedId` **只在 seed 账户出现**——观察、private-key、keystore、Ledger 账户没有种子,不能拿 `accountId` 顶上(现状 watch 条目的 `seedId` 与 `accountId` 同值,是个伪字段,本版去掉);`family` **只在单族账户出现**(watch / Ledger),两族齐备的账户不给该字段,哪族看 `addresses` 的键即可。判定「这个账户能不能派生」一律看 `seedId` 在不在,不看 `type` 的字符串。 + +**Help 输出** + +> **相对现状**:描述补两句;**新增全局 `--network`**(现状 `list` 无此项);Examples 由 `--output json` 一条换为两族三条。 + +```text +$ wallet-cli list --help + +Usage: + wallet-cli list [options] + +List wallets/accounts (no unlock needed). The address column shows the family of +the selected network; JSON output always carries every family's address. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli list + wallet-cli list --network sepolia + wallet-cli list --output json +``` + +### 3.8 `current` —— 当前活跃账户 + +> **本版改动**:账户有哪族地址就显示哪族,各一行;`--qr` 出当前网络 family 的地址。 + +**用法** + +``` +wallet-cli current [--qr] [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 显示**选定账户**(默认为活跃账户) | +| EVM 增量 | **账户有哪族地址就显示哪族,各一行**;`--qr` 生成**当前网络 family** 的地址二维码 | +| 账户 | 支持全局 `--account`;省略则为活跃账户 | +| 错误 | `family_mismatch`(`--qr` 时账户在选定网络 family 下没有地址;**text 与 json 一致**) | + +**示例与输出** + +```bash +$ wallet-cli current +Active account: main + TRON address TSRmq8kP...9dEf + EVM address 0x7a3f...c19b +``` + +```bash +# 单 family 账户只有一行地址 +$ wallet-cli use team-vault && wallet-cli current +Active account: team-vault + EVM address 0xC4d9...30ab +``` + +> **地址行按账户实际拥有的 family 出**:`create` / `import mnemonic` / `import private-key` / `import keystore` 建出的账户两族齐备,出两行;`import watch` / `import ledger` 是单 family,只出一行——空值行被渲染层丢弃,不存在 `EVM address` 留空这种输出。 +> +> `--qr` 取**当前网络 family** 的地址:选定账户在该 family 下没有地址时(如 EVM 单族账户配 `--network nile`)报 `family_mismatch`,而不是回退到它拥有的那一族——回退会让用户拿到一个另一条链的收款码。 +> +> **该检核与输出格式无关**:`-o json` 同样拒绝并报 `family_mismatch` / exit 2,通过时回 `receiveAddress`。**QR 图是这条命令唯一属于 text 的部分,也是唯一由输出格式决定的部分。** +> +> **本版支持全局 `--account`**:`current` 先前是唯一一条「显示某个账户」却不接受它的命令。支持它让「看一眼另一个账户的地址」不必先 `use` 过去再 `use` 回来——后者会改动活跃账户这个全局状态,只为读一次。 +> +> **单族账户的 family 检核时机本版后移**:先前在**解析网络**的当下就用账户的 family 去比对,现在移到「真的要这一族的地址」那一刻。旧时机让 `current` 这种**纯本地查看**命令,在账户与默认网络不同族时**完全无法查看自己的账户**;而那道提前的检核并没有防住任何事——没有它,真正需要地址的命令一样会在任何 RPC 之前失败。连带效果:`list`、`backup --records` 与 `current` 得以支持 `--network`(先前该旗标被静默忽略且不出现在 help)。 +> +> **后果需写进 release note**:`config.defaultNetwork` 无法解析时,`list` 与 `backup --records` 会**失败**——这是把它们改为 network-aware 的代价,决定维持硬失败(行为一致、早点报错更清楚),release note 应点明「先修 defaultNetwork」。 + +**Help 输出** + +> **相对现状**:**新增全局 `--network`**(决定 `--qr` 出哪族地址)**与全局 `--account`**(并带对应的 Requires 段);描述补「按账户拥有的 family 每族一行」;`--qr` 描述补「该族无地址时失败」;Examples 补两条。 + +```text +$ wallet-cli current --help + +Usage: + wallet-cli current [options] + +Show the current active account, with one address line per chain family it has + +Options: + --qr print a receive QR code for the selected network's address; fails when the account has none for that family [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli current + wallet-cli current --qr + wallet-cli current --qr --account main + wallet-cli current --qr --network sepolia +``` + +### 3.9 `derive` —— 派生下一个 HD 账户 🔒 + +> **本版改动**:一次派生两族地址。**不新增 `--path`**——见 §1.2「软件账户本版只支持默认模板」。 + +**用法** + +``` +wallet-cli derive --seed-id [--index ] [--label ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 从种子钱包派生新账户 | +| EVM 增量 | 一次产出两族地址 | +| 错误 | `seed_not_found`、`account_exists` | + +**Options** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--seed-id ` | 是 | —— | 种子钱包 id(`list` 的 HD 组头) | +| `--index ` | 否 | 下一个空闲 | 账户序号,按各族默认模板套用 | +| `--label ` | 否 | `<钱包名>-` | 账户标签 | + +**示例与输出** + +```bash +$ wallet-cli derive --seed-id wlt_baezdw0b --password-stdin +✅ Derived sub-account "main-1" + Account ID wlt_baezdw0b.1 + Index 1 + TRON address TFtFc27ig1NKYLkmapdFmhHgrUS1YWMdha + EVM address 0x1486AbC087a7442d44C43d802b2637560fADf895 + Active yes + Note shares master mnemonic; no separate backup needed +``` + +> 一次派生两族地址,两行并出。 +> +> **`--path` 在本版不存在**,敲了会得到 `invalid_option: unknown option(s): --path`。理由与取舍见 §1.2;`invalid_path` 这个错误码**仍然保留**,由 `import ledger --path` 在路径格式非法时产生(§11)。 + +**Help 输出** + +> **相对现状**:描述补一句「每族一套 BIP44 模板,一次 derive 产出每族一个地址」;`--index` / `--label` 描述精简并补字数上限;Requires 冠词按 §10.1 统一。 + +```text +$ wallet-cli derive --help + +Usage: + wallet-cli derive [options] + +Derive the next HD account from a seed wallet (by --seed-id). Each family uses +its own BIP44 template, so one derive yields an address per family. + +Requires: + the master password — pass --password-stdin; this command never prompts + +Options: + --seed-id seed id (wlt_…) of the HD wallet to derive from — shown as the HD group header in `list` [required] + --index explicit HD account index, in account index; omit to use the next free index [optional] + --label label for the new derived account, 1-64 chars; omit to auto-generate - [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli derive --seed-id wlt_ab12cd34 +``` + +### 3.10 `backup` —— 导出账户机密 🔒⚠️ + +> **本版改动**:seed 账户导出**私钥**时,由**既有的全局 `--network`** 决定导哪一族;助记词导出无歧义。**不新增 `--family`。** + +**用法** + +``` +wallet-cli backup [--keystore] [--network ] [--out ] [--password-stdin] +wallet-cli backup [] --records [--from ] [--to ] [--limit ] [--offset ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 把助记词 / 私钥导出到 0600 文件(**从不写 stdout**) | +| EVM 增量 | 导出**私钥**时由 `--network` 选定链(seed 账户两族私钥不同);未给则用 `config.defaultNetwork` | +| 错误 | `account_not_found`、`not_exportable`(观察 / Ledger 账户)、`output_exists` | + +**Options(增量)** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--network ` | 否 | `config.defaultNetwork` | **全局旗标**。导出私钥时决定导哪一族的钥匙;助记词导出与 private-key 账户不受影响 | + +**为什么用 `--network` 而不是新增 `--family`** + +**family 是系统内部概念,不对外暴露成选项。** 全 CLI 目前没有任何一个旗标让用户直接打 family 名——`import ledger` 用 `--app tron|ethereum`、`contact` 从地址推断(§3.11)、`list` / `current --qr` 用 `--network`。`--family` 会是唯一的例外,等于为同一个概念引入第二套词汇。「网络作为显示/选择哪一族的选择器」是本版已经确立的模式,`backup` 沿用它。 + +**但问题本身仍然必须修**(换旗标不会让它消失):seed 账户两族是**两把不同的私钥**(§1.2:coin 195 vs coin 60),而 V3 keystore 结构上只装一把。修前写死导出 TRON 那把,实测后果是——钱包显示 EVM 地址,导出的钥匙导入 MetaMask 得到另一个地址。**导入完全成功、地址看起来正常,只是不是用户的**,而且没有任何错误信息。 + +**补偿**:因为没给 `--network` 会静默吃默认值,回执与 json 都带 `Family` 栏,让用户一眼看到拿到的是哪一把。 + +**示例与输出** + +```bash +# 默认网络为 tron:728126428,故导出 TRON 那把 +$ wallet-cli backup main --keystore --password-stdin +⚠️ Keystore written /wlt_baezdw0b.0-1787825194541.keystore.json + Account ID wlt_baezdw0b.0 + Family tron + Secret private key + File mode 0600 + Bytes 608 + +⚠️ Secret material was written only to the keystore file, never to stdout. +``` + +```bash +# 同一个账户,切到 EVM 网络导出的是另一把钥匙 +$ wallet-cli backup main --keystore --network sepolia --out ./main-evm.keystore.json --password-stdin +⚠️ Keystore written /main-evm.keystore.json + Account ID wlt_baezdw0b.0 + Family evm + Secret private key + File mode 0600 + Bytes 608 + +⚠️ Secret material was written only to the keystore file, never to stdout. +``` + +> 以上为实测输出,仅把绝对路径的目录部分省略为 ``。 +> +> **文件默认落在当前工作目录**,不是 `~/.wallet-cli/backup/`(这是既有行为,与 EVM 无关,此前文档写错)。默认文件名为 `./-.json`(`--keystore` 时为 `.keystore.json`),以 0600 创建且**从不覆盖**已存在的文件。因此**不要在共享目录或 git 仓库里跑这条命令**——help 描述里有对应的一行警告。 +> +> 助记词导出无歧义(一句助记词覆盖两族),不受 `--network` 影响。 + +**Help 输出** + +> **相对现状**:描述改写(keystore 语义、「只写文件不写 stdout」、**默认写当前目录的警告**、`--records` 段);**不新增 `--family`**;`--out` 描述补默认文件名与「从不覆盖」;`--records` 全套沿用现状;新增全局 `--network`(§3.8 的检核时机后移使其可用)。 + +```text +$ wallet-cli backup --help + +Usage: + wallet-cli backup [] [options] + +Export an account's secret to a 0600 file — the native backup format, or a standard Web3 +keystore JSON with --keystore (importable by TronLink and others, encrypted with your master +password). A keystore holds a single private key, so an HD account exports only its current +derived key; use the native backup to move a whole seed. + +The secret is written only to the file, never to stdout; watch-only and Ledger accounts have +none to export. Files default to the CURRENT DIRECTORY — do not run this in a shared directory +or a git repository. + +With --records and no account, nothing is exported: it shows the local audit log of past +exports instead — one row per 'backup' and 'backup --keystore', newest first, with the file +each secret went to. Imports are not logged. The log keeps the most recent 1000 entries. + +Args: + account account or wallet to export, addressed by accountId, label, or address; with --records, the account whose exports to list + +Requires: + the master password — pass --password-stdin, or enter it interactively in a TTY + +Options: + --keystore export as a standard Web3 keystore JSON (importable by TronLink and others, encrypted with your master password) instead of the native format [optional, default: false] + --out output file path; omit to write ./-.json in the current directory (.keystore.json with --keystore); file is created with mode 0600 and never overwritten [optional] + --records list past secret exports instead of exporting anything [optional, default: false] + --from with --records: only records at or after this UTC time; format YYYY-MM-DD or 'YYYY-MM-DD HH:mm:ss', parsed as UTC [optional] + --to with --records: only records at or before this UTC time; format YYYY-MM-DD or 'YYYY-MM-DD HH:mm:ss', parsed as UTC [optional] + --limit with --records: maximum records to return; omit for all [optional] + --offset with --records: pagination offset [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli backup main --out ~/main-backup.json --password-stdin + wallet-cli backup main --keystore --password-stdin + wallet-cli backup --records --limit 20 + wallet-cli backup --records --account main --from 2026-08-01 +``` + +### 3.11 `contact` 组 —— 收款人通讯录(family 感知改造) + +> **本版改动**:**必须改造**——条目按地址格式识别并持久化 family,`--to ` 跨族报错,否则会把 EVM 地址拿去 TRON 网络发交易。**定案本版修订**:名称与地址均**全局唯一**,family 不出现在任何用户可见的表面。 + +**用法** + +``` +wallet-cli contact add

[--note ] +wallet-cli contact list +wallet-cli contact remove +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 本地收款人通讯录;`tx send --to ` 可直接用联系人名代替地址 | +| EVM 增量 | **条目内部记录 family**(按地址格式识别),`--to ` 解析时校验 family 与当前网络一致 | +| 错误 | `already_exists`(**同名或同址**)、`invalid_address`、**`family_mismatch`**(联系人地址与当前网络不同族)、`contact_not_found` | + +> **这是本版必须改造的一条,否则会转错链**:通讯录现状只存「名字 → 地址」、不分网络,而 `--to` 接受联系人名。加入 EVM 后,`tx send --to exchange --network nile` 若 `exchange` 存的是 `0x…`,就会拿一个 EVM 地址去 TRON 网络发交易。地址格式校验能挡住这一例(TRON 侧 base58 解码失败),但**依赖下游校验兜底不是设计**——通讯录自己就该知道每条记录属于哪条链。 + +#### 定案(本版修订:推翻「同名可在两族各存一条」) + +**对外是一张扁平的 `name ↔ address` 表,两者各自全局唯一。** family 只是内部存储分桶与 `--to` 路由的细节,**任何用户可见的表面都不出现它**——没有 `Family` 列、没有 `family` json 字段、没有 `--family` 旗标。 + +**为什么推翻**:原定案写的是「同名允许在不同 family 下各存一条」。但**「同名允许」不是谁决定的,是存储结构的副产物**——`contacts.json` 是 `entries: { tron: […], evm: […] }` 按 family 分桶,实作把「名称唯一性」也继承了桶的范围。没有人问过唯一性的范围**该**是什么,文档后来为这个既有行为补了理由。 + +改成全局唯一之后,三件事同时消失: + +| 原问题 | 全局唯一之后 | +| --- | --- | +| `remove ` 跨族同名该删哪一条 | **问题不存在**——不需要 `--family`,也不需要第二个位置参数 | +| 「`--to ` 跨族报 `family_mismatch`」 | **才真正有用**。允许同名时,这个错误对「两族都有的名字」永远不会触发 | +| `--family` 与「family 不对外暴露」原则冲突(§3.10) | 一并消失 | + +**代价**:想要两条就得叫 `exchange-tron` 与 `exchange-evm`——明确、不会搞错,成本仅止于多打几个字。 + +`contact list` 保持纯本地、**无 `--network`**、不按网络过滤:条目数通常个位数,过滤省不下多少噪声,却会让刚 `contact add` 完的用户在默认网络下看不到自己刚加的条目(`add` 按地址格式定 family,不看 `--network`)。`--to` 选哪条由名称直接决定,不依赖列表怎么显示。 + +**示例与输出** + +```bash +$ wallet-cli contact add exchange TKyeCYyEtgNs5X2srhKcjLiimVmWFy1q61 --note "CEX deposit" +✅ Contact added + Name exchange + Address TKyeCYyEtgNs5X2srhKcjLiimVmWFy1q61 + Note CEX deposit +``` + +```bash +# 同名不再允许——即使属于另一族 +$ wallet-cli contact add exchange 0x1486AbC087a7442d44C43d802b2637560fADf895 +error [already_exists]: a contact named exchange already exists +``` + +```bash +$ wallet-cli contact add exchange-evm 0x1486AbC087a7442d44C43d802b2637560fADf895 --note "CEX deposit" +✅ Contact added + Name exchange-evm + Address 0x1486AbC087a7442d44C43d802b2637560fADf895 + Note CEX deposit +``` + +```bash +$ wallet-cli contact list +| Name | Address | Note | +| ------------ | ------------------------------------------ | ----------- | +| exchange | TKyeCYyEtgNs5X2srhKcjLiimVmWFy1q61 | CEX deposit | +| exchange-evm | 0x1486AbC087a7442d44C43d802b2637560fADf895 | CEX deposit | +``` + +```bash +$ wallet-cli contact list -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"contact.list","data":{ "contacts":[ { "name":"exchange","address":"TKyeCYyEtgNs5X2srhKcjLiimVmWFy1q61","note":"CEX deposit" },{ "name":"exchange-evm","address":"0x1486AbC087a7442d44C43d802b2637560fADf895","note":"CEX deposit" } ] },"meta":{ "durationMs":16,"warnings":[] } } +``` + +```bash +$ wallet-cli contact remove exchange-evm +✅ Contact removed + Name exchange-evm + Address 0x1486AbC087a7442d44C43d802b2637560fADf895 +``` + +> 以上均为实测输出。**没有 `Family` 列,json 也没有 `family` 字段**——地址本身 `T…` / `0x…` 已经表明是哪条链。 +> +> **`--to ` 跨族的错误措辞描述地址,不描述 family**,用户不必学会那个词:`contact exchange holds the address T…, which the selected network cannot pay`。 +> +> **破坏性后果**(release note):`contact list` 的 text 少了 `Family` 列,json 少了 `family` 字段。 + +**Help 输出** + +> **相对现状**:`add` 的描述与 `name` / `address` 参数补 family 说明(按地址格式校验、名称可在任何接受地址的地方使用);`name` 补「1-64 字符、不得形似地址」的上限;**`remove` 不新增 `--family`**;三条命令的 flag 集合与现状一致。 + +```text +$ wallet-cli contact add --help + +Usage: + wallet-cli contact add
[options] + +Add a locally stored recipient. The address is validated against the family it belongs to (T… = TRON, 0x… = EVM), and the name can then be used anywhere an address is accepted. + +Args: + name local name for this recipient; 1-64 safe characters and must not look like a chain address. Usable anywhere an address is accepted + address recipient address to store under this name + +Options: + --note free-form note, up to 128 safe characters [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli contact add alice TBy6... --note 'Alice mainnet' +``` + +```text +$ wallet-cli contact list --help + +Usage: + wallet-cli contact list [options] + +List every recipient in the local plaintext address book. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli contact list +``` + +```text +$ wallet-cli contact remove --help + +Usage: + wallet-cli contact remove [options] + +Remove one recipient from the local address book without changing any on-chain state. + +Args: + name name of the contact to delete + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli contact remove alice +``` + +### 3.12 `encoding convert` / `address generate` —— 编码工具的边界说明 + +> **本版改动**:**行为零改造**,只在两条 help 的描述里各补一句边界说明。 + +这两条纯本地命令一直同时输出 TRON 与 EVM 两种地址,容易被读成「账户模型」的一部分——**它们是编码工具**:给的是同一把 key 的两种编码,与 §1.1 账户模型里「seed 账户两族私钥不同」是两回事。不补这句,用户会拿 `encoding convert` 的输出去对 `create` 的两行地址,然后发现对不上。 + +| 命令 | 补的那句(英文原文) | +| --- | --- | +| `encoding convert` | `The two address forms are encodings of one 20-byte key hash, not two derived accounts.` | +| `address generate` | `The TRON and EVM addresses shown are two encodings of the same generated key.` | + +**Help 输出** + +> **相对现状**:描述末尾各加一句边界说明;**flag 集合、Args、Examples 全部不变**。 + +```text +$ wallet-cli encoding convert --help + +Usage: + wallet-cli encoding convert [options] + +Auto-detect the input and print every equivalent representation, validating +checksums. Two families: ADDRESS (TRON base58 / TRON 41-hex / EVM 0x / public +key hex -> address forms) and ENCODING (arbitrary hex <-> Base64 <-> +Base58Check). Routing is automatic by whether the input is address-shaped. +Purely local. Private keys and mnemonics are NOT accepted (secrets must never +appear on the command line). The two address forms are encodings of one 20-byte +key hash, not two derived accounts. + +Args: + input value to convert: an address, a public key hex, or any hex/Base64/Base58Check string + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli encoding convert TBhCfAyt...3TCUp + wallet-cli encoding convert 0x12E9...6D29 + wallet-cli encoding convert deadbeef0102 +``` + +```text +$ wallet-cli address generate --help + +Usage: + wallet-cli address generate [options] + +Generate a random keypair locally (works offline). The private key is written to +a 0600 file by default and is NOT stored in the wallet — import it with +`import private-key` to sign with it. The TRON and EVM addresses shown are two +encodings of the same generated key. + +Options: + --out file to write the keypair to (0600); refuses to overwrite [optional, default: /generated/keypair-
] + --print-secret print the private key to stdout instead of writing a file (use offline) [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli address generate + wallet-cli address generate --out /secure/usb/key.json +``` + +--- + +## 4. account 组 + +### 4.1 `account balance` —— 原生币余额 + +> **本版改动**:走 `eth_getBalance`,单位 ETH / wei;json 结构与 TRON 侧完全一致。 + +**用法** + +``` +wallet-cli account balance [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 查询账户原生币余额 | +| EVM 增量 | 走 `eth_getBalance`;单位 ETH / wei(18 位) | +| 网络 | 可选(缺省 `config.defaultNetwork`) | +| 错误 | `family_mismatch`、`rpc_error`(含端点限流 429) | + +**示例与输出** + +```bash +$ wallet-cli account balance --network sepolia +Label main +Balance 12.3456 ETH +``` + +```bash +$ wallet-cli account balance --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"account.balance","data":{ "address":"0x7a3f...c19b","balance":"12345600000000000000","decimals":18,"symbol":"ETH" },"meta":{ "durationMs":180,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> json 结构与 TRON 侧**完全一致**(`address` / `balance` / `decimals` / `symbol`),只是值与单位不同:`balance` 恒为最小单位整数字符串(TRON 给 sun、EVM 给 wei),人话单位只在 text 出现。 + +**Help 输出** + +> **相对现状**:描述由 `Show native balance (TRX/SUN)` 改为族中立;全局 `--network` 示例值改为跨两族;Examples 改为两族对称。 + +```text +$ wallet-cli account balance --help + +Usage: + wallet-cli account balance [options] + +Show the native coin balance for the selected network + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli account balance --network nile + wallet-cli account balance --network sepolia +``` + +### 4.2 `account portfolio` —— 持仓与估值 + +> **本版改动**:代币为 ERC20;价格源需补 EVM 链与代币的 id 映射。 + +**用法** + +``` +wallet-cli account portfolio [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 原生币 + 地址簿内代币的余额与 USD 估值 | +| EVM 增量 | 代币为 ERC20;价格源需补 EVM 链与代币的 id 映射 | +| 错误 | 同 §4.1;价格源不可用时估值列留空、进 `meta.warnings`,并给 `priceUnavailable` / `priceReason` 两个可程序判断的字段 | + +**示例与输出** + +```bash +$ wallet-cli account portfolio --network ethereum +| Token | Balance | Price (USD) | Value (USD) | +| ----- | ------- | ----------- | ----------- | +| ETH | 12.3456 | $3,321.40 | $41,004.35 | +| USDC | 2500 | $1.0000 | $2,500.00 | +| USDT | 1000 | $0.9998 | $999.80 | + +Total ≈ $44,504.15 +``` + +```bash +$ wallet-cli account portfolio --network ethereum -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"account.portfolio","data":{ "network":"eip155:1","account":"wlt_ab12cd34.0","address":"0x7a3f...c19b","priceSource":"coingecko","holdings":[ { "kind":"native","symbol":"ETH","decimals":18,"rawBalance":"12345600000000000000","balance":"12.3456","priceUsd":"3321.40","valueUsd":"41004.35" },{ "kind":"erc20","id":"0xA0b8...eB48","symbol":"USDC","decimals":6,"rawBalance":"2500000000","balance":"2500","priceUsd":"1.0000","valueUsd":"2500.00" },{ "kind":"erc20","id":"0xdAC1...1ec7","symbol":"USDT","decimals":6,"rawBalance":"1000000000","balance":"1000","priceUsd":"0.9998","valueUsd":"999.80" } ],"totalValueUsd":"44504.15" },"meta":{ "durationMs":642,"warnings":[] },"chain":{ "family":"evm","network":"eip155:1","chainId":"1" } } +``` + +> 结构沿用既有:`rawBalance`(最小单位)与 `balance`(人话单位)并存,价格不可用时 `priceUsd` / `valueUsd` / `totalValueUsd` 为 `null`、text 显示 `-`。代币条目的合约地址走 `id` 字段(与 token 地址簿同名)。 + +#### 降级语义(本版新增两组字段) + +| 情况 | 字段 | 语义 | +| --- | --- | --- | +| 价格源整体失败 | `priceUnavailable: true` + `priceReason` | 全表估值列为 `null`;同时进 `meta.warnings` | +| 单个代币余额读不到 | 该条目 `balanceUnavailable: true` + `reason` | **该行仍在**,余额与估值为 `null` | + +**为什么要布尔字段而不只是 `meta.warnings`**:`warnings` 是给人看的字符串,**agent 要分支就得比对字符串**。两个布尔字段让「为什么没有估值」可程序判断。 + +**为什么逐币降级**:一个下市合约、一次 `balanceOf` revert 或一次 RPC 抖动,**不该让整张持仓表消失**;而该行报 0 会是一个假的事实——**「读不到」与「是零」是两件事**。 + +> EVM 端逐币并行读取,**刻意不用 multicall**:那要引入合约依赖与每条链一个待验证的地址,只为省下几次往返。 + +#### 测试网估值规则(本版新增) + +**标记为测试网的网络(§2.2)一律不估值,币价与代币价固定为 `0`,且不发任何外部请求。** + +- **取 `0` 而不是 `null`**:`null` 的意思是「我们查不到」,而测试网不是查不到——**是确定没有价值**。说出后者比留白诚实,`totalValueUsd` 也会有一个明确的 0 而不是一片 `-`。 +- **TRON 侧同步变更**:`nile` / `shasta` 先前显示**真实 TRX 币价**,本版起为 0。这是刻意一并改的——**两族在同一条命令上给相反的答案,比任何一种答案都糟**。(破坏性后果,进 release note。) +- **顺带关掉一个真实曝险**:测试网代币先前用**主网平台**查价,而确定性部署可能让同一个地址同时存在于两条链——那会让测试代币拿到真币的价格。 +- **未申报为测试网的自配网络维持 `null`**:不知道 ≠ 不值钱。 +- **币种名称维持该链的正式名称**(ETH / BNB / TRX),不改成 `SepoliaETH` / `tBNB`——「这不是真钱」由估值规则表达,比改币种名更直接,也不必偏离链本身的称呼。 +> +> **余额列按 §1.4 的精度规则**:最多 6 位小数、尾随零去除,故 `2500` 不写成 `2500.00`。**价格与估值列不适用该规则**——它们是法币金额,按 USD 惯例固定 2 位(价格因单价可能极小,保留 4 位),补零是可读性所需,不是精度损失。 + +**Help 输出** + +> **相对现状**:描述补一句「代币取自所选网络的地址簿」;`--network` 示例值改为跨两族;Examples 两族对称。 + +```text +$ wallet-cli account portfolio --help + +Usage: + wallet-cli account portfolio [options] + +Show native + token balances with best-effort USD value. Tokens come from the +address book of the selected network. + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli account portfolio --network nile + wallet-cli account portfolio --network sepolia +``` + +### 4.3 `account info` —— 账户状态摘要 + +> **本版改动**:EVM 侧给 Balance / Nonce / Type / Code size——**Nonce 是排查卡单的唯一入口**。 + +**用法** + +``` +wallet-cli account info [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 该账户在当前链上的关键状态摘要 | +| EVM 增量 | `eth_getBalance` + `eth_getTransactionCount` + `eth_getCode`;字段按 EVM 账户模型取舍 | +| 错误 | `account_not_found`(`--account` 传了非本地账户)、`family_mismatch`、`rpc_error`(含端点限流 429) | + +**示例与输出** + +```bash +$ wallet-cli account info --network sepolia +Label main +Address 0x7a3f...c19b +Balance 12.3456 ETH +Nonce 42 +Type EOA +``` + +```bash +# 账户地址上有字节码时:Type 变为 contract,附字节码大小 +# team-vault 是 `import watch` 注册的团队多签合约地址(§3.5) +$ wallet-cli account info --account team-vault --network ethereum +Label team-vault +Address 0xC4d9...30ab +Balance 18.42 ETH +Nonce 1 +Type contract +Code size 3,124 bytes +``` + +> **`--account` 只解析本地账户**(accountId / 标签 / 该账户自己的地址,§1.3),不是「查任意链上地址」的入口——传一个不在本地的地址报 `account_not_found`。要看别人的合约,先 `import watch --address ` 注册成观察账户再查;这与 `account balance` / `portfolio` 的口径一致,全组不为 EVM 破例。 + +```bash +$ wallet-cli account info --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"account.info","data":{ "label":"main","address":"0x7a3f...c19b","balance":"12345600000000000000","decimals":18,"symbol":"ETH","nonce":42,"type":"eoa" },"meta":{ "durationMs":260,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> 合约地址的 json 多 `codeSize`(字节数整数)、`type` 为 `contract`;EOA 不给 `codeSize` 而非给 `0`——空值行被丢弃是渲染层规则,json 同样不塞无意义的零。`type` 的取值只有 `eoa` / `contract` 两种,全小写(与 `Status` 的收敛口径一致,§6.5)。 + +> **字段按 family 取舍,不是「TRON 有什么 EVM 也要有什么」**:TRON 侧给 `Staked` / `Energy` / `Bandwidth` / `Permissions` / `Created`(资源与多签模型),EVM 一个都没有;EVM 给 `Nonce` 与 `Type`,TRON 没有。两族共有的只有 `Label` / `Address` / `Balance`。 +> +> **`Nonce` 是这条命令在 EVM 上存在的主要理由**:它是 `--nonce` 手动指定、nonce gap 排查、`--wait` 超时后判断交易是否还挂在内存池的唯一查询入口(§6.1)。业内对应 `cast nonce`;`Type` 对应 `cast code` 的有无判断,转账前确认收款方是不是合约。 + +**Help 输出** + +> **相对现状**:描述由 `Show raw account data (getAccount; …)` 改写为按 family 说差异、TRON 在前(去 RPC 方法名,§10.1 规则 3);`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli account info --help + +Usage: + wallet-cli account info [options] + +Show the account's on-chain state for the selected network. Fields differ by +family: TRON reports staked amounts, resources and permissions; EVM reports the +transaction nonce and whether the address holds code. + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli account info --network nile + wallet-cli account info --network sepolia +``` + +### 4.4 关于 `account history`(EVM 后续版本,本节无命令规格) + +**本版 EVM 不做**,但理由不是「做不了」,写清楚以免下一版重新论证: + +| 项 | 说明 | +| --- | --- | +| TRON 现状 | 靠 TronGrid 的账户交易接口,公共端点即可用、无需 key | +| EVM 所需 | 节点 JSON-RPC **不提供**按账户查历史的接口——`eth_*` 里没有这个能力,`eth_getLogs` 只能按 topic 捞 ERC20 的 Transfer 事件,**捞不到原生币转账**(它不产生 log)。可用的路子有三条,**互不兼容**:① **Etherscan 兼容 API**——要 key,且免费档在收紧(2026-07 起单次返回上限由 10,000 降至 1,000);② **Blockscout**——公共实例**无需 key**,key 只用于提高限额,但按链覆盖不齐;③ **服务商增强方法**(如 `alchemy_getAssetTransfers`)——**不需要额外 key**,走用户已配的 `httpEndpoint` 即可,但只有部分服务商提供 | +| 本版不做的原因 | 不是「必须有 key」,而是**没有标准接口**:三条路子的请求与响应结构完全不同,各要一个适配器;更棘手的是**能力取决于用户碰巧配了哪个端点**——同一条命令在不同机器上有无历史可查,这对确定性 CLI 是硬伤,得先定「运行时探测还是要求显式声明」。这是独立一块工作,塞进本版会稀释 EVM 转账主线 | +| 后续方案 | 新增 `explorer` 类 port,配置为 `networks..explorerUrl`(选哪个浏览器)+ **可选**的 `networks..explorerApiKey`(Etherscan 必填、Blockscout 可空);沿用 §10.1「help 文案规范」的 Requires 规则 6,把「一个 Etherscan 兼容或 Blockscout 端点」写进该命令的 Requires 段(与 TRON 侧 `account history` 的 TronGrid Requires 对称) | + +--- + +## 5. token 组 + +代币条目的 `kind` 增加 `erc20` 一档(既有 `trc20` / `trc10` 不变)。地址簿按 network id 分区存储,跨链天然隔离。 + +### 5.1 `token balance` —— 单个代币余额 + +> **本版改动**:走 ERC20 `balanceOf`;`--asset-id`(TRC10)在 EVM 网络下被拒。 + +**用法** + +``` +wallet-cli token balance (--contract | --asset-id ) [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 查询单个代币余额 | +| EVM 增量 | 走 ERC20 `balanceOf(address)`;`--asset-id`(TRC10)**降为 TRON 专属旗标**,help 中标 `(TRON only)`,在 EVM 网络下传入报 `invalid_option` | +| 错误 | `token_metadata_unavailable`、`token_not_in_book`、`family_mismatch` | + +**示例与输出** + +```bash +$ wallet-cli token balance --contract 0xA0b8...eB48 --network ethereum +Label main +Name USD Coin +Symbol USDC +Balance 2500 USDC +``` + +```bash +$ wallet-cli token balance --contract 0xA0b8...eB48 --network ethereum -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"token.balance","data":{ "address":"0x7a3f...c19b","kind":"erc20","id":"0xA0b8...eB48","name":"USD Coin","symbol":"USDC","decimals":6,"balance":"2500000000" },"meta":{ "durationMs":210,"warnings":[] },"chain":{ "family":"evm","network":"eip155:1","chainId":"1" } } +``` + +**Help 输出** + +> **`--contract` 与 `--asset-id` 的分层(§5 全组适用)**:`--contract` 留在**共用层**并降为不做格式检查的字符串——地址格式改由各族 binding 的 refine 验证;`--asset-id` **与那条「二选一」规则一起移进 TRON binding**。 +> +> 理由是 **TRC10 是 TRON 专属概念,EVM 没有对应物**——那条「二选一」的 refine 在 EVM 上恒为错误规则;只标注 `(TRON only)` 是文字,规则本身还是会跑。 +> +> **为何不让两族各自声明 `--contract`**:help 与 `--catalog` 合并同名的 family 字段时是**后盖前**,两族都声明会让说明文字只剩最后注册那族的版本。(验证本身不受影响——`z.toJSONSchema` 不序列化 refinement——受害的只有描述文字。) +> +> **TRON 用户看到的东西没变**:错误信息与 issue path 与先前完全相同(`invalid tron address`)。 + +> **相对现状**:描述去掉 `(--contract / --asset-id)`;**`--contract` 降为族中立的「token contract address」,不再带「二选一」叙述**(那条规则连同 `--asset-id` 一起移入 TRON binding,见下);`--asset-id` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token balance --help + +Usage: + wallet-cli token balance [options] + +Show a single token balance + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --contract token contract address [optional] + --asset-id TRC10 numeric asset id; provide exactly one of --asset-id or --contract [optional] (TRON only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token balance --contract TR7... --network nile + wallet-cli token balance --contract 0xA0b8... --network sepolia +``` + +### 5.2 `token info` —— 代币元数据 + +> **本版改动**:走标准 ERC20 只读方法,输出字段与 TRON 侧完全一致。 + +**用法** + +``` +wallet-cli token info (--contract | --asset-id ) [--network ] +``` + +**概览** + +| 项 | 内容 | +| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 功能 | 查询代币名称 / 符号 / 精度 | +| EVM 增量 | 走标准 ERC20 只读方法(`name` / `symbol` / `decimals` / `totalSupply`),json 与 TRC20 对称;**text 同样只显三行**,不新增输出字段 | +| 错误 | `token_metadata_unavailable`(目标地址的链上元数据读不到,含「不是可探测的 ERC20」这一情形) | + +**示例与输出** + +```bash +$ wallet-cli token info --contract 0xA0b8...eB48 --network ethereum +Name USD Coin +Symbol USDC +Decimals 6 +``` + +> text 三行与 TRON 侧完全一致(实测现状即为 `Name` / `Symbol` / `Decimals`)。 +> +> **`totalSupply` 的 text / json 不一致是既有缺陷,本版有意不动**:数据在查(4 次 constant call)、json 里有,唯独 text 不显示。它与 EVM 无关,两族一样,本版不趁改造顺手动它——修的时候要连带处理另一个同源问题:**单个字段调用失败会静默丢行**(`name()` 失败时 `Name` 整行消失且 `meta.warnings` 为空,「该代币没有此字段」与「这次没取到」无法区分)。两者一并修:补 text 行或从 json 去掉,以及把失败写进 warnings。 +> +> **help 的描述已不再宣称 `totalSupply`**(2026-08-28 PM 拍板,按实作):组 help 与命令 help 的一行描述均为 `Show token metadata`。这是对的——**help 不该替一个 text 里看不到的字段背书**;等上面那条缺陷修完,要不要把字段列举加回描述再议。 + +**Help 输出** + +> **相对现状**:描述去掉 `(name/symbol/decimals/totalSupply)` 字段列举;**`--contract` 降为族中立、不带「二选一」叙述**;`--asset-id` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token info --help + +Usage: + wallet-cli token info [options] + +Show token metadata + +Options: + --contract token contract address [optional] + --asset-id TRC10 numeric asset id; provide exactly one of --asset-id or --contract [optional] (TRON only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token info --contract TR7... --network nile + wallet-cli token info --contract 0xA0b8... --network sepolia +``` + +### 5.3 `token add` —— 加入地址簿 + +> **本版改动**:探测走 ERC20 只读调用,**兼容 bytes32 元数据**;`kind` 记为 `erc20`。 + +**用法** + +``` +wallet-cli token add (--contract | --asset-id ) [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 把代币加入当前网络的地址簿,自动探测符号 / 精度 / 名称 | +| EVM 增量 | 探测走 ERC20 只读调用;`kind` 记为 `erc20` | +| 错误 | `token_metadata_unavailable`、`token_already_listed` | + +> **探测要兼容 bytes32 元数据**:ERC20 定稿前的老代币(MKR 等)把 `name()` / `symbol()` 返回成 `bytes32` 而非 `string`,按 string 解码会失败。业内库(ethers / web3)均做双解码回退,我方同样:先按 `string` 解,失败再按 `bytes32` 解并去除尾部零字节;两者都失败才报 `token_metadata_unavailable`。`decimals()` 缺失时不猜默认值,直接报 `token_metadata_unavailable`——猜错精度会让后续每一笔转账金额都错。 + +**示例与输出** + +```bash +$ wallet-cli token add --contract 0xA0b8...eB48 --network ethereum +✅ Added to token book + Name USD Coin + Symbol USDC + Decimals 6 +``` + +**Help 输出** + +> **相对现状**:描述改写为「加入所选网络的地址簿」;**`--contract` 降为族中立、不带「二选一」叙述**;`--asset-id` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token add --help + +Usage: + wallet-cli token add [options] + +Add a token to the address book of the selected network, fetching its name, +symbol and decimals from the contract + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --contract token contract address [optional] + --asset-id TRC10 numeric asset id; provide exactly one of --asset-id or --contract [optional] (TRON only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token add --contract TR7... --network nile + wallet-cli token add --contract 0xA0b8... --network sepolia +``` + +### 5.4 `token list` —— 列出地址簿 + +> **本版改动**:条目 `kind` 扩 `erc20`;分区方式沿用现状(按 network id),EVM 网络各自一本。 + +**用法** + +``` +wallet-cli token list [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 列出当前网络地址簿的全部条目(内置 + 用户自加) | +| EVM 增量 | 条目 `kind` 为 `erc20`;地址簿**按 network id 分区**(沿用现状),`eip155:1` 与 `eip155:56` 是两本,`tron:mainnet` 与 `tron:nile` 也是两本(分区键是**规范 id**,不是别名)——不按 family、不跨网络合并 | +| 错误 | `family_mismatch`(账户与网络 family 不符) | + +**示例与输出** + +```bash +$ wallet-cli token list --network ethereum +| Symbol | Name | Source | Contract / ID | +| ------ | ---------- | -------- | ------------- | +| USDT | Tether USD | official | 0xdAC1...1ec7 | +| USDC | USD Coin | official | 0xA0b8...eB48 | +| MYTK | My Token | user | 0x4f2a...9b03 | +``` + +> `official` 条目按规范 id 内置(`eip155:1` 填 USDT / USDC;测试网留空,同 `nile` 的处理),用户不可删除。 + +**Help 输出** + +> **相对现状**:描述补「所选网络的」;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token list --help + +Usage: + wallet-cli token list [options] + +List the address book of the selected network (official + user entries) + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token list --network nile + wallet-cli token list --network sepolia +``` + +### 5.5 `token remove` —— 移出地址簿 + +> **本版改动**:无 EVM 特有行为,仅 `kind` 扩 `erc20`。 + +**用法** + +``` +wallet-cli token remove (--contract | --asset-id ) [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 移除用户自加的代币条目 | +| 错误 | `token_not_in_book`、`token_is_official`(内置条目不可删) | + +**示例与输出** + +```bash +$ wallet-cli token remove --contract 0x4f2a...9b03 --network ethereum +✅ Removed from token book + Name My Token + Symbol MYTK +``` + +**Help 输出** + +> **相对现状**:描述补一句「内置条目不可删」;**`--contract` 降为族中立、不带「二选一」叙述**;`--asset-id` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token remove --help + +Usage: + wallet-cli token remove [options] + +Remove a user-added token from the address book. Official entries cannot be +removed. + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --contract token contract address [optional] + --asset-id TRC10 numeric asset id; provide exactly one of --asset-id or --contract [optional] (TRON only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token remove --contract TR7... --network nile + wallet-cli token remove --contract 0xA0b8... --network sepolia +``` + +--- + +## 6. tx 组 + +### 6.1 `tx send` —— 转账 ✍️🔒 + +> **本版改动**:新增 gas 四选项与 `--nonce`;回执含 `Nonce`;Fee 行改为 gas 构成。 + +**用法** + +``` +wallet-cli tx send --to (--amount | --raw-amount ) [--token | --contract ] + [--gas-limit ] [--max-fee ] [--priority-fee ] [--nonce ] + [--dry-run | --sign-only | --build-only] [--wait] + [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 转出原生币或 ERC20 代币 | +| EVM 增量 | gas 四项选项;回执含 `Nonce`;Fee 行为 gas 构成 | +| gas 模型 | **由链上偵测:`baseFeePerGas` 字段存在即 EIP-1559,即使值为零**;`NetworkDescriptor.feeModel: "legacy"` 为覆盖用逃生口。**不写 `meta.warnings`** | +| nonce | 默认取 `eth_getTransactionCount(address, "pending")` | +| 错误 | `insufficient_balance`、`execution_reverted`、`nonce_too_low`、`family_mismatch` | + +**Options(EVM 增量;help 中全量列出并标 `(EVM only)`,在 TRON 网络下传入报 `invalid_option`)** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--gas-limit ` | 否 | 链上估算值 | gas 上限;省略则取节点估算值,**不乘任何倍数**。估算失败时**不回退 21000**,见下 | +| `--max-fee ` | 否 | `base×2 + 建议 tip` | EIP-1559 maxFeePerGas。**低于当前 base fee 时在 `meta.warnings` 给出警告**(与 `--nonce` 同一先例:异常但仍可执行 → warnings,不是错误) | +| `--priority-fee ` | 否 | 节点建议值 | EIP-1559 maxPriorityFeePerGas | +| `--nonce ` | 否 | 链上 pending 值 | 显式指定 nonce;**大于链上 pending 值时交易会一直挂起**(nonce gap),此时在 `meta.warnings` 给出警告 | + +#### 费率旗标:只接受 `gwei` 后缀 + +**`--max-fee 25` 与 `--max-fee 25gwei` 等价**(后缀大小写不敏感);**`wei` / `ether` 等其他单位点名拒绝**,报 `invalid_value` 并说明本旗标读 gwei。两半分开看: + +- **`gwei` 后缀收**——它命名的就是这个旗标本来的单位,不可能改变数值;拒收它只惩罚了从 `cast` 那行复制过来的人,换不到任何安全。 +- **其他单位不收**——九个数量级的风险完全落在 `wei` 与 `ether`:`--max-fee 0.01ether` 与 `--max-fee 25` 差十亿倍,而打错的代价就是实付费用差十亿倍。**点名拒绝而不是默默改读**,让用户知道发生了什么。 + +#### gas 模型判定与费率推导 + +**判定规则:`baseFeePerGas` 字段存在即 EIP-1559,即使值为零。** + +**零基准费仍是 1559**:BSC 的 base fee 恒为 `0x0`——存在但为零。把零当成「没有 1559」会误判整条链,并逼出第二条代码路径;而 1559 的算式在 base=0 时**本来就退化成** legacy 的语义,那条路径没有存在的必要。**侦测结果是事实,不是降级,所以不写 `meta.warnings`。**(`NetworkDescriptor.feeModel` 保留为逃生口:设 `"legacy"` 可强制覆盖,供「回报 baseFee 却拒收 type-2 交易」的链使用。) + +**只给一半费率旗标时的推导**: + +| 给了什么 | 推导 | +| --- | --- | +| 都不给 | `maxFee = base×2 + 建议 tip` | +| 只给 `--max-fee` | tip 取建议值并夹到 `≤ maxFee`;**夹住时发 `meta.warnings`** | +| 只给 `--priority-fee` | `maxFee = base×2 + 该值` | +| legacy 链上给任一个 | `invalid_option` 拒绝,**不默默忽略**(否则回报的内容与实际签出的不符) | + +**两条 `meta.warnings`**(都产生「签得出来也送得出去、但不是用户以为的那样」的交易,而没有任何错误会报这件事): + +| 情况 | 为什么要警告 | +| --- | --- | +| 建议 tip 被夹到 `--max-fee` | 我们替用户改了他给的费率(节点会拒绝 tip > fee cap,所以必须夹),但他不会知道 | +| `--max-fee` 低于当前 base fee | 节点接受,交易就一直躺在那里,直到 base fee 跌下来为止 | + +> **明确给了 `--priority-fee` 就不警告**——那是用户自己的决定,不是替他做的。 + +#### `--gas-limit` 省略时的估算 + +**默认就是估算值,不乘任何倍数。** 乘 1.2 会让 `--dry-run` 显示的最高成本失真,而那个数字的意义就是「真相」。估算真的太紧时,`--gas-limit` 就是明确的手动出口。 + +**估算失败不猜**:不回退 21000——那会签出一笔**注定失败**的 ERC-20 转账并报告一切正常。节点拒绝估算时说的话(余额不足、会 revert)比我们猜的数字有用得多。 + +**估算失败的错误码是节点侧的码,不是 `invalid_option`**:这个 catch 盖住的是节点侧的事实,连端点连不上、HTTP 503、超时都算在内。报 `invalid_option`(**exit 2 —— 「你的命令行有问题」**)会让「重试 exit 1、放弃 exit 2」的调用者对一次暂时性网络故障直接放弃。规则是——本来就有类型的错误**保留自己的码与 exit 类别**(`rpc_error` / `timeout` / …),只在信息后面接上 `--gas-limit` 这条出路;没有类型的异常转成 `rpc_error`(否则会在最上层被 redact 成 `internal_error`,把节点原话一起丢掉,而那句原话正是这个函数不猜的理由)。**破坏性后果,进 release note。** + +**示例与输出** + +```bash +$ wallet-cli tx send --to 0x91b2...4d0e --amount 0.25 --network sepolia --wait +# text 输出(沿用「动词摘要 + 字段独占一行」体例) +✅ Sent 0.25 ETH + To 0x91b2...4d0e + Nonce 42 + TxID 0x9c4e...81af + Block #11,204,113 + Fee 0.000441 ETH (21,000 gas × 21.0 gwei) + Status success +``` + +> **Fee 行格式**:`Fee <数额> <符号> ( gas × gwei)`——TRON 侧 `Fee 1.1 TRX (285 bandwidth)` 的同构写法,**金额是纯数字、括号里放消耗构成**。 + +```bash +# ERC20 转账:gas 消耗显著高于原生转账 +$ wallet-cli tx send --to 0x91b2...4d0e --amount 100 --token USDC --network ethereum --wait +✅ Sent 100 USDC + To 0x91b2...4d0e + Token USDC (0xA0b8...eB48) + Nonce 43 + TxID 0x2f7b...05dc + Block #25,118,904 + Fee 0.001209 ETH (65,000 gas × 18.6 gwei) + Status success +``` + +```bash +$ wallet-cli tx send --to 0x91b2...4d0e --amount 0.25 --network sepolia --dry-run +⏳ Dry run tx send + Fee ≤ 0.00048 ETH (21,000 gas × 22.9 gwei max) + Tx 0x02f86e83aa...57352fc8 +``` + +> **dry-run 回执固定 `Fee` + `Tx` 两行**(沿用现状),不因 EVM 增字段——nonce 要到回执阶段才看。估算与实际共用 `Fee` 这一个字段名,靠 **`≤`** 与 `max` 区分。 +> +> **前缀是 `≤` 而不是 `~`**:`~` 的意思是「大约」——它同时允许实际值**高于**这个数字。而这个数字不是估计值,是**上限**(`gasLimit × 每单位 gas 的上限`),交易签出去之后实际费用不可能超过它。用 `~` 会让一个确定的保证读起来像一个可能失准的猜测,而 dry-run 存在的理由正是「我最多会花多少」。括号里保留 `max`:那修饰的是 gas **单价**——实际结算的单价通常低于它。 +> +> **只有两处例外,且都是「不看就没法判断这笔该不该发」的信息**:`contract send` 在 `approve` 时多出 `Spender` / `Allowance`(§7.2),`contract deploy` 多出 `Address`(§7.3,由 sender + nonce 算出,不需上链)。除这两项外,任何命令都不得往 dry-run 加字段。 + +```bash +$ wallet-cli tx send --to 0x91b2...4d0e --amount 0.25 --network sepolia --wait -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"tx.send","data":{ "kind":"native","stage":"confirmed","txId":"0x9c4e...81af","to":"0x91b2...4d0e","rawAmount":"250000000000000000","nonce":42,"blockNumber":11204113,"confirmed":true,"failed":false,"feeWei":"441000000000000","gasUsed":21000,"effectiveGasPriceWei":"21000000000","maxFeePerGasWei":"22900000000","maxPriorityFeePerGasWei":"1500000000" },"meta":{ "durationMs":14820,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> **`confirmed` 与 `failed` 是两个独立的布尔字段,不是一个状态旗标。** `status: "0x0"` 是「上链、付了 gas、但 revert」——合并成一个旗标,会让一笔 revert 的转账报告成成功。实付费用 `feeWei` 两种情况都回报:**revert 的交易不是免费的**。 +> +> **交易 id 由我们签的内容导出,不是节点指派的**:签名策略回传 `{raw, hash}`,`hash` 是 `keccak256(签名后的字节)`。既有的 `authoritativeTxId` 刻意优先采用本地导出的 id,否则节点回报错误的哈希后,`--wait` 会去轮询别人的交易、再把别人的成功当成你的回执。用 `hash` 这个键让两族共用同一条路径、零分支。 + +**Help 输出** + +> **相对现状**:改动最大:描述族中立并说明 `(TRON only)` / `(EVM only)` 标注含义;Requires 段改为**「只有签名的模式才需要主密码」**(`--dry-run` / `--build-only` 确实不需要);`--to` / `--amount` / `--contract` 描述去 TRON 化;**新增 EVM 四项**(`--gas-limit` / `--max-fee` / `--priority-fee` / `--nonce`);`--build-only` / `--permission-id` / `--expiration` 沿用现状;`--asset-id` / `--fee-limit` / `--permission-id` / `--expiration` 加 `(TRON only)` 标注;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli tx send --help + +Usage: + wallet-cli tx send [options] + +Send the native coin or a token. Flags marked (TRON only) or (EVM only) are accepted +only on networks of that family; using one on the other family is rejected. + +Requires: + the master password only when the selected mode signs — pass --password-stdin then; other modes need no password + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --to recipient address, or a contact name from the address book [required] + --amount amount in whole coins/tokens; mutually exclusive with --raw-amount [optional] + --raw-amount amount in the smallest unit (wei / sun / token base unit) [optional] + --token send this token instead of the native coin, by symbol [optional] + --contract token contract address; alternative to --token [optional] + --asset-id TRC10 numeric asset id; omit with --contract for the native coin [optional] (TRON only) + --fee-limit maximum energy fee to burn, in SUN [optional, default: 100000000] (TRON only) + --permission-id permission group to sign with, for multi-sig accounts [optional] (TRON only) + --expiration transaction expiration window, in milliseconds [optional] (TRON only) + --gas-limit gas cap; estimated from the chain when omitted [optional] (EVM only) + --max-fee EIP-1559 max fee per gas; accepts a unit suffix (25 or 25gwei) [optional] (EVM only) + --priority-fee EIP-1559 max priority fee per gas; accepts a unit suffix [optional] (EVM only) + --nonce transaction nonce; taken from the chain (pending) when omitted [optional] (EVM only) + --dry-run estimate only; do not sign or broadcast [optional, default: false] + --sign-only sign and print the raw transaction; do not broadcast [optional, default: false] + --build-only build an unsigned transaction; do not sign [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --wait after broadcast, poll until the tx is confirmed/failed before returning; default returns the submitted txid without blocking [optional, default: false] + --wait-timeout --wait polling cap, in milliseconds; on timeout return the submitted receipt [optional, default: config.waitTimeoutMs (built-in: 60000)] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli tx send --to T... --amount 1 --network nile + wallet-cli tx send --to 0x742d... --amount 1 --network sepolia + wallet-cli tx send --to T... --token USDT --amount 5 --network nile + wallet-cli tx send --to 0x742d... --token USDC --amount 5 --network sepolia + wallet-cli tx send --to T... --asset-id 1002000 --raw-amount 1000000 --network nile +``` + +> help 一次列全两族的 flag,靠行尾 `(TRON only)` / `(EVM only)` 区分——它不随 `--network` 变化(横切约定)。 + +### 6.2 `tx sign` —— 签名离线交易 🔒 + +> **本版改动**:`--hex` / `--file` 除 TRON protobuf hex 外,也接受 EVM 的 RLP raw tx;新增 chain id 校验。输入形态沿用现状,不改名。 + +**用法** + +``` +wallet-cli tx sign (--hex | --file | --transaction ) [--offline] [--out ] + [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 对本 CLI 之外构造的交易签名,输出可广播的结果 | +| EVM 增量 | `--hex` / `--file` 的内容多一种可接受形态:**RLP 编码 raw tx**(1559 交易以 `0x02` 开头)。`--transaction ` 是 TRON 专属的兼容路径,不接 EVM | +| 校验 | 交易的 family 与网络一致;**EVM 侧另校验交易里携带的 EIP-155 chain id 与目标网络一致** | +| 错误 | `family_mismatch`(交易与网络不同族)、`chain_id_mismatch`(同族但不是同一条链)、`invalid_value` | + +**示例与输出** + +```bash +$ wallet-cli tx sign --hex 0x02f86e83aa36a72a... --network sepolia +✅ Signed send + Address 0x7a3f...c19b + TxID 0x9c4e...81af + Raw tx 0x02f8b1...6f2a41 +``` + +> **EVM 侧这一行是 `Raw tx`,不是 `Signature`**:签名已经嵌在 RLP 里,输出的是一整笔可直接广播的 typed transaction(`0x02…`),下一步原样贴给 `tx broadcast --hex`。TRON 侧的签名交易是 protobuf hex,字段名同样按 family 分派。**`0x` 前缀带在输出里**,与 `--hex` 的输入形式一致,复制即可用;长 hex 走 `--out` 写文件、再用 `--file` 接力。 +> +> 贴入另一族的交易报 `family_mismatch`——EVM RLP 与 TRON protobuf hex 外观相近,误贴概率高,不落到通用的 `invalid_value`。 +> +> **交易里携带的 chain id 必须与目标网络一致**:EIP-155 的 chainId 就编码在 RLP 里,签名前解出来与 `--network` 的 `chainId` 比对,不符报 `chain_id_mismatch`。这一条挡的是同族跨链——贴一笔 `chainId=1` 的主网交易、却选了 `sepolia`,`family_mismatch` 不会触发,而签出来的是一笔真实的主网交易。业内(`cast`、ethers)一律以交易自带的 chainId 为准并做校验。 + +**Help 输出** + +> **相对现状**:描述改为一句人话的「必须是为所选网络构建的交易,否则签名前就拒绝」;**输入形态沿用现状**(`--hex` / `--file` / `--transaction` 三选一,`--offline` / `--out` 不变),仅给 `--hex` / `--file` 的说明加上 EVM 形态、`--transaction` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli tx sign --help + +Usage: + wallet-cli tx sign [options] + +Sign a transaction that was built elsewhere and output the signed result; +broadcast it later with `tx broadcast`. The transaction must have been built for +the network you select — one built for another chain is rejected before it is +signed, so you cannot sign a mainnet transaction by mistake. + +Requires: + the master password — pass --password-stdin; this command never prompts + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + Exactly one of these — the transaction to sign: + --hex transaction hex: protobuf hex for TRON, RLP for EVM + --file file containing the transaction hex + --transaction unsigned transaction JSON; compatibility path, never checked online (TRON only) + + --offline sign locally without contacting the node; only with --hex/--file [optional, default: false] + --out write the signed hex to a file instead of stdout [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli tx sign --transaction '{"txID":"...","raw_data":{...},"raw_data_hex":"..."}' + wallet-cli tx sign --file unsigned.hex --out signed.hex --network nile --password-stdin + wallet-cli tx sign --file unsigned.hex --out signed.hex --network sepolia --password-stdin + wallet-cli tx sign --file partially-signed.hex --offline --password-stdin +``` + +> **`tx sign` 拒收已签名的交易**:EVM 一笔交易只吃一个签名,重签会产出「换了签名的另一笔交易」——回一个 `invalid_transaction` 比默默产出一个不同的东西诚实。 +> +> **`--transaction` / `--tx-stdin` 是 TRON 专属路径**,在 EVM 网络上明确拒绝而非静默忽略:`--transaction` 报「本命令的 tron 选项」,把 payload 灌进 `--tx-stdin` 也由「静默忽略」变成明确拒绝。 + +### 6.3 `tx broadcast` —— 广播已签名交易 ✍️ + +> **本版改动**:走 `eth_sendRawTransaction`;`--hex` / `--file` 多接受 RLP raw tx,新增 chain id 校验。输入形态沿用现状,不改名。 + +**用法** + +``` +wallet-cli tx broadcast (--hex | --file | --transaction | --tx-stdin) + [--dry-run] [--wait] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 广播已签名交易 | +| EVM 增量 | 走 `eth_sendRawTransaction`;`--hex` / `--file` 的内容多一种可接受形态:RLP raw tx。`--transaction` / `--tx-stdin`(JSON)是 TRON 专属 | +| 校验 | 同 §6.2:family 一致 + **EIP-155 chain id 与目标网络一致**,两道都在发出请求前做 | +| `--dry-run` | **本版新增**,两族语义不同(见下);`--dry-run --wait` 报 `invalid_option`(与 TRON 一致) | +| 错误 | `family_mismatch`、`chain_id_mismatch`、`nonce_too_low`、`insufficient_balance`、`rpc_error`,以及下列**广播拒绝码** | + +#### 广播的接受判断是白名单 + +**只有 32 字节哈希算成功,其余一律拒绝。** 这直接沿用 TRON 侧的教训:先前 `res.result === false` 的黑名单判断从未触发,因为被拒绝的回应根本没有 `result` 字段——结果**每一笔被拒交易都被报成 submitted**。 + +**例外:`already known` 判为成功**——交易已在 mempool,用户的意图已达成,重跑同一个指令不该把既成事实报成失败(回应带 `alreadyKnown: true`)。 + +节点的拒绝信息经映射表转成**稳定的错误码**——`nonce_too_high` / `replacement_underpriced` / `gas_too_low` / `fee_too_low` / `gas_limit_exceeded`;认不出来的才保留节点原话于 `transaction_rejected`。没有这组码,调用者只能比对节点的英文句子,而各家客户端的措辞不同。 + +#### `--dry-run`(本版新增) + +「这笔已签名的交易送得出去吗」是一个**在送出去之前**该能问的问题,而 TRON 侧早就能问(多签门槛是否凑齐)。EVM 没有多签,但有三件事会挡下一笔已签名的交易:链不对、nonce 用过了、余额不够——所以做的是同一件事的 EVM 版本。 + +| family | 检查项 | +| --- | --- | +| TRON | 签名、门槛、过期、动态多签费 | +| EVM | 回报 `checks` 四项:`signature`(回推签名者)、`chainId`、`nonce`(太低直接失败;有 gap 给警告)、`balance`(不足直接失败) | + +**节点读取是 best-effort**:端点不可达时把 `nonce` / `balance` 两项降级为 `skipped` 并发 warning,而**不是**让命令失败——跑不到节点的 dry run 仍比没有 dry run 有价值,而报「不能广播」会是一个这段代码**并未建立**的宣称。 + +**示例与输出** + +```bash +$ wallet-cli tx broadcast --hex 0x02f8b183aa36a72a... --network sepolia --wait +✅ Broadcast + TxID 0x9c4e...81af + Block #11,204,113 + Fee 0.000441 ETH (21,000 gas × 21.0 gwei) + Status success +``` + +**Help 输出** + +> **相对现状**:描述改为一句人话的「必须是为所选网络构建的交易,否则发送前就拒绝」;**输入形态沿用现状**(`--hex` / `--file` / `--transaction` / `--tx-stdin` 四选一),仅给 `--hex` / `--file` 的说明加上 EVM 形态、JSON 两项标 `(TRON only)`;**新增 `--dry-run`**;`--network` 示例值;Examples 两族对称。 +> +> ⚠️ **实作的 `--dry-run` 描述目前只写了 TRON 语义**(`validate signatures, threshold, expiration, and dynamic multi-sign fee`),未涵盖 EVM 的四项 `checks`——该文案需补,属 help 文案层待办。 + +```text +$ wallet-cli tx broadcast --help + +Usage: + wallet-cli tx broadcast [options] + +Broadcast an already-signed transaction. It must have been built for the network +you select — one built for another chain is rejected before it is sent. + +Options: + Exactly one of these — the signed transaction to broadcast: + --hex signed transaction hex: protobuf hex for TRON, RLP for EVM + --file file containing the signed transaction hex + --transaction signed transaction JSON (TRON only) + --tx-stdin read the signed transaction JSON from stdin (fd 0) (TRON only) + + --dry-run validate signatures, threshold, expiration, and dynamic multi-sign fee without broadcasting [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --wait after broadcast, poll until the tx is confirmed/failed before returning; default returns the submitted txid without blocking [optional, default: false] + --wait-timeout --wait polling cap, in milliseconds; on timeout return the submitted receipt [optional, default: config.waitTimeoutMs (built-in: 60000)] + +Examples: + wallet-cli tx broadcast --file signed.hex --network nile + wallet-cli tx broadcast --file signed.hex --network sepolia + wallet-cli tx broadcast --tx-stdin < signed.json --network nile +``` + +### 6.4 `tx status` —— 交易状态 + +> **本版改动**:收到 receipt 即判终态;四态枚举与 TRON 侧一致,不新增状态词;新增 `Confirmations` 行(两族同时生效)。 + +**用法** + +``` +wallet-cli tx status --txid <0x…> [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 查询交易确认状态 | +| EVM 增量 | 收到 receipt 即判定终态:`status=1` → confirmed、`status=0` → failed;无 receipt → pending;查不到 → not_found | +| 状态枚举 | 与 TRON 侧同为四态,不新增状态词 | +| 新增字段 | `Confirmations`(head block − 该交易所在区块),confirmed 时才出现;两族同时生效,非 EVM 专属。`--wait` 只等到 receipt,等几个确认由用户读这个数自己判断(§6.5) | +| 错误 | `rpc_error`(含端点限流 429)——**查不到交易不是错误**,是 `not_found` 这个状态,退出码仍为 0 | +| `not_found` 的警告 | **该状态一律附一条 `meta.warnings`**:公开节点常剪枝,这可能表示节点没有记录,而非交易不存在;建议改用归档节点 | + +> **为什么 `not_found` 必须带警告**:它是这条命令**唯一可能说错过去**的答案。一笔真的上链过的交易,在一个剪枝过的公开端点上一样回 null——而一句光秃秃的「not found」会让读者得出「它从没发生过」的结论。 +> +> 实作上并用 `eth_getTransactionByHash` 与 `eth_getTransactionReceipt` 才能分辨「在 mempool」与「从不存在」——收据对两者都回 null。与 TRON 并用 `getTransactionById` 的模式相同。 + +**示例与输出** + +```bash +$ wallet-cli tx status --txid 0x9c4e...81af --network sepolia +TxID 0x9c4e...81af +Status confirmed ✅ +Block #11,204,113 +Confirmations 36 +``` + +```bash +$ wallet-cli tx status --txid 0x9c4e...81af --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"tx.status","data":{ "txid":"0x9c4e...81af","state":"confirmed","blockNumber":11204113,"confirmations":36 },"meta":{ "durationMs":190,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +**Help 输出** + +> **相对现状**:`--txid` 描述去掉 `TRON`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli tx status --help + +Usage: + wallet-cli tx status [options] + +Show confirmation status of a transaction + +Options: + --txid transaction id/hash [required] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli tx status --txid abc123 --network nile + wallet-cli tx status --txid 0x9c4e... --network sepolia +``` + +### 6.5 `tx info` —— 交易详情 + +> **本版改动**:新增 `Confirmations` 行;Fee 行为 gas 构成,无 TRON 的资源分项。 + +**用法** + +``` +wallet-cli tx info --txid <0x…> [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 交易详情与回执 | +| EVM 增量 | 增 `Confirmations` 行;Fee 行为 gas 构成;无 TRON 的资源分项 | +| 查不到 txid | 与 `tx status` 不同——`tx info` 要给的是**详情**,无详情可给时报 `not_found`(退出码 1),不返回空壳。想区分「尚未上链」与「不存在」用 `tx status` | +| 错误 | `not_found`(该 txid 无交易详情)、`rpc_error`(含端点限流 429) | + +**示例与输出** + +```bash +$ wallet-cli tx info --txid 0x9c4e...81af --network sepolia +TxID 0x9c4e...81af +Type transfer +From 0x7a3f...c19b +To 0x91b2...4d0e +Amount 0.25 ETH +Nonce 42 +Block #11,204,113 +Block time 2026-08-06 09:14:32 UTC +Confirmations 36 +Fee 0.000441 ETH (21,000 gas × 21.0 gwei) +Status success +``` + +```bash +$ wallet-cli tx info --txid 0x9c4e...81af --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"tx.info","data":{ "txid":"0x9c4e...81af","type":"transfer","from":"0x7a3f...c19b","to":"0x91b2...4d0e","rawAmount":"250000000000000000","nonce":42,"blockNumber":11204113,"blockTime":1786007672,"confirmations":36,"feeWei":"441000000000000","gasUsed":21000,"effectiveGasPriceWei":"21000000000","status":"success" },"meta":{ "durationMs":240,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> `blockTime` 是 Unix 秒(与 `chain node` 的 `headBlock.timestamp` 同口径),text 才格式化为 `YYYY-MM-DD HH:MM:SS UTC`;费用三项(`feeWei` / `gasUsed` / `effectiveGasPriceWei`)与 `tx send` 回执同名同义,text 的 Fee 行由它们合成。 + +#### json 另带两个透传的原始对象 + +上面十三个扁平键**都在**,另外多带 **`transaction` 与 `receipt`** 两个对象,**节点原话全带**。 + +`tx info` 是**排查用**的命令,而我们挑出来的十几个字段不可能涵盖每一次排查需要的东西——access list、logs、`v/r/s`、`type` 的原始值、`maxFeePerGas`……透传让用户不必为了一个字段改用 `cast`;扁平键则让常见的九成不必自己从原始对象里挖。两者不互斥,代价只是 payload 大一些。 + +> **TRON 侧早就这么做**——`tx info` 一直带 `transaction` 与 `info` 两个原始对象。EVM 沿用同一个形状,而不是自创一个。 + +#### `type` 的取值(本版定义为三个) + +| 值 | 含义 | +| --- | --- | +| `transfer` | 原生转账,**或解得出的 ERC-20 `transfer`** | +| `contract-creation` | `to` 为 null | +| `contract-call` | 其余 | + +**三个取值刻意粗**:再细就得去读 calldata 的方法名,而那正是下面决定不做的事。`contract-creation` 不是猜的——`to` 为 null 就是它成为部署的定义。 + +#### calldata:只解 ERC-20 `transfer`,其余照实回报 + +**只解 `transfer(address,uint256)` 这一个选择器**,输出对齐 TRON 对 TRC20 的既有字段(`contract` + `symbol` + 以该代币 decimals 换算的 `amount`);代币 metadata 读不到时退回 base unit 数量。**其余 calldata 一律不解。** + +**为什么必须解这一个**:一笔 ERC-20 转账的原始交易里,`to` 是**合约**、`value` 是 **0**,真正的收款人与金额在 calldata。照实回报等于**指错收款人**——而这正是 TRON 侧对 TRC20 早已避免的事。 + +**为什么只解这一个**:猜测未知调用的语义,等于发明签名没有承载的意义。`transfer` 之所以例外,是因为它的形状是 ERC-20 标准的一部分,不是猜的。 + +> **与 `contract send` 的 `approve` 回执(§7.2)不冲突**:那里**没有在解码**——调用者自己打了 `--method "approve(address,uint256)"` 与参数,意义是他说出来的。这里面对的是一串没人交代过形状的 calldata。**同一条界线的两侧。** + +> **`Status` 一律小写**:`tx info` 现状输出大写 `SUCCESS`,而 `tx status`(§6.4)与各写命令回执给的是小写 `success` / `confirmed`。同一个字段名在同一 CLI 里出现两种大小写,agent 侧要写两套匹配。本版一并收敛为小写,TRON 侧同步。 + +> `--wait` 只等到 receipt,不额外等 N 个确认——等多少个是场景决定,交由用户读 `Confirmations` 自行判断。重组风险的静态说明进 help,不进输出。`--wait` 超时的语义见 §6.1。 + +**Help 输出** + +> **相对现状**:`--txid` 描述去掉 `TRON`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli tx info --help + +Usage: + wallet-cli tx info [options] + +Show full transaction detail + receipt + +Options: + --txid transaction id/hash [required] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli tx info --txid abc123 --network nile + wallet-cli tx info --txid 0x9c4e... --network sepolia +``` + +--- + +## 7. contract 组 + +既有实现显式传函数签名与参数,**不读链上 ABI**,EVM 侧直接复用。 + +### 7.1 `contract call` —— 只读调用 + +> **本版改动**:走 `eth_call`;**不依赖链上 ABI**,函数签名与参数类型显式传。 + +**用法** + +``` +wallet-cli contract call --contract <0x…> --method [--params ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 只读调用(不上链、不花费) | +| EVM 增量 | 走 `eth_call`;合约地址收 `0x…` | +| 错误 | `execution_reverted`、`invalid_address`、`invalid_value` | + +**示例与输出** + +```bash +$ wallet-cli contract call --contract 0xA0b8...eB48 --method "balanceOf(address)" \ + --params '[{"type":"address","value":"0x7a3f...c19b"}]' --network sepolia +Method balanceOf(address) +Result 0x00000000000000000000000000000000000000000000000000000000950f9ac0 (raw) +``` + +> **`Result` 是原始 hex,不解码**,渲染层在值后标 `(raw)`。 +> +> **为什么不解码**:`--method "balanceOf(address)"` 只声明**入参**类型,**不带返回类型**——没有 ABI 就无从解码。要解就得猜,而猜错的方式很多:`uint256` 与 `int256`、`address` 与 `bytes20`、多返回值的边界。TRON 侧现状就是回原始 hex,**两族一致而不是各自为政**。要解码就得先有一个声明返回类型的旗标(`--returns` 之类),而本版不提供那个旗标,所以也不解码。 +> +> **没有 `Contract` 列**:合约地址是命令行**刚敲过的输入**,回显它不增加信息(与 dry-run 不放 `Contract` 是同一个理由)。text 只有 `Method` / `Result` 两行。 + +**Help 输出** + +> **相对现状**:描述去掉 `triggerConstantContract`(§10.1 规则 3)并说明不查 ABI;`--contract` 去 TRON 化;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli contract call --help + +Usage: + wallet-cli contract call [options] + +Read-only contract call. The function signature and parameter types are supplied +explicitly; no ABI lookup is performed. + +Options: + --contract contract address [required] + --method function signature, e.g. balanceOf(address) [required] + --params JSON array of ABI parameters as {type,value}; omit to pass no parameters [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli contract call --contract TR7... --method "balanceOf(address)" --params '[{"type":"address","value":"T..."}]' --network nile + wallet-cli contract call --contract 0xA0b8... --method "balanceOf(address)" --params '[{"type":"address","value":"0x742d..."}]' --network sepolia +``` + +### 7.2 `contract send` —— 状态变更调用 ✍️🔒 + +> **本版改动**:gas 选项同 `tx send`;**`approve` 特例显示授权额度**,无限授权标 `unlimited`——**该特例本版起两族通用,不再是 EVM 增量**。 + +**用法** + +``` +wallet-cli contract send --contract --method [--params ] [--value ] + [--gas-limit ] [--max-fee ] [--priority-fee ] [--nonce ] + [--dry-run | --sign-only | --build-only] [--wait] + [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 发起会改变链上状态的合约调用 | +| EVM 增量 | gas 选项同 §6.1 | +| `--value` 取代 `--call-value-sun` | 随调用附带的原生币改用**族中立、人话单位**的 `--value`(TRX / ETH)。现状的 `--call-value-sun`(最小单位 SUN)名字里带 TRON 单位,EVM 上不成立,**本版保留为 TRON 兼容别名并标弃用、下一版删**;两者同时传报 `invalid_option` | +| `approve` 特例 | **两族通用**。`--method` 为 `approve(address,uint256)` 时,回执与 `--dry-run` 额外给 `Spender` / `Allowance` 两行;额度按代币 decimals 换算为人话单位,`2^256-1` 显示为 `unlimited` | + +> **`approve` 回执两族都有,不是 EVM 增量。** TRC20 与 ERC-20 **共用这个方法、共用这个危险、也共用那个没人读得懂的参数**——一个 `uint256`,按代币 decimals 缩放,最大值 78 位数。而 approve 是这两条链上最常让人损失资金的一次签名。只在 EVM 上把它翻译成人话,等于认定 TRON 用户比较不需要看懂自己批准了多少。 +> +> 解码逻辑为两族共用,差别只有两处:**spender 地址怎么写**(TRON 的 41-hex 转 base58,故 TRON 侧示例用 base58)、**decimals 从哪来**(`getTokenInfo` vs `getErc20Metadata`)。 +> +> **这不违反 §6.5「不猜 calldata」那条界线**:那里拒绝的是**猜别人交易的意义**;这里调用者自己打了 `--method "approve(address,uint256)"` 与参数,**意义是他说出来的**,我们只做单位换算。同一条界线的两侧。 +> +> **`unlimited` 在读 metadata 之前就短路**:78 位数的形式只告诉读者「这个数字很长」,再多的 decimals 也救不了它,没有必要为此向合约发一次请求。decimals 读不到则退回原始整数——我们标不出单位,不影响这笔授权本身。 +| 差异 | TRON 的 `--fee-limit`(能量模型)对应 EVM 的 `--gas-limit`;两者在 help 中并列、各标 `(TRON only)` / `(EVM only)`,用错族报 `invalid_option` | +| 错误 | `execution_reverted`(含节点返回的 revert reason)、`insufficient_balance`、`nonce_too_low` | + +**示例与输出** + +```bash +$ wallet-cli contract send --contract 0xA0b8...eB48 --method "approve(address,uint256)" \ + --params '[{"type":"address","value":"0x4f2a...9b03"},{"type":"uint256","value":"1000000"}]' \ + --network ethereum --wait +✅ Called approve + Contract 0xA0b8...eB48 + Spender 0x4f2a...9b03 + Allowance 1 USDC + Nonce 44 + TxID 0x81de...92c7 + Block #25,118,940 + Fee 0.000892 ETH (46,200 gas × 19.3 gwei) + Status success +``` + +```bash +# 无限授权:额度显示为 unlimited,不显示那串 78 位数字 +$ wallet-cli contract send --contract 0xA0b8...eB48 --method "approve(address,uint256)" \ + --params '[{"type":"address","value":"0x4f2a...9b03"},{"type":"uint256","value":"115792089237316195423570985008687907853269984665640564039457584007913129639935"}]' \ + --network ethereum --dry-run +⏳ Dry run contract send + Spender 0x4f2a...9b03 + Allowance unlimited + Fee ≤ 0.00091 ETH (46,200 gas × 19.7 gwei max) + Tx 0x02f8b183aa...4c91e7a0 +``` + +> dry-run 沿用 `Fee` + `Tx` 两行,只为 `approve` 多出 `Spender` / `Allowance`——**`Contract` 与 `Nonce` 不进 dry-run**:合约地址是命令行刚敲过的输入,nonce 到回执阶段再看不迟(§6.1)。`Allowance` 必须进,因为它是命令行传的那串 `uint256` 按 decimals 换算后的结果,用户没法心算,而这正是 dry-run 要替他确认的东西。 + +**Help 输出** + +> **相对现状**:描述改写,补 `(TRON only)` / `(EVM only)` 标注含义与 `approve` 特例说明;Requires 冠词统一;**新增族中立的 `--value`(人话单位),`--call-value-sun` 保留为标了弃用的 TRON 别名**;新增 EVM 四项;`--build-only` / `--permission-id` 沿用现状;`--fee-limit` 等加 `(TRON only)` 标注;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli contract send --help + +Usage: + wallet-cli contract send [options] + +State-changing contract call. Flags marked (TRON only) or (EVM only) are accepted only +on networks of that family; using one on the other family is rejected. For +approve(address,uint256) the receipt also reports the spender and the allowance in +human units; an allowance of 2^256-1 is shown as unlimited. + +Requires: + the master password only when the selected mode signs — pass --password-stdin then; other modes need no password + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --contract contract address [required] + --method function signature, e.g. transfer(address,uint256) [required] + --params JSON array of ABI parameters as {type,value} [optional] + --value native coin sent with the call, in whole coins [optional, default: 0] + --call-value-sun deprecated alias for --value, in SUN; removed next release [optional] (TRON only) + --fee-limit maximum energy fee to burn, in SUN [optional, default: 100000000] (TRON only) + --permission-id permission group to sign with, for multi-sig accounts [optional] (TRON only) + --gas-limit gas cap; estimated from the chain when omitted [optional] (EVM only) + --max-fee EIP-1559 max fee per gas; accepts a unit suffix (25 or 25gwei) [optional] (EVM only) + --priority-fee EIP-1559 max priority fee per gas; accepts a unit suffix [optional] (EVM only) + --nonce transaction nonce; taken from the chain (pending) when omitted [optional] (EVM only) + --dry-run estimate only; do not sign or broadcast [optional, default: false] + --sign-only sign and print the raw transaction; do not broadcast [optional, default: false] + --build-only build an unsigned transaction; do not sign [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --wait after broadcast, poll until the tx is confirmed/failed before returning; default returns the submitted txid without blocking [optional, default: false] + --wait-timeout --wait polling cap, in milliseconds; on timeout return the submitted receipt [optional, default: config.waitTimeoutMs (built-in: 60000)] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli contract send --contract TR7... --method "transfer(address,uint256)" --params '[...]' --network nile + wallet-cli contract send --contract 0xA0b8... --method "transfer(address,uint256)" --params '[...]' --network sepolia +``` + +### 7.3 `contract deploy` —— 部署合约 ✍️🔒 + +> **本版改动**:EVM 侧合约地址由 sender + nonce 确定性算出,构建期即给出,不必等上链;**构造参数改为三来源,旗标改名且不保留旧别名**。 + +**用法** + +``` +wallet-cli contract deploy (--artifact | --code | --code-file ) + [--constructor-args | --constructor-params ] + [--constructor-signature ] [--abi ] + [--gas-limit ] [--max-fee ] [--priority-fee ] [--nonce ] + [--fee-limit ] [--permission-id ] [--expiration ] + [--dry-run | --sign-only | --build-only] [--wait] + [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 部署合约字节码,回执给出新合约地址 | +| EVM 增量 | 地址由 `keccak(rlp([sender, nonce]))` 取后 20 字节**确定性算出**,构建期即可给出、不必等上链 | +| 差异 | TRON 的 `--fee-limit`(能量)对应 EVM 的 `--gas-limit`,两者在 help 中并列、各标 family;**EVM 不需要 ABI,类型由构造函数参数的来源提供** | +| 错误 | `execution_reverted`(构造函数回滚)、`insufficient_balance`、`invalid_value`(字节码非法 hex / 在 TRON 上传 `--constructor-signature`)、`invalid_option`(在 EVM 上传 `--permission-id` / `--expiration`) | + +> **deploy 属第二档**:`contract call` / `send` 已把 ABI 参数编码与 gas 估算做完,deploy 的净增量只有「构造参数编码 + 地址推算 + 字节码输入通道」;EVM 是合约生态、部署是基本诉求(`forge create` / `cast send --create` 都是标配),TRON 侧 `contract deploy` 亦早已存在。 +> +> `--code-file` 沿用 TRON 侧的既有理由:字节码常达上万字符,塞不进命令行。 + +#### 旗标改名(破坏性变更,不保留旧别名) + +| 旧名 | 新名 | 备注 | +| --- | --- | --- | +| `--bytecode` | `--code` | 旧名**直接消失**,无别名 | +| `--params` | `--constructor-params` | 旧名**直接消失**,无别名 | +| `--abi` | `--abi` | **保留**,标 `(TRON only)`,`[required]` **unless `--artifact`** | + +**不保留旧别名是明确的决定**:旧名字留着会让两套词汇并存,且 help 里必须同时出现两套;deploy 的调用量本身很低,脚本改一行的成本远小于长期双词汇。**破坏性后果进 release note。** + +**另两项连带决定**: + +- **deploy 不提供 `--call-value`**(用法行本来就没列)。提供一个实作会忽略的旗标,比不提供更糟——调用者会以为值生效了。 +- **`--permission-id` / `--expiration` 移进 TRON binding**,因此在 EVM 上由「静默接受并忽略」变成 `invalid_option` / exit 2。 + +#### 构造函数参数的三种类型来源 + +> **设计原则:类型来自签名或编译器产物,永不来自值。** + +`constructor(uint128)` 误写成 `uint256`,两族都会编码成功、部署出一个参数错误的合约——而**部署不可逆**。 + +| 来源 | 适用 | 说明 | +| --- | --- | --- | +| `--artifact ` | **两族** | 编译器产物(Foundry / Hardhat / sunhat / TronBox),同时含 `abi` 与 `bytecode`。**首选** | +| `--constructor-signature ` | **仅 EVM** | 只有 bytecode 时用签名字符串,如 `constructor(uint256,string)`。在 TRON 上**明确拒绝**(`invalid_value`,信息说明 TronWeb 需要完整 ABI),不静默忽略 | +| `--abi ` | **仅 TRON** | 完整 ABI JSON。`--artifact` 已供 ABI 时可省 | + +参数值本身走 `--constructor-args`(**裸值 JSON 数组**,如 `["18","MyToken"]`);`--constructor-params`(`{type,value}` 形式)**保留可用**,help 中降为次选。 + +**为什么 TRON 必须有 ABI**:TronWeb 的 `createSmartContract` 靠 ABI 推导 constructor 类型,`parameters` 只吃裸值;ethers 不需要 ABI。要让 `--abi` 在 TRON 上也可省略,唯一的做法是从 `{type,value}` 的内嵌类型**合成**一份 ABI 喂给 TronWeb——而合成出来的 ABI **没有任何东西可以校验**:用户把类型打错时 TronWeb 会照着错的类型编码成功,事前无从发现。 + +**`--artifact` 是更强的来源,不是放宽**:`--abi` 因 `--artifact` 而变成「required unless `--artifact`」,看似放宽了上一段的要求,实则相反——上一段反对的是「从内嵌类型**合成**一份无法校验的 ABI」,而 `--artifact` 提供的是**编译器输出的真 ABI**,比人手贴上的更可信(连手写 ABI 的打字错误都排除了)。等于换一个更强的来源满足同一个要求。 + +**业界形状**:`forge create --constructor-args`(类型来自编译产物)、`cast send --create `(只有 bytecode 时用签名字符串)。**没有任何主流工具要求用户写 `[{"type":"uint256","value":"42"}]`**——那是 TronWeb `triggerSmartContract` 的内部 JSON 形状漏到了 CLI 表面。 + +**`--artifact` 对 TRON 用户收益最大**:`--abi` 必填逼他们自己从那份 JSON 里挖出动辄数 KB 的 ABI 贴到命令行,那是**转抄,不是输入**。Foundry / Hardhat / sunhat / TronBox 的产物都同时含 `abi` 与 `bytecode`,只有 bytecode 的包装差一层(Foundry 是 `{object}`,其余是字符串),两种都收。 + +> **实测**:五种输入形式产生的 calldata **逐字节相同**,且与 `cast abi-encode` 的输出一致(独立实现,非自我一致性检查);Sepolia 与 Nile 各实际部署并回读成功,预测的 CREATE 地址与回执逐字符相同。 + +**示例与输出** + +```bash +$ wallet-cli contract deploy --artifact ./out/Token.sol/Token.json \ + --constructor-args '["18","MyToken"]' --network sepolia --wait +✅ Contract deployed + Address 0x5d71...a3f4 + Nonce 45 + TxID 0xb2c8...71fe + Block #11,204,301 + Fee 0.008408 ETH (1,204,551 gas × 6.98 gwei) + Status success +``` + +```bash +$ wallet-cli contract deploy --artifact ./out/Token.sol/Token.json --network sepolia --wait -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"contract.deploy","data":{ "stage":"confirmed","contractAddress":"0x5d71...a3f4","txId":"0xb2c8...71fe","nonce":45,"blockNumber":11204301,"confirmed":true,"failed":false,"feeWei":"8407765980000000","gasUsed":1204551,"effectiveGasPriceWei":"6980000000" },"meta":{ "durationMs":16420,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> `contractAddress` 在 `--dry-run` / `--sign-only` / submitted 三个阶段都给,值不变(由 sender + nonce 算出);`stage` 随阶段取 `estimated` / `signed` / `submitted` / `confirmed`,与 `tx send` 同一枚举。 + +```bash +$ wallet-cli contract deploy --code-file ./Token.bin --network sepolia --dry-run +⏳ Dry run contract deploy + Address 0x5d71...a3f4 + Fee ≤ 0.008926 ETH (1,204,551 gas × 7.41 gwei max) + Tx 0x02f9049a83aa...b7c0e215 +``` + +> `Address` 在 `--dry-run` / `--sign-only` 阶段同样给出——它只取决于发送方与 nonce,不需要上链。这与 TRON 侧「build 期确定性算出、submitted 就带」的处理一致;它是 dry-run 的两个例外之一(另一个是 `approve` 的额度,§7.2)。**但前提是 nonce 不变**:若该 nonce 被另一笔交易抢先占用,实际地址会不同,这句说明进 help、不进字段。 +> +> 以上 `Address` / `TxID` / `Fee` 等示例值为**设计稿,未实测**(本节的实测结论见上文「实测」一段)。 + +**Help 输出** + +> **相对现状**:描述改写,补 family 标注含义并说明地址在上链前即可给出;**`--bytecode` / `--params` 改名为 `--code` / `--code-file` / `--constructor-params`,不保留旧别名**;**新增 `--artifact` / `--constructor-args` / `--constructor-signature` 三个来源旗标**;`--abi` 保留、标 `(TRON only)`、`required unless --artifact`;`--fee-limit` 由 `[required]` 变 `[optional]` 并标 family;新增 EVM 四项;Requires 段改为「只有签名的模式才需要主密码」;Examples 两族对称,且首选形式用 `--artifact`。 + +```text +$ wallet-cli contract deploy --help + +Usage: + wallet-cli contract deploy [options] + +Deploy contract creation bytecode and report the new contract's address. +Flags marked (TRON only) or (EVM only) are accepted only on networks of that family; using one on the other family is rejected. + +Requires: + the master password only when the selected mode signs — pass --password-stdin then; other modes need no password + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --artifact path to a compiler artifact (Foundry, Hardhat/sunhat, TronBox) holding both the bytecode and the ABI; the preferred source, because the constructor's types then come from the compiler [optional] + --code contract creation bytecode, hex-encoded; provide exactly one of --artifact, --code or --code-file [optional] + --code-file path to a file holding the creation bytecode; bytecode often exceeds the shell's argument limit [optional] + --constructor-signature the constructor's types when there is no ABI, e.g. "constructor(uint256,string)"; not needed with --artifact, and not accepted on TRON, which needs the full ABI [optional] + --constructor-args constructor arguments as a JSON array of bare values, e.g. ["18","MyToken"]; the types come from --artifact, --constructor-signature, or --abi on TRON [optional] + --constructor-params constructor arguments as a JSON array of {type,value} entries, e.g. [{"type":"uint8","value":"18"}]; prefer --constructor-args with --artifact [optional] + --dry-run build and estimate only, with no signature and no broadcast [optional, default: false] + --sign-only sign and output complete transaction hex without broadcasting [optional, default: false] + --build-only build an unsigned transaction without signing or broadcasting; mutually exclusive with --dry-run/--sign-only [optional, default: false] + --abi contract ABI as a JSON array string; required unless --artifact supplies one [optional] (TRON only) + --fee-limit maximum energy fee to burn, in SUN [optional, default: 100000000] (TRON only) + --permission-id TRON permission group to sign with (0=owner, 1=witness, 2-9=active) [optional, default: 0] (TRON only) + --expiration transaction expiration in ms, up to 86400000 (24h); only with --sign-only or --build-only; omitted = node default (~60s) [optional] (TRON only) + --gas-limit gas units to authorise; defaults to the node's estimate, unpadded [optional] (EVM only) + --max-fee maximum total fee per gas, in gwei — 25 or 25gwei (EIP-1559 only) [optional] (EVM only) + --priority-fee tip per gas, in gwei — 25 or 25gwei (EIP-1559 only) [optional] (EVM only) + --nonce transaction nonce; defaults to the account's pending nonce [optional] (EVM only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --wait after submitting, poll until the transaction is confirmed/failed before returning; default returns the submitted receipt without blocking [optional, default: false] + --wait-timeout --wait polling cap, in milliseconds; on timeout return the submitted receipt [optional, default: config.waitTimeoutMs (built-in: 60000)] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli contract deploy --artifact ./build/contracts/Token.json --constructor-args '["18","MyToken"]' --network nile + wallet-cli contract deploy --artifact ./out/Token.sol/Token.json --constructor-args '["18","MyToken"]' --network sepolia + wallet-cli contract deploy --code-file ./Token.bin --constructor-signature 'constructor(uint8,string)' --constructor-args '["18","MyToken"]' --network sepolia +``` + +--- + +## 8. 签名组 + +两条命令的服务层已是 family 无关的,哈希算法在 signer 层按 family 分派。 + +### 8.1 `message sign` —— 签名任意消息 🔒 + +> **本版改动**:算法按 family 分派:EVM 用 EIP-191 前缀,TRON 用 TIP-191。 + +**用法** + +``` +wallet-cli message sign (--message | --message-stdin) [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 对任意消息签名 | +| 算法 | TRON = TIP-191(前缀 `\x19TRON Signed Message:\n`);EVM = EIP-191(前缀 `\x19Ethereum Signed Message:\n`) | +| 输出契约 | 不变(地址、摘要、签名) | +| stdin 通道 | `--message-stdin` 与 `--password-stdin` **不能同时用**——一次运行只有一个 `*-stdin` 能占用 fd 0;用 `--message-stdin` 时主密码须走 TTY | + +**示例与输出** + +```bash +$ wallet-cli message sign --message "hello" --network sepolia +Address 0x7a3f...c19b +Digest 0x50b2...ce31 +Signature 0x4c8f...1b1c +``` + +**Help 输出** + +> **相对现状**:描述改写为按 family 说前缀、TRON 在前;Requires 冠词统一;`--message-stdin` 沿用现状;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli message sign --help + +Usage: + wallet-cli message sign [options] + +Sign an arbitrary message. The prefix follows the selected network's family: +TIP-191 for TRON, EIP-191 for EVM. + +Requires: + the master password — pass --password-stdin; this command never prompts + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --message message to sign; provide this OR --message-stdin [optional] + --message-stdin read the message from stdin (fd 0) [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli message sign --message "hello" --network nile + wallet-cli message sign --message "hello" --network sepolia +``` + +### 8.2 `typed-data sign` —— 签名结构化数据 🔒 + +> **本版改动**:EVM 用 EIP-712,TRON 用 TIP-712;输出契约与 flag 集合均不变。 + +**用法** + +``` +wallet-cli typed-data sign --typed-data [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 对结构化数据签名 | +| 算法 | TRON = TIP-712;EVM = EIP-712 | +| 错误 | `invalid_payload`(domain / types / primaryType 不完整) | + +**示例与输出** + +```bash +$ wallet-cli typed-data sign --typed-data '{"domain":{...},"types":{...},"message":{...}}' --network ethereum +Address 0x7a3f...c19b +Primary type Permit +Digest 0xa71c...4e08 +Signature 0x9d3b...77ea +``` + +**Help 输出** + +> **相对现状**:描述由「讲输出」改为「讲动作」(§10.1 规则 1);Requires 冠词统一;**flag 集合无变化**;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli typed-data sign --help + +Usage: + wallet-cli typed-data sign [options] + +Sign an EIP-712 / TIP-712 typed-data payload + +Requires: + the master password — pass --password-stdin; this command never prompts + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --typed-data EIP-712/TIP-712 JSON: {"domain":…,"types":…,"primaryType"?:…,"message":…} [required] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli typed-data sign --typed-data '{"domain":{...},"types":{...},"message":{...}}' --network nile + wallet-cli typed-data sign --typed-data '{"domain":{...},"types":{...},"message":{...}}' --network sepolia +``` + +--- + +## 9. 链信息组 + +### 9.1 `block` —— 查询区块 + +> **本版改动**:走 `eth_getBlockByNumber`;**json 原样透传节点返回**,text 才是格式化层。 + +**用法** + +``` +wallet-cli block [] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 查询区块(省略号数则取最新) | +| EVM 增量 | 走 `eth_getBlockByNumber`;字段按 EVM 区块结构 | +| 错误 | `not_found`(指定号数的区块不存在)、`rpc_error`(含端点限流 429) | + +**示例与输出** + +```bash +$ wallet-cli block --network sepolia +Number #11,204,149 +Hash 0x6b2f...d40a +Parent hash 0x1e83...77bc +Time 2026-08-06 09:21:47 UTC +Transactions 142 +Gas used 12,840,221 / 30,000,000 +Base fee 18.4 gwei +``` + +```bash +$ wallet-cli block --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"block","data":{ "number":"0xaaf635","hash":"0x6b2f...d40a","parentHash":"0x1e83...77bc","timestamp":"0x6a74522b","gasUsed":"0xc3ed1d","gasLimit":"0x1c9c380","baseFeePerGas":"0x448b9b800","transactions":[ "0x9c4e...81af", "…共 142 项…" ] },"meta":{ "durationMs":310,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> **注意 json 里全是 `0x` 十六进制字符串**——这正是「原样透传」的含义:`eth_getBlockByNumber` 的返回不做任何数值转换、字段不重命名、`transactions` 不裁剪。想要十进制与 UTC 时间读 text。这与 TRON 侧透传 protobuf JSON 是同一条规则,因此**本命令是全文唯一不遵守「json 给最小单位十进制整数字符串」(§1.4)的地方**:透传优先。 + +> **json 原样透传节点返回**:TRON 侧 `block` 的 json 就是链上 protobuf JSON 原样(`{block:{blockID, block_header:{…}, transactions:[…]}}`),不做字段重塑;EVM 侧同理给 `eth_getBlockByNumber` 的原始返回。text 才是我方格式化的那一层,两族各按自己的区块结构取字段。 + +**Help 输出** + +> **相对现状**:描述补一句「json 为节点响应原样」;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli block --help + +Usage: + wallet-cli block [] [options] + +Get a block (latest if omitted). JSON output is the node's response verbatim. + +Args: + number block number to fetch, in block height; omit to fetch the latest block + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli block + wallet-cli block 12345 --network nile + wallet-cli block 12345 --network sepolia +``` + +### 9.2 `chain node` —— 节点状态 + +> **本版改动**:EVM 新增 `Chain id` 与 `Syncing` 两行;**`Solid block` 与 `Peers` 两行照样出现**——EVM 的不可逆区块就是 `finalized`。 + +**用法** + +``` +wallet-cli chain node [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 显示所连节点的状态 | +| EVM 增量 | 新增 `Chain id` 与 `Syncing` 两行;`Solid block` 取 **finalized 区块**标签,`Peers` 取 `net_peerCount`;`p2pVersion` 在 EVM 恒为 `null` | +| 错误 | `rpc_error`(含端点限流 429)——端点不可达时这条命令本身就是探测手段,失败即结论 | + +**示例与输出** + +```bash +$ wallet-cli chain node --network tron:nile +Endpoint nile.trongrid.io +Version java-tron 4.8.2.1.PQ1_build1 +Head block #70,435,374 2026-08-27 09:39:33 (~8s ago — in sync) +Solid block #70,435,358 (16 blocks behind head) +Peers 59 connected / 3 active +``` + +```bash +$ wallet-cli chain node --network sepolia +Endpoint ethereum-sepolia-rpc.publicnode.com +Version reth/v2.4.1-8eb2101/x86_64-unknown-linux-gnu +Chain id 11155111 +Head block #11,577,037 2026-08-27 09:39:36 (~9s ago — in sync) +Solid block #11,576,965 (72 blocks behind head) +Syncing no +Peers 33 connected / 33 active +``` + +> 以上两段均为实测输出。 +> +> **EVM 有「不可逆区块」这个概念——合并之后就叫 `finalized`,与 TRON 的 solid block 是同一件事。** 文档此前写「EVM 无 solidified 区块」在事实上是错的:实测 Sepolia(落后 head 约 72 块)与 BSC(落后 2 块)皆回得出值。把它藏起来反而少给了一个真实且有用的数字。 +> +> **`Peers` 取 `net_peerCount`,端点未暴露时显示 `—`**。`net_peerCount` 是标准 JSON-RPC 方法,但部分托管服务商禁用 `net_*` 命名空间。**为 EVM 破例改成「整行消失」会让同一条命令有两套规则**——`—` 正是这条命令**自己的既有惯例**(help 明写「端点未暴露的字段显示 `—`」)。 +> +> 两族独有的字段:EVM 有 `Chain id`(EIP-155,签名要用,值得摆出来核对)与 `Syncing`,TRON 没有;`p2pVersion` 在 EVM 恒为 `null`。`Endpoint` 两族都只显主机名(§2.3)。 + +**Help 输出** + +> **相对现状**:描述改写为按 family 说字段差异、TRON 在前(现状那句「端点未暴露的字段显示 —」并入 §9.2 正文);`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli chain node --help + +Usage: + wallet-cli chain node [options] + +Show the connected node's status. Fields differ by family: TRON reports the +solidified block and peer counts, EVM reports the chain id and sync state. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli chain node --network nile + wallet-cli chain node --network sepolia +``` + +### 9.3 `chain prices` —— 当前交易单价 + +> **本版改动**:EVM 侧给 base fee、建议 priority fee 与实际 gas price,并折算一笔转账的成本。 + +**用法** + +``` +wallet-cli chain prices [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 发一笔交易此刻的单位成本 | +| EVM 增量 | 新增 `feeModel` 字段(见下);base fee 取最新区块头的 `baseFeePerGas`;priority fee 取 `eth_maxPriorityFeePerGas`,**不做 `eth_feeHistory` 回退**;非 1559 链退到 `eth_gasPrice` | + +> **不做 `eth_feeHistory` 回退**:四条内置网络**都支持** `eth_maxPriorityFeePerGas`,所以这条回退今天**一次都不会触发**。而一条永远跑不到的路径既无法验证、也会在下一次改动时被当成有效行为对待。要做的话是独立一小项,且需要先找到一个真的不支持 `eth_maxPriorityFeePerGas` 的端点来验证它走得通。读不到就是 undefined,该行不显示。 +| 错误 | `rpc_error`(含端点限流 429) | + +> **这条在 EVM 上成立**:TRON 侧它给 `Energy price` / `Bandwidth price` / `Memo fee`,回答的是「现在发一笔交易,单位成本多少」——该问题在 EVM 上不但成立,而且**更常被问**(gas 波动远大于 TRON 的资源单价)。业内对应 `cast gas-price` / `cast base-fee`。字段按 family 各取各的,问题是同一个。 + +**示例与输出** + +```bash +$ wallet-cli chain prices --network sepolia +Fee model eip1559 +Base fee 0.97768 gwei +Priority fee 0.001 gwei +Gas price 0.97868 gwei +Transfer cost 0.00002 ETH (21,000 gas) +``` + +```bash +$ wallet-cli chain prices --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"chain.prices","data":{ "feeModel":"eip1559","baseFeeWei":"977680801","priorityFeeWei":"1000000","gasPriceWei":"978680801","transferGas":21000,"transferCostWei":"20552296821000" },"meta":{ "durationMs":3024,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> 以上为实测输出。 +> +> **`feeModel` 是本版新增字段**(`"eip1559" | "legacy"`),text 对应 `Fee model` 一行。这条命令要回答「现在发一笔交易多少钱」,而**费用模型决定了读者该看哪些数字**:1559 链看 base + priority,legacy 链只有 gas price。靠「`baseFeeWei` 在不在」隐含地表达模型,要求读者知道这条规则,而且在 **BSC(base fee 为零但仍是 1559)上特别容易误读**。明讲一个字段,比让人从字段的有无去推断便宜得多。 +> +> `feeModel` 由**链上侦测**(§6.1 的同一条规则:`baseFeePerGas` 字段存在即 1559,零也算),`NetworkDescriptor.feeModel` 为覆盖用。 + +> 三个价一律给 **wei 整数字符串**(text 才换算成 gwei,§1.4);`gasPriceWei` 是前两者之和、不是 `eth_gasPrice` 的返回值。非 1559 链只给 `gasPriceWei` 与转账折算两项,`baseFeeWei` / `priorityFeeWei` 不出现(不给 `null`)。 + +> `Transfer cost` 是把单价折算成「一笔原生币转账要花多少」——单看 gwei 数字,多数用户判断不出贵不贵;21,000 gas 是协议固定的转账消耗,折算无歧义。这与 TRON 侧列 `Memo fee` 是同一个意图:把单价翻译成一次实际支出。 +> +> **`Gas price` 是 base + priority 的和,由前两行算出,不是 `eth_gasPrice` 的返回值**——`eth_gasPrice` 给的是节点自己的建议值,与 1559 的两段式定价不是一回事,两者并列会对不上账。`Transfer cost` 按这个和乘 21,000 得出。 +> +> 不支持 EIP-1559 的链没有 base fee,此时 `Gas price` 直接取 `eth_gasPrice`、只显这一行与 `Transfer cost`,`Base fee` / `Priority fee` 不显示。 + +**Help 输出** + +> **相对现状**:描述由 TRON 专属(energy/bandwidth/memo fee)改写为按 family 说差异、TRON 在前;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli chain prices --help + +Usage: + wallet-cli chain prices [options] + +Show what a transaction costs per unit right now. TRON reports energy/bandwidth +unit prices and the memo fee; EVM reports base fee, suggested priority fee and +the resulting gas price. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli chain prices --network nile + wallet-cli chain prices --network sepolia +``` + +--- + +## 10. help 规格(三层) + +help 分 root / 组 / 命令三层。本章给前两层的完整输出与三层共同的文案规范;**命令层的 help 附在 §3–§9 各命令小节末尾**,与该命令的用法、Options 对照阅读。 + +> ### ⚠️ 本章尚有一项 help 文案未与实作对齐(待办) +> +> 本轮同步已把**旗标集合发生变化**的 help 区块按实测输出重贴(§2.3 `networks`、§2.4 `config`、§3.9 `derive`、§3.10 `backup`、§3.11 `contact` 三条、§6.3 `tx broadcast`、§7.3 `contract deploy`)。**全局旗标文案**(`--network` / `--timeout`)已于修订 4 按实作回贴至全部 **38 个**含 `Global options` 段的命令层区块。**尚未重贴的只剩一项**: +> +> | 项 | 内容 | 规模 | +> | --- | --- | --- | +> | 命令层描述 / Args / Examples | 实作的一行描述与 Examples 多处比规格更长且**多出来的内容是真的** | **38 个命令层 help 区块** | +> +> **已回贴的两条全局旗标文案**(实作版比原文档版准确,故据此改了文档): +> +> ```text +> --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted +> --timeout per node, service, or device call timeout, in milliseconds +> ``` +> +> - `--timeout`:原文档版的 `RPC` 是**实作名**,违反 §10.1 规则 3(RPC 方法名/类别名不进 help)。而 `node, service, or device` 也更准确——这条超时同时涵盖节点调用、GasFree 这类外部服务、以及 Ledger 设备。 +> - `--network`:实作版列出 `nile` / `sepolia` / `bsc` / `eip155:11155111` 四种形态(**两族别名 + 规范 id**),比原文档版更能让读者一眼看出「别名与规范 id 都收」。 +> +> **family 标注词表已折叠进本版且实作已追平**:原 v4.13.1 主题 1(`(tron)` / `(evm)` → `(TRON only)` / `(EVM only)`)整体并入 v4.13.0,本文档全部 132 处标注已改(§10.1);实作已于 `e206c00a` 输出全大写,**小写残留为 0**。 +> +> **回贴命令层区块时的两条硬约束**:① 示例网络一律用 `nile` / `sepolia` 等测试网,**不得贴回 `--network tron` / `--network ethereum` 主网形态**;② 实作侧仍有若干处与 §10.1 文案规范不符(见 §10.1 末尾「待实作修正」),那几处**以本文档为准,不要照抄实作**。 + +### 10.1 help 文案规范 + +由实测既有 52 条命令的 help 归纳而来。骨架(`Usage → Args → Requires → Options → Global options → Examples`)由渲染层集中生成、52 条已完全一致,规范只约束**手写文案**。 + +**三层 help 各自的职责** + +| 层 | 描述写什么 | family 差异怎么表达 | +| --- | --- | --- | +| root help | **动词概括**(`Query on-chain account state`),**不列举子命令名** | 整组仅一族可用时标组级 `(TRON only)`;混合组不标 | +| 组 help | 同上,可略具体 | **逐条子命令行尾标 `(TRON only)` / `(EVM only)`** | +| 命令 help | 该命令做什么,两族行为不同时说差异本身 | **逐个 flag 行尾标 `(TRON only)` / `(EVM only)`** | + +**family 标注的词表是封闭的两个值:`(TRON only)` 与 `(EVM only)`。** + +- **全大写、括号包裹**;后续新增 family 按 `( only)` 构词,family 名全大写。 +- **不得使用动词短语形式**(如 `only support TRON`)——标注列是**属性列**,成员一律为名词 / 形容词短语;且该形式超 80 列会打散标注列的对齐。 +- 标注的**适用范围**(哪些项该标、哪些不该标、组级 vs 子命令级 vs flag 级的分工)由上表规定,与词表无关。 + +> **词表的由来**:原定为 `(tron)` / `(evm)`,评审反馈**看不懂**——单看 `(tron)` 读者无从判断这是「只在 TRON 可用」还是「在 TRON 上行为不同」。`only` 把语义补全,全大写让它在一列小写 flag 名里读得出是标注而不是取值。 +> +> ✅ **实作已追平**(`e206c00a`):全量 help 扫描小写 `(tron only)` / `(evm only)` 残留为 **0**,大写标注 **57 处**(root 9 + 组 11 + 命令层 37)。本文档 §3–§9 help 区块的标注列现已是实测值。 + +> **为什么不做按 family 过滤的人类 help**:主流 CLI 的 help 都不随运行时上下文变化——`git` / `docker` / `kubectl` / `aws` 的 help 不因 `--context`、`--region` 而增删条目。需要按某个维度分家时,业内是把该维度**做进命令路径**(`aws s3 …` / `aws ec2 …`),而不是让同一条路径的 help 变形。我方的 family 不是命令路径的一段(`wallet-cli tx send` 两族共用),所以走标注而非过滤;同一条命令的 help 永远只有一个版本,可直接引用、可缓存、可写进文档。 + +> root help 的组描述**不许列举子命令名**——`chain` 原描述 `Query chain params, prices & node info` 点名了 TRON 专属的 `params`,EVM 用户照着找会扑空;且全表只有它在列举,本就是体例偏差。 + +**Requires 段** + +1. 每条是一个**名词短语**,小写开头、句尾无标点;补充说明用 ` — ` 接续。 +2. 冠词统一:主密码类一律带 `the`;硬件与账户类为不定指,用 `a` / `an`。 +3. 顺序固定:命令特有前置 → 主密码 → 账户。 +4. 多条同类前置按**用户输入顺序**排列。 +5. **只列硬前置**(缺了就无法执行);交互确认不属前置,不进 Requires。 +6. 外部服务依赖属硬前置,**必须进 Requires**,不许塞在一行描述的括号里(EVM 侧 `account history` 将来接索引服务时按此办,§4.4)。 + +主密码四种语义的统一写法: + +| 场景 | 规范文案 | +| --- | --- | +| 只能 TTY 交互输入 | `the master password — entered interactively in a TTY` | +| 可 stdin 可交互 | `the master password — pass --password-stdin, or enter it interactively in a TTY` | +| 只能 stdin,从不提示 | `the master password — pass --password-stdin; this command never prompts` | +| **是否需要密码取决于模式** | `the master password only when the selected mode signs — pass --password-stdin then; other modes need no password` | + +> **第四种是本版补入的**(2026-08-28 PM 拍板,按实作)。它覆盖**全部 ✍️ 写命令**——`--dry-run` / `--build-only` 不签名、因而不需要主密码,`--sign-only` 与默认广播路径才需要。把这类命令一律写成第三种(「从不提示,必须给 --password-stdin」)是**错的**:调用方会为一条 `--dry-run` 白准备一次密码。**实测 24 条命令用此文案**(`tx send` / `contract send` / `contract deploy` / `stake` 全组 / `asset` / `exchange` / `vote cast` / `reward withdraw` / `permission update` / `account activate` / `account set` / `gasfree transfer` / `tx multisig`)。 +> +> 它带一个条件从句,形式上比前三种长,但 §10.1 规则 1 约束的是「每条是一个名词短语」——本条主词仍是 `the master password`,条件从句挂在破折号后的补充说明里,与前三种同构。 +> +> `change-password` 的 `the new master password — entered interactively in a TTY` 是第一种的实例,不另立一种。 + +**一行描述** + +1. **祈使动词开头**,描述命令做什么,不描述输出内容。 +2. **标点按层分**:**组 help 的描述是完整句、句尾带句号**(`Query on-chain account state.`);**命令 help 的一行描述单句不加句号**(`Show native balance`),多句时每句都加。组描述是独立段落、命令描述是标题式短语,两者惯例本就不同,别拉平。 +3. **不出现实现细节名**——protobuf 字段名、Java 类名、RPC 方法名(`eth_getBalance` / `triggerConstantContract` / `getAccount`)一律不进 help。需要交代对应关系的写进本文档的「概览」表,那里是设计溯源该待的地方。**旗标名不算实现细节名**——组 help 的子命令描述可以点名本命令的招牌旗标(`tx` 组的 `send Send native coins or tokens with human --amount`,2026-08-28 PM 拍板按实作):`--amount` 是**面向用户的契约**,且它正是这条命令与 `--raw-amount` 路径的分界,点出来比省略更有信息量。规则 3 挡的是**读者用不上的内部名**,不是旗标。 +4. 两族行为不同时,描述里说**差异本身**、不说实现(如 `chain prices` 写 "EVM reports base fee…; TRON reports energy/bandwidth unit prices…",不写调了哪个 RPC)。 + +**family 专属项的标注** + +1. help 静态、不随 `--network` 变化;某个 flag 只属于一族时,**行尾加 `(TRON only)` / `(EVM only)`**,位置在 `[optional, …]` tag 之后。同一条命令的 Options 里**同时出现两族标注**时,其一行描述必须交代标注含义(`Flags marked (TRON only) or (EVM only) are accepted only on networks of that family; using one on the other family is rejected.`)——help 要能被单独读懂,不能依赖读者先看过本规范。 +2. 标注语义是**当前版本仅该族可用**,不承诺未来(`account history` 标 `(TRON only)` 是因为 EVM 侧等索引服务,补齐后摘掉)。 +3. 两族都有的 flag 不标注,且描述必须**族中立**——`--to` 写 "recipient address",不写 "recipient TRON base58 address";`--contract` 写 "token contract address",不写 "TRC20 contract address"。 +4. 组级标注沿用 root help 现状(`stake … (TRON only)`);混合组不标组级,差异下沉到子命令与 flag。 + +**Examples 的 family 配比** + +help 是**两族共用的静态文本**,Examples 因此必须两族兼顾、**TRON 在前**(主推)。适用范围按命令分三档: + +| 档 | 命令 | 要求 | +| --- | --- | --- | +| **吃 `--network` 的命令**(21 条 EVM 绑定命令) | `account` / `token` / `tx` / `contract` / 签名 / 链信息各组 | **必须两族对称**——同一个 flag 组合、只换 `--network` | +| **family 相关的本地命令** | `import ledger`(本版新增 `--app ethereum` 的就是它)、`import watch`(§3.5 明列了 EVM 示例) | **需涵盖两族**,但不要求「只换 `--network`」的对称形式 | +| **其余纯本地命令** | `create` / `derive` / `backup` / `contact` / `encoding` / `address` / `config` / `networks` | **豁免**——它们不吃 `--network`,「只换 `--network`」的对称形式对它们不成立 | + +**这条只约束 help**——本文档 §3–§9 的「示例与输出」段是 EVM 规格正文,示例用 EVM 是必要的,不受此约束。 + +> **示例网络一律用测试网**:本节 Examples 的 `--network` 取值恒为 `nile` / `sepolia` 等测试网。help 的 Examples 是全文档**最可复制**的形态,主网命令不得以可复制形态出现(根 `CLAUDE.md` 示例安全公约)。实作已合规,回贴时不得改回 `--network tron` / `--network ethereum`。 + +**待实作修正(本节规范 vs 当前实作,`e206c00a` 实测)** + +以下两处**以本节为准、需改实作**;回贴命令层 help 区块时**不要照抄实作**: + +| # | 位置 | 本节规定 | 实作当前 | +| :---: | --- | --- | --- | +| 1 | `typed-data sign` 一行描述 | 祈使动词开头、不描述输出内容(规则 1) | 首行是 `Prints the signature, the digest that was signed, and the primary type.`——**整条命令没有祈使动词描述行**,两项都违反。应恢复为 `Sign an EIP-712 / TIP-712 typed-data payload` | +| 2 | `import ledger` 一行描述 | `Register a Ledger account (watch-only; signs on device)` | `Register a Ledger account`——删掉了「设备上签名」这个关键限定 | + +> **另有五处已于 2026-08-28 拍板「按实作」、本文档已同步**,不需改实作:主密码 Requires 第四种写法(已补入本节上方的四行表)· `token info` 描述去掉字段列举(§5.2)· `tx` 组 help 的 `send` 行点名 `--amount`(规则 3 已加注)· §0.3「不提示」正名为「不问主密码」· **flag 标注免责句改用 `are accepted`**(规则 1 的定死文案已同步,见下)。 + +> **免责句为何是 `are accepted` 而非 `apply`**:`apply` 说的是「这个 flag 在另一族不起作用」,读起来像**被忽略**;实际行为是**被拒绝**(EVM 网络上传 `--fee-limit` 报 `invalid_option`)。`are accepted only on…` 与后半句 `using one on the other family is rejected` 同指一件事,语义自洽;`apply` 会让读者以为传了也无妨。 + +### 10.2 root `--help`(完整版) + +> **有两行刻意保留实作版,不照原规格**(其余八处差异实作已照规格,不需再动): +> +> | 组 | 本表采用 | 原规格 | 为什么 | +> | --- | --- | --- | --- | +> | `exchange` | `Create and trade Bancor exchange pairs` | `On-chain Bancor exchange` | 规格版是**名词短语**,违反 §10.1「一行描述以祈使动词开头」,而且全表只有它一列是名词短语 | +> | `contract` | `Call, deploy, govern, and inspect smart contracts` | `Call, send, deploy, and inspect…` | 规格版把 `govern` 换成 `send`,等于**舍弃了对治理四条命令的概括**(`clear-abi` / `set-origin-energy-limit` / `set-user-resource-percent` / `create2`),改成再列一个子命令名;而 `send` 与 `call` 在 root 这一层的区别对读者没有意义。**§10.3 的组 help 同步。** + +```text +$ wallet-cli --help + +Usage: wallet-cli [OPTIONS] COMMAND + +wallet-cli — CLI wallet for TRON and EVM networks. +Agent-first: deterministic exit codes, JSON output. + +Common Commands: + create Create a new HD wallet (BIP39 seed) + import Import a wallet + list List wallets / accounts + +Management Commands: + account Query on-chain account state + permission View / update account permissions (multi-sig) (TRON only) + token Manage the token address book and query tokens + tx Build, send, broadcast, and inspect transactions + gasfree Gas-free token transfers via the GasFree service (TRON only) + contract Call, deploy, govern, and inspect smart contracts + proposal Create / vote on governance proposals (TRON only) + witness Register / operate a super representative (TRON only) + asset Issue & manage TRC10 tokens (TRON only) + exchange Create and trade Bancor exchange pairs (TRON only) + stake Stake / delegate resources & query state (TRON only) + vote Vote for super representatives (TRON only) + reward Query / withdraw voting rewards (TRON only) + chain Query chain and node state + message Sign arbitrary messages + typed-data Sign EIP-712 / TIP-712 structured data + block Get a block (latest if omitted) + +Commands: + use Set the active account + current Show the current (active) account + rename Rename an account label + derive Derive the next HD account from a seed wallet + backup Export an account's secret + metadata (0600) + delete Delete a wallet / account + config Show / get / set configuration values + networks List known networks + change-password Change the master password (re-encrypt keystores) + encoding Convert / validate addresses & encodings + address Generate a random keypair (local, not stored) + contact Manage the recipient address book + +Global Options: + -o, --output string Output format ("text", "json") (default from config) + --network string Network id or alias, e.g. "tron", "ethereum", "sepolia" + --account string Account label or address to act as (overrides active) + --timeout int Request timeout in milliseconds + -v, --verbose Verbose / debug logging + -h, --help Show help + -V, --version Print version information and quit + +Run 'wallet-cli COMMAND --help' for more information on a command. +``` + +**相对现状的三处改动**(其余原样): + +| 项 | 现状(实测) | v4.13.0 | +| --------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| 主描述首行 | `wallet-cli — CLI wallet for TRON.` | `wallet-cli — CLI wallet for TRON and EVM networks.`(第二行 `Agent-first: …` 不变) | +| `chain` 组描述 | `Query chain params, prices & node info` | `Query chain and node state`——**改为动词概括、不列举子命令**:原描述点名了 `params`,而它是 TRON 专属,EVM 用户照着找会扑空;全表也只有它在列举子命令,与 `account` / `tx` / `contract` 的「动词 + 对象」体例不一致 | +| `--network` 示例值 | `"tron:728126428", "tron:3448148188", "tron:2494104990"` | `"tron", "ethereum", "sepolia"`,跨两族各取一个、且用简写别名,让「多链」在第一屏可见 | + +> **`(TRON only)` 标注只给纯 TRON 组**:`permission` / `gasfree` / `proposal` / `witness` / `asset` / `exchange` / `stake` / `vote` / `reward` 九组在 EVM 上整组不适用。**混合组一律不标**——`account`(`activate` / `set` 是 TRON 专属)、`tx`(`approvals` / `multisig`)、`contract`(治理四条)、`chain`(`params` / `prices`)都含 TRON 专属子命令,但组本身两族可用,标了会让人以为整组不可用;差异下沉到子命令与 flag 的 `(TRON only)` / `(EVM only)` 标注(见 §10.1)。这与实现现状一致——实测 root help 里 `chain` 就没有标注。 + +### 10.3 组 `--help`(中间层) + +组 help 是「root → 组 → 命令」三层里的中间层,本版同样要改:现状的子命令描述**写死了 TRON**(`prices` 写 `Energy/bandwidth unit price and memo fee`、`info` 写 `getAccount`、`balance` 写 `(TRX/SUN)`),在 EVM 网络下全不成立。改造两件事:**描述族中立化** + **family 专属子命令行尾标 `(TRON only)` / `(EVM only)`**。 + +> **相对现状**:`balance` / `info` 描述族中立化(去 `TRX/SUN`、去 `getAccount`);`history` 的 `(requires TronGrid)` 移出描述并改标 `(TRON only)`;补 `activate` / `set` 两条并标 `(TRON only)`;`portfolio` 调整到 `info` 之后。 + +```text +$ wallet-cli account --help + +Usage: wallet-cli account COMMAND + +Query on-chain account state. + +Commands: + balance Show the native coin balance + info Show the account's on-chain state + portfolio Show native + token balances with best-effort USD value + history Show transaction history (TRON only) + activate Activate an unactivated account (TRON only) + set Set the on-chain account name / id (TRON only) + +Run 'wallet-cli account COMMAND --help' for more information on a command. +``` + +> **相对现状**:组描述改为动词概括、不列举子命令;`prices` 去掉 `Energy/bandwidth`、`node` 去掉 `(version / sync / peers)`;`params` 标 `(TRON only)` 并移到末位。 + +```text +$ wallet-cli chain --help + +Usage: wallet-cli chain COMMAND + +Query chain and node state. + +Commands: + node Connected node status + prices Current transaction unit prices + params On-chain governance parameters (TRON only) + +Run 'wallet-cli chain COMMAND --help' for more information on a command. +``` + +> **相对现状**:五条子命令描述统一精简,去掉 `(--contract / --asset-id)` 与 `totalSupply` 这类 flag / 字段细节。 + +```text +$ wallet-cli token --help + +Usage: wallet-cli token COMMAND + +Manage the token address book and query tokens. + +Commands: + balance Show a single token balance + info Show token metadata + add Add a token to the address book + list List the address book + remove Remove a user-added token + +Run 'wallet-cli token COMMAND --help' for more information on a command. +``` + +> **相对现状**:`send` / `sign` 描述族中立化;补 `approvals` / `multisig` 两条并标 `(TRON only)`。 + +```text +$ wallet-cli tx --help + +Usage: wallet-cli tx COMMAND + +Build, send, broadcast, and inspect transactions. + +Commands: + send Send native coins or tokens with human --amount + sign Sign a transaction built elsewhere + broadcast Broadcast a presigned transaction + status Show confirmation status of a transaction + info Show full transaction detail + receipt + approvals Show collected signatures on a multi-sig transaction (TRON only) + multisig Create / co-sign a multi-sig transaction (TRON only) + +Run 'wallet-cli tx COMMAND --help' for more information on a command. +``` + +> **相对现状**:`call` / `send` / `deploy` 描述去掉 RPC 方法名;`info` 改标 `(TRON only)`;补 `clear-abi` / `set-origin-energy-limit` / `set-user-resource-percent` / `create2` 四条并标 `(TRON only)`;名列宽随之加宽。 + +```text +$ wallet-cli contract --help + +Usage: wallet-cli contract COMMAND + +Call, deploy, govern, and inspect smart contracts. + +Commands: + call Read-only contract call + send State-changing contract call + deploy Deploy contract bytecode + info Show contract ABI + metadata (TRON only) + clear-abi Clear a contract's on-chain ABI (TRON only) + set-origin-energy-limit Set the deployer's energy cap (TRON only) + set-user-resource-percent Set the caller-paid resource share (TRON only) + create2 Precompute a CREATE2 address (TRON only) + +Run 'wallet-cli contract COMMAND --help' for more information on a command. +``` + +> **相对现状**:组描述精简为 `Import a wallet.`;补 `keystore` 一条;`ledger` / `watch` 描述精简(括号里的说明下沉到各自命令 help)。 + +```text +$ wallet-cli import --help + +Usage: wallet-cli import COMMAND + +Import a wallet. + +Commands: + mnemonic Import a BIP39 mnemonic phrase + private-key Import a raw private key + keystore Import a Web3 keystore file + ledger Register a Ledger account + watch Register a watch-only address + +Run 'wallet-cli import COMMAND --help' for more information on a command. +``` + +> **相对现状**:`list` 的子命令描述补「给出每条的 chain family」;组描述与另两条沿用现状。 + +```text +$ wallet-cli contact --help + +Usage: wallet-cli contact COMMAND + +Manage the recipient address book. + +Commands: + add Add a payee to the address book + list List every contact + remove Remove a contact + +Run 'wallet-cli contact COMMAND --help' for more information on a command. +``` + +> **标注语义**:`(TRON only)` / `(EVM only)` 表示**当前版本仅该族可用**,不承诺未来——`account history` 与 `contract info` 标 `(TRON only)` 是因为 EVM 侧要等索引服务(§4.4、能力矩阵),将来补齐后标注即摘掉。整组仅 TRON 的(`permission` / `gasfree` / `stake` / `vote` / `reward` / `proposal` / `witness` / `asset` / `exchange`)在 root help 标组级 `(TRON only)`,其组 help 内部不再逐条重复。 +> +> **`message` / `typed-data` 两组各只有一个子命令 `sign`,两族均可用**,组 help 无标注、无改造,此处从略。 + +--- + +## 11. 错误码 + +> **「这份表是唯一的错误码索引,不得出现表外的码」这句承诺保留、不弱化**——agent 就是靠 `error.code` 分支的,一个没被文档写过的码等于一个它无法处理的码。 +> +> **会失效的不是承诺,是手工维护的表**:上一版的 §11 列了 9 个从不产生的码、漏了 30 多个真的会产生的码,正是手工维护的结果。因此索引改为与错误定义放在一起、由测试守住,让它结构上不可能再漂移: +> +> - 真理源是 `src/domain/errors/codes.ts` 的 `ERROR_CODES`,逐码附一行语义; +> - 一条测试扫描全部源码**双向**比对——**丢得出来却没登记 → 失败;登记了却没人丢 → 也失败**; +> - **机器可读版本在 `--json-schema` 的 `errorCodes` 键**,agent 一次调用即可取得全量(纯新增,不影响既有键)。 +> +> 下表由该真理源生成,共 **129 条**。各节「概览」的错误行只从这里取值。 + +### 11.1 本版相关的新增与更名 + +| 错误码 | 说明 | +| --- | --- | +| `migration_required` | **本版新增**。注册文件落后于本体,且无法取得主密码(无 TTY 且未给 `--password-stdin`),见 §0 | +| `invalid_config` / `insecure_config` | **本版新增**。config 文件格式错 / 权限或内容不安全(§2.2、§2.4) | +| `unsupported_network` / `missing_network` | 网络解析(§2.1) | +| `family_mismatch` | **本版由 `network_family_mismatch` 更名**——对照旧字符串的 agent/脚本会坏,**进 release note** | +| `missing_wallet_address` | 与 `family_mismatch` **分家**:「账户存在但在另一条链上」先前与「你根本没有账户」共用同一个码,而两者的解法完全不同 | +| EVM 广播拒绝码 | `nonce_too_high` / `replacement_underpriced` / `gas_too_low` / `fee_too_low` / `gas_limit_exceeded`(§6.3 白名单判断的产物) | +| `token_metadata_unavailable` | **取代规格里的 `not_a_token`**——读不到 metadata 的原因不只「不是代币」,新名字更准 | +| `token_already_listed` | **取代规格里的 `token_exists`** | +| `token_not_in_book` | **取代规格里的 `token_not_found`**——说出了「不在地址簿」而不是含糊的「找不到」 | +| `ledger_unsupported` | **规格里的 `app_not_open` 并入此码**:它与「app 版本不支持这条指令」共用同一个 status word(`0x6d00` INS_NOT_SUPPORTED),**单看它分不出是哪一个**,故合并,信息同时涵盖两种原因 | +| `ledger_setting_required` | **本版新增**:TRON app 的设定类状态 | +| `provider_rate_limited` | **外部服务**的限流专用码。**节点限流(HTTP 429)仍归 `rpc_error`**,两者的处置不同 | + +**从 `invalid_value` 这个泛用桶里分出来的六个**(破坏性:比对 `invalid_value` 的脚本会漏接,**进 release note**): + +| 码 | 分出来的理由 | +| --- | --- | +| `account_not_found` | `--account` 打错的下一步是 `list`;而秘密是在隐藏提示下输入的,envelope 连 issue path 都没有,**码是调用者唯一拿得到的东西** | +| `invalid_mnemonic` | 同上。顺带修掉一个真的缺陷:私钥含非十六进制字符时 `hexToBytes` 抛的是自己的 Error,会被归为**信息被 redact 的 `internal_error`**——同一个打字错误的两种形态先前回两个不同的码,其中一个还是错的 | +| `invalid_private_key` | 同上 | +| `seed_not_found` | `--seed-id` 指到一个非 seed 钱包时信息说得清楚、码什么都没说 | +| `invalid_path` | `import ledger --path` 把「这根本不是一条 BIP32 路径」报成 `--path coin_type ? does not match --app tron`——一句在谈 coin_type 的话,而用户的问题不是 coin_type。**同时旧检查只比对 `m/44'/'/` 前缀,`m/44'/195'/garbage` 会通过验证直接送进设备。** 现在路径格式错误报 `invalid_path`,币别不符才留在 `invalid_option`(那是两个旗标之间真正的矛盾) | +| `device_not_found` / `device_locked` | 先前一起归进 `auth_required`——**装置没插时没有任何凭证可以提供,`auth_required` 说的是错的事**;而「没插」与「锁着」的解法一个是插上、一个是输 PIN | + +> **查找「歧义」的情形维持 `invalid_value`**:值是有效的,只是选中多个,解法是缩小范围而不是去找一个不存在的账户。 + +### 11.2 `family_mismatch` 的触发场景(本版扩为六个) + +| # | 场景 | +| :---: | --- | +| 1 | 账户与目标网络 family 不符 | +| 2 | raw tx 与目标网络 family 不符 | +| 3 | **该命令在目标网络的 family 下没有实作**(`stake info --network eip155:1`) | +| 4 | **收款人地址属于另一族**(`--to 0x…` 配 `--network nile`)。先前报 `contact_not_found`——会让用户去找一个他**从没建立过**的通讯录条目 | +| 5 | **通讯录条目的地址属于另一族**。信息刻意描述**地址**而非 family(§3.11),用户不必学会那个词 | +| 6 | **以 family 前缀查询一条该族没有实作的命令**(`evm account history --help`)。命令**存在**,只是没有那一族的实作;报 `unknown_command` 会把读者导向去找不存在的错字 | + +### 11.3 两条跨命令的判定规则 + +**① `unknown_command` 涵盖 meta 路径。** 无法解析的命令路径一律 `unknown_command`(exit 2),**`--help` / `--json-schema` 不例外**。 + +先前 `handleMeta()` 对**任何**无法解析的路径都退回 root help 并 `return 0`——同一个错字,不带 meta 旗标时是 `unknown_command` / exit 2,加上 `--help` 就变成「成功」。**meta 旗标等于在退出码契约上开了一个洞**,而 agent 打错命令名时会拿到一个看似成功的回应。(顺带修掉一个既有缺陷:`tx send --to T... --help` 先前拿到的是**组** help 而非该命令的。)**破坏性,进 release note。** + +**② `--to` 两者皆非时,错误码由值的形状决定。** `--to` 接受地址**或**通讯录名称,所以「解析不出来」有两个可能的原因,而**只讲其中一个会把一半的人送去错的地方找**。 + +| 值的形状 | 码 | +| --- | --- | +| `0x…` 或 `T…` 开头 | `invalid_address` | +| 其余 | `contact_not_found` | + +**两种信息都必须提到另一种可能**: + +```text +invalid_address: 0xnotanaddress is not a valid evm address, and no contact is named that either +contact_not_found: no contact named nosuchname, and it is not an address either +``` + +> 判断用的是最宽的那个问法:**不是「这是不是有效地址」,也不是「这是不是地址形状」**(那两个更早就判掉了),而是**「他是不是想打一个地址」**——没有人会在想打通讯录名称时键入 `0x`。 + +### 11.4 全量索引 + +> 由 `ERROR_CODES` 生成。每条一行:**从调用者这一侧看,发生了什么**;不写该怎么办——那属于 message,message 可以点名涉及的文件、旗标或地址。 + +| 错误码 | 语义 | +| --- | --- | +| `usage_error` | the command line could not be parsed | +| `unknown_command` | no such command path, including under --help / --json-schema | +| `invalid_option` | an option is not accepted here, or contradicts another one | +| `missing_option` | a required option was not given | +| `invalid_value` | an option's value is not of the shape that option takes | +| `unknown_parameter` | no chain parameter by that name | +| `limit_exceeded` | a bounded input (file size, list length, page size) was over its limit | +| `family_mismatch` | the account, recipient, raw transaction or command does not belong to the selected network's chain | +| `missing_network` | the command needs a network and none was selected or configured | +| `unsupported_network` | no network by that id or alias | +| `unsupported_network_capability` | the selected network does not offer what this command needs | +| `missing_wallet_address` | no account is available to act as | +| `account_not_found` | no local account by that id, label or address | +| `seed_not_found` | the reference does not name a seed (HD) wallet | +| `account_exists` | an account with that address is already in the keystore | +| `invalid_account` | the account reference is not well-formed | +| `not_exportable` | the account holds no exportable secret (watch-only or Ledger) | +| `no_software_wallet` | the operation needs a locally stored key and none exists | +| `watch_only_no_signer` | the selected account can be watched but cannot sign | +| `auth_required` | the master password is needed and was not available | +| `auth_failed` | the master password was wrong | +| `weak_password` | the proposed master password does not meet the strength rule | +| `wrong_keystore_password` | the keystore file's own password was wrong | +| `invalid_keystore` | the file is not a valid V3 keystore | +| `invalid_mnemonic` | the phrase is not a valid BIP39 mnemonic | +| `invalid_path` | the value is not a usable BIP44 derivation path | +| `invalid_private_key` | the private key is not 32 bytes of hex | +| `keystore_not_found` | no keystore file at that path | +| `secret_source_error` | a secret channel (stdin / TTY) could not be read | +| `tty_required` | the operation only accepts input from a terminal, and there is none | +| `entropy_failure` | the system random source failed | +| `insecure_permissions` | a wallet file's permissions are wider than 0600 | +| `migration_required` | a registry file is older than this build and must be migrated first | +| `audit_append_failed` | the local export/audit log could not be appended to | +| `file_not_found` | an input file does not exist | +| `output_exists` | the output path is already taken and would be overwritten | +| `io_error` | a local read or write failed | +| `encoding_error` | data on disk or on the wire is not in the form its format requires | +| `invalid_config` | the config file is malformed, or a network in it is missing a required field | +| `insecure_config` | the config file's permissions or contents are unsafe to load | +| `contact_not_found` | no contact by that name, and the value is not an address either | +| `invalid_address` | the value is not a valid address for the relevant chain | +| `already_exists` | a contact with that name or address is already stored | +| `token_not_in_book` | no token by that reference in the local address book | +| `token_already_listed` | that token is already in the local address book | +| `token_is_official` | the entry is a built-in and cannot be edited or removed | +| `token_metadata_unavailable` | the token's on-chain metadata could not be read | +| `unsupported_token` | the token standard is not one this command handles | +| `ambiguous_token_symbol` | the symbol matches more than one token; address it by contract | +| `ambiguous_asset_name` | the TRC10 name matches more than one asset; address it by id | +| `invalid_transaction` | the transaction is malformed, or already carries a signature | +| `invalid_payload` | the payload does not decode as what the flag says it is | +| `invalid_amount` | the amount is not positive, or is finer than the asset's precision | +| `precision_loss` | the amount cannot be represented exactly at the required precision | +| `tx_integrity` | the transaction re-encoded differently than it arrived — it was altered in flight | +| `chain_id_mismatch` | the transaction was built for a different chain than the one selected | +| `signing_rejected` | the signature was declined on the device | +| `dry_run_violation` | a --dry-run path attempted to broadcast; the attempt was barred | +| `invalid_permission` | no such permission group on the account, or it cannot be used here | +| `not_authorized` | the account is not permitted to perform this operation | +| `already_signed` | this account has already signed the transaction | +| `already_approved` | the approval was already recorded | +| `not_approved` | the transaction has not gathered the approvals it needs | +| `tx_expired` | the transaction's expiration has passed | +| `transaction_rejected` | the node refused the transaction, in its own words | +| `nonce_too_low` | nonce already used; the account has moved on | +| `nonce_too_high` | nonce is ahead of the account; an earlier transaction is missing | +| `replacement_underpriced` | replacing a pending transaction needs a higher fee than the original | +| `gas_too_low` | the gas limit is below what this transaction needs | +| `gas_limit_exceeded` | the gas limit exceeds the block gas limit | +| `fee_too_low` | the fee is below what the network is currently accepting | +| `insufficient_balance` | the balance cannot cover the amount plus the maximum fee | +| `insufficient_token_balance` | the token balance cannot cover the amount | +| `execution_reverted` | the contract reverted the call | +| `execution_error` | the transaction ran on-chain and failed | +| `not_found` | the transaction, block or record does not exist at this node | +| `rpc_error` | the node answered with an error | +| `invalid_node_response` | the node's answer was not in the shape the API defines | +| `provider_error` | an external service failed | +| `provider_rate_limited` | an external service is rate-limiting this client | +| `timeout` | the node, service or device did not answer in time | +| `aborted` | the operation was stopped before it finished | +| `cancelled` | the operation was cancelled before it reached the device | +| `history_not_supported` | the selected network exposes no transaction history endpoint | +| `chain_parameter_unavailable` | the node does not report that chain parameter | +| `gasfree_auth_failed` | the GasFree service rejected the request's credentials | +| `gasfree_credentials_missing` | no GasFree credentials are configured | +| `gasfree_integrity` | the GasFree service's answer failed its integrity check | +| `gasfree_rejected` | the GasFree service refused the transfer | +| `tronlink_credentials_missing` | no TronLink multi-sig service credentials are configured | +| `device_not_found` | no Ledger device answered | +| `device_locked` | the Ledger device is connected but locked | +| `ledger_setting_required` | a setting in the Ledger app must be enabled for this operation | +| `ledger_unsupported` | the Ledger app does not implement this operation or cannot decode it | +| `ledger_address_not_found` | the address was not found within the scanned derivation range | +| `wrong_device_seed` | the device holds a different seed than the account was registered with | +| `account_not_active` | the account is not activated on-chain | +| `account_already_active` | the account is already activated on-chain | +| `insufficient_stake` | the staked amount cannot cover this operation | +| `insufficient_voting_power` | the account has less voting power than the votes cast | +| `no_frozen_supply` | there is nothing frozen to act on | +| `not_yet_unfreezable` | the stake is still within its lock-up period | +| `nothing_to_withdraw` | there is nothing available to withdraw | +| `withdraw_too_frequent` | the withdrawal interval has not elapsed yet | +| `no_reward` | there is no reward to claim | +| `not_a_witness` | the address is not a witness | +| `already_witness` | the address is already a witness | +| `asset_not_found` | no TRC10 asset by that id or name | +| `invalid_asset_name` | the TRC10 name is not of an acceptable form | +| `already_issued_asset` | the account has already issued a TRC10 asset | +| `not_an_issuer` | the account did not issue this asset | +| `not_in_ico_window` | the asset's participation window is not open | +| `id_taken` | that id is already in use | +| `proposal_not_found` | no proposal by that id | +| `proposal_expired` | the proposal's voting window has closed | +| `not_proposal_owner` | the account did not create this proposal | +| `already_canceled` | the proposal was already withdrawn | +| `exchange_not_found` | no Bancor exchange pair by that id | +| `exchange_closed` | the exchange pair is not accepting this operation | +| `exchange_trading_disabled` | this network is not accepting Bancor trades | +| `not_exchange_creator` | the account did not create this exchange pair | +| `token_not_in_exchange` | that token is not one of the pair's two sides | +| `same_token` | both sides of the pair would be the same token | +| `insufficient_reserve` | the pair's reserve cannot support the requested amount | +| `self_participation` | the account cannot take both sides of this operation | +| `slippage_exceeded` | the trade would have returned less than the floor set for it | +| `contract_not_found` | no contract at that address | +| `not_contract_deployer` | the account did not deploy this contract | +| `internal_error` | an unexpected internal failure; the message is redacted on purpose | + +> **`rpc_error` 与 `invalid_option` 的退出码不同**:前者是端点侧的失败(重试或换端点即可,exit 1),后者是调用方错误(要改命令行,exit 2)。§6.1 的 gas 估算失败正是按这条界线**由 `invalid_option` 改回节点侧码**的。 + +--- + +## 12. Java 版移除 Standard CLI + +### 12.1 决策 + +Java 版内嵌两套入口:交互式 shell 与 Standard CLI(`org.tron.walletcli.cli`,非交互命令行层)。TS 版已完整覆盖非交互场景(`-o json`、错误码契约、`--*-stdin` 秘密通道),两套并存只会让行为契约长期漂移。**本版一次性移除 Java Standard CLI**,不留弃用过渡版本。 + +移除后 Java 版只剩交互式 shell 一个入口;所有非交互 / 脚本 / CI 场景改用 TS 版。 + +**这是破坏性变更,触达使用者靠四件事**(缺一不可,且都在发版前完成): + +| 交付物 | 内容 | 实测状态 @ `e206c00a` | +| --- | --- | --- | +| 命令映射表 | Standard CLI 全部命令 → TS 版对应命令,逐条列出,含参数与输出差异;进 release note 与 `java/README.md`,移除后继续保留在 docs | ❌ **未做**——全仓不存在;`java/README.md` 无横幅、无 Standard CLI 字样、无迁移指引 | +| 版本号 | Java 版随本版做 **major 级跳跃**,让依赖锁定的构建不会自动升上来 | ❌ **未达成**——`Utils.VERSION = " v4.13.0"`,4.12→4.13 是 **minor**;`java/build.gradle` 仍 `version '1.0-SNAPSHOT'` | +| 启动兜底 | 交互式 shell 收到 Standard CLI 形态的调用(带子命令参数启动)时,打一行**含替代命令与映射表地址**的提示再退出,**而不是**报未知参数 | ⚠️ **半数达成**——`Client.runMain` 已对带参调用打 stderr 提示并 exit 2,文案为 `Standard CLI has been removed in v4.13.0. Use the TypeScript CLI instead: @tron-walletcli/wallet-cli`;**但只给了包名,没有替代命令、没有映射表地址** | +| 社区通知 | GitHub Release note + Discussions 置顶帖 + 官方开发者渠道,随本版发布同步发出 | 🔵 发版动作,仓库内不可核 | + +> 没有弃用版做缓冲,**映射表与启动兜底就是仅有的两条触达渠道**——使用者是脚本与 CI,不读 release note,只在流水线红掉时才发现。映射表必须**逐条覆盖**、可直接照着改脚本,不能只给一句「请改用 TS 版」;启动兜底的那行提示,是他们在故障现场唯一能看到的东西,必须自带出路。 + +> ⚠️ **发版阻塞**:上表两个 ❌ 与一个 ⚠️ 落在同一条链上——**兜底提示之所以只能给包名,正是因为映射表还不存在**(无址可指)。四件交付物「缺一不可、且都在发版前完成」是本节自订的条件,目前仅社区通知一项待发。 + +### 12.2 移除范围(实测量化) + +> **本节的删除工作已在 PR #990 内完成**(`e206c00a`,含 PR #991 合入):`java/…/org/tron/walletcli/cli/` 已不存在,`fbf5362c..HEAD` 的 java 侧净删 **19,329 行 / 77 文件**。下表规模数字已按 `e206c00a` 复核——**「Standard CLI 包」一行逐位吻合**(39 文件 / 6,773 行);测试与文档两行原为 2 / 5,实测为 24 / 1,已订正(原文点名的另外四篇文档在本文档自己的旧基准 `fbf5362c` 上就不存在)。 + +| 项 | 规模 | 位置 | +| --- | --- | --- | +| Standard CLI 包 | **39 个文件 / 6,773 行** | `java/src/main/java/org/tron/walletcli/cli/` | +| 入口挂钩 | 4 个 import + `initRegistry()` 的 9 处 register + main 分支 | `java/src/main/java/org/tron/walletcli/Client.java` | +| 测试 | **24 个测试文件** | 整个 `java/src/test/java/org/tron/walletcli/cli/` 测试树(含 `aliases/` 5 个、`ledger/` 3 个、`commands/` 3 个)+ QA harness(`org/tron/qa/QARunner`、`QASecretImporter`)+ `TransactionUtilsTest` / `UtilsPasswordTest` / `ClearWalletUtilsTest` | +| 文档 | **1 篇** | `java/docs/standard-cli-contract-spec.md` | +| 关联方法族 | `WalletApi` 的 `*ForCli`、`WalletApiWrapper` 的 GasFree 签名分支 | 处置见 §12.3 | + +### 12.3 动工前置(阻塞项,必须先有结论) + +一次性移除没有回退窗口,以下两条**必须先查清调用关系再动手**,否则会删出「Java 版悄悄没了 Ledger」这类回归: + +| # | 前置 | 两种结论各自怎么做 | +| --- | --- | --- | +| 1 | **Ledger 支持的归属**——Java 侧 Ledger 只在 Standard CLI 这条路上做过(`cli/ledger/`、`WalletApi.signTransactionForCli` 的 Ledger 分支、`WalletApiWrapper` 的 GasFree 签名分支) | 若交互式 shell 也要保留 Ledger:先把 Ledger 相关代码**迁出** `cli/` 包并接到交互路,再删其余;若确认放弃:在 release note 里**明写「Java 版不再支持 Ledger」**,不能让它随包静默消失 | +| 2 | **`*ForCli` 方法族**——`processTransactionForCli` / `processTransactionExtentionForCli` / `signTransactionForCli` | 若只有 Standard CLI 调用:随包删;若交互路也在用:只删调用方、方法改名去掉 `ForCli` 后缀 | + +> 第 1 条的两种结论**都可接受,但都必须落在 release note 里**——不可接受的是没结论就删。 diff --git "a/ts/prd-\345\221\275\344\273\244\351\234\200\346\261\202\346\226\207\346\241\243-v4.13.0 (5).md" "b/ts/prd-\345\221\275\344\273\244\351\234\200\346\261\202\346\226\207\346\241\243-v4.13.0 (5).md" new file mode 100644 index 000000000..3e41c0a70 --- /dev/null +++ "b/ts/prd-\345\221\275\344\273\244\351\234\200\346\261\202\346\226\207\346\241\243-v4.13.0 (5).md" @@ -0,0 +1,3915 @@ +# wallet-cli 命令需求文档 v4.13.0 + +`wallet-cli` 是一个多链 CLI 钱包,架构覆盖 TRON 与 EVM。**本版起 EVM 从「架构预留」变为「实际可用」**——同一套助记词、同一批命令,靠 `--network` 选链。 + +本文档为 **v4.13.0**,承接 [v4.12.0](../v4.12.0/prd-命令需求文档-v4.12.0.md)(90 条命令,治理 / SR 竞选 / 合约治理 / TRC10 / Bancor / keystore 互导已闭合)。本版两个主题: + +1. **EVM 落地**——账户地址层 + 只读、转账与部署。**不新增任何命令**,而是给既有命令补 EVM family。 +2. **Java 版 Standard CLI 移除**——TS 版已完整接管非交互命令行场景,**本版一次性删除**,不留弃用过渡版本(§12)。 + +**本版另有一次强制的启动迁移(§0)**:`ChainAddresses` 因加入 `evm` 而变为不兼容既有 `wallets.json`,注册文件必须一次迁移到齐。这是本版**唯一一处在任何命令执行之前就可能阻断**的机制,故单列为 §0。 + +本版新增的每一处 help 输出都必须满足统一的文案规范(§10.1)。 + +> **阅读方式**:先看「范围与命令一览」(本版主题 / 能力矩阵 / 命令树 / root help / 横切约定),再看 **§0(启动前置:强制迁移)** 与 §1–§2(账户模型、网络配置),然后是 §3–§9 的命令逐条规格,最后 §10–§12。 +> +> **通用约定**(沿用 v4.12.0,此处只列与本版相关的): +> - **命令文法**:`<必填>` | `[可选]` | `a | b`(互斥二选一)| `(… | …)`(互斥组必选其一)。 +> - **图标**:🔒 需主密码 | ✍️ 改链上状态(会广播交易)| ⚠️ 高风险 / 不可逆 | 无图标 = 纯读 / 仅本地。 +> - **输出**:text(人读,字段独占一行)与 json(envelope `wallet-cli.result.v1`)两种;text/json 对称,**输出字段必须是数据、静态说明进 help**。 +> - **数量单位**:命令行与 text 用**人话单位**(TRX / ETH / gwei),json 给**链上原始值**(sun / wei)。**单位与小数位**由网络所属的 family 决定(TRX 6 位 / ETH 18 位);**币种名称**(TRX / ETH / BNB)由**网络**决定,不由 family 决定(§2.2)。 +> - **时间与时区**:一律 **UTC**、精确到秒(`YYYY-MM-DD HH:MM:SS UTC`);键值块标签含 `time` 字样、值带 `UTC`,表格把 `(UTC)` 挂在列名上。 +> - **stdout / stderr 分流**:stdout 只放结果(text 回执或 json envelope),提示、诊断、警告走 stderr。示例块中出现的 `? …` 提示行与 `password ✓ via pipe` 均来自 stderr,为还原真实终端观感而并列展示,**机器只读 stdout 即可**。 +> - **「相对现状」注解**:每个 Help 输出块上方一行,说明该 help 相对现状改了什么、没改什么,便于逐条核对。 +> - **示例省略**:地址、TxID、区块哈希写成 `TSRmq8kP...9dEf` / `0x7a3f...c19b` 只是排版省略,实际输出为完整值,json 亦然。 + +--- + +## 修订记录 + +| # | 日期 | 修订 | 依据 | +| :---: | --- | --- | --- | +| 1 | 2026-08-27 | **按实作同步全文**:§0 新增;§1–§2 / §3.2 / §3.6–§3.11 / §4.2 / §5 / §6 / §7 / §9.2–§9.3 / §10.1–§10.2 / §11 改写 | `spec-deviations-全量-v4.13.0.md` 的 A 档 26 项 + B 档 8 项(B 档均取推荐选项) | +| 2 | 2026-08-27 | **§12 改回一次性移除**:本版直接删除 Java Standard CLI,不设弃用过渡版本;头部主题与范围表同步 | PM 决策 | +| 3 | 2026-08-27 | **family 标注词表改为 `(TRON only)` / `(EVM only)`**:全文 132 处,词表规则写进 §10.1;原 v4.13.1 主题 1 整体折叠进本版 | PM 决策 | +| 4 | 2026-08-28 | **按 `e206c00a` 重新核实**:核实基准前移;修订 3 的标注改造与 §12 的 Java 移除**均已在实作落地**,两处 ⚠️ 注记删除;全局旗标文案回贴;命令数 90→91、§10 待办 46→38、§12.2 测试 2→24 / 文档 5→1 四个数字改正;help 区块示例的主网网络改回测试网 | `doc-verify-全量-v4.13.0-20260828.md` | + +> **核实基准**:PR [#990](https://github.com/tronprotocol/wallet-cli/pull/990) head `feat/v4.13.0` @ `e206c00a`(2026-08-27 18:49 +0800)。 +> +> **示例真实性**:§2.3 / §2.4 / §3.9 / §3.10 / §3.11 / §7.1 / §9.2 / §9.3 的示例与 help 区块为**实测输出**(`backup` 的绝对路径目录部分省略为 ``);§7.3 的 `Address` / `TxID` / `Fee` 已标注为设计稿;其余示例沿用原文。 +> **family 标注列已追平**——修订 3 的 `(TRON only)` / `(EVM only)` 全大写词表实作已于 `e206c00a` 落地,全量 help 扫描小写残留为 0,该列现已是实测值。 +> +> **本轮未做**:§10 的**命令层描述 / Args / Examples 重贴**(38 个命令层区块),见 §10 开头的待办说明。全局旗标文案(`--network` / `--timeout`)已于修订 4 回贴完毕。 + +--- + +## 范围与命令一览 + +### 本版主题与非目标 + +| 主题 | 范围 | 非目标(本版明确不做) | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **EVM 第一档:账户地址层** | EVM 地址编解码、family 派生、`create` / `import` 五路 / `list` / `current` / `derive` / `backup` 的 EVM 适配、**`contact` 的 family 感知改造**、EVM 网络与端点配置(§1–§3) | —— | +| **EVM 第二档:只读 + 转账 + 部署** | `account balance` / `portfolio` / `info`、`token` 全组、`tx send` / `sign` / `broadcast` / `status` / `info`、`contract call` / `send` / `deploy`、`message sign`、`typed-data sign`、`block`、`chain node` / `prices`(§4–§9) | `contract info`、`account history`、**ENS 域名解析**(后续版本支持,§1.3)、交易替换(同 nonce 提速 / 取消)、**GasFree 的 EVM 端**(后续跟进)、EVM 合约多签(Safe)、**Base / Arbitrum 等二层链**(费用模型不同,后续版本支持,§2.2)、NFT | +| **Java Standard CLI 移除** | 本版**一次性删除** 39 文件 / 6,773 行 + 入口挂钩 + 24 个测试文件 + 1 篇文档;配套命令映射表、Java 版 major 号跳跃、交互式 shell 的迁移提示、社区通知(§12) | 不动 Java 交互式 shell 本身 | +| **强制启动迁移** | `wallets.json` v1 → v2 的一次性阻断式迁移,在任何命令派发之前执行(§0) | **不做降版保护**;**未知 `source.type` 视为无秘密**(§0.4) | + +### 能力矩阵(命令 × family) + +**本版不新增命令,命令总数仍为 91**,变化的是「命令 × 网络」这个维度,故不再有「命令总数对照」表。 + +> **91 而非 90**:v4.12.0 起沿用的「90 条」是一处 off-by-one。实测 `e206c00a`:逐条抓 help 得 **91** 个命令层 help,`--json-schema` 的 `commands` 长度亦为 **91**,且**下方命令树枚举的正是这 91 条**(91 条全部能在树里找到,树里也无多余项)。本版据实改为 91,`v4.12.0` 侧的同一数字与根 `CLAUDE.md` 待 `baseline-merge` 时一并订正。矩阵与运行时的命令注册同源、不是手工维护的清单,但 **help 恒为全量**——命令树与 root help 是静态的,family 专属项靠 `(TRON only)` / `(EVM only)` 标注区分(§10.1)。 + +#### 本版交付(EVM) + +| 档 | 组 | 命令 | EVM 侧要点 | +| :---: | --- | --- | --- | +| 一 | 本地钱包 | `create` / `import` 五路 / `list` / `current` / `derive` / `backup` | 一次产出两族地址(keystore 本就是 EVM 原生格式);`import watch` 自动识别 `0x…`、`import ledger` 加 `--app ethereum`;地址列按网络 family;`backup --keystore` 导出私钥时按 `--network` 选族(§3.1–3.10) | +| 一 | 本地工具 | `contact add` / `list` / `remove` | **必须改造**:条目按地址格式识别 family 并持久化;**名称与地址均全局唯一,family 不对外呈现**,`--to ` 跨族报错(§3.11) | +| 一 | 本地工具 | `config` / `networks` | 端点可读可写(`networks..httpEndpoint`);`networks` 新增 `Alias` 列、`Network` 列改放规范 id(§2.2–2.4) | +| 一 | —— | `use` / `rename` / `delete` / `change-password` / `encoding convert` / `address generate` / `backup --records` | **无改造**:与 family 无关,或已同时输出两族地址;仅在 `encoding convert` / `address generate` 的 help 补一句「这是编码工具、与账户模型无关」的边界说明(§3.12 给出两句原文与完整 help) | +| 二 | account | `balance` / `portfolio` / `info` | `info` 给 Balance / Nonce / Type(§4.1–4.3) | +| 二 | token | `balance` / `info` / `add` / `list` / `remove` | ERC20;`kind` 扩 `erc20`;探测兼容 bytes32(§5) | +| 二 | tx | `send` / `sign` / `broadcast` / `status` / `info` | gas 四选项、nonce、RLP raw tx(§6) | +| 二 | contract | `call` / `send` / `deploy` | 不依赖链上 ABI;deploy 地址确定性算出(§7) | +| 二 | 签名 · 链信息 | `message sign` / `typed-data sign` · `block` / `chain node` / `chain prices` | EIP-191 / EIP-712;`prices` 给 base / priority / gas price + 转账折算(§8–§9) | + +#### 本版不做(EVM) + +| 命令 | 结论 | 为什么 | +| --- | :---: | --- | +| `account history` | 后续 | JSON-RPC **没有**按账户查历史的接口;可用的三条路子(Etherscan 兼容 API / Blockscout / 服务商增强方法)**互不兼容**,且能力取决于用户配了哪个端点;前置=新增 `explorer` port + `networks..explorerUrl`(key 可选)+ Requires 行(§4.4) | +| `contract info` | 后续 | 链上**不存 ABI**,只有字节码;同样依赖 explorer,或要求用户自带 ABI 文件 | +| ENS 域名解析(`--to .eth`) | 后续 | 不是一条命令,是 `--to` / `--account` 的收款人形态;本版只接受 `0x` 地址。后续版本支持,解析结果必须回显、不静默替换(§1.3) | +| `gasfree` 3 条 | 后续 | **GasFree 本身正从 TRON 扩展到 Ethereum 及 EVM 兼容链**,届时它就不再是 TRON 专属服务,`info` / `transfer` / `trace` 三条的命令形状两族通用,我方跟进接入即可。增量在:开放平台端点与鉴权(是否与 TRON 端同一套 API Key)、签名结构由 TIP-712 换成 EIP-712、费用口径(TRON 端是一次性激活费 + 每笔服务费从 USDT 扣)。**前置=GasFree 的 EVM 端正式可用**,具体档位待其上线时间明确后再定 | +| `permission` 2、`tx approvals` / `multisig`(4) | 未定档 | TRON 多签是**协议层**权限;EVM 多签是 **Safe 等合约**,属应用层,形态是 Safe 交易构造与协同签名,与现有多签命令不共用模型 | +| `account activate` / `set`(2) | 不做 | EVM 账户无需激活、链上无账户名 | +| `stake` 8、`chain params`(9) | 不做 | EVM 无质押换资源;协议参数由硬分叉决定,不可查询 | +| `proposal` 5、`witness` 3、`vote` 3、`reward` 2(13) | 不做 | EVM 无链上提案 / SR 选举 / 出块分红 | +| `asset` 6、`exchange` 6(12) | 不做 | TRC10 与 Bancor 池是 TRON 协议原生;EVM 对应物全在合约层(ERC20 已由 `token` 组覆盖,DEX 属应用) | +| `contract` 治理 4 条 | 不做 | EVM 无 origin energy limit / user resource percent / 链上 ABI 这些概念 | + +### 命令树(v4.13.0) + +命令集与 v4.12.0 **完全相同**(本版不增删命令),变的是标注口径:`(TRON only)` = TRON 专属,**无标注 = 两族通用**。因此 `account info`、`contract deploy`、`chain prices` 从 `(TRON only)` 行移到无标注行(本版 EVM 交付,§4.3 / §7.3 / §9.3)。 + +``` +wallet-cli 多链 CLI 钱包(TRON + EVM,--network 选链) +│ +├─ Common Commands ── 高频入口 +│ ├── create 新建 HD 钱包(BIP39,一次产出两族地址) +│ ├── import 导入钱包(mnemonic/private-key/ledger/watch/keystore) +│ └── list 列出钱包 / 账号 +│ +├─ Management Commands ── 链上资源(--network 选链;(TRON only)=TRON 专属) +│ ├── account balance | portfolio | info +│ │ history | activate | set (TRON only) +│ ├── permission show | update (TRON only) +│ ├── token balance | info | add | list | remove +│ ├── tx send | broadcast | status | info | sign +│ │ approvals | multisig (TRON only) +│ ├── gasfree info | transfer | trace (TRON only) +│ ├── contract call | send | deploy +│ │ info | clear-abi | set-origin-energy-limit +│ │ set-user-resource-percent | create2 (TRON only) +│ ├── proposal list | show | create | approve | delete (TRON only) +│ ├── witness create | update | set-brokerage (TRON only) +│ ├── asset issue | update | participate | unfreeze | info | list (TRON only) +│ ├── exchange create | inject | withdraw | trade | show | list (TRON only) +│ ├── stake freeze | unfreeze | withdraw | cancel-unfreeze (TRON only) +│ │ delegate | undelegate | info | delegated +│ ├── vote cast | list | status (TRON only) +│ ├── reward balance | withdraw (TRON only) +│ ├── chain node | prices +│ │ params (TRON only) +│ ├── message sign +│ ├── typed-data sign +│ └── block +│ +└─ Commands ── 其余本机命令 + ├── use / current(--qr) / rename / derive / delete / config / networks + ├── backup (--keystore) | --records (本地) + ├── change-password + ├── encoding convert 编码/地址互转(纯本地) + ├── address generate 随机密钥对(纯本地) + └── contact add | list | remove 收款人通讯录(纯本地) +``` + +### 横切约定(本版新增,全文有效) + +- **一个账户、多链地址**:账户是链无关的身份。`--account` 选谁、`--network` 选哪条链,两者正交。 +- **原生币单位由 family 决定,币种名称由网络决定**:**单位与小数位**归 family——TRX/sun(6 位)、ETH/wei(18 位);**币种名称**(TRX / ETH / BNB)归**网络**——`eip155:1` 与 `eip155:56` 同族但币种是 ETH 与 BNB,族级 symbol 对其中一条链必然是错的。分界线是:**族拥有编码与算术规则,网络拥有那条链的身份**(§2.2 的内置网络表因此有「原生币」列)。json 字段随单位命名:TRON 侧 `feeSun` 不变,EVM 侧 `feeWei`。 +- **gas 价格单位一律 gwei**:命令行接受 `--max-fee 25` 与 `--max-fee 25gwei` 两种写法(后缀大小写不敏感);**`wei` / `ether` 等其他单位点名拒绝**并报 `invalid_value`,不静默改读——`--max-fee 0.01ether` 与 `--max-fee 25` 差十亿倍,打错的代价就是实付费用差十亿倍。text `21.0 gwei`,json 给 wei 整数字符串。**本条同时列入 §1.4 的规则表**,因为各命令引用的是那张表。 +- **family 不匹配显式报错**:账户与网络不符、raw tx 与网络不符,统一 `family_mismatch`。 +- **签名能力按账户类型分档,全部 ✍️ / 🔒 命令通用**:软件账户(seed / private-key / keystore)本地解密后签名;**观察账户没有私钥,一律拒绝签名,报 `watch_only_no_signer`**;Ledger 账户在设备上签名,需设备连接并解锁,少数交易类型 Ledger app 不支持时报 `ledger_unsupported`。各命令小节不再重复这条。 +- **EVM 侧不为既有命令新增 family 专属字段**:EVM 沿用该命令在 TRON 下已有的字段集,只换值与单位;某个字段两族语义不同才按 family 取舍(如 `account info` 的 `Nonce` / `Type`)。**全文新增的输出字段限于下列各处,除此之外不得新增**:`list` 的 `derivationPath`(§3.7)、`networks` 的 `Alias` 与 `Endpoint` 列(§2.3)、`config` 的 `networks.` 对象形状与 `aliases`(§2.4)、`tx status` / `tx info` 的 `Confirmations`(§6.4–6.5)、`portfolio` 代币条目的 `id` 与两个价格状态字段 `priceUnavailable` / `balanceUnavailable`(§4.2)、`account info` 的 `decimals`(§4.3)、`chain prices` 的 `feeModel`(§9.3)、`tx info` 透传的 `transaction` / `receipt` 两个原始对象(§6.5)、`tx broadcast --dry-run` 的 `checks`(§6.3)。**其中两族同时生效的是**:`derivationPath`、`Confirmations`、`id`、价格状态字段;`feeModel` / `decimals` / 透传对象 / `checks` 按各族既有形状对齐(TRON 侧 `tx info` 本就透传 `transaction` / `info` 两个原始对象)。既有的字段级不一致(如 `token info` 的 `totalSupply` 在 json 与 help 里有、text 没有)本版不处理,见 §5.2。 +- **text 输出只有四种形状**:无标题的 `<字段> <值>` 块 · `<标题>: <值>` + 缩进字段 · `<标记> <动词摘要>` + 缩进字段(标记 ✅/❌/⏳/⚠️/❓)· **Markdown 管道表格**(含 `| --- |` 分隔行)。本版全部 EVM 示例按此书写。 +- **EVM 写命令继承既有横切**:`--dry-run` / `--sign-only` / `--build-only` / `--wait` / `--wait-timeout` 语义不变。 +- **family 专属 flag 在 help 里全量展示、按族标注,不按网络裁剪**:help 是**静态**的——`--network` 不影响它,渲染层把各 family 的 flag 合并后一次列全。故 TRON 的 `--asset-id` / `--fee-limit` / `--permission-id` 与 EVM 的 `--gas-limit` / `--max-fee` / `--priority-fee` / `--nonce` **会同时出现在 `tx send --help` 里**,各自行尾标 `(TRON only)` / `(EVM only)`——沿用 root help 已在用的组级标注体例(`stake … (TRON only)`)。**运行时仍按 family 严格校验**:EVM 网络上传 `--fee-limit` 会被该网络拒绝,报 `invalid_option`。 + +--- + +## 0. 启动前置:强制迁移 + +> **本版新增的机制,先于一切命令发生。** 位置反映执行顺序:读者读到任何命令规格之前,就该知道有这道闸门。 + +### 0.1 为什么需要 + +`ChainAddresses` 是**完整类型**,加入 `evm` 之后,既有的 `wallets.json` 对自己的类型失效。两条路: + +| 方案 | 代价 | +| --- | --- | +| 类型改成 `Partial` | **每一处读取**都要处理「这一族可能没有」 | +| **一次迁移到齐**(采用) | 一次阻断式启动迁移 | + +选后者,也是 §1.2 拒绝 `derive --path` 的同一个理由——迁移之所以能保持 `ChainAddresses` 完整,正是因为每个账户都两族齐备。 + +### 0.2 闸门的位置与阻断范围 + +- 在 **`--help` / `--version` 短路之后**、**任何命令派发之前**执行。 +- 只要有注册文件落后版本,**任何命令都不跑**。 +- 升级完成后重跑为 **no-op**。 + +> ❌ **实作与本节相反,需改实作**(`e206c00a` 实测):闸门跑在 help/meta **之前**——stale v1 文件下 `wallet-cli --help` **不打印 help**、`wallet-cli -V` **不打印版本**,两者都被闸门接管。源码自述亦然(`src/bootstrap/migration-gate.ts` 开头:"Runs on every invocation **before help/meta handling**, argument validation, or command dispatch")。 +> +> **建议改实作、而非改本节**:文件落后时连 `--help` 都不给,等于把用户在故障现场唯一的自助工具也关掉;且 §0.5 只承诺「连 `list` 都不能跑」,实际比承诺的更狠。`--help` / `--version` 不读也不写钱包状态,没有必须挡的理由。 + +### 0.2.1 机器可读输出(`-o json`) + +闸门在 json 模式下**不打印散文,而是产出一个正规信封**——这是 agent 侧唯一能可靠判读迁移发生过的途径: + +```json +{ "schema":"wallet-cli.result.v1","success":true,"command":"migration","data":{ "upgraded":true,"files":[{ "path":"…/wallets.json","from":1,"to":2,"backup":"…/wallets.json.v1.bak" }],"originalCommandExecuted":false },"meta":{ "durationMs":20,"warnings":[] } } +``` + +> 以上为实测输出(`e206c00a`)。**退出码 0**,且 `success: true`——迁移本身办成了,故不是错误;**`originalCommandExecuted: false` 是关键字段**:它告诉调用方「你原本那条命令没跑,请重发」。text 模式下对应的是末行 `Upgrade complete. Please run your command again.`。 +> +> 本信封的 `command:"migration"` 与 `data` 四个字段是本版新增的机器契约面,**不受「横切约定」那条输出字段封闭清单的约束**(该清单列的是既有命令的字段增量,闸门不是命令)。无法取得主密码时不走本信封,而是 `migration_required` + **退出码 2**(§0.4)——text 与 json 两模式的退出码一致,均实测。 + +> ❌ **text 形态需改实作**:闸门当前的 text 输出用的是 `==> …` 前缀段、`✓`、以及 `🎉 Upgrade complete. Please run your command again.`,**三者都不在「横切约定」允许的四种 text 形状之内**,`🎉` / `✓` 也不在标记词表(✅/❌/⏳/⚠️/❓)之内。 +> +> 应改为既有的「**`<标记> <动词摘要>` + 缩进字段**」形状——完成回执用 `✅`,告知段(stderr)用无标题键值块。**这不是排版洁癖**:四种形状是 text 渲染层的封闭集合,多一种就多一处解析器与后续命令都对不上的地方,而闸门恰恰是**每个用户升级后见到的第一屏**。 + +### 0.3 成本不对称是设计的核心 + +| source 类型 | 是否持有本机秘密 | 迁移行为 | +| --- | :---: | --- | +| `seed` / `privateKey` | 是 | **需要主密码**,走同意流程 | +| `ledger` / `watch` | 否 | **不问主密码,自动升级**(仍照常打印告知段与完成回执) | + +**只有 watch / ledger 的用户从未设过主密码**——若此处误问,他将无解。这条不对称不是优化,是可用性的下限。 + +> **「不提示」指的是不问主密码,不是无输出**(2026-08-28 PM 拍板,按实作)。原文「完全静默升级,不提示」有歧义,已改写。`e206c00a` 实测:watch-only 的 v1 文件迁移**跳过主密码那一步**,但仍在 stderr 打完整告知段(检测到旧格式 / 文件路径 / v1→v2 / 备份路径 / 只跑一次)、在 stdout 打完成回执。 +> +> **告知段该留**——迁移会改写钱包文件并留下一个永不自动清除的 `.bak`,这件事对 watch / ledger 用户同样成立;省掉主密码是因为他没有秘密可解,不是因为这件事不值得告诉他。 + +### 0.4 同意流程与其余规则 + +**需要主密码时,闸门先说明、再要求答复,答完才问密码**:说清「哪个文件、v几到v几、备份留在哪、只跑一次」。 + +> 旧行为是直接跳一个没有前因后果的 `Master password (hidden):`——没有理由、没说要改写文件、除了 Ctrl+C 没有拒绝的方式。**说明走 stderr**,stdout 保留给命令输出。 + +| 规则 | 内容 | +| --- | --- | +| 原子性 | **全成或全不成**(同一个事务) | +| 备份 | 迁移前留 `.v.bak`,**永不自动清除**——既有的事务机制只防崩溃,不防「成功但写错」 | +| 无 TTY | 报 `migration_required`(**退出码 2**,text / json 两模式一致),但**接受 `--password-stdin`**,CI 可自愈 | +| 密码错误 | TTY 下最多三次然后 `auth_failed`;**失败不留 `.bak`** | +| 全新安装 | 文件不存在 → 回报为当前版本,闸门放行 | +| `version` 缺失或非法 | `encoding_error`,**绝不当成第 0 版**——对一个装着钱包状态的文件,跑一个针对未知结构的迁移比挡下来更危险 | +| 迁移产出 | **== 新建产出**:重跑 `create` / `import` 用的同一组 derive 函数,不由既有 TRON 地址反推。已实测迁移后的 `wallets.json` 地址表与本版全新建立的**逐字节相同** | +| 其余注册文件 | `contacts.json` 与 `tokens.json` **不需要迁移**——前者落盘格式本来就是 family 分键、每笔自带 `family`(只需放宽校验),后者以 network id 为键,EVM 只是多几个键 | + +**两个「决定不做」的边界**(如实反映,不是待办): + +| 边界 | 内容 | +| --- | --- | +| **无降版保护** | 版本高于本体的文件不算落后,直接放行 | +| **未知 `source.type` 视为无秘密** | 不当成需要主密码的类型 | + +两者是同一个形状:**未知的东西被当成安全的东西放过去**。决定不挡的理由是今日皆无实害(只有四种 source type,且 v2 是最新版),而挡下来要付出的是**把用户锁在自己文件外面**的风险。 + +### 0.5 锁死后果(必须写进 release note) + +**忘记主密码且钥匙圈内有本机秘密者,连 `list` 都不能跑,且每次执行都会再挡一次。** + +这是**刻意接受**的——该用户本来就已无法签名/备份/导出,闸门没有新增损失,只是让它更早、更明显。 + +> **用 `--password-stdin` 的用户看不到屏幕上的说明,release note 是唯一告知管道**;同时应点明迁移会留下 `wallets.json.v1.bak` 且永不自动清除。 + +### 0.6 验证 + +47 项非交互情境 + 6 项真实 TTY 情境(pty 驱动)全数通过。 + +--- + +## 1. EVM 账户与密钥模型 + +### 1.1 账户模型 + +**账户是链无关的身份,同一个账户在每条链上按该链的 BIP44 coin type 各派生一把 key。** 与 OKX、Trust Wallet 等主流多链钱包一致,也与既有的账户存储结构一致。 + +| 账户来源 | TRON 地址 | EVM 地址 | 私钥关系 | +| ---------------------------------- | ------------------- | ------------------ | ---------------- | +| `create` / `import mnemonic`(seed) | `m/44'/195'/N'/0/0` | `m/44'/60'/0'/0/N` | 两族各一把,**不同** | +| `import private-key` | 该 key 的 TRON 编码 | 该 key 的 EVM 编码 | **同一把** | +| `import watch` | 仅当地址是 `T...` | 仅当地址是 `0x...` | 无(单 family) | +| `import ledger` | `--app tron` | `--app ethereum` | device(单 family) | + +### 1.2 派生路径 + +**每族跟随各自生态惯例,账户序号挂的层级不同。** + +``` +TRON m/44'/195'/'/0/0 序号在 account 层(保持现状,不动存量) +EVM m/44'/60'/0'/0/ 序号在 address_index 层 +``` + +以太坊标准路径为 `m/44'/60'/0'/0/x`,MetaMask、Trezor、Rabby 及绝大多数 dApp 钱包递增 address_index;走 account 层的只有 Ledger Live 一支。**跟随生态优先于跨族形状对称**——同 §3.6 Ledger EVM 用 Live 模板。 + +**互导手段**(覆盖从 Ledger Live / Legacy 等别家钱包迁入): + +| 手段 | 命令 | 说明 | +| --- | --- | --- | +| 硬件账户显式路径 | `import ledger --path ` | 指定完整路径注册硬件账户,绕开默认模板(§3.6) | +| 事后核对 | `list -o json` 的 `derivationPath` | 看出账户用的哪套模板 | + +> **软件账户本版只支持默认模板**:`derive` 不提供 `--path`。原因是显式路径会产生「单 family 的 seed 账户」——`Source.seed.addresses` 的 `ChainAddresses` 是完整类型,单族槽位表达不了,只能改成 `Partial` 或加槽位判别式;两者都会反噬本版的强制启动迁移(§0),而该迁移能保持 `ChainAddresses` 完整,正是因为每个账户都两族齐备。同时 `derivationPath` 会从「由 index 算出」变成「必须落盘」,等于在本版**第一次**强制迁移的同时再加一项 schema 变更。 +> +> 被挡住的只有「只有助记词、且资产在 Ledger Live / MEW 模板上」的用户——属功能缺口,不是资产风险;硬件用户走 `import ledger --path` 不受影响。绕行手段是用外部工具按目标路径导出私钥后 `import private-key`。 + +### 1.3 地址表示 + +| 项 | 规则 | +| --- | --- | +| 输出 | EVM 地址一律按 **EIP-55 校验和大小写**输出(text 与 json 一致) | +| 输入·全小写 / 全大写 | 视为**未带校验和**,接受 | +| 输入·混合大小写 | **必须通过 EIP-55 校验**,不匹配一律报 `invalid_address`、拒绝执行 | +| 输入·其它 | 必须带 `0x`、长度与十六进制合法性校验失败报 `invalid_address` | +| family 识别 | 由地址编解码器自动识别(`T...` → tron、`0x...` → evm),`--account 0x...` 可直接定位账户 | + +> **混合大小写必须校验**:协议层地址不区分大小写,但一个带校验和的地址被改动一位后校验必然失败——放行等于把「打错一位」和「剪贴板被替换」这两类事故直接变成资金损失。MetaMask、Trust Wallet 与硬件钱包均拒绝校验和不匹配的地址,ethers 的 `getAddress()` 同样抛错。我方对齐这一行为。 +> +> **ENS 本版不解析,后续版本支持**:`--to vitalik.eth` 在本版报 `invalid_address`;需要的用户自行解析后传入 `0x` 地址。**这是排期问题、不是拒绝**——ENS 是 EVM 生态的默认收款人形态,长期缺席不合理。 + +### 1.4 金额与精度显示 + +原生币 18 位小数(TRON 6 位),全部 18 位铺在 text 里既不可读也无意义,故定: + +| 场景 | 规则 | +| --- | --- | +| text 原生币 / 代币 | 最多保留 **6 位小数**,尾随零去除(`12.3456 ETH`、`0.25 ETH`) | +| text 非零但小于显示精度 | 显示 `<0.000001`,**绝不显示 `0`** | +| json | 恒给**最小单位整数字符串**(wei / sun / 代币基本单位),不做任何截断 | +| 命令行输入 | 按人话单位接受完整精度(`--amount 0.000000000000000001` 合法),超出该代币 decimals 才报 `invalid_amount` | +| USD 价格与估值 | **不适用上面的规则**:估值固定 2 位小数、单价 4 位,按 USD 惯例补零(`$2,500.00`、`$0.9998`) | +| gas 价格(`--max-fee` / `--priority-fee`) | 命令行**一律按 gwei 读**:`25` 与 `25gwei` 等价(后缀大小写不敏感);**`wei` / `ether` 等其他单位点名拒绝**并报 `invalid_value`。text 按 gwei 显示,json 给 wei 整数字符串 | +| 千分位 | **text 里的整数部分一律加千分位**——金额(`$41,004.35`)、区块号(`#11,204,113`)、gas(`1,204,551 gas`)、字节数(`3,124 bytes`)同此一条规则。**json 一律不加**(`"valueUsd":"41004.35"`),那是给机器解析的 | + +本规则适用于全文所有出现金额的输出:`account balance` / `portfolio`(§4.1–4.2)、`token balance`(§5.1)、`tx send` 的转账额与 Fee 行(§6.1)、`contract send` 的 `Allowance`(§7.2)、`chain prices` 的 `Transfer cost`(§9.3)。 + +> 「非零不显示 0」是关键:余额 1 wei 若显示成 `0 ETH`,用户会认定账户空了。截断只发生在 text,json 与实际转账金额始终是精确整数。 +--- + +## 2. 网络与配置 + +### 2.1 网络 ID 与别名 + +**规范 id = `<命名空间>:<链自身的 id>`,命名空间取 CAIP-2 的写法。** EVM 侧命名空间为 `eip155`,冒号后那一段就是 EIP-155 的数字 chain id(`eip155:1`、`eip155:11155111`、`eip155:56`、`eip155:97`);TRON 侧同样取 CAIP-2 的写法——**本版将 TRON 规范 id 由网络名改为十进制 chain id**: + +| 新规范 id | 旧形式(v4.12.0 及以前) | 别名 | +| --- | --- | --- | +| `tron:728126428` | `tron:mainnet` | `tron` | +| `tron:3448148188` | `tron:nile` | `nile` | +| `tron:2494104990` | `tron:shasta` | `shasta` | + +chain id 取**创世块哈希末 4 字节**(TIP-474),十进制渲染——与 `eip155` 用十进制、与 TRON 在 `ethereum-lists/chains` / ChainList 的既有登记一致。 + +> **为什么改**:原口径「TRON 没有数字 chain id」是**事实错误**——它有(TIP-474)。规范 id 取网络名是历史遗留,而 `tron` 命名空间的 CAIP-2 规范已在立项,定十进制为规范形式并明确 **`0x` 十六进制不是合法 CAIP-2 引用**。两侧都用 CAIP-2 写法之后,「规范 id = CAIP-2」这条规则才对全族成立,`--network` 的取值集合也不再分族记忆。 + +> **命名空间不是 family。** `eip155` 是链标识体系里的命名空间,而 family 是我方的适配层分组,值仍为 `evm`——json 的 `chain.family` 恒为 `evm`、`config.yaml` 自配网络的 `family` 字段也填 `evm`,只有 `chain.network` 这类**网络 id** 用 `eip155:` 前缀。两者不同名是有意的:将来若同一个 family 要覆盖非 EIP-155 的链,不必再改一次 id 形式。 + +这样定的理由是**可寻址性**:chain id 由链自己定义、全网唯一且永不变,于是**任何 EVM 链无需我方先起名字就能被指定**——用户配一个 Polygon 端点,直接 `--network eip155:137` 即可,不必等我方在代码里登记一个 `eip155:polygon`。**前缀直接取 `eip155` 而不是我方的 family 名,是为了让这个 id 与业内既有写法逐字相同**——CAIP-2 的 `eip155:1`、WalletConnect / SIWE 的链标识都是这个形式,用户与 agent 从别处拿到的 id 可以原样贴进 `--network`,我方不做一层翻译。EIP-3085 的 `chainId` 同样以数字为准。 + +**每条内置网络另给一个别名**,因为 `eip155:1` 不可读,而人要在命令行里天天敲它。**别名是不带命名空间前缀的简写**,与 hardhat(`--network sepolia`)、foundry(`--chain sepolia`)的习惯一致: + +| 规范 id | 别名 | 兼容别名(历史 id) | +| --- | --- | --- | +| `tron:728126428` | `tron` | `tron:mainnet` | +| `tron:3448148188` | `nile` | `tron:nile` | +| `tron:2494104990` | `shasta` | `tron:shasta` | +| `eip155:1` | `ethereum` | —— | +| `eip155:11155111` | `sepolia` | —— | +| `eip155:56` | `bsc` | —— | +| `eip155:97` | `bsc-testnet` | —— | + +> **「兼容别名」列不是新机制**,就是别名簿里的普通记录——上表内置全量因此为 **10 条**(7 条短别名 + 3 条历史 id)。 + +#### 历史 id 的兼容 + +破坏面只在**输出侧**,输入侧零成本: + +| 面 | 处置 | +| --- | --- | +| **`--network` 输入** | `tron:mainnet` / `tron:nile` / `tron:shasta` **永久保留为别名**,与 `tron` / `nile` / `shasta` 并列进别名簿。老脚本一个字不用改 | +| **`config.yaml`** | `networks..*` 的键与 `defaultNetwork` 的值由 **§0 的强制启动迁移**一并改写;`aliases` 里指向旧 id 的用户自定义别名同步重定向 | +| **json `chain.network`** | ⚠️ **这是唯一的破坏**——值由 `tron:nile` 变为 `tron:3448148188`。按旧值做分支的 agent 脚本必须改 | +| **触达** | 强制迁移是阻断式的、且在 json 模式产出正规信封(§0.2.1),是**唯一能保证被看见**的渠道。迁移信封的 `data` 须列出 `networkIdsRemapped: [{from, to}]`,让 agent 能程序化得知这次改名 | + +> **别名簿容得下这三条**是因为它本就是 `别名 → 规范 id` 的扁平表(见下):旧 id 降级为别名不需要新机制,只是多三条记录。§2.1 的「解析顺序先查规范 id、后查别名簿」不变,`tron:nile` 走别名簿命中同一张网络描述符。 + +**`--network` 接受两种写法**,运行时一律归一到规范 id:规范 id(`eip155:11155111`)与别名(`sepolia`)。**解析顺序固定为「先查规范 id、后查别名簿」**,由此得到一条比消歧更重要的保证——**别名永远不能遮蔽规范 id**:`--network eip155:1` 恒为以太坊主网,无论用户在 `config.yaml` 的别名簿里写了什么。 + +不设 **带命名空间前缀的别名**(`eip155:sepolia`):它存在的理由是「别名重名时消歧」,而下面的别名簿让重名在结构上不可能发生。`eip155:sepolia` 两次查找都不中,报 `unsupported_network`。 + +别名是可读性糖、可能随生态改名而调整(如 BSC 官方已更名 BNB Smart Chain),**规范 id 永不变**——所以机器面(json 的 `network` 字段、`config` 的 `networks..*` 键)只认规范 id,agent 与脚本不要拿别名做匹配。 + +**别名以「别名簿」这一张扁平表实现**——`config.aliases` 是 `别名 → 规范 id` 的一层 map,别名**不是**挂在网络描述符上的字段。上表七条即其内置全量。 + +这个形状让三条原本要写死并校验的规则**结构上自动成立**,不需要任何校验代码: + +| 原规则 | 在扁平表下为何自动成立 | +| --- | --- | +| 别名在全部 family 范围内唯一 | 一张 map 不可能有重复的键 | +| family 名是保留字(不设 `evm` 别名) | 表里没有 `evm` 这个键。`tron` 作为 `tron:728126428` 的别名是表里的一条普通记录 | +| 用户自配网络不自动获得别名 | 没写进表就没有别名 | + +配套两点: + +- **匹配只发生在 `--network` 解析这一步**,之后全流程只见规范 id。 +- **别名指向未知网络时,错误同时点名别名与它的目标**——`alias "polygon" points at unknown network eip155:99999`,而不是只说 `unknown network: polygon`(后者会让用户去检查自己敲的字,而问题在别名簿里)。 + +### 2.2 内置网络与 RPC 端点 + +| 规范 id | 别名 | family | 原生币 | 测试网 | feeModel | 端点主机 | +| --- | --- | --- | --- | :---: | --- | --- | +| `tron:728126428` | `tron` | tron | TRX | | `tron-resource` | `api.trongrid.io` | +| `tron:3448148188` | `nile` | tron | TRX | ✅ | `tron-resource` | `nile.trongrid.io` | +| `tron:2494104990` | `shasta` | tron | TRX | ✅ | `tron-resource` | `api.shasta.trongrid.io` | +| `eip155:1` | `ethereum` | evm | ETH | | `evm-gas` | `ethereum-rpc.publicnode.com` | +| `eip155:11155111` | `sepolia` | evm | ETH | ✅ | `evm-gas` | `ethereum-sepolia-rpc.publicnode.com` | +| `eip155:56` | `bsc` | evm | BNB | | `evm-gas` | `bsc-dataseed.bnbchain.org` | +| `eip155:97` | `bsc-testnet` | evm | BNB | ✅ | `evm-gas` | `bsc-testnet-dataseed.bnbchain.org` | + +> **「原生币」是网络级字段,不是 family 级**(§横切约定):`eip155:1` 与 `eip155:56` 同族而币种是 ETH 与 BNB,从 family 表读会把 BSC 上的 0.5 BNB 显示成 `0.5 ETH`。本列的存在也让将来接入 Polygon 时类型会强制填写,不会默默继承 ETH。 +> +> **「测试网」标记决定估值行为**(§4.2):标记为测试网的四条网络,币价与代币价一律为 **0**,且**不发任何外部请求**。**未申报为测试网的用户自配网络维持 `null`**——不知道 ≠ 不值钱。 +> +> **端点主机名随官方域名迁移更新**:BSC 的 dataseed 已由 `binance.org` 迁至 `bnbchain.org`,表中为迁移后的值。 + +> 本文档 §3–§9 的示例一律用**别名**书写(`--network sepolia`),与用户实际会敲的形式一致;json 示例里的 `network` 字段则一律是规范 id。 + +**本版内置的 EVM 网络限于一层链:Ethereum 与 BNB Smart Chain,各带一条测试网。** 每条主网都配测试网是硬要求——签名、nonce、gas 估算这些东西不该拿主网真钱去试,`bsc` 与 `bsc-testnet` 的关系等同 `ethereum` 与 `sepolia`。 + +**Base、Arbitrum、Optimism 等二层链后续版本支持**,本版不内置。原因是**费用模型不同,不是加个端点的事**:L2 上一笔交易的成本 = L2 执行费 + **把数据写回 L1 的 data fee**,后者由 L1 的 blob / calldata 价格决定,且各家 L2 的取值方式不一样(OP Stack 有 `GasPriceOracle` 预编译,Arbitrum 把它折进 gas 用量)。现有的 `evm-gas` 费用模型只算 `gasLimit × gasPrice`,**在 L2 上会系统性低估**——`tx send --dry-run` 报的费用比实际扣的少,这比不支持更糟。后续版本要新增 `evm-l2-gas` 费用模型并逐条对齐各 L2 的取数方式。 + +> **未内置的 EVM 链仍可指定,但费用估算不保证**:规范 id 的形式让任何 EVM 链开箱可寻址(`--network eip155:8453` + 自配端点即可查询与转账)。查询类命令与转账本身没有问题,**只有费用估算在 L2 上会偏低**。本版不阻止这种用法,也不为它背书。 + +**自配网络的必填字段**(写在 `config.yaml` 的 `networks.` 下): + +| 字段 | 必填 | 说明 | +| --- | :---: | --- | +| `family` | 是 | 必须是受支持的 family(本版为 `tron` / `evm`) | +| `chainId` | 是 | EVM 侧为 EIP-155 数字 chain id,与规范 id 后半段一致 | +| `nativeSymbol` | 是 | 该链原生币名称;缺了没有可回退的正确值(见上表说明) | +| `httpEndpoint` | 实务上必填 | 未内置的网络没有默认端点 | +| `capabilities` | 否 | 缺则视为空——没有额外特性是正常情况,不是错误 | +| `testnet` | 否 | 缺则视为主网,估值走真实价格源 | + +**校验发生在载入 `config.yaml` 的当下**:缺 `family` / `chainId` / `nativeSymbol`,或 family 不受支持,一律报 `invalid_value` 并**点名是哪条网络的哪个字段**。这条规则的意义在于错误的形态——config 的错误必须以 config 错误的形式、在读文件的当下报出;先前写错的后果是 bootstrap 崩溃,任何命令都回一个没有线索的 `internal_error`。 + +**四条 EVM 网络都内置可用端点,装完即可查询与转账**,不必先做配置。与 TRON 的差别不在有没有默认,而在谁运营:TronGrid 是链方第一方端点,EVM 侧没有单一权威运营方,内置的是第三方公共 RPC——**有限流、无 SLA、可能下线**,且默认会把查询地址暴露给该服务商。因此生产环境与高频调用建议换成自建节点或商用网关: + +```bash +wallet-cli config set networks.ethereum.httpEndpoint https:/// +``` + +`sepolia`(`eip155:11155111`)是本版冒烟测试网(等同 TRON 侧 Nile 地位),示例一律用它。 + +### 2.3 `networks` + +```bash +$ wallet-cli networks +| Network | Alias | Family | Chain id | Fee model | Endpoint | +| --------------- | ----------- | ------ | -------- | ------------- | ----------------------------------- | +| tron:728126428 | tron | tron | 728126428 | tron-resource | api.trongrid.io | +| tron:3448148188 | nile | tron | 3448148188 | tron-resource | nile.trongrid.io | +| tron:2494104990 | shasta | tron | 2494104990 | tron-resource | api.shasta.trongrid.io | +| eip155:1 | ethereum | evm | 1 | evm-gas | ethereum-rpc.publicnode.com | +| eip155:11155111 | sepolia | evm | 11155111 | evm-gas | ethereum-sepolia-rpc.publicnode.com | +| eip155:56 | bsc | evm | 56 | evm-gas | bsc-dataseed.bnbchain.org | +| eip155:97 | bsc-testnet | evm | 97 | evm-gas | bsc-testnet-dataseed.bnbchain.org | +``` + +> **六列,`Network` 放规范 id、`Alias` 单列一列。** 取舍是「一列还是两列」:只显示别名的话,用户看得到自己要敲什么,却无从得知机器面该用什么;而**规范 id 是稳定值,别名是可读性糖、会随生态改名而调整**(§2.1)。两列则两者都看得到,原本「用户要看到自己该敲什么」的顾虑没有损失。没有别名的用户自配网络,`Alias` 列为空。 +> +> **`Chain id` 是本版由 `Chain` 改名**,以对应规范 id 的后半段——那个值就是规范 id 冒号后的部分。 +> +> **`Endpoint` 是本版新增列,且只输出主机名,不输出完整 URL。** 理由是**端点路径常夹带 API key**(`…/v2/`、`…?apikey=`),而 `networks` 是列表输出、不是机密接口——它的结果会被贴进 issue 与 CI log。裁到主机名是唯一不需要猜「哪一段是密钥」的切法。要看完整 URL 走指名读取:`config networks..httpEndpoint`(§2.4)。 + +**Help 输出** + +> **相对现状**:描述行由 `List known networks` 扩写为含 family / chain id / fee model / endpoint host,并说明 `Network` / `Alias` 两列的分工与「端点只给主机名」。 + +```text +$ wallet-cli networks --help + +Usage: + wallet-cli networks [options] + +List known networks with their family, chain id, fee model and endpoint host. +Network is the canonical id (family:chain-id); Alias is the short name --network +also accepts. Endpoints are shown as hosts only. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli networks +``` + +### 2.4 `config` 新增项 + +| key | 读 | 写 | 默认 | 说明 | +| --- | :---: | :---: | --- | --- | +| `networks..httpEndpoint` | **本版新增** | **本版新增** | 内置值 | 该网络的 RPC 端点;此前只能手工编辑 `config.yaml`。`` **别名与规范 id 都接受**,写入时归一为规范 id。**指名读取回完整 URL**(`networks` 列表只印主机名,§2.3) | +| `defaultNetwork` | 既有 | 既有 | `tron:728126428` | 可设为任一 EVM 网络,无需改动;别名与规范 id 都接受,存储时归一为规范 id | + +**本版只加这一个键。** EVM 落地要的是「用户能配自己的端点」——主流 EVM RPC(Alchemy / Infura / QuickNode)的凭证都在端点 URL 里,`httpEndpoint` 一项即可覆盖。 + +**`` 段接受别名**,与 `--network` 同一套解析:`config networks.sepolia.httpEndpoint ` 与 `config networks.eip155:11155111.httpEndpoint ` 等价。三条规则配套: + +| 规则 | 说明 | +| --- | --- | +| **写入归一** | 无论用户敲的是别名还是规范 id,落到 `config.yaml` 的键**一律是规范 id**。否则同一条网络可能同时存在 `networks.sepolia` 与 `networks.eip155:11155111` 两个键,合并顺序决定谁生效——用户改了端点却不生效,且看不出原因 | +| **读取也归一** | 手工编辑 `config.yaml` 写成别名(TRON 时代就是这么改端点的)同样生效。**不认别名就等于静默失效**:配了跟没配一样,是最难排查的一类故障 | +| **重复键报错** | 若 `config.yaml` 里同一条网络既有别名键又有规范 id 键,**启动即报 `invalid_value` 并点名这两个键**,不静默取其一 | + +**示例与输出** + +```bash +$ wallet-cli config networks.nile.httpEndpoint https://nile.trongrid.io +✅ Set networks.tron:3448148188.httpEndpoint + Value https://nile.trongrid.io +``` + +```bash +$ wallet-cli config networks.tron:3448148188.httpEndpoint +https://nile.trongrid.io +``` + +> 指名读取给**完整 URL**,`networks` 列表只印主机名(§2.3)——分界线是**指名即意图**:用户点名要看这一条,那是他自己的意思;列表输出则会被贴进 issue 与 CI log。 + +**Help 输出** + +> **相对现状**:`key` 的 Args 文案**列出全部合法键名**(agent 读得到,散文里读不到);Examples 增加端点的读写两例。 + +```text +$ wallet-cli config --help + +Usage: + wallet-cli config [] [] [options] + +Show / get / set configuration values + +Args: + key config key to read or set (defaultNetwork, defaultOutput, timeoutMs, waitTimeoutMs, networks, tronlinkSecretId, tronlinkSecretKey, tronlinkChannel, gasfreeApiKey, gasfreeApiSecret, or networks..httpEndpoint); omit to show the whole effective config + value new value; omit to read the key + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli config + wallet-cli config defaultNetwork + wallet-cli config defaultNetwork nile + wallet-cli config networks.nile.httpEndpoint + wallet-cli config networks.nile.httpEndpoint https://nile.trongrid.io +``` + +--- + + +## 3. 本地钱包组(EVM 适配) + +### 3.1 `create` —— 新建 HD 钱包 🔒 + +> **本版改动**:回执多一行 EVM 地址;助记词一次产出两族地址。 + +**用法** + +``` +wallet-cli create [--label ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 生成 BIP39 助记词并落库,**一次产出两族地址** | +| EVM 增量 | 回执地址行由一行变两行(TRON / EVM) | +| 错误 | `account_exists`、`tty_required` | + +**示例与输出** + +```bash +$ wallet-cli create --label main +# 首次创建:设置主密码两步(keystore 已存在时只提示一行 Master password);助记词加密落库、不打印到任何输出 +? Set master password (hidden): +? Confirm master password: +✅ Created wallet "main" + Account ID wlt_ab12cd34.0 + Type HD + TRON address TSRmq8kP...9dEf + EVM address 0x7a3f...c19b + Active yes + +⚠️ Recovery phrase is encrypted locally and was not printed. +⚠️ Run `backup` soon and store the file offline. +``` + +```bash +$ wallet-cli create --label main --password-stdin -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"create","data":{ "status":"created","accountId":"wlt_ab12cd34.0","label":"main","type":"seed","index":0,"active":true,"addresses":{ "tron":"TSRmq8kP...9dEf","evm":"0x7a3f...c19b" },"seedId":"wlt_ab12cd34" },"meta":{ "durationMs":1088,"warnings":[] } } +``` + +> **EVM 增量只有 `addresses.evm` 一个键**——`status` / `accountId`(带 `.0` 后缀)/ `type`(`seed`,非 text 里的 `HD`)/ `seedId` 全部沿用既有结构。 + +**Help 输出** + +> **相对现状**:描述补两句(每族各派生一个地址、助记词本地加密不打印);Requires 主密码文案按 §10.1 统一。 + +```text +$ wallet-cli create --help + +Usage: + wallet-cli create [options] + +Create a new HD wallet (BIP39 seed). Derives one address per chain family +from the same seed; the recovery phrase is encrypted locally and never printed. + +Requires: + the master password — pass --password-stdin, or enter it interactively in a TTY + +Options: + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli create --label main +``` + +### 3.2 `import mnemonic` —— 导入助记词 🔒 + +> **本版改动**:一次导入产出两族地址。 + +**用法** + +``` +wallet-cli import mnemonic [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 导入 BIP39 助记词;助记词与主密码经隐藏 TTY 读取 | +| EVM 增量 | 两族地址(一次导入两族齐备) | +| 错误 | `invalid_mnemonic`、`account_exists`、`tty_required` | + +**Options** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--label ` | 否 | 自动 | 账户标签 | + +**示例与输出** + +```bash +$ wallet-cli import mnemonic --label cold +# 提示顺序固定:先主密码、后助记词(密码在 dispatch 阶段 prime) +? Master password (hidden): +? Paste recovery phrase (hidden): +✅ Imported wallet "cold" + Account ID wlt_9f7e21aa.0 + Type HD + TRON address TKq3xW7v...2bNc + EVM address 0x91b2...4d0e + Active yes + +⚠️ Recovery phrase was read from hidden input and was not printed. +``` + +> 一次导入两族地址齐备,无需为 EVM 再导一次。 +> +> **软件账户本版只支持默认模板**(§1.2):`derive` 不提供 `--path`。迁自 Ledger Live / MEW 等非默认模板的用户,硬件账户走 `import ledger --path`(§3.6);纯助记词用户需用外部工具按目标路径导出私钥后 `import private-key`。 + +**Help 输出** + +> **相对现状**:仅 `--label` 去掉重复的「助记词交互输入」尾注(该信息已在描述段)。 + +```text +$ wallet-cli import mnemonic --help + +Usage: + wallet-cli import mnemonic [options] + +Import a BIP39 mnemonic phrase. The recovery phrase and master password are read +interactively from the TTY (hidden input); they never touch argv or stdin. + +Requires: + the master password — entered interactively in a TTY + +Options: + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import mnemonic --label main +``` + +### 3.3 `import private-key` —— 导入裸私钥 🔒 + +> **本版改动**:同一把 key 输出两族地址(与 seed 账户不同,私钥相同)。 + +**用法** + +``` +wallet-cli import private-key [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 导入一把 secp256k1 私钥;私钥与主密码经隐藏 TTY 读取,**不接受 argv / stdin** | +| EVM 增量 | 同一把 key 输出两族地址(**私钥相同**,与 seed 账户不同) | +| 错误 | `invalid_private_key`、`account_exists`、`tty_required` | + +**示例与输出** + +```bash +$ wallet-cli import private-key --label hot +? Master password (hidden): +? Paste private key (hidden): +✅ Imported wallet "hot" + Account ID wlt_5c0d88b1 + Type private key + TRON address TBhCfAyt...3TCUp + EVM address 0x12E9...6D29 + Active yes + +⚠️ Private key was read from hidden input and was not printed. +``` + +> 两个地址是同一把 key 的两种编码——与 `encoding convert` 的输出一致。 +> +> 导入即设为活跃账户,与 `create` / `import mnemonic` 一致,故有 `Active yes` 行。`import watch` 是例外(观察账户不自动激活)。 + +**Help 输出** + +> **相对现状**:描述补一句「一把 key 每族各一个地址」;`--label` 去掉重复尾注。 + +```text +$ wallet-cli import private-key --help + +Usage: + wallet-cli import private-key [options] + +Import a raw private key. The private key and master password are read +interactively from the TTY (hidden input); they never touch argv or stdin. +One key yields an address on every chain family. + +Requires: + the master password — entered interactively in a TTY + +Options: + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import private-key --label hot +``` + +### 3.4 `import keystore` —— 导入 keystore 文件 🔒 + +> **本版改动**:keystore 本就是 EVM 原生格式;导入后为 private-key 类型、不可再派生。 + +**用法** + +``` +wallet-cli import keystore [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 导入 Web3 标准 keystore JSON(scrypt/aes),装的是**一把私钥**、非种子 | +| EVM 增量 | 两族地址(同 `import private-key`:一把 key 两种编码);keystore 本就是 EVM 原生格式,TRON 侧属借用 | +| 秘密输入 | keystore 文件密码经隐藏 TTY 读取,**仅交互式**,无 TTY 报 `tty_required` | +| 错误 | `invalid_keystore`、`wrong_keystore_password`、`account_exists`、`tty_required` | + +**示例与输出** + +```bash +$ wallet-cli import keystore ./UTC--2026-08-06--0x7a3f.json --label from-mm +? Master password (hidden): +? Keystore password (hidden): +✅ Imported wallet "from-mm" + Account ID wlt_3d81f0aa + Type private key + TRON address TDq7mW4x...8sVnP + EVM address 0x6Ae4...b1F7 + Active yes + +⚠️ Private key was read from the keystore file and was not printed. +``` + +> keystore 装单条私钥,**不可再派生**——导入后是 private-key 类型账户,没有 `index`,`derive` 对它不适用。这与 MetaMask / Geth 导出的 keystore 语义一致。 +> +> 同地址重复导入报 `account_exists`(不静默覆盖,先 `delete`)。 + +**Help 输出** + +> **相对现状**:描述精简改写,并补一句「一把 key 每族各一个地址」(与 `import private-key` 同一句);**flag 集合无变化**。 + +```text +$ wallet-cli import keystore --help + +Usage: + wallet-cli import keystore [options] + +Import a Web3 keystore JSON file. It holds a single private key, not a seed: +the account cannot be derived from. One key yields an address on every chain +family. The keystore password is read interactively from the TTY (hidden input). + +Args: + path path to the keystore JSON file + +Requires: + the master password — entered interactively in a TTY + +Options: + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import keystore ./keystore.json --label from-mm +``` + +### 3.5 `import watch` —— 注册观察地址 + +> **本版改动**:接受 `0x…`,建出 EVM 单 family 账户;地址行标签改为 family 标签。 + +**用法** + +``` +wallet-cli import watch --address [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 注册只读地址(无秘密),family 由地址格式自动识别 | +| EVM 增量 | 接受 `0x…`;建出的账户为 **EVM 单 family** | +| 错误 | `invalid_address`、`account_exists` | + +**Options** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--address ` | 是 | —— | TRON base58 `T…` 或 EVM `0x…`;family 自动识别。混合大小写的 EVM 地址必须通过 EIP-55 校验(§1.3) | +| `--label ` | 否 | 自动 | 账户标签 | + +**示例与输出** + +```bash +$ wallet-cli import watch --address 0xC4d9...30ab --label team-vault +✅ Added watch-only account "team-vault" + EVM address 0xC4d9...30ab + Note read-only; signing operations will be rejected +``` + +> **本版把地址行标签从通用的 `Address` 改为 family 标签**(`TRON address` / `EVM address`,与其它 import 回执一致):两族并存后,`Address` 不告诉用户这是哪条链的地址。单 family 账户在另一族网络下使用报 `family_mismatch`(§11)。 + +**Help 输出** + +> **相对现状**:描述补 family 自动识别与单族可用;`--address` 由「TRON base58」改为两族;Examples 补 EVM 一条。 + +```text +$ wallet-cli import watch --help + +Usage: + wallet-cli import watch [options] + +Register a watch-only address (no secret). The chain family is detected from the +address format; the account is usable only on networks of that family. + +Options: + --address address to track: TRON base58 T... or EVM 0x... [required] + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import watch --address TKq3xW7v...2bNc --label team-vault + wallet-cli import watch --address 0xC4d9...30ab --label team-evm +``` + +### 3.6 `import ledger` —— 注册 Ledger 账户 + +> **本版改动**:新增 `--app ethereum`,EVM 路径默认跟随 Ledger Live 模板。 + +**用法** + +``` +wallet-cli import ledger --app (tron | ethereum) [--index | --path | --address [--scan-limit ]] + [--label ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 注册硬件账户(本地不存私钥,签名在设备上完成) | +| EVM 增量 | `--app ethereum`;EVM 路径**默认用 Ledger Live 模板** `m/44'/60'/N'/0/0` | +| app 取值 | 只有 `tron` 与 `ethereum` 两个。**Ethereum app 覆盖全部 EVM 网络**(`ethereum` / `sepolia` / `bsc` / `bsc-testnet` 四条网络共用一个账户),不按链单列 `--app` | +| 错误 | `device_not_found`、`device_locked`、`ledger_unsupported`(**规格里的 `app_not_open` 并入此码**,§11.1)、`ledger_setting_required`、`invalid_path`、`account_exists` | + +> Ledger 的 EVM 默认模板与 §1.2 软件账户的默认模板不同——**各自跟随所属生态的默认**:软件账户跟随 MetaMask,硬件账户跟随 Ledger Live。Legacy / MEW 用户走 `--path`。 +> +> **EVM 侧本版不做 clear-signing——设备屏幕上是盲签。** 签名时传给 `hw-app-eth` 的 resolution 为 `null`:传入 resolution 会让它在**签名过程中**向 Ledger 的 CDN 抓取 clear-signing 描述子,好让设备显示「转 100 USDT 给 0xabc」而非一串原始哈希。 +> +> **由项目负责人决定采用 `null`:wallet-cli 在签名时不对任何第三方发出请求。** +> +> | | | +> | --- | --- | +> | **得到** | 签名流程无网络请求,不外泄合约地址与交易意图;离线 / 受限环境可用 | +> | **失去** | **设备上显示的是原始哈希**,用户无法在硬件上核对收款人与金额 | +> +> 这是**用户可见**的行为差异,而硬件钱包用户尤其在意——clear-signing 正是他们买硬件钱包的理由之一。后续是否开放待定。 +> +> **`--app` 不按链细分**:设备上的 Ethereum app 能为任意 EVM 链签名(chain id 在交易里,由 app 读取),且各 EVM 链共用 coinType 60 的同一把 key,所以一次 `--app ethereum` 注册出的账户在四条 EVM 网络上通用。Ledger 的 clone app(BSC、Polygon 等)是给想要自有品牌界面的链做的可选项,不是签名前提;新 EVM 网络接入 Ledger 走的是 Crypto Asset List 登记,不是新增一个 app。`--app` 的取值是**设备上要打开的 app 名**(故为 `ethereum` 而非 `evm`),账户 family 仍记为 `evm`。 + +**示例与输出** + +```bash +$ wallet-cli import ledger --app ethereum --index 0 --label cold-evm +✅ Registered Ledger account "cold-evm" + Account ID wlt_e18b45c0 + App evm + Path m/44'/60'/0'/0/0 + EVM address 0x3c8d...77a1 + +⚠️ No private key is stored locally. Signing requires device confirmation. +``` + +> `App` 行的值取自账户 family(`evm`),不是 `--app` 的输入值(`ethereum`)——现状如此,本版不改:family 才是后续所有命令的匹配依据。 + +**Help 输出** + +> **相对现状**:描述与 Requires 与现状一致,只动 Options 与 Examples——`--app` 由 `` 扩为 ``;**`--scan-limit` 的默认值由描述移入 `[optional, default: …]` tag**;`--path` 去掉 TRON 专属的路径举例;`--app` 描述去掉「address-derivation scheme」改为「选定 chain family」;Examples 补 ethereum 一条。 +> +> **`--index` 是这条规则的例外,默认值留在描述文字里。** 那个 tag 由 schema 的 `.default()` 推导——要显示 `default: 0`,字段就必须真的有默认值。而 `--index` 参与「`--index` / `--path` / `--address` 三个定位器只能给一个」的互斥规则,**该规则数的是「有没有给」**:加上默认值后 `index` 恒为已给,`--path` 单独使用会被判成两个定位器而被拒(实测确认会发生)。 +> +> (`--scan-limit` 之所以能做,是因为它不参与互斥;实作直接引用服务层的 `DEFAULT_SCAN_LIMIT` 作 `.default()`,既消掉重复的默认值副本,也让 `--json-schema` 的 `inputSchema` 有 `"default": 20`——散文里的默认值 agent 读不到。) + +```text +$ wallet-cli import ledger --help + +Usage: + wallet-cli import ledger [options] + +Register a Ledger account (watch-only; signs on device) + +Requires: + a connected, unlocked Ledger with the selected app (--app) open + +Options: + --app Ledger app to open on the device; selects the chain family [required] + --index account index under the app's default path; mutually exclusive with --path and --address [optional, default: 0] + --path explicit derivation path; mutually exclusive with --index and --address [optional] + --address locate this address by scanning indexes; mutually exclusive with --index and --path [optional] + --scan-limit how many indexes to scan when using --address [optional, default: 20] + --label human-friendly unique account label, 1-64 chars; omit to auto-generate [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli import ledger --app tron --index 0 --label cold + wallet-cli import ledger --app ethereum --index 0 --label cold-evm +``` + +### 3.7 `list` —— 列出钱包 / 账户 + +> **本版改动**:地址列按当前网络 family 显示;json 恒给两族全量 + 新增 `derivationPath`。 + +**用法** + +``` +wallet-cli list [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 列出全部本地账户,按 HD 种子 / 类型分组 | +| EVM 增量 | 地址列**按当前网络的 family 显示**;json 恒给两族全量 | +| 网络 | 可选,仅用于决定显示哪族地址(不访问节点) | + +**示例与输出** + +```bash +$ wallet-cli list --network sepolia +HD wlt_ab12cd34 +├─ [0] main 0x7a3f...c19b (active) +└─ [1] main-1 0x91b2...4d0e + +private key +└─ hot 0x12E9...6D29 + +watch +└─ team-vault 0xC4d9...30ab +``` + +```bash +$ wallet-cli list --network nile +# 同一批账户,地址列切到 TRON 族;watch 账户因是 EVM 单 family,不在此网络下展示 +HD wlt_ab12cd34 +├─ [0] main TSRmq8kP...9dEf (active) +└─ [1] main-1 TKq3xW7v...2bNc + +private key +└─ hot TBhCfAyt...3TCUp +``` + +```bash +$ wallet-cli list -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"list","data":[ { "accountId":"wlt_ab12cd34.0","label":"main","type":"seed","index":0,"active":true,"addresses":{ "tron":"TSRmq8kP...9dEf","evm":"0x7a3f...c19b" },"seedId":"wlt_ab12cd34","derivationPath":{ "tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0" } },{ "accountId":"wlt_c4a70e93","label":"team-vault","type":"watch","index":null,"active":false,"family":"evm","addresses":{ "evm":"0xC4d9...30ab" },"derivationPath":null } ],"meta":{ "durationMs":13,"warnings":[] } } +``` + +> **被网络过滤掉的账户会在 stderr 补一行提示**(不进 stdout): +> +> ```text +> warning: 2 account(s) have no tron address and are not shown; use --network to switch, or --output json to see every family +> ``` +> +> 理由是 **Ledger 账户与 watch 一样是单族,而 Ledger 是能签名的真实账户**:一个只有 EVM Ledger 的用户,在默认 TRON 网络下跑 `list` 会**什么硬件账户都看不到**,且没有任何线索告诉他 `--network` 的存在。走 stderr 而不是 stdout,是为了让 stdout 保持干净(机器只读 stdout),json 不受影响。 +> +> text 不并排两族地址:表会宽一倍,且用户当下只关心在用的链。json 给全量。**`derivationPath` 是本版新增字段**(按 family 的 map,watch / private-key 账户为 `null`)——现状 json 只有 `accountId` / `label` / `type` / `index` / `active` / `addresses` / `seedId`,没有路径,用户无从判断账户用的哪套派生模板(§1.2)。 +> +> **两个按账户类型出现/消失的字段,规则本版写死**:`seedId` **只在 seed 账户出现**——观察、private-key、keystore、Ledger 账户没有种子,不能拿 `accountId` 顶上(现状 watch 条目的 `seedId` 与 `accountId` 同值,是个伪字段,本版去掉);`family` **只在单族账户出现**(watch / Ledger),两族齐备的账户不给该字段,哪族看 `addresses` 的键即可。判定「这个账户能不能派生」一律看 `seedId` 在不在,不看 `type` 的字符串。 + +**Help 输出** + +> **相对现状**:描述补两句;**新增全局 `--network`**(现状 `list` 无此项);Examples 由 `--output json` 一条换为两族三条。 + +```text +$ wallet-cli list --help + +Usage: + wallet-cli list [options] + +List wallets/accounts (no unlock needed). The address column shows the family of +the selected network; JSON output always carries every family's address. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli list + wallet-cli list --network sepolia + wallet-cli list --output json +``` + +### 3.8 `current` —— 当前活跃账户 + +> **本版改动**:账户有哪族地址就显示哪族,各一行;`--qr` 出当前网络 family 的地址。 + +**用法** + +``` +wallet-cli current [--qr] [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 显示**选定账户**(默认为活跃账户) | +| EVM 增量 | **账户有哪族地址就显示哪族,各一行**;`--qr` 生成**当前网络 family** 的地址二维码 | +| 账户 | 支持全局 `--account`;省略则为活跃账户 | +| 错误 | `family_mismatch`(`--qr` 时账户在选定网络 family 下没有地址;**text 与 json 一致**) | + +**示例与输出** + +```bash +$ wallet-cli current +Active account: main + TRON address TSRmq8kP...9dEf + EVM address 0x7a3f...c19b +``` + +```bash +# 单 family 账户只有一行地址 +$ wallet-cli use team-vault && wallet-cli current +Active account: team-vault + EVM address 0xC4d9...30ab +``` + +> **地址行按账户实际拥有的 family 出**:`create` / `import mnemonic` / `import private-key` / `import keystore` 建出的账户两族齐备,出两行;`import watch` / `import ledger` 是单 family,只出一行——空值行被渲染层丢弃,不存在 `EVM address` 留空这种输出。 +> +> `--qr` 取**当前网络 family** 的地址:选定账户在该 family 下没有地址时(如 EVM 单族账户配 `--network nile`)报 `family_mismatch`,而不是回退到它拥有的那一族——回退会让用户拿到一个另一条链的收款码。 +> +> **该检核与输出格式无关**:`-o json` 同样拒绝并报 `family_mismatch` / exit 2,通过时回 `receiveAddress`。**QR 图是这条命令唯一属于 text 的部分,也是唯一由输出格式决定的部分。** +> +> **本版支持全局 `--account`**:`current` 先前是唯一一条「显示某个账户」却不接受它的命令。支持它让「看一眼另一个账户的地址」不必先 `use` 过去再 `use` 回来——后者会改动活跃账户这个全局状态,只为读一次。 +> +> **单族账户的 family 检核时机本版后移**:先前在**解析网络**的当下就用账户的 family 去比对,现在移到「真的要这一族的地址」那一刻。旧时机让 `current` 这种**纯本地查看**命令,在账户与默认网络不同族时**完全无法查看自己的账户**;而那道提前的检核并没有防住任何事——没有它,真正需要地址的命令一样会在任何 RPC 之前失败。连带效果:`list`、`backup --records` 与 `current` 得以支持 `--network`(先前该旗标被静默忽略且不出现在 help)。 +> +> **后果需写进 release note**:`config.defaultNetwork` 无法解析时,`list` 与 `backup --records` 会**失败**——这是把它们改为 network-aware 的代价,决定维持硬失败(行为一致、早点报错更清楚),release note 应点明「先修 defaultNetwork」。 + +**Help 输出** + +> **相对现状**:**新增全局 `--network`**(决定 `--qr` 出哪族地址)**与全局 `--account`**(并带对应的 Requires 段);描述补「按账户拥有的 family 每族一行」;`--qr` 描述补「该族无地址时失败」;Examples 补两条。 + +```text +$ wallet-cli current --help + +Usage: + wallet-cli current [options] + +Show the current active account, with one address line per chain family it has + +Options: + --qr print a receive QR code for the selected network's address; fails when the account has none for that family [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli current + wallet-cli current --qr + wallet-cli current --qr --account main + wallet-cli current --qr --network sepolia +``` + +### 3.9 `derive` —— 派生下一个 HD 账户 🔒 + +> **本版改动**:一次派生两族地址。**不新增 `--path`**——见 §1.2「软件账户本版只支持默认模板」。 + +**用法** + +``` +wallet-cli derive --seed-id [--index ] [--label ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 从种子钱包派生新账户 | +| EVM 增量 | 一次产出两族地址 | +| 错误 | `seed_not_found`、`account_exists` | + +**Options** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--seed-id ` | 是 | —— | 种子钱包 id(`list` 的 HD 组头) | +| `--index ` | 否 | 下一个空闲 | 账户序号,按各族默认模板套用 | +| `--label ` | 否 | `<钱包名>-` | 账户标签 | + +**示例与输出** + +```bash +$ wallet-cli derive --seed-id wlt_baezdw0b --password-stdin +✅ Derived sub-account "main-1" + Account ID wlt_baezdw0b.1 + Index 1 + TRON address TFtFc27ig1NKYLkmapdFmhHgrUS1YWMdha + EVM address 0x1486AbC087a7442d44C43d802b2637560fADf895 + Active yes + Note shares master mnemonic; no separate backup needed +``` + +> 一次派生两族地址,两行并出。 +> +> **`--path` 在本版不存在**,敲了会得到 `invalid_option: unknown option(s): --path`。理由与取舍见 §1.2;`invalid_path` 这个错误码**仍然保留**,由 `import ledger --path` 在路径格式非法时产生(§11)。 + +**Help 输出** + +> **相对现状**:描述补一句「每族一套 BIP44 模板,一次 derive 产出每族一个地址」;`--index` / `--label` 描述精简并补字数上限;Requires 冠词按 §10.1 统一。 + +```text +$ wallet-cli derive --help + +Usage: + wallet-cli derive [options] + +Derive the next HD account from a seed wallet (by --seed-id). Each family uses +its own BIP44 template, so one derive yields an address per family. + +Requires: + the master password — pass --password-stdin; this command never prompts + +Options: + --seed-id seed id (wlt_…) of the HD wallet to derive from — shown as the HD group header in `list` [required] + --index explicit HD account index, in account index; omit to use the next free index [optional] + --label label for the new derived account, 1-64 chars; omit to auto-generate - [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli derive --seed-id wlt_ab12cd34 +``` + +### 3.10 `backup` —— 导出账户机密 🔒⚠️ + +> **本版改动**:seed 账户导出**私钥**时,由**既有的全局 `--network`** 决定导哪一族;助记词导出无歧义。**不新增 `--family`。** + +**用法** + +``` +wallet-cli backup [--keystore] [--network ] [--out ] [--password-stdin] +wallet-cli backup [] --records [--from ] [--to ] [--limit ] [--offset ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 把助记词 / 私钥导出到 0600 文件(**从不写 stdout**) | +| EVM 增量 | 导出**私钥**时由 `--network` 选定链(seed 账户两族私钥不同);未给则用 `config.defaultNetwork` | +| 错误 | `account_not_found`、`not_exportable`(观察 / Ledger 账户)、`output_exists` | + +**Options(增量)** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--network ` | 否 | `config.defaultNetwork` | **全局旗标**。导出私钥时决定导哪一族的钥匙;助记词导出与 private-key 账户不受影响 | + +**为什么用 `--network` 而不是新增 `--family`** + +**family 是系统内部概念,不对外暴露成选项。** 全 CLI 目前没有任何一个旗标让用户直接打 family 名——`import ledger` 用 `--app tron|ethereum`、`contact` 从地址推断(§3.11)、`list` / `current --qr` 用 `--network`。`--family` 会是唯一的例外,等于为同一个概念引入第二套词汇。「网络作为显示/选择哪一族的选择器」是本版已经确立的模式,`backup` 沿用它。 + +**但问题本身仍然必须修**(换旗标不会让它消失):seed 账户两族是**两把不同的私钥**(§1.2:coin 195 vs coin 60),而 V3 keystore 结构上只装一把。修前写死导出 TRON 那把,实测后果是——钱包显示 EVM 地址,导出的钥匙导入 MetaMask 得到另一个地址。**导入完全成功、地址看起来正常,只是不是用户的**,而且没有任何错误信息。 + +**补偿**:因为没给 `--network` 会静默吃默认值,回执与 json 都带 `Family` 栏,让用户一眼看到拿到的是哪一把。 + +**示例与输出** + +```bash +# 默认网络为 tron:728126428,故导出 TRON 那把 +$ wallet-cli backup main --keystore --password-stdin +⚠️ Keystore written /wlt_baezdw0b.0-1787825194541.keystore.json + Account ID wlt_baezdw0b.0 + Family tron + Secret private key + File mode 0600 + Bytes 608 + +⚠️ Secret material was written only to the keystore file, never to stdout. +``` + +```bash +# 同一个账户,切到 EVM 网络导出的是另一把钥匙 +$ wallet-cli backup main --keystore --network sepolia --out ./main-evm.keystore.json --password-stdin +⚠️ Keystore written /main-evm.keystore.json + Account ID wlt_baezdw0b.0 + Family evm + Secret private key + File mode 0600 + Bytes 608 + +⚠️ Secret material was written only to the keystore file, never to stdout. +``` + +> 以上为实测输出,仅把绝对路径的目录部分省略为 ``。 +> +> **文件默认落在当前工作目录**,不是 `~/.wallet-cli/backup/`(这是既有行为,与 EVM 无关,此前文档写错)。默认文件名为 `./-.json`(`--keystore` 时为 `.keystore.json`),以 0600 创建且**从不覆盖**已存在的文件。因此**不要在共享目录或 git 仓库里跑这条命令**——help 描述里有对应的一行警告。 +> +> 助记词导出无歧义(一句助记词覆盖两族),不受 `--network` 影响。 + +**Help 输出** + +> **相对现状**:描述改写(keystore 语义、「只写文件不写 stdout」、**默认写当前目录的警告**、`--records` 段);**不新增 `--family`**;`--out` 描述补默认文件名与「从不覆盖」;`--records` 全套沿用现状;新增全局 `--network`(§3.8 的检核时机后移使其可用)。 + +```text +$ wallet-cli backup --help + +Usage: + wallet-cli backup [] [options] + +Export an account's secret to a 0600 file — the native backup format, or a standard Web3 +keystore JSON with --keystore (importable by TronLink and others, encrypted with your master +password). A keystore holds a single private key, so an HD account exports only its current +derived key; use the native backup to move a whole seed. + +The secret is written only to the file, never to stdout; watch-only and Ledger accounts have +none to export. Files default to the CURRENT DIRECTORY — do not run this in a shared directory +or a git repository. + +With --records and no account, nothing is exported: it shows the local audit log of past +exports instead — one row per 'backup' and 'backup --keystore', newest first, with the file +each secret went to. Imports are not logged. The log keeps the most recent 1000 entries. + +Args: + account account or wallet to export, addressed by accountId, label, or address; with --records, the account whose exports to list + +Requires: + the master password — pass --password-stdin, or enter it interactively in a TTY + +Options: + --keystore export as a standard Web3 keystore JSON (importable by TronLink and others, encrypted with your master password) instead of the native format [optional, default: false] + --out output file path; omit to write ./-.json in the current directory (.keystore.json with --keystore); file is created with mode 0600 and never overwritten [optional] + --records list past secret exports instead of exporting anything [optional, default: false] + --from with --records: only records at or after this UTC time; format YYYY-MM-DD or 'YYYY-MM-DD HH:mm:ss', parsed as UTC [optional] + --to with --records: only records at or before this UTC time; format YYYY-MM-DD or 'YYYY-MM-DD HH:mm:ss', parsed as UTC [optional] + --limit with --records: maximum records to return; omit for all [optional] + --offset with --records: pagination offset [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli backup main --out ~/main-backup.json --password-stdin + wallet-cli backup main --keystore --password-stdin + wallet-cli backup --records --limit 20 + wallet-cli backup --records --account main --from 2026-08-01 +``` + +### 3.11 `contact` 组 —— 收款人通讯录(family 感知改造) + +> **本版改动**:**必须改造**——条目按地址格式识别并持久化 family,`--to ` 跨族报错,否则会把 EVM 地址拿去 TRON 网络发交易。**定案本版修订**:名称与地址均**全局唯一**,family 不出现在任何用户可见的表面。 + +**用法** + +``` +wallet-cli contact add
[--note ] +wallet-cli contact list +wallet-cli contact remove +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 本地收款人通讯录;`tx send --to ` 可直接用联系人名代替地址 | +| EVM 增量 | **条目内部记录 family**(按地址格式识别),`--to ` 解析时校验 family 与当前网络一致 | +| 错误 | `already_exists`(**同名或同址**)、`invalid_address`、**`family_mismatch`**(联系人地址与当前网络不同族)、`contact_not_found` | + +> **这是本版必须改造的一条,否则会转错链**:通讯录现状只存「名字 → 地址」、不分网络,而 `--to` 接受联系人名。加入 EVM 后,`tx send --to exchange --network nile` 若 `exchange` 存的是 `0x…`,就会拿一个 EVM 地址去 TRON 网络发交易。地址格式校验能挡住这一例(TRON 侧 base58 解码失败),但**依赖下游校验兜底不是设计**——通讯录自己就该知道每条记录属于哪条链。 + +#### 定案(本版修订:推翻「同名可在两族各存一条」) + +**对外是一张扁平的 `name ↔ address` 表,两者各自全局唯一。** family 只是内部存储分桶与 `--to` 路由的细节,**任何用户可见的表面都不出现它**——没有 `Family` 列、没有 `family` json 字段、没有 `--family` 旗标。 + +**为什么推翻**:原定案写的是「同名允许在不同 family 下各存一条」。但**「同名允许」不是谁决定的,是存储结构的副产物**——`contacts.json` 是 `entries: { tron: […], evm: […] }` 按 family 分桶,实作把「名称唯一性」也继承了桶的范围。没有人问过唯一性的范围**该**是什么,文档后来为这个既有行为补了理由。 + +改成全局唯一之后,三件事同时消失: + +| 原问题 | 全局唯一之后 | +| --- | --- | +| `remove ` 跨族同名该删哪一条 | **问题不存在**——不需要 `--family`,也不需要第二个位置参数 | +| 「`--to ` 跨族报 `family_mismatch`」 | **才真正有用**。允许同名时,这个错误对「两族都有的名字」永远不会触发 | +| `--family` 与「family 不对外暴露」原则冲突(§3.10) | 一并消失 | + +**代价**:想要两条就得叫 `exchange-tron` 与 `exchange-evm`——明确、不会搞错,成本仅止于多打几个字。 + +`contact list` 保持纯本地、**无 `--network`**、不按网络过滤:条目数通常个位数,过滤省不下多少噪声,却会让刚 `contact add` 完的用户在默认网络下看不到自己刚加的条目(`add` 按地址格式定 family,不看 `--network`)。`--to` 选哪条由名称直接决定,不依赖列表怎么显示。 + +**示例与输出** + +```bash +$ wallet-cli contact add exchange TKyeCYyEtgNs5X2srhKcjLiimVmWFy1q61 --note "CEX deposit" +✅ Contact added + Name exchange + Address TKyeCYyEtgNs5X2srhKcjLiimVmWFy1q61 + Note CEX deposit +``` + +```bash +# 同名不再允许——即使属于另一族 +$ wallet-cli contact add exchange 0x1486AbC087a7442d44C43d802b2637560fADf895 +error [already_exists]: a contact named exchange already exists +``` + +```bash +$ wallet-cli contact add exchange-evm 0x1486AbC087a7442d44C43d802b2637560fADf895 --note "CEX deposit" +✅ Contact added + Name exchange-evm + Address 0x1486AbC087a7442d44C43d802b2637560fADf895 + Note CEX deposit +``` + +```bash +$ wallet-cli contact list +| Name | Address | Note | +| ------------ | ------------------------------------------ | ----------- | +| exchange | TKyeCYyEtgNs5X2srhKcjLiimVmWFy1q61 | CEX deposit | +| exchange-evm | 0x1486AbC087a7442d44C43d802b2637560fADf895 | CEX deposit | +``` + +```bash +$ wallet-cli contact list -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"contact.list","data":{ "contacts":[ { "name":"exchange","address":"TKyeCYyEtgNs5X2srhKcjLiimVmWFy1q61","note":"CEX deposit" },{ "name":"exchange-evm","address":"0x1486AbC087a7442d44C43d802b2637560fADf895","note":"CEX deposit" } ] },"meta":{ "durationMs":16,"warnings":[] } } +``` + +```bash +$ wallet-cli contact remove exchange-evm +✅ Contact removed + Name exchange-evm + Address 0x1486AbC087a7442d44C43d802b2637560fADf895 +``` + +> 以上均为实测输出。**没有 `Family` 列,json 也没有 `family` 字段**——地址本身 `T…` / `0x…` 已经表明是哪条链。 +> +> **`--to ` 跨族的错误措辞描述地址,不描述 family**,用户不必学会那个词:`contact exchange holds the address T…, which the selected network cannot pay`。 +> +> **破坏性后果**(release note):`contact list` 的 text 少了 `Family` 列,json 少了 `family` 字段。 + +**Help 输出** + +> **相对现状**:`add` 的描述与 `name` / `address` 参数补 family 说明(按地址格式校验、名称可在任何接受地址的地方使用);`name` 补「1-64 字符、不得形似地址」的上限;**`remove` 不新增 `--family`**;三条命令的 flag 集合与现状一致。 + +```text +$ wallet-cli contact add --help + +Usage: + wallet-cli contact add
[options] + +Add a locally stored recipient. The address is validated against the family it belongs to (T… = TRON, 0x… = EVM), and the name can then be used anywhere an address is accepted. + +Args: + name local name for this recipient; 1-64 safe characters and must not look like a chain address. Usable anywhere an address is accepted + address recipient address to store under this name + +Options: + --note free-form note, up to 128 safe characters [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli contact add alice TBy6... --note 'Alice mainnet' +``` + +```text +$ wallet-cli contact list --help + +Usage: + wallet-cli contact list [options] + +List every recipient in the local plaintext address book. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli contact list +``` + +```text +$ wallet-cli contact remove --help + +Usage: + wallet-cli contact remove [options] + +Remove one recipient from the local address book without changing any on-chain state. + +Args: + name name of the contact to delete + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli contact remove alice +``` + +### 3.12 `encoding convert` / `address generate` —— 编码工具的边界说明 + +> **本版改动**:**行为零改造**,只在两条 help 的描述里各补一句边界说明。 + +这两条纯本地命令一直同时输出 TRON 与 EVM 两种地址,容易被读成「账户模型」的一部分——**它们是编码工具**:给的是同一把 key 的两种编码,与 §1.1 账户模型里「seed 账户两族私钥不同」是两回事。不补这句,用户会拿 `encoding convert` 的输出去对 `create` 的两行地址,然后发现对不上。 + +| 命令 | 补的那句(英文原文) | +| --- | --- | +| `encoding convert` | `The two address forms are encodings of one 20-byte key hash, not two derived accounts.` | +| `address generate` | `The TRON and EVM addresses shown are two encodings of the same generated key.` | + +**Help 输出** + +> **相对现状**:描述末尾各加一句边界说明;**flag 集合、Args、Examples 全部不变**。 + +```text +$ wallet-cli encoding convert --help + +Usage: + wallet-cli encoding convert [options] + +Auto-detect the input and print every equivalent representation, validating +checksums. Two families: ADDRESS (TRON base58 / TRON 41-hex / EVM 0x / public +key hex -> address forms) and ENCODING (arbitrary hex <-> Base64 <-> +Base58Check). Routing is automatic by whether the input is address-shaped. +Purely local. Private keys and mnemonics are NOT accepted (secrets must never +appear on the command line). The two address forms are encodings of one 20-byte +key hash, not two derived accounts. + +Args: + input value to convert: an address, a public key hex, or any hex/Base64/Base58Check string + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli encoding convert TBhCfAyt...3TCUp + wallet-cli encoding convert 0x12E9...6D29 + wallet-cli encoding convert deadbeef0102 +``` + +```text +$ wallet-cli address generate --help + +Usage: + wallet-cli address generate [options] + +Generate a random keypair locally (works offline). The private key is written to +a 0600 file by default and is NOT stored in the wallet — import it with +`import private-key` to sign with it. The TRON and EVM addresses shown are two +encodings of the same generated key. + +Options: + --out file to write the keypair to (0600); refuses to overwrite [optional, default: /generated/keypair-
] + --print-secret print the private key to stdout instead of writing a file (use offline) [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli address generate + wallet-cli address generate --out /secure/usb/key.json +``` + +--- + +## 4. account 组 + +### 4.1 `account balance` —— 原生币余额 + +> **本版改动**:走 `eth_getBalance`,单位 ETH / wei;json 结构与 TRON 侧完全一致。 + +**用法** + +``` +wallet-cli account balance [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 查询账户原生币余额 | +| EVM 增量 | 走 `eth_getBalance`;单位 ETH / wei(18 位) | +| 网络 | 可选(缺省 `config.defaultNetwork`) | +| 错误 | `family_mismatch`、`rpc_error`(含端点限流 429) | + +**示例与输出** + +```bash +$ wallet-cli account balance --network sepolia +Label main +Balance 12.3456 ETH +``` + +```bash +$ wallet-cli account balance --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"account.balance","data":{ "address":"0x7a3f...c19b","balance":"12345600000000000000","decimals":18,"symbol":"ETH" },"meta":{ "durationMs":180,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> json 结构与 TRON 侧**完全一致**(`address` / `balance` / `decimals` / `symbol`),只是值与单位不同:`balance` 恒为最小单位整数字符串(TRON 给 sun、EVM 给 wei),人话单位只在 text 出现。 + +**Help 输出** + +> **相对现状**:描述由 `Show native balance (TRX/SUN)` 改为族中立;全局 `--network` 示例值改为跨两族;Examples 改为两族对称。 + +```text +$ wallet-cli account balance --help + +Usage: + wallet-cli account balance [options] + +Show the native coin balance for the selected network + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli account balance --network nile + wallet-cli account balance --network sepolia +``` + +### 4.2 `account portfolio` —— 持仓与估值 + +> **本版改动**:代币为 ERC20;价格源需补 EVM 链与代币的 id 映射。 + +**用法** + +``` +wallet-cli account portfolio [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 原生币 + 地址簿内代币的余额与 USD 估值 | +| EVM 增量 | 代币为 ERC20;价格源需补 EVM 链与代币的 id 映射 | +| 错误 | 同 §4.1;价格源不可用时估值列留空、进 `meta.warnings`,并给 `priceUnavailable` / `priceReason` 两个可程序判断的字段 | + +**示例与输出** + +```bash +$ wallet-cli account portfolio --network ethereum +| Token | Balance | Price (USD) | Value (USD) | +| ----- | ------- | ----------- | ----------- | +| ETH | 12.3456 | $3,321.40 | $41,004.35 | +| USDC | 2500 | $1.0000 | $2,500.00 | +| USDT | 1000 | $0.9998 | $999.80 | + +Total ≈ $44,504.15 +``` + +```bash +$ wallet-cli account portfolio --network ethereum -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"account.portfolio","data":{ "network":"eip155:1","account":"wlt_ab12cd34.0","address":"0x7a3f...c19b","priceSource":"coingecko","holdings":[ { "kind":"native","symbol":"ETH","decimals":18,"rawBalance":"12345600000000000000","balance":"12.3456","priceUsd":"3321.40","valueUsd":"41004.35" },{ "kind":"erc20","id":"0xA0b8...eB48","symbol":"USDC","decimals":6,"rawBalance":"2500000000","balance":"2500","priceUsd":"1.0000","valueUsd":"2500.00" },{ "kind":"erc20","id":"0xdAC1...1ec7","symbol":"USDT","decimals":6,"rawBalance":"1000000000","balance":"1000","priceUsd":"0.9998","valueUsd":"999.80" } ],"totalValueUsd":"44504.15" },"meta":{ "durationMs":642,"warnings":[] },"chain":{ "family":"evm","network":"eip155:1","chainId":"1" } } +``` + +> 结构沿用既有:`rawBalance`(最小单位)与 `balance`(人话单位)并存,价格不可用时 `priceUsd` / `valueUsd` / `totalValueUsd` 为 `null`、text 显示 `-`。代币条目的合约地址走 `id` 字段(与 token 地址簿同名)。 + +#### 降级语义(本版新增两组字段) + +| 情况 | 字段 | 语义 | +| --- | --- | --- | +| 价格源整体失败 | `priceUnavailable: true` + `priceReason` | 全表估值列为 `null`;同时进 `meta.warnings` | +| 单个代币余额读不到 | 该条目 `balanceUnavailable: true` + `reason` | **该行仍在**,余额与估值为 `null` | + +**为什么要布尔字段而不只是 `meta.warnings`**:`warnings` 是给人看的字符串,**agent 要分支就得比对字符串**。两个布尔字段让「为什么没有估值」可程序判断。 + +**为什么逐币降级**:一个下市合约、一次 `balanceOf` revert 或一次 RPC 抖动,**不该让整张持仓表消失**;而该行报 0 会是一个假的事实——**「读不到」与「是零」是两件事**。 + +> EVM 端逐币并行读取,**刻意不用 multicall**:那要引入合约依赖与每条链一个待验证的地址,只为省下几次往返。 + +#### 测试网估值规则(本版新增) + +**标记为测试网的网络(§2.2)一律不估值,币价与代币价固定为 `0`,且不发任何外部请求。** + +- **取 `0` 而不是 `null`**:`null` 的意思是「我们查不到」,而测试网不是查不到——**是确定没有价值**。说出后者比留白诚实,`totalValueUsd` 也会有一个明确的 0 而不是一片 `-`。 +- **TRON 侧同步变更**:`nile` / `shasta` 先前显示**真实 TRX 币价**,本版起为 0。这是刻意一并改的——**两族在同一条命令上给相反的答案,比任何一种答案都糟**。(破坏性后果,进 release note。) +- **顺带关掉一个真实曝险**:测试网代币先前用**主网平台**查价,而确定性部署可能让同一个地址同时存在于两条链——那会让测试代币拿到真币的价格。 +- **未申报为测试网的自配网络维持 `null`**:不知道 ≠ 不值钱。 +- **币种名称维持该链的正式名称**(ETH / BNB / TRX),不改成 `SepoliaETH` / `tBNB`——「这不是真钱」由估值规则表达,比改币种名更直接,也不必偏离链本身的称呼。 +> +> **余额列按 §1.4 的精度规则**:最多 6 位小数、尾随零去除,故 `2500` 不写成 `2500.00`。**价格与估值列不适用该规则**——它们是法币金额,按 USD 惯例固定 2 位(价格因单价可能极小,保留 4 位),补零是可读性所需,不是精度损失。 + +**Help 输出** + +> **相对现状**:描述补一句「代币取自所选网络的地址簿」;`--network` 示例值改为跨两族;Examples 两族对称。 + +```text +$ wallet-cli account portfolio --help + +Usage: + wallet-cli account portfolio [options] + +Show native + token balances with best-effort USD value. Tokens come from the +address book of the selected network. + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli account portfolio --network nile + wallet-cli account portfolio --network sepolia +``` + +### 4.3 `account info` —— 账户状态摘要 + +> **本版改动**:EVM 侧给 Balance / Nonce / Type / Code size——**Nonce 是排查卡单的唯一入口**。 + +**用法** + +``` +wallet-cli account info [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 该账户在当前链上的关键状态摘要 | +| EVM 增量 | `eth_getBalance` + `eth_getTransactionCount` + `eth_getCode`;字段按 EVM 账户模型取舍 | +| 错误 | `account_not_found`(`--account` 传了非本地账户)、`family_mismatch`、`rpc_error`(含端点限流 429) | + +**示例与输出** + +```bash +$ wallet-cli account info --network sepolia +Label main +Address 0x7a3f...c19b +Balance 12.3456 ETH +Nonce 42 +Type EOA +``` + +```bash +# 账户地址上有字节码时:Type 变为 contract,附字节码大小 +# team-vault 是 `import watch` 注册的团队多签合约地址(§3.5) +$ wallet-cli account info --account team-vault --network ethereum +Label team-vault +Address 0xC4d9...30ab +Balance 18.42 ETH +Nonce 1 +Type contract +Code size 3,124 bytes +``` + +> **`--account` 只解析本地账户**(accountId / 标签 / 该账户自己的地址,§1.3),不是「查任意链上地址」的入口——传一个不在本地的地址报 `account_not_found`。要看别人的合约,先 `import watch --address ` 注册成观察账户再查;这与 `account balance` / `portfolio` 的口径一致,全组不为 EVM 破例。 + +```bash +$ wallet-cli account info --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"account.info","data":{ "label":"main","address":"0x7a3f...c19b","balance":"12345600000000000000","decimals":18,"symbol":"ETH","nonce":42,"type":"eoa" },"meta":{ "durationMs":260,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> 合约地址的 json 多 `codeSize`(字节数整数)、`type` 为 `contract`;EOA 不给 `codeSize` 而非给 `0`——空值行被丢弃是渲染层规则,json 同样不塞无意义的零。`type` 的取值只有 `eoa` / `contract` 两种,全小写(与 `Status` 的收敛口径一致,§6.5)。 + +> **字段按 family 取舍,不是「TRON 有什么 EVM 也要有什么」**:TRON 侧给 `Staked` / `Energy` / `Bandwidth` / `Permissions` / `Created`(资源与多签模型),EVM 一个都没有;EVM 给 `Nonce` 与 `Type`,TRON 没有。两族共有的只有 `Label` / `Address` / `Balance`。 +> +> **`Nonce` 是这条命令在 EVM 上存在的主要理由**:它是 `--nonce` 手动指定、nonce gap 排查、`--wait` 超时后判断交易是否还挂在内存池的唯一查询入口(§6.1)。业内对应 `cast nonce`;`Type` 对应 `cast code` 的有无判断,转账前确认收款方是不是合约。 + +**Help 输出** + +> **相对现状**:描述由 `Show raw account data (getAccount; …)` 改写为按 family 说差异、TRON 在前(去 RPC 方法名,§10.1 规则 3);`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli account info --help + +Usage: + wallet-cli account info [options] + +Show the account's on-chain state for the selected network. Fields differ by +family: TRON reports staked amounts, resources and permissions; EVM reports the +transaction nonce and whether the address holds code. + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli account info --network nile + wallet-cli account info --network sepolia +``` + +### 4.4 关于 `account history`(EVM 后续版本,本节无命令规格) + +**本版 EVM 不做**,但理由不是「做不了」,写清楚以免下一版重新论证: + +| 项 | 说明 | +| --- | --- | +| TRON 现状 | 靠 TronGrid 的账户交易接口,公共端点即可用、无需 key | +| EVM 所需 | 节点 JSON-RPC **不提供**按账户查历史的接口——`eth_*` 里没有这个能力,`eth_getLogs` 只能按 topic 捞 ERC20 的 Transfer 事件,**捞不到原生币转账**(它不产生 log)。可用的路子有三条,**互不兼容**:① **Etherscan 兼容 API**——要 key,且免费档在收紧(2026-07 起单次返回上限由 10,000 降至 1,000);② **Blockscout**——公共实例**无需 key**,key 只用于提高限额,但按链覆盖不齐;③ **服务商增强方法**(如 `alchemy_getAssetTransfers`)——**不需要额外 key**,走用户已配的 `httpEndpoint` 即可,但只有部分服务商提供 | +| 本版不做的原因 | 不是「必须有 key」,而是**没有标准接口**:三条路子的请求与响应结构完全不同,各要一个适配器;更棘手的是**能力取决于用户碰巧配了哪个端点**——同一条命令在不同机器上有无历史可查,这对确定性 CLI 是硬伤,得先定「运行时探测还是要求显式声明」。这是独立一块工作,塞进本版会稀释 EVM 转账主线 | +| 后续方案 | 新增 `explorer` 类 port,配置为 `networks..explorerUrl`(选哪个浏览器)+ **可选**的 `networks..explorerApiKey`(Etherscan 必填、Blockscout 可空);沿用 §10.1「help 文案规范」的 Requires 规则 6,把「一个 Etherscan 兼容或 Blockscout 端点」写进该命令的 Requires 段(与 TRON 侧 `account history` 的 TronGrid Requires 对称) | + +--- + +## 5. token 组 + +代币条目的 `kind` 增加 `erc20` 一档(既有 `trc20` / `trc10` 不变)。地址簿按 network id 分区存储,跨链天然隔离。 + +### 5.1 `token balance` —— 单个代币余额 + +> **本版改动**:走 ERC20 `balanceOf`;`--asset-id`(TRC10)在 EVM 网络下被拒。 + +**用法** + +``` +wallet-cli token balance (--contract | --asset-id ) [--account ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 查询单个代币余额 | +| EVM 增量 | 走 ERC20 `balanceOf(address)`;`--asset-id`(TRC10)**降为 TRON 专属旗标**,help 中标 `(TRON only)`,在 EVM 网络下传入报 `invalid_option` | +| 错误 | `token_metadata_unavailable`、`token_not_in_book`、`family_mismatch` | + +**示例与输出** + +```bash +$ wallet-cli token balance --contract 0xA0b8...eB48 --network ethereum +Label main +Name USD Coin +Symbol USDC +Balance 2500 USDC +``` + +```bash +$ wallet-cli token balance --contract 0xA0b8...eB48 --network ethereum -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"token.balance","data":{ "address":"0x7a3f...c19b","kind":"erc20","id":"0xA0b8...eB48","name":"USD Coin","symbol":"USDC","decimals":6,"balance":"2500000000" },"meta":{ "durationMs":210,"warnings":[] },"chain":{ "family":"evm","network":"eip155:1","chainId":"1" } } +``` + +**Help 输出** + +> **`--contract` 与 `--asset-id` 的分层(§5 全组适用)**:`--contract` 留在**共用层**并降为不做格式检查的字符串——地址格式改由各族 binding 的 refine 验证;`--asset-id` **与那条「二选一」规则一起移进 TRON binding**。 +> +> 理由是 **TRC10 是 TRON 专属概念,EVM 没有对应物**——那条「二选一」的 refine 在 EVM 上恒为错误规则;只标注 `(TRON only)` 是文字,规则本身还是会跑。 +> +> **为何不让两族各自声明 `--contract`**:help 与 `--catalog` 合并同名的 family 字段时是**后盖前**,两族都声明会让说明文字只剩最后注册那族的版本。(验证本身不受影响——`z.toJSONSchema` 不序列化 refinement——受害的只有描述文字。) +> +> **TRON 用户看到的东西没变**:错误信息与 issue path 与先前完全相同(`invalid tron address`)。 + +> **相对现状**:描述去掉 `(--contract / --asset-id)`;**`--contract` 降为族中立的「token contract address」,不再带「二选一」叙述**(那条规则连同 `--asset-id` 一起移入 TRON binding,见下);`--asset-id` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token balance --help + +Usage: + wallet-cli token balance [options] + +Show a single token balance + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --contract token contract address [optional] + --asset-id TRC10 numeric asset id; provide exactly one of --asset-id or --contract [optional] (TRON only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token balance --contract TR7... --network nile + wallet-cli token balance --contract 0xA0b8... --network sepolia +``` + +### 5.2 `token info` —— 代币元数据 + +> **本版改动**:走标准 ERC20 只读方法,输出字段与 TRON 侧完全一致。 + +**用法** + +``` +wallet-cli token info (--contract | --asset-id ) [--network ] +``` + +**概览** + +| 项 | 内容 | +| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 功能 | 查询代币名称 / 符号 / 精度 | +| EVM 增量 | 走标准 ERC20 只读方法(`name` / `symbol` / `decimals` / `totalSupply`),json 与 TRC20 对称;**text 同样只显三行**,不新增输出字段 | +| 错误 | `token_metadata_unavailable`(目标地址的链上元数据读不到,含「不是可探测的 ERC20」这一情形) | + +**示例与输出** + +```bash +$ wallet-cli token info --contract 0xA0b8...eB48 --network ethereum +Name USD Coin +Symbol USDC +Decimals 6 +``` + +> text 三行与 TRON 侧完全一致(实测现状即为 `Name` / `Symbol` / `Decimals`)。 +> +> **`totalSupply` 的 text / json 不一致是既有缺陷,本版有意不动**:数据在查(4 次 constant call)、json 里有,唯独 text 不显示。它与 EVM 无关,两族一样,本版不趁改造顺手动它——修的时候要连带处理另一个同源问题:**单个字段调用失败会静默丢行**(`name()` 失败时 `Name` 整行消失且 `meta.warnings` 为空,「该代币没有此字段」与「这次没取到」无法区分)。两者一并修:补 text 行或从 json 去掉,以及把失败写进 warnings。 +> +> **help 的描述已不再宣称 `totalSupply`**(2026-08-28 PM 拍板,按实作):组 help 与命令 help 的一行描述均为 `Show token metadata`。这是对的——**help 不该替一个 text 里看不到的字段背书**;等上面那条缺陷修完,要不要把字段列举加回描述再议。 + +**Help 输出** + +> **相对现状**:描述去掉 `(name/symbol/decimals/totalSupply)` 字段列举;**`--contract` 降为族中立、不带「二选一」叙述**;`--asset-id` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token info --help + +Usage: + wallet-cli token info [options] + +Show token metadata + +Options: + --contract token contract address [optional] + --asset-id TRC10 numeric asset id; provide exactly one of --asset-id or --contract [optional] (TRON only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token info --contract TR7... --network nile + wallet-cli token info --contract 0xA0b8... --network sepolia +``` + +### 5.3 `token add` —— 加入地址簿 + +> **本版改动**:探测走 ERC20 只读调用,**兼容 bytes32 元数据**;`kind` 记为 `erc20`。 + +**用法** + +``` +wallet-cli token add (--contract | --asset-id ) [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 把代币加入当前网络的地址簿,自动探测符号 / 精度 / 名称 | +| EVM 增量 | 探测走 ERC20 只读调用;`kind` 记为 `erc20` | +| 错误 | `token_metadata_unavailable`、`token_already_listed` | + +> **探测要兼容 bytes32 元数据**:ERC20 定稿前的老代币(MKR 等)把 `name()` / `symbol()` 返回成 `bytes32` 而非 `string`,按 string 解码会失败。业内库(ethers / web3)均做双解码回退,我方同样:先按 `string` 解,失败再按 `bytes32` 解并去除尾部零字节;两者都失败才报 `token_metadata_unavailable`。`decimals()` 缺失时不猜默认值,直接报 `token_metadata_unavailable`——猜错精度会让后续每一笔转账金额都错。 + +**示例与输出** + +```bash +$ wallet-cli token add --contract 0xA0b8...eB48 --network ethereum +✅ Added to token book + Name USD Coin + Symbol USDC + Decimals 6 +``` + +**Help 输出** + +> **相对现状**:描述改写为「加入所选网络的地址簿」;**`--contract` 降为族中立、不带「二选一」叙述**;`--asset-id` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token add --help + +Usage: + wallet-cli token add [options] + +Add a token to the address book of the selected network, fetching its name, +symbol and decimals from the contract + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --contract token contract address [optional] + --asset-id TRC10 numeric asset id; provide exactly one of --asset-id or --contract [optional] (TRON only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token add --contract TR7... --network nile + wallet-cli token add --contract 0xA0b8... --network sepolia +``` + +### 5.4 `token list` —— 列出地址簿 + +> **本版改动**:条目 `kind` 扩 `erc20`;分区方式沿用现状(按 network id),EVM 网络各自一本。 + +**用法** + +``` +wallet-cli token list [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 列出当前网络地址簿的全部条目(内置 + 用户自加) | +| EVM 增量 | 条目 `kind` 为 `erc20`;地址簿**按 network id 分区**(沿用现状),`eip155:1` 与 `eip155:56` 是两本,`tron:mainnet` 与 `tron:nile` 也是两本(分区键是**规范 id**,不是别名)——不按 family、不跨网络合并 | +| 错误 | `family_mismatch`(账户与网络 family 不符) | + +**示例与输出** + +```bash +$ wallet-cli token list --network ethereum +| Symbol | Name | Source | Contract / ID | +| ------ | ---------- | -------- | ------------- | +| USDT | Tether USD | official | 0xdAC1...1ec7 | +| USDC | USD Coin | official | 0xA0b8...eB48 | +| MYTK | My Token | user | 0x4f2a...9b03 | +``` + +> `official` 条目按规范 id 内置(`eip155:1` 填 USDT / USDC;测试网留空,同 `nile` 的处理),用户不可删除。 + +**Help 输出** + +> **相对现状**:描述补「所选网络的」;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token list --help + +Usage: + wallet-cli token list [options] + +List the address book of the selected network (official + user entries) + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token list --network nile + wallet-cli token list --network sepolia +``` + +### 5.5 `token remove` —— 移出地址簿 + +> **本版改动**:无 EVM 特有行为,仅 `kind` 扩 `erc20`。 + +**用法** + +``` +wallet-cli token remove (--contract | --asset-id ) [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 移除用户自加的代币条目 | +| 错误 | `token_not_in_book`、`token_is_official`(内置条目不可删) | + +**示例与输出** + +```bash +$ wallet-cli token remove --contract 0x4f2a...9b03 --network ethereum +✅ Removed from token book + Name My Token + Symbol MYTK +``` + +**Help 输出** + +> **相对现状**:描述补一句「内置条目不可删」;**`--contract` 降为族中立、不带「二选一」叙述**;`--asset-id` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli token remove --help + +Usage: + wallet-cli token remove [options] + +Remove a user-added token from the address book. Official entries cannot be +removed. + +Requires: + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --contract token contract address [optional] + --asset-id TRC10 numeric asset id; provide exactly one of --asset-id or --contract [optional] (TRON only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli token remove --contract TR7... --network nile + wallet-cli token remove --contract 0xA0b8... --network sepolia +``` + +--- + +## 6. tx 组 + +### 6.1 `tx send` —— 转账 ✍️🔒 + +> **本版改动**:新增 gas 四选项与 `--nonce`;回执含 `Nonce`;Fee 行改为 gas 构成。 + +**用法** + +``` +wallet-cli tx send --to (--amount | --raw-amount ) [--token | --contract ] + [--gas-limit ] [--max-fee ] [--priority-fee ] [--nonce ] + [--dry-run | --sign-only | --build-only] [--wait] + [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 转出原生币或 ERC20 代币 | +| EVM 增量 | gas 四项选项;回执含 `Nonce`;Fee 行为 gas 构成 | +| gas 模型 | **由链上偵测:`baseFeePerGas` 字段存在即 EIP-1559,即使值为零**;`NetworkDescriptor.feeModel: "legacy"` 为覆盖用逃生口。**不写 `meta.warnings`** | +| nonce | 默认取 `eth_getTransactionCount(address, "pending")` | +| 错误 | `insufficient_balance`、`execution_reverted`、`nonce_too_low`、`family_mismatch` | + +**Options(EVM 增量;help 中全量列出并标 `(EVM only)`,在 TRON 网络下传入报 `invalid_option`)** + +| Option | 必填 | 默认 | 说明 | +| --- | --- | --- | --- | +| `--gas-limit ` | 否 | 链上估算值 | gas 上限;省略则取节点估算值,**不乘任何倍数**。估算失败时**不回退 21000**,见下 | +| `--max-fee ` | 否 | `base×2 + 建议 tip` | EIP-1559 maxFeePerGas。**低于当前 base fee 时在 `meta.warnings` 给出警告**(与 `--nonce` 同一先例:异常但仍可执行 → warnings,不是错误) | +| `--priority-fee ` | 否 | 节点建议值 | EIP-1559 maxPriorityFeePerGas | +| `--nonce ` | 否 | 链上 pending 值 | 显式指定 nonce;**大于链上 pending 值时交易会一直挂起**(nonce gap),此时在 `meta.warnings` 给出警告 | + +#### 费率旗标:只接受 `gwei` 后缀 + +**`--max-fee 25` 与 `--max-fee 25gwei` 等价**(后缀大小写不敏感);**`wei` / `ether` 等其他单位点名拒绝**,报 `invalid_value` 并说明本旗标读 gwei。两半分开看: + +- **`gwei` 后缀收**——它命名的就是这个旗标本来的单位,不可能改变数值;拒收它只惩罚了从 `cast` 那行复制过来的人,换不到任何安全。 +- **其他单位不收**——九个数量级的风险完全落在 `wei` 与 `ether`:`--max-fee 0.01ether` 与 `--max-fee 25` 差十亿倍,而打错的代价就是实付费用差十亿倍。**点名拒绝而不是默默改读**,让用户知道发生了什么。 + +#### gas 模型判定与费率推导 + +**判定规则:`baseFeePerGas` 字段存在即 EIP-1559,即使值为零。** + +**零基准费仍是 1559**:BSC 的 base fee 恒为 `0x0`——存在但为零。把零当成「没有 1559」会误判整条链,并逼出第二条代码路径;而 1559 的算式在 base=0 时**本来就退化成** legacy 的语义,那条路径没有存在的必要。**侦测结果是事实,不是降级,所以不写 `meta.warnings`。**(`NetworkDescriptor.feeModel` 保留为逃生口:设 `"legacy"` 可强制覆盖,供「回报 baseFee 却拒收 type-2 交易」的链使用。) + +**只给一半费率旗标时的推导**: + +| 给了什么 | 推导 | +| --- | --- | +| 都不给 | `maxFee = base×2 + 建议 tip` | +| 只给 `--max-fee` | tip 取建议值并夹到 `≤ maxFee`;**夹住时发 `meta.warnings`** | +| 只给 `--priority-fee` | `maxFee = base×2 + 该值` | +| legacy 链上给任一个 | `invalid_option` 拒绝,**不默默忽略**(否则回报的内容与实际签出的不符) | + +**两条 `meta.warnings`**(都产生「签得出来也送得出去、但不是用户以为的那样」的交易,而没有任何错误会报这件事): + +| 情况 | 为什么要警告 | +| --- | --- | +| 建议 tip 被夹到 `--max-fee` | 我们替用户改了他给的费率(节点会拒绝 tip > fee cap,所以必须夹),但他不会知道 | +| `--max-fee` 低于当前 base fee | 节点接受,交易就一直躺在那里,直到 base fee 跌下来为止 | + +> **明确给了 `--priority-fee` 就不警告**——那是用户自己的决定,不是替他做的。 + +#### `--gas-limit` 省略时的估算 + +**默认就是估算值,不乘任何倍数。** 乘 1.2 会让 `--dry-run` 显示的最高成本失真,而那个数字的意义就是「真相」。估算真的太紧时,`--gas-limit` 就是明确的手动出口。 + +**估算失败不猜**:不回退 21000——那会签出一笔**注定失败**的 ERC-20 转账并报告一切正常。节点拒绝估算时说的话(余额不足、会 revert)比我们猜的数字有用得多。 + +**估算失败的错误码是节点侧的码,不是 `invalid_option`**:这个 catch 盖住的是节点侧的事实,连端点连不上、HTTP 503、超时都算在内。报 `invalid_option`(**exit 2 —— 「你的命令行有问题」**)会让「重试 exit 1、放弃 exit 2」的调用者对一次暂时性网络故障直接放弃。规则是——本来就有类型的错误**保留自己的码与 exit 类别**(`rpc_error` / `timeout` / …),只在信息后面接上 `--gas-limit` 这条出路;没有类型的异常转成 `rpc_error`(否则会在最上层被 redact 成 `internal_error`,把节点原话一起丢掉,而那句原话正是这个函数不猜的理由)。**破坏性后果,进 release note。** + +**示例与输出** + +```bash +$ wallet-cli tx send --to 0x91b2...4d0e --amount 0.25 --network sepolia --wait +# text 输出(沿用「动词摘要 + 字段独占一行」体例) +✅ Sent 0.25 ETH + To 0x91b2...4d0e + Nonce 42 + TxID 0x9c4e...81af + Block #11,204,113 + Fee 0.000441 ETH (21,000 gas × 21.0 gwei) + Status success +``` + +> **Fee 行格式**:`Fee <数额> <符号> ( gas × gwei)`——TRON 侧 `Fee 1.1 TRX (285 bandwidth)` 的同构写法,**金额是纯数字、括号里放消耗构成**。 + +```bash +# ERC20 转账:gas 消耗显著高于原生转账 +$ wallet-cli tx send --to 0x91b2...4d0e --amount 100 --token USDC --network ethereum --wait +✅ Sent 100 USDC + To 0x91b2...4d0e + Token USDC (0xA0b8...eB48) + Nonce 43 + TxID 0x2f7b...05dc + Block #25,118,904 + Fee 0.001209 ETH (65,000 gas × 18.6 gwei) + Status success +``` + +```bash +$ wallet-cli tx send --to 0x91b2...4d0e --amount 0.25 --network sepolia --dry-run +⏳ Dry run tx send + Fee ≤ 0.00048 ETH (21,000 gas × 22.9 gwei max) + Tx 0x02f86e83aa...57352fc8 +``` + +> **dry-run 回执固定 `Fee` + `Tx` 两行**(沿用现状),不因 EVM 增字段——nonce 要到回执阶段才看。估算与实际共用 `Fee` 这一个字段名,靠 **`≤`** 与 `max` 区分。 +> +> **前缀是 `≤` 而不是 `~`**:`~` 的意思是「大约」——它同时允许实际值**高于**这个数字。而这个数字不是估计值,是**上限**(`gasLimit × 每单位 gas 的上限`),交易签出去之后实际费用不可能超过它。用 `~` 会让一个确定的保证读起来像一个可能失准的猜测,而 dry-run 存在的理由正是「我最多会花多少」。括号里保留 `max`:那修饰的是 gas **单价**——实际结算的单价通常低于它。 +> +> **只有两处例外,且都是「不看就没法判断这笔该不该发」的信息**:`contract send` 在 `approve` 时多出 `Spender` / `Allowance`(§7.2),`contract deploy` 多出 `Address`(§7.3,由 sender + nonce 算出,不需上链)。除这两项外,任何命令都不得往 dry-run 加字段。 + +```bash +$ wallet-cli tx send --to 0x91b2...4d0e --amount 0.25 --network sepolia --wait -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"tx.send","data":{ "kind":"native","stage":"confirmed","txId":"0x9c4e...81af","to":"0x91b2...4d0e","rawAmount":"250000000000000000","nonce":42,"blockNumber":11204113,"confirmed":true,"failed":false,"feeWei":"441000000000000","gasUsed":21000,"effectiveGasPriceWei":"21000000000","maxFeePerGasWei":"22900000000","maxPriorityFeePerGasWei":"1500000000" },"meta":{ "durationMs":14820,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> **`confirmed` 与 `failed` 是两个独立的布尔字段,不是一个状态旗标。** `status: "0x0"` 是「上链、付了 gas、但 revert」——合并成一个旗标,会让一笔 revert 的转账报告成成功。实付费用 `feeWei` 两种情况都回报:**revert 的交易不是免费的**。 +> +> **交易 id 由我们签的内容导出,不是节点指派的**:签名策略回传 `{raw, hash}`,`hash` 是 `keccak256(签名后的字节)`。既有的 `authoritativeTxId` 刻意优先采用本地导出的 id,否则节点回报错误的哈希后,`--wait` 会去轮询别人的交易、再把别人的成功当成你的回执。用 `hash` 这个键让两族共用同一条路径、零分支。 + +**Help 输出** + +> **相对现状**:改动最大:描述族中立并说明 `(TRON only)` / `(EVM only)` 标注含义;Requires 段改为**「只有签名的模式才需要主密码」**(`--dry-run` / `--build-only` 确实不需要);`--to` / `--amount` / `--contract` 描述去 TRON 化;**新增 EVM 四项**(`--gas-limit` / `--max-fee` / `--priority-fee` / `--nonce`);`--build-only` / `--permission-id` / `--expiration` 沿用现状;`--asset-id` / `--fee-limit` / `--permission-id` / `--expiration` 加 `(TRON only)` 标注;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli tx send --help + +Usage: + wallet-cli tx send [options] + +Send the native coin or a token. Flags marked (TRON only) or (EVM only) are accepted +only on networks of that family; using one on the other family is rejected. + +Requires: + the master password only when the selected mode signs — pass --password-stdin then; other modes need no password + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --to recipient address, or a contact name from the address book [required] + --amount amount in whole coins/tokens; mutually exclusive with --raw-amount [optional] + --raw-amount amount in the smallest unit (wei / sun / token base unit) [optional] + --token send this token instead of the native coin, by symbol [optional] + --contract token contract address; alternative to --token [optional] + --asset-id TRC10 numeric asset id; omit with --contract for the native coin [optional] (TRON only) + --fee-limit maximum energy fee to burn, in SUN [optional, default: 100000000] (TRON only) + --permission-id permission group to sign with, for multi-sig accounts [optional] (TRON only) + --expiration transaction expiration window, in milliseconds [optional] (TRON only) + --gas-limit gas cap; estimated from the chain when omitted [optional] (EVM only) + --max-fee EIP-1559 max fee per gas; accepts a unit suffix (25 or 25gwei) [optional] (EVM only) + --priority-fee EIP-1559 max priority fee per gas; accepts a unit suffix [optional] (EVM only) + --nonce transaction nonce; taken from the chain (pending) when omitted [optional] (EVM only) + --dry-run estimate only; do not sign or broadcast [optional, default: false] + --sign-only sign and print the raw transaction; do not broadcast [optional, default: false] + --build-only build an unsigned transaction; do not sign [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --wait after broadcast, poll until the tx is confirmed/failed before returning; default returns the submitted txid without blocking [optional, default: false] + --wait-timeout --wait polling cap, in milliseconds; on timeout return the submitted receipt [optional, default: config.waitTimeoutMs (built-in: 60000)] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli tx send --to T... --amount 1 --network nile + wallet-cli tx send --to 0x742d... --amount 1 --network sepolia + wallet-cli tx send --to T... --token USDT --amount 5 --network nile + wallet-cli tx send --to 0x742d... --token USDC --amount 5 --network sepolia + wallet-cli tx send --to T... --asset-id 1002000 --raw-amount 1000000 --network nile +``` + +> help 一次列全两族的 flag,靠行尾 `(TRON only)` / `(EVM only)` 区分——它不随 `--network` 变化(横切约定)。 + +### 6.2 `tx sign` —— 签名离线交易 🔒 + +> **本版改动**:`--hex` / `--file` 除 TRON protobuf hex 外,也接受 EVM 的 RLP raw tx;新增 chain id 校验。输入形态沿用现状,不改名。 + +**用法** + +``` +wallet-cli tx sign (--hex | --file | --transaction ) [--offline] [--out ] + [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 对本 CLI 之外构造的交易签名,输出可广播的结果 | +| EVM 增量 | `--hex` / `--file` 的内容多一种可接受形态:**RLP 编码 raw tx**(1559 交易以 `0x02` 开头)。`--transaction ` 是 TRON 专属的兼容路径,不接 EVM | +| 校验 | 交易的 family 与网络一致;**EVM 侧另校验交易里携带的 EIP-155 chain id 与目标网络一致** | +| 错误 | `family_mismatch`(交易与网络不同族)、`chain_id_mismatch`(同族但不是同一条链)、`invalid_value` | + +**示例与输出** + +```bash +$ wallet-cli tx sign --hex 0x02f86e83aa36a72a... --network sepolia +✅ Signed send + Address 0x7a3f...c19b + TxID 0x9c4e...81af + Raw tx 0x02f8b1...6f2a41 +``` + +> **EVM 侧这一行是 `Raw tx`,不是 `Signature`**:签名已经嵌在 RLP 里,输出的是一整笔可直接广播的 typed transaction(`0x02…`),下一步原样贴给 `tx broadcast --hex`。TRON 侧的签名交易是 protobuf hex,字段名同样按 family 分派。**`0x` 前缀带在输出里**,与 `--hex` 的输入形式一致,复制即可用;长 hex 走 `--out` 写文件、再用 `--file` 接力。 +> +> 贴入另一族的交易报 `family_mismatch`——EVM RLP 与 TRON protobuf hex 外观相近,误贴概率高,不落到通用的 `invalid_value`。 +> +> **交易里携带的 chain id 必须与目标网络一致**:EIP-155 的 chainId 就编码在 RLP 里,签名前解出来与 `--network` 的 `chainId` 比对,不符报 `chain_id_mismatch`。这一条挡的是同族跨链——贴一笔 `chainId=1` 的主网交易、却选了 `sepolia`,`family_mismatch` 不会触发,而签出来的是一笔真实的主网交易。业内(`cast`、ethers)一律以交易自带的 chainId 为准并做校验。 + +**Help 输出** + +> **相对现状**:描述改为一句人话的「必须是为所选网络构建的交易,否则签名前就拒绝」;**输入形态沿用现状**(`--hex` / `--file` / `--transaction` 三选一,`--offline` / `--out` 不变),仅给 `--hex` / `--file` 的说明加上 EVM 形态、`--transaction` 标 `(TRON only)`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli tx sign --help + +Usage: + wallet-cli tx sign [options] + +Sign a transaction that was built elsewhere and output the signed result; +broadcast it later with `tx broadcast`. The transaction must have been built for +the network you select — one built for another chain is rejected before it is +signed, so you cannot sign a mainnet transaction by mistake. + +Requires: + the master password — pass --password-stdin; this command never prompts + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + Exactly one of these — the transaction to sign: + --hex transaction hex: protobuf hex for TRON, RLP for EVM + --file file containing the transaction hex + --transaction unsigned transaction JSON; compatibility path, never checked online (TRON only) + + --offline sign locally without contacting the node; only with --hex/--file [optional, default: false] + --out write the signed hex to a file instead of stdout [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli tx sign --transaction '{"txID":"...","raw_data":{...},"raw_data_hex":"..."}' + wallet-cli tx sign --file unsigned.hex --out signed.hex --network nile --password-stdin + wallet-cli tx sign --file unsigned.hex --out signed.hex --network sepolia --password-stdin + wallet-cli tx sign --file partially-signed.hex --offline --password-stdin +``` + +> **`tx sign` 拒收已签名的交易**:EVM 一笔交易只吃一个签名,重签会产出「换了签名的另一笔交易」——回一个 `invalid_transaction` 比默默产出一个不同的东西诚实。 +> +> **`--transaction` / `--tx-stdin` 是 TRON 专属路径**,在 EVM 网络上明确拒绝而非静默忽略:`--transaction` 报「本命令的 tron 选项」,把 payload 灌进 `--tx-stdin` 也由「静默忽略」变成明确拒绝。 + +### 6.3 `tx broadcast` —— 广播已签名交易 ✍️ + +> **本版改动**:走 `eth_sendRawTransaction`;`--hex` / `--file` 多接受 RLP raw tx,新增 chain id 校验。输入形态沿用现状,不改名。 + +**用法** + +``` +wallet-cli tx broadcast (--hex | --file | --transaction | --tx-stdin) + [--dry-run] [--wait] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 广播已签名交易 | +| EVM 增量 | 走 `eth_sendRawTransaction`;`--hex` / `--file` 的内容多一种可接受形态:RLP raw tx。`--transaction` / `--tx-stdin`(JSON)是 TRON 专属 | +| 校验 | 同 §6.2:family 一致 + **EIP-155 chain id 与目标网络一致**,两道都在发出请求前做 | +| `--dry-run` | **本版新增**,两族语义不同(见下);`--dry-run --wait` 报 `invalid_option`(与 TRON 一致) | +| 错误 | `family_mismatch`、`chain_id_mismatch`、`nonce_too_low`、`insufficient_balance`、`rpc_error`,以及下列**广播拒绝码** | + +#### 广播的接受判断是白名单 + +**只有 32 字节哈希算成功,其余一律拒绝。** 这直接沿用 TRON 侧的教训:先前 `res.result === false` 的黑名单判断从未触发,因为被拒绝的回应根本没有 `result` 字段——结果**每一笔被拒交易都被报成 submitted**。 + +**例外:`already known` 判为成功**——交易已在 mempool,用户的意图已达成,重跑同一个指令不该把既成事实报成失败(回应带 `alreadyKnown: true`)。 + +节点的拒绝信息经映射表转成**稳定的错误码**——`nonce_too_high` / `replacement_underpriced` / `gas_too_low` / `fee_too_low` / `gas_limit_exceeded`;认不出来的才保留节点原话于 `transaction_rejected`。没有这组码,调用者只能比对节点的英文句子,而各家客户端的措辞不同。 + +#### `--dry-run`(本版新增) + +「这笔已签名的交易送得出去吗」是一个**在送出去之前**该能问的问题,而 TRON 侧早就能问(多签门槛是否凑齐)。EVM 没有多签,但有三件事会挡下一笔已签名的交易:链不对、nonce 用过了、余额不够——所以做的是同一件事的 EVM 版本。 + +| family | 检查项 | +| --- | --- | +| TRON | 签名、门槛、过期、动态多签费 | +| EVM | 回报 `checks` 四项:`signature`(回推签名者)、`chainId`、`nonce`(太低直接失败;有 gap 给警告)、`balance`(不足直接失败) | + +**节点读取是 best-effort**:端点不可达时把 `nonce` / `balance` 两项降级为 `skipped` 并发 warning,而**不是**让命令失败——跑不到节点的 dry run 仍比没有 dry run 有价值,而报「不能广播」会是一个这段代码**并未建立**的宣称。 + +**示例与输出** + +```bash +$ wallet-cli tx broadcast --hex 0x02f8b183aa36a72a... --network sepolia --wait +✅ Broadcast + TxID 0x9c4e...81af + Block #11,204,113 + Fee 0.000441 ETH (21,000 gas × 21.0 gwei) + Status success +``` + +**Help 输出** + +> **相对现状**:描述改为一句人话的「必须是为所选网络构建的交易,否则发送前就拒绝」;**输入形态沿用现状**(`--hex` / `--file` / `--transaction` / `--tx-stdin` 四选一),仅给 `--hex` / `--file` 的说明加上 EVM 形态、JSON 两项标 `(TRON only)`;**新增 `--dry-run`**;`--network` 示例值;Examples 两族对称。 +> +> ⚠️ **实作的 `--dry-run` 描述目前只写了 TRON 语义**(`validate signatures, threshold, expiration, and dynamic multi-sign fee`),未涵盖 EVM 的四项 `checks`——该文案需补,属 help 文案层待办。 + +```text +$ wallet-cli tx broadcast --help + +Usage: + wallet-cli tx broadcast [options] + +Broadcast an already-signed transaction. It must have been built for the network +you select — one built for another chain is rejected before it is sent. + +Options: + Exactly one of these — the signed transaction to broadcast: + --hex signed transaction hex: protobuf hex for TRON, RLP for EVM + --file file containing the signed transaction hex + --transaction signed transaction JSON (TRON only) + --tx-stdin read the signed transaction JSON from stdin (fd 0) (TRON only) + + --dry-run validate signatures, threshold, expiration, and dynamic multi-sign fee without broadcasting [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --wait after broadcast, poll until the tx is confirmed/failed before returning; default returns the submitted txid without blocking [optional, default: false] + --wait-timeout --wait polling cap, in milliseconds; on timeout return the submitted receipt [optional, default: config.waitTimeoutMs (built-in: 60000)] + +Examples: + wallet-cli tx broadcast --file signed.hex --network nile + wallet-cli tx broadcast --file signed.hex --network sepolia + wallet-cli tx broadcast --tx-stdin < signed.json --network nile +``` + +### 6.4 `tx status` —— 交易状态 + +> **本版改动**:收到 receipt 即判终态;四态枚举与 TRON 侧一致,不新增状态词;新增 `Confirmations` 行(两族同时生效)。 + +**用法** + +``` +wallet-cli tx status --txid <0x…> [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 查询交易确认状态 | +| EVM 增量 | 收到 receipt 即判定终态:`status=1` → confirmed、`status=0` → failed;无 receipt → pending;查不到 → not_found | +| 状态枚举 | 与 TRON 侧同为四态,不新增状态词 | +| 新增字段 | `Confirmations`(head block − 该交易所在区块),confirmed 时才出现;两族同时生效,非 EVM 专属。`--wait` 只等到 receipt,等几个确认由用户读这个数自己判断(§6.5) | +| 错误 | `rpc_error`(含端点限流 429)——**查不到交易不是错误**,是 `not_found` 这个状态,退出码仍为 0 | +| `not_found` 的警告 | **该状态一律附一条 `meta.warnings`**:公开节点常剪枝,这可能表示节点没有记录,而非交易不存在;建议改用归档节点 | + +> **为什么 `not_found` 必须带警告**:它是这条命令**唯一可能说错过去**的答案。一笔真的上链过的交易,在一个剪枝过的公开端点上一样回 null——而一句光秃秃的「not found」会让读者得出「它从没发生过」的结论。 +> +> 实作上并用 `eth_getTransactionByHash` 与 `eth_getTransactionReceipt` 才能分辨「在 mempool」与「从不存在」——收据对两者都回 null。与 TRON 并用 `getTransactionById` 的模式相同。 + +**示例与输出** + +```bash +$ wallet-cli tx status --txid 0x9c4e...81af --network sepolia +TxID 0x9c4e...81af +Status confirmed ✅ +Block #11,204,113 +Confirmations 36 +``` + +```bash +$ wallet-cli tx status --txid 0x9c4e...81af --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"tx.status","data":{ "txid":"0x9c4e...81af","state":"confirmed","blockNumber":11204113,"confirmations":36 },"meta":{ "durationMs":190,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +**Help 输出** + +> **相对现状**:`--txid` 描述去掉 `TRON`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli tx status --help + +Usage: + wallet-cli tx status [options] + +Show confirmation status of a transaction + +Options: + --txid transaction id/hash [required] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli tx status --txid abc123 --network nile + wallet-cli tx status --txid 0x9c4e... --network sepolia +``` + +### 6.5 `tx info` —— 交易详情 + +> **本版改动**:新增 `Confirmations` 行;Fee 行为 gas 构成,无 TRON 的资源分项。 + +**用法** + +``` +wallet-cli tx info --txid <0x…> [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 交易详情与回执 | +| EVM 增量 | 增 `Confirmations` 行;Fee 行为 gas 构成;无 TRON 的资源分项 | +| 查不到 txid | 与 `tx status` 不同——`tx info` 要给的是**详情**,无详情可给时报 `not_found`(退出码 1),不返回空壳。想区分「尚未上链」与「不存在」用 `tx status` | +| 错误 | `not_found`(该 txid 无交易详情)、`rpc_error`(含端点限流 429) | + +**示例与输出** + +```bash +$ wallet-cli tx info --txid 0x9c4e...81af --network sepolia +TxID 0x9c4e...81af +Type transfer +From 0x7a3f...c19b +To 0x91b2...4d0e +Amount 0.25 ETH +Nonce 42 +Block #11,204,113 +Block time 2026-08-06 09:14:32 UTC +Confirmations 36 +Fee 0.000441 ETH (21,000 gas × 21.0 gwei) +Status success +``` + +```bash +$ wallet-cli tx info --txid 0x9c4e...81af --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"tx.info","data":{ "txid":"0x9c4e...81af","type":"transfer","from":"0x7a3f...c19b","to":"0x91b2...4d0e","rawAmount":"250000000000000000","nonce":42,"blockNumber":11204113,"blockTime":1786007672,"confirmations":36,"feeWei":"441000000000000","gasUsed":21000,"effectiveGasPriceWei":"21000000000","status":"success" },"meta":{ "durationMs":240,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> `blockTime` 是 Unix 秒(与 `chain node` 的 `headBlock.timestamp` 同口径),text 才格式化为 `YYYY-MM-DD HH:MM:SS UTC`;费用三项(`feeWei` / `gasUsed` / `effectiveGasPriceWei`)与 `tx send` 回执同名同义,text 的 Fee 行由它们合成。 + +#### json 另带两个透传的原始对象 + +上面十三个扁平键**都在**,另外多带 **`transaction` 与 `receipt`** 两个对象,**节点原话全带**。 + +`tx info` 是**排查用**的命令,而我们挑出来的十几个字段不可能涵盖每一次排查需要的东西——access list、logs、`v/r/s`、`type` 的原始值、`maxFeePerGas`……透传让用户不必为了一个字段改用 `cast`;扁平键则让常见的九成不必自己从原始对象里挖。两者不互斥,代价只是 payload 大一些。 + +> **TRON 侧早就这么做**——`tx info` 一直带 `transaction` 与 `info` 两个原始对象。EVM 沿用同一个形状,而不是自创一个。 + +#### `type` 的取值(本版定义为三个) + +| 值 | 含义 | +| --- | --- | +| `transfer` | 原生转账,**或解得出的 ERC-20 `transfer`** | +| `contract-creation` | `to` 为 null | +| `contract-call` | 其余 | + +**三个取值刻意粗**:再细就得去读 calldata 的方法名,而那正是下面决定不做的事。`contract-creation` 不是猜的——`to` 为 null 就是它成为部署的定义。 + +#### calldata:只解 ERC-20 `transfer`,其余照实回报 + +**只解 `transfer(address,uint256)` 这一个选择器**,输出对齐 TRON 对 TRC20 的既有字段(`contract` + `symbol` + 以该代币 decimals 换算的 `amount`);代币 metadata 读不到时退回 base unit 数量。**其余 calldata 一律不解。** + +**为什么必须解这一个**:一笔 ERC-20 转账的原始交易里,`to` 是**合约**、`value` 是 **0**,真正的收款人与金额在 calldata。照实回报等于**指错收款人**——而这正是 TRON 侧对 TRC20 早已避免的事。 + +**为什么只解这一个**:猜测未知调用的语义,等于发明签名没有承载的意义。`transfer` 之所以例外,是因为它的形状是 ERC-20 标准的一部分,不是猜的。 + +> **与 `contract send` 的 `approve` 回执(§7.2)不冲突**:那里**没有在解码**——调用者自己打了 `--method "approve(address,uint256)"` 与参数,意义是他说出来的。这里面对的是一串没人交代过形状的 calldata。**同一条界线的两侧。** + +> **`Status` 一律小写**:`tx info` 现状输出大写 `SUCCESS`,而 `tx status`(§6.4)与各写命令回执给的是小写 `success` / `confirmed`。同一个字段名在同一 CLI 里出现两种大小写,agent 侧要写两套匹配。本版一并收敛为小写,TRON 侧同步。 + +> `--wait` 只等到 receipt,不额外等 N 个确认——等多少个是场景决定,交由用户读 `Confirmations` 自行判断。重组风险的静态说明进 help,不进输出。`--wait` 超时的语义见 §6.1。 + +**Help 输出** + +> **相对现状**:`--txid` 描述去掉 `TRON`;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli tx info --help + +Usage: + wallet-cli tx info [options] + +Show full transaction detail + receipt + +Options: + --txid transaction id/hash [required] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli tx info --txid abc123 --network nile + wallet-cli tx info --txid 0x9c4e... --network sepolia +``` + +--- + +## 7. contract 组 + +既有实现显式传函数签名与参数,**不读链上 ABI**,EVM 侧直接复用。 + +### 7.1 `contract call` —— 只读调用 + +> **本版改动**:走 `eth_call`;**不依赖链上 ABI**,函数签名与参数类型显式传。 + +**用法** + +``` +wallet-cli contract call --contract <0x…> --method [--params ] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 只读调用(不上链、不花费) | +| EVM 增量 | 走 `eth_call`;合约地址收 `0x…` | +| 错误 | `execution_reverted`、`invalid_address`、`invalid_value` | + +**示例与输出** + +```bash +$ wallet-cli contract call --contract 0xA0b8...eB48 --method "balanceOf(address)" \ + --params '[{"type":"address","value":"0x7a3f...c19b"}]' --network sepolia +Method balanceOf(address) +Result 0x00000000000000000000000000000000000000000000000000000000950f9ac0 (raw) +``` + +> **`Result` 是原始 hex,不解码**,渲染层在值后标 `(raw)`。 +> +> **为什么不解码**:`--method "balanceOf(address)"` 只声明**入参**类型,**不带返回类型**——没有 ABI 就无从解码。要解就得猜,而猜错的方式很多:`uint256` 与 `int256`、`address` 与 `bytes20`、多返回值的边界。TRON 侧现状就是回原始 hex,**两族一致而不是各自为政**。要解码就得先有一个声明返回类型的旗标(`--returns` 之类),而本版不提供那个旗标,所以也不解码。 +> +> **没有 `Contract` 列**:合约地址是命令行**刚敲过的输入**,回显它不增加信息(与 dry-run 不放 `Contract` 是同一个理由)。text 只有 `Method` / `Result` 两行。 + +**Help 输出** + +> **相对现状**:描述去掉 `triggerConstantContract`(§10.1 规则 3)并说明不查 ABI;`--contract` 去 TRON 化;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli contract call --help + +Usage: + wallet-cli contract call [options] + +Read-only contract call. The function signature and parameter types are supplied +explicitly; no ABI lookup is performed. + +Options: + --contract contract address [required] + --method function signature, e.g. balanceOf(address) [required] + --params JSON array of ABI parameters as {type,value}; omit to pass no parameters [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli contract call --contract TR7... --method "balanceOf(address)" --params '[{"type":"address","value":"T..."}]' --network nile + wallet-cli contract call --contract 0xA0b8... --method "balanceOf(address)" --params '[{"type":"address","value":"0x742d..."}]' --network sepolia +``` + +### 7.2 `contract send` —— 状态变更调用 ✍️🔒 + +> **本版改动**:gas 选项同 `tx send`;**`approve` 特例显示授权额度**,无限授权标 `unlimited`——**该特例本版起两族通用,不再是 EVM 增量**。 + +**用法** + +``` +wallet-cli contract send --contract --method [--params ] [--value ] + [--gas-limit ] [--max-fee ] [--priority-fee ] [--nonce ] + [--dry-run | --sign-only | --build-only] [--wait] + [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 发起会改变链上状态的合约调用 | +| EVM 增量 | gas 选项同 §6.1 | +| `--value` 取代 `--call-value-sun` | 随调用附带的原生币改用**族中立、人话单位**的 `--value`(TRX / ETH)。现状的 `--call-value-sun`(最小单位 SUN)名字里带 TRON 单位,EVM 上不成立,**本版保留为 TRON 兼容别名并标弃用、下一版删**;两者同时传报 `invalid_option` | +| `approve` 特例 | **两族通用**。`--method` 为 `approve(address,uint256)` 时,回执与 `--dry-run` 额外给 `Spender` / `Allowance` 两行;额度按代币 decimals 换算为人话单位,`2^256-1` 显示为 `unlimited` | + +> **`approve` 回执两族都有,不是 EVM 增量。** TRC20 与 ERC-20 **共用这个方法、共用这个危险、也共用那个没人读得懂的参数**——一个 `uint256`,按代币 decimals 缩放,最大值 78 位数。而 approve 是这两条链上最常让人损失资金的一次签名。只在 EVM 上把它翻译成人话,等于认定 TRON 用户比较不需要看懂自己批准了多少。 +> +> 解码逻辑为两族共用,差别只有两处:**spender 地址怎么写**(TRON 的 41-hex 转 base58,故 TRON 侧示例用 base58)、**decimals 从哪来**(`getTokenInfo` vs `getErc20Metadata`)。 +> +> **这不违反 §6.5「不猜 calldata」那条界线**:那里拒绝的是**猜别人交易的意义**;这里调用者自己打了 `--method "approve(address,uint256)"` 与参数,**意义是他说出来的**,我们只做单位换算。同一条界线的两侧。 +> +> **`unlimited` 在读 metadata 之前就短路**:78 位数的形式只告诉读者「这个数字很长」,再多的 decimals 也救不了它,没有必要为此向合约发一次请求。decimals 读不到则退回原始整数——我们标不出单位,不影响这笔授权本身。 +| 差异 | TRON 的 `--fee-limit`(能量模型)对应 EVM 的 `--gas-limit`;两者在 help 中并列、各标 `(TRON only)` / `(EVM only)`,用错族报 `invalid_option` | +| 错误 | `execution_reverted`(含节点返回的 revert reason)、`insufficient_balance`、`nonce_too_low` | + +**示例与输出** + +```bash +$ wallet-cli contract send --contract 0xA0b8...eB48 --method "approve(address,uint256)" \ + --params '[{"type":"address","value":"0x4f2a...9b03"},{"type":"uint256","value":"1000000"}]' \ + --network ethereum --wait +✅ Called approve + Contract 0xA0b8...eB48 + Spender 0x4f2a...9b03 + Allowance 1 USDC + Nonce 44 + TxID 0x81de...92c7 + Block #25,118,940 + Fee 0.000892 ETH (46,200 gas × 19.3 gwei) + Status success +``` + +```bash +# 无限授权:额度显示为 unlimited,不显示那串 78 位数字 +$ wallet-cli contract send --contract 0xA0b8...eB48 --method "approve(address,uint256)" \ + --params '[{"type":"address","value":"0x4f2a...9b03"},{"type":"uint256","value":"115792089237316195423570985008687907853269984665640564039457584007913129639935"}]' \ + --network ethereum --dry-run +⏳ Dry run contract send + Spender 0x4f2a...9b03 + Allowance unlimited + Fee ≤ 0.00091 ETH (46,200 gas × 19.7 gwei max) + Tx 0x02f8b183aa...4c91e7a0 +``` + +> dry-run 沿用 `Fee` + `Tx` 两行,只为 `approve` 多出 `Spender` / `Allowance`——**`Contract` 与 `Nonce` 不进 dry-run**:合约地址是命令行刚敲过的输入,nonce 到回执阶段再看不迟(§6.1)。`Allowance` 必须进,因为它是命令行传的那串 `uint256` 按 decimals 换算后的结果,用户没法心算,而这正是 dry-run 要替他确认的东西。 + +**Help 输出** + +> **相对现状**:描述改写,补 `(TRON only)` / `(EVM only)` 标注含义与 `approve` 特例说明;Requires 冠词统一;**新增族中立的 `--value`(人话单位),`--call-value-sun` 保留为标了弃用的 TRON 别名**;新增 EVM 四项;`--build-only` / `--permission-id` 沿用现状;`--fee-limit` 等加 `(TRON only)` 标注;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli contract send --help + +Usage: + wallet-cli contract send [options] + +State-changing contract call. Flags marked (TRON only) or (EVM only) are accepted only +on networks of that family; using one on the other family is rejected. For +approve(address,uint256) the receipt also reports the spender and the allowance in +human units; an allowance of 2^256-1 is shown as unlimited. + +Requires: + the master password only when the selected mode signs — pass --password-stdin then; other modes need no password + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --contract contract address [required] + --method function signature, e.g. transfer(address,uint256) [required] + --params JSON array of ABI parameters as {type,value} [optional] + --value native coin sent with the call, in whole coins [optional, default: 0] + --call-value-sun deprecated alias for --value, in SUN; removed next release [optional] (TRON only) + --fee-limit maximum energy fee to burn, in SUN [optional, default: 100000000] (TRON only) + --permission-id permission group to sign with, for multi-sig accounts [optional] (TRON only) + --gas-limit gas cap; estimated from the chain when omitted [optional] (EVM only) + --max-fee EIP-1559 max fee per gas; accepts a unit suffix (25 or 25gwei) [optional] (EVM only) + --priority-fee EIP-1559 max priority fee per gas; accepts a unit suffix [optional] (EVM only) + --nonce transaction nonce; taken from the chain (pending) when omitted [optional] (EVM only) + --dry-run estimate only; do not sign or broadcast [optional, default: false] + --sign-only sign and print the raw transaction; do not broadcast [optional, default: false] + --build-only build an unsigned transaction; do not sign [optional, default: false] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --wait after broadcast, poll until the tx is confirmed/failed before returning; default returns the submitted txid without blocking [optional, default: false] + --wait-timeout --wait polling cap, in milliseconds; on timeout return the submitted receipt [optional, default: config.waitTimeoutMs (built-in: 60000)] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli contract send --contract TR7... --method "transfer(address,uint256)" --params '[...]' --network nile + wallet-cli contract send --contract 0xA0b8... --method "transfer(address,uint256)" --params '[...]' --network sepolia +``` + +### 7.3 `contract deploy` —— 部署合约 ✍️🔒 + +> **本版改动**:EVM 侧合约地址由 sender + nonce 确定性算出,构建期即给出,不必等上链;**构造参数改为三来源,旗标改名且不保留旧别名**。 + +**用法** + +``` +wallet-cli contract deploy (--artifact | --code | --code-file ) + [--constructor-args | --constructor-params ] + [--constructor-signature ] [--abi ] + [--gas-limit ] [--max-fee ] [--priority-fee ] [--nonce ] + [--fee-limit ] [--permission-id ] [--expiration ] + [--dry-run | --sign-only | --build-only] [--wait] + [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 部署合约字节码,回执给出新合约地址 | +| EVM 增量 | 地址由 `keccak(rlp([sender, nonce]))` 取后 20 字节**确定性算出**,构建期即可给出、不必等上链 | +| 差异 | TRON 的 `--fee-limit`(能量)对应 EVM 的 `--gas-limit`,两者在 help 中并列、各标 family;**EVM 不需要 ABI,类型由构造函数参数的来源提供** | +| 错误 | `execution_reverted`(构造函数回滚)、`insufficient_balance`、`invalid_value`(字节码非法 hex / 在 TRON 上传 `--constructor-signature`)、`invalid_option`(在 EVM 上传 `--permission-id` / `--expiration`) | + +> **deploy 属第二档**:`contract call` / `send` 已把 ABI 参数编码与 gas 估算做完,deploy 的净增量只有「构造参数编码 + 地址推算 + 字节码输入通道」;EVM 是合约生态、部署是基本诉求(`forge create` / `cast send --create` 都是标配),TRON 侧 `contract deploy` 亦早已存在。 +> +> `--code-file` 沿用 TRON 侧的既有理由:字节码常达上万字符,塞不进命令行。 + +#### 旗标改名(破坏性变更,不保留旧别名) + +| 旧名 | 新名 | 备注 | +| --- | --- | --- | +| `--bytecode` | `--code` | 旧名**直接消失**,无别名 | +| `--params` | `--constructor-params` | 旧名**直接消失**,无别名 | +| `--abi` | `--abi` | **保留**,标 `(TRON only)`,`[required]` **unless `--artifact`** | + +**不保留旧别名是明确的决定**:旧名字留着会让两套词汇并存,且 help 里必须同时出现两套;deploy 的调用量本身很低,脚本改一行的成本远小于长期双词汇。**破坏性后果进 release note。** + +**另两项连带决定**: + +- **deploy 不提供 `--call-value`**(用法行本来就没列)。提供一个实作会忽略的旗标,比不提供更糟——调用者会以为值生效了。 +- **`--permission-id` / `--expiration` 移进 TRON binding**,因此在 EVM 上由「静默接受并忽略」变成 `invalid_option` / exit 2。 + +#### 构造函数参数的三种类型来源 + +> **设计原则:类型来自签名或编译器产物,永不来自值。** + +`constructor(uint128)` 误写成 `uint256`,两族都会编码成功、部署出一个参数错误的合约——而**部署不可逆**。 + +| 来源 | 适用 | 说明 | +| --- | --- | --- | +| `--artifact ` | **两族** | 编译器产物(Foundry / Hardhat / sunhat / TronBox),同时含 `abi` 与 `bytecode`。**首选** | +| `--constructor-signature ` | **仅 EVM** | 只有 bytecode 时用签名字符串,如 `constructor(uint256,string)`。在 TRON 上**明确拒绝**(`invalid_value`,信息说明 TronWeb 需要完整 ABI),不静默忽略 | +| `--abi ` | **仅 TRON** | 完整 ABI JSON。`--artifact` 已供 ABI 时可省 | + +参数值本身走 `--constructor-args`(**裸值 JSON 数组**,如 `["18","MyToken"]`);`--constructor-params`(`{type,value}` 形式)**保留可用**,help 中降为次选。 + +**为什么 TRON 必须有 ABI**:TronWeb 的 `createSmartContract` 靠 ABI 推导 constructor 类型,`parameters` 只吃裸值;ethers 不需要 ABI。要让 `--abi` 在 TRON 上也可省略,唯一的做法是从 `{type,value}` 的内嵌类型**合成**一份 ABI 喂给 TronWeb——而合成出来的 ABI **没有任何东西可以校验**:用户把类型打错时 TronWeb 会照着错的类型编码成功,事前无从发现。 + +**`--artifact` 是更强的来源,不是放宽**:`--abi` 因 `--artifact` 而变成「required unless `--artifact`」,看似放宽了上一段的要求,实则相反——上一段反对的是「从内嵌类型**合成**一份无法校验的 ABI」,而 `--artifact` 提供的是**编译器输出的真 ABI**,比人手贴上的更可信(连手写 ABI 的打字错误都排除了)。等于换一个更强的来源满足同一个要求。 + +**业界形状**:`forge create --constructor-args`(类型来自编译产物)、`cast send --create `(只有 bytecode 时用签名字符串)。**没有任何主流工具要求用户写 `[{"type":"uint256","value":"42"}]`**——那是 TronWeb `triggerSmartContract` 的内部 JSON 形状漏到了 CLI 表面。 + +**`--artifact` 对 TRON 用户收益最大**:`--abi` 必填逼他们自己从那份 JSON 里挖出动辄数 KB 的 ABI 贴到命令行,那是**转抄,不是输入**。Foundry / Hardhat / sunhat / TronBox 的产物都同时含 `abi` 与 `bytecode`,只有 bytecode 的包装差一层(Foundry 是 `{object}`,其余是字符串),两种都收。 + +> **实测**:五种输入形式产生的 calldata **逐字节相同**,且与 `cast abi-encode` 的输出一致(独立实现,非自我一致性检查);Sepolia 与 Nile 各实际部署并回读成功,预测的 CREATE 地址与回执逐字符相同。 + +**示例与输出** + +```bash +$ wallet-cli contract deploy --artifact ./out/Token.sol/Token.json \ + --constructor-args '["18","MyToken"]' --network sepolia --wait +✅ Contract deployed + Address 0x5d71...a3f4 + Nonce 45 + TxID 0xb2c8...71fe + Block #11,204,301 + Fee 0.008408 ETH (1,204,551 gas × 6.98 gwei) + Status success +``` + +```bash +$ wallet-cli contract deploy --artifact ./out/Token.sol/Token.json --network sepolia --wait -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"contract.deploy","data":{ "stage":"confirmed","contractAddress":"0x5d71...a3f4","txId":"0xb2c8...71fe","nonce":45,"blockNumber":11204301,"confirmed":true,"failed":false,"feeWei":"8407765980000000","gasUsed":1204551,"effectiveGasPriceWei":"6980000000" },"meta":{ "durationMs":16420,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> `contractAddress` 在 `--dry-run` / `--sign-only` / submitted 三个阶段都给,值不变(由 sender + nonce 算出);`stage` 随阶段取 `estimated` / `signed` / `submitted` / `confirmed`,与 `tx send` 同一枚举。 + +```bash +$ wallet-cli contract deploy --code-file ./Token.bin --network sepolia --dry-run +⏳ Dry run contract deploy + Address 0x5d71...a3f4 + Fee ≤ 0.008926 ETH (1,204,551 gas × 7.41 gwei max) + Tx 0x02f9049a83aa...b7c0e215 +``` + +> `Address` 在 `--dry-run` / `--sign-only` 阶段同样给出——它只取决于发送方与 nonce,不需要上链。这与 TRON 侧「build 期确定性算出、submitted 就带」的处理一致;它是 dry-run 的两个例外之一(另一个是 `approve` 的额度,§7.2)。**但前提是 nonce 不变**:若该 nonce 被另一笔交易抢先占用,实际地址会不同,这句说明进 help、不进字段。 +> +> 以上 `Address` / `TxID` / `Fee` 等示例值为**设计稿,未实测**(本节的实测结论见上文「实测」一段)。 + +**Help 输出** + +> **相对现状**:描述改写,补 family 标注含义并说明地址在上链前即可给出;**`--bytecode` / `--params` 改名为 `--code` / `--code-file` / `--constructor-params`,不保留旧别名**;**新增 `--artifact` / `--constructor-args` / `--constructor-signature` 三个来源旗标**;`--abi` 保留、标 `(TRON only)`、`required unless --artifact`;`--fee-limit` 由 `[required]` 变 `[optional]` 并标 family;新增 EVM 四项;Requires 段改为「只有签名的模式才需要主密码」;Examples 两族对称,且首选形式用 `--artifact`。 + +```text +$ wallet-cli contract deploy --help + +Usage: + wallet-cli contract deploy [options] + +Deploy contract creation bytecode and report the new contract's address. +Flags marked (TRON only) or (EVM only) are accepted only on networks of that family; using one on the other family is rejected. + +Requires: + the master password only when the selected mode signs — pass --password-stdin then; other modes need no password + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --artifact path to a compiler artifact (Foundry, Hardhat/sunhat, TronBox) holding both the bytecode and the ABI; the preferred source, because the constructor's types then come from the compiler [optional] + --code contract creation bytecode, hex-encoded; provide exactly one of --artifact, --code or --code-file [optional] + --code-file path to a file holding the creation bytecode; bytecode often exceeds the shell's argument limit [optional] + --constructor-signature the constructor's types when there is no ABI, e.g. "constructor(uint256,string)"; not needed with --artifact, and not accepted on TRON, which needs the full ABI [optional] + --constructor-args constructor arguments as a JSON array of bare values, e.g. ["18","MyToken"]; the types come from --artifact, --constructor-signature, or --abi on TRON [optional] + --constructor-params constructor arguments as a JSON array of {type,value} entries, e.g. [{"type":"uint8","value":"18"}]; prefer --constructor-args with --artifact [optional] + --dry-run build and estimate only, with no signature and no broadcast [optional, default: false] + --sign-only sign and output complete transaction hex without broadcasting [optional, default: false] + --build-only build an unsigned transaction without signing or broadcasting; mutually exclusive with --dry-run/--sign-only [optional, default: false] + --abi contract ABI as a JSON array string; required unless --artifact supplies one [optional] (TRON only) + --fee-limit maximum energy fee to burn, in SUN [optional, default: 100000000] (TRON only) + --permission-id TRON permission group to sign with (0=owner, 1=witness, 2-9=active) [optional, default: 0] (TRON only) + --expiration transaction expiration in ms, up to 86400000 (24h); only with --sign-only or --build-only; omitted = node default (~60s) [optional] (TRON only) + --gas-limit gas units to authorise; defaults to the node's estimate, unpadded [optional] (EVM only) + --max-fee maximum total fee per gas, in gwei — 25 or 25gwei (EIP-1559 only) [optional] (EVM only) + --priority-fee tip per gas, in gwei — 25 or 25gwei (EIP-1559 only) [optional] (EVM only) + --nonce transaction nonce; defaults to the account's pending nonce [optional] (EVM only) + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --wait after submitting, poll until the transaction is confirmed/failed before returning; default returns the submitted receipt without blocking [optional, default: false] + --wait-timeout --wait polling cap, in milliseconds; on timeout return the submitted receipt [optional, default: config.waitTimeoutMs (built-in: 60000)] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli contract deploy --artifact ./build/contracts/Token.json --constructor-args '["18","MyToken"]' --network nile + wallet-cli contract deploy --artifact ./out/Token.sol/Token.json --constructor-args '["18","MyToken"]' --network sepolia + wallet-cli contract deploy --code-file ./Token.bin --constructor-signature 'constructor(uint8,string)' --constructor-args '["18","MyToken"]' --network sepolia +``` + +--- + +## 8. 签名组 + +两条命令的服务层已是 family 无关的,哈希算法在 signer 层按 family 分派。 + +### 8.1 `message sign` —— 签名任意消息 🔒 + +> **本版改动**:算法按 family 分派:EVM 用 EIP-191 前缀,TRON 用 TIP-191。 + +**用法** + +``` +wallet-cli message sign (--message | --message-stdin) [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 对任意消息签名 | +| 算法 | TRON = TIP-191(前缀 `\x19TRON Signed Message:\n`);EVM = EIP-191(前缀 `\x19Ethereum Signed Message:\n`) | +| 输出契约 | 不变(地址、摘要、签名) | +| stdin 通道 | `--message-stdin` 与 `--password-stdin` **不能同时用**——一次运行只有一个 `*-stdin` 能占用 fd 0;用 `--message-stdin` 时主密码须走 TTY | + +**示例与输出** + +```bash +$ wallet-cli message sign --message "hello" --network sepolia +Address 0x7a3f...c19b +Digest 0x50b2...ce31 +Signature 0x4c8f...1b1c +``` + +**Help 输出** + +> **相对现状**:描述改写为按 family 说前缀、TRON 在前;Requires 冠词统一;`--message-stdin` 沿用现状;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli message sign --help + +Usage: + wallet-cli message sign [options] + +Sign an arbitrary message. The prefix follows the selected network's family: +TIP-191 for TRON, EIP-191 for EVM. + +Requires: + the master password — pass --password-stdin; this command never prompts + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --message message to sign; provide this OR --message-stdin [optional] + --message-stdin read the message from stdin (fd 0) [optional] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli message sign --message "hello" --network nile + wallet-cli message sign --message "hello" --network sepolia +``` + +### 8.2 `typed-data sign` —— 签名结构化数据 🔒 + +> **本版改动**:EVM 用 EIP-712,TRON 用 TIP-712;输出契约与 flag 集合均不变。 + +**用法** + +``` +wallet-cli typed-data sign --typed-data [--account ] [--network ] [--password-stdin] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 对结构化数据签名 | +| 算法 | TRON = TIP-712;EVM = EIP-712 | +| 错误 | `invalid_payload`(domain / types / primaryType 不完整) | + +**示例与输出** + +```bash +$ wallet-cli typed-data sign --typed-data '{"domain":{...},"types":{...},"message":{...}}' --network ethereum +Address 0x7a3f...c19b +Primary type Permit +Digest 0xa71c...4e08 +Signature 0x9d3b...77ea +``` + +**Help 输出** + +> **相对现状**:描述由「讲输出」改为「讲动作」(§10.1 规则 1);Requires 冠词统一;**flag 集合无变化**;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli typed-data sign --help + +Usage: + wallet-cli typed-data sign [options] + +Sign an EIP-712 / TIP-712 typed-data payload + +Requires: + the master password — pass --password-stdin; this command never prompts + an account — defaults to active; override with --account (or run `wallet-cli use ` to change the active account) + +Options: + --typed-data EIP-712/TIP-712 JSON: {"domain":…,"types":…,"primaryType"?:…,"message":…} [required] + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --account accountId, label, or address for wallet-bound commands; falls back to the active account set by use [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + --password-stdin read the master password from stdin (fd 0); only one *-stdin flag can consume stdin per run [optional] + +Examples: + wallet-cli typed-data sign --typed-data '{"domain":{...},"types":{...},"message":{...}}' --network nile + wallet-cli typed-data sign --typed-data '{"domain":{...},"types":{...},"message":{...}}' --network sepolia +``` + +--- + +## 9. 链信息组 + +### 9.1 `block` —— 查询区块 + +> **本版改动**:走 `eth_getBlockByNumber`;**json 原样透传节点返回**,text 才是格式化层。 + +**用法** + +``` +wallet-cli block [] [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 查询区块(省略号数则取最新) | +| EVM 增量 | 走 `eth_getBlockByNumber`;字段按 EVM 区块结构 | +| 错误 | `not_found`(指定号数的区块不存在)、`rpc_error`(含端点限流 429) | + +**示例与输出** + +```bash +$ wallet-cli block --network sepolia +Number #11,204,149 +Hash 0x6b2f...d40a +Parent hash 0x1e83...77bc +Time 2026-08-06 09:21:47 UTC +Transactions 142 +Gas used 12,840,221 / 30,000,000 +Base fee 18.4 gwei +``` + +```bash +$ wallet-cli block --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"block","data":{ "number":"0xaaf635","hash":"0x6b2f...d40a","parentHash":"0x1e83...77bc","timestamp":"0x6a74522b","gasUsed":"0xc3ed1d","gasLimit":"0x1c9c380","baseFeePerGas":"0x448b9b800","transactions":[ "0x9c4e...81af", "…共 142 项…" ] },"meta":{ "durationMs":310,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> **注意 json 里全是 `0x` 十六进制字符串**——这正是「原样透传」的含义:`eth_getBlockByNumber` 的返回不做任何数值转换、字段不重命名、`transactions` 不裁剪。想要十进制与 UTC 时间读 text。这与 TRON 侧透传 protobuf JSON 是同一条规则,因此**本命令是全文唯一不遵守「json 给最小单位十进制整数字符串」(§1.4)的地方**:透传优先。 + +> **json 原样透传节点返回**:TRON 侧 `block` 的 json 就是链上 protobuf JSON 原样(`{block:{blockID, block_header:{…}, transactions:[…]}}`),不做字段重塑;EVM 侧同理给 `eth_getBlockByNumber` 的原始返回。text 才是我方格式化的那一层,两族各按自己的区块结构取字段。 + +**Help 输出** + +> **相对现状**:描述补一句「json 为节点响应原样」;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli block --help + +Usage: + wallet-cli block [] [options] + +Get a block (latest if omitted). JSON output is the node's response verbatim. + +Args: + number block number to fetch, in block height; omit to fetch the latest block + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli block + wallet-cli block 12345 --network nile + wallet-cli block 12345 --network sepolia +``` + +### 9.2 `chain node` —— 节点状态 + +> **本版改动**:EVM 新增 `Chain id` 与 `Syncing` 两行;**`Solid block` 与 `Peers` 两行照样出现**——EVM 的不可逆区块就是 `finalized`。 + +**用法** + +``` +wallet-cli chain node [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 显示所连节点的状态 | +| EVM 增量 | 新增 `Chain id` 与 `Syncing` 两行;`Solid block` 取 **finalized 区块**标签,`Peers` 取 `net_peerCount`;`p2pVersion` 在 EVM 恒为 `null` | +| 错误 | `rpc_error`(含端点限流 429)——端点不可达时这条命令本身就是探测手段,失败即结论 | + +**示例与输出** + +```bash +$ wallet-cli chain node --network tron:nile +Endpoint nile.trongrid.io +Version java-tron 4.8.2.1.PQ1_build1 +Head block #70,435,374 2026-08-27 09:39:33 (~8s ago — in sync) +Solid block #70,435,358 (16 blocks behind head) +Peers 59 connected / 3 active +``` + +```bash +$ wallet-cli chain node --network sepolia +Endpoint ethereum-sepolia-rpc.publicnode.com +Version reth/v2.4.1-8eb2101/x86_64-unknown-linux-gnu +Chain id 11155111 +Head block #11,577,037 2026-08-27 09:39:36 (~9s ago — in sync) +Solid block #11,576,965 (72 blocks behind head) +Syncing no +Peers 33 connected / 33 active +``` + +> 以上两段均为实测输出。 +> +> **EVM 有「不可逆区块」这个概念——合并之后就叫 `finalized`,与 TRON 的 solid block 是同一件事。** 文档此前写「EVM 无 solidified 区块」在事实上是错的:实测 Sepolia(落后 head 约 72 块)与 BSC(落后 2 块)皆回得出值。把它藏起来反而少给了一个真实且有用的数字。 +> +> **`Peers` 取 `net_peerCount`,端点未暴露时显示 `—`**。`net_peerCount` 是标准 JSON-RPC 方法,但部分托管服务商禁用 `net_*` 命名空间。**为 EVM 破例改成「整行消失」会让同一条命令有两套规则**——`—` 正是这条命令**自己的既有惯例**(help 明写「端点未暴露的字段显示 `—`」)。 +> +> 两族独有的字段:EVM 有 `Chain id`(EIP-155,签名要用,值得摆出来核对)与 `Syncing`,TRON 没有;`p2pVersion` 在 EVM 恒为 `null`。`Endpoint` 两族都只显主机名(§2.3)。 + +**Help 输出** + +> **相对现状**:描述改写为按 family 说字段差异、TRON 在前(现状那句「端点未暴露的字段显示 —」并入 §9.2 正文);`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli chain node --help + +Usage: + wallet-cli chain node [options] + +Show the connected node's status. Fields differ by family: TRON reports the +solidified block and peer counts, EVM reports the chain id and sync state. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli chain node --network nile + wallet-cli chain node --network sepolia +``` + +### 9.3 `chain prices` —— 当前交易单价 + +> **本版改动**:EVM 侧给 base fee、建议 priority fee 与实际 gas price,并折算一笔转账的成本。 + +**用法** + +``` +wallet-cli chain prices [--network ] +``` + +**概览** + +| 项 | 内容 | +| --- | --- | +| 功能 | 发一笔交易此刻的单位成本 | +| EVM 增量 | 新增 `feeModel` 字段(见下);base fee 取最新区块头的 `baseFeePerGas`;priority fee 取 `eth_maxPriorityFeePerGas`,**不做 `eth_feeHistory` 回退**;非 1559 链退到 `eth_gasPrice` | + +> **不做 `eth_feeHistory` 回退**:四条内置网络**都支持** `eth_maxPriorityFeePerGas`,所以这条回退今天**一次都不会触发**。而一条永远跑不到的路径既无法验证、也会在下一次改动时被当成有效行为对待。要做的话是独立一小项,且需要先找到一个真的不支持 `eth_maxPriorityFeePerGas` 的端点来验证它走得通。读不到就是 undefined,该行不显示。 +| 错误 | `rpc_error`(含端点限流 429) | + +> **这条在 EVM 上成立**:TRON 侧它给 `Energy price` / `Bandwidth price` / `Memo fee`,回答的是「现在发一笔交易,单位成本多少」——该问题在 EVM 上不但成立,而且**更常被问**(gas 波动远大于 TRON 的资源单价)。业内对应 `cast gas-price` / `cast base-fee`。字段按 family 各取各的,问题是同一个。 + +**示例与输出** + +```bash +$ wallet-cli chain prices --network sepolia +Fee model eip1559 +Base fee 0.97768 gwei +Priority fee 0.001 gwei +Gas price 0.97868 gwei +Transfer cost 0.00002 ETH (21,000 gas) +``` + +```bash +$ wallet-cli chain prices --network sepolia -o json +{ "schema":"wallet-cli.result.v1","success":true,"command":"chain.prices","data":{ "feeModel":"eip1559","baseFeeWei":"977680801","priorityFeeWei":"1000000","gasPriceWei":"978680801","transferGas":21000,"transferCostWei":"20552296821000" },"meta":{ "durationMs":3024,"warnings":[] },"chain":{ "family":"evm","network":"eip155:11155111","chainId":"11155111" } } +``` + +> 以上为实测输出。 +> +> **`feeModel` 是本版新增字段**(`"eip1559" | "legacy"`),text 对应 `Fee model` 一行。这条命令要回答「现在发一笔交易多少钱」,而**费用模型决定了读者该看哪些数字**:1559 链看 base + priority,legacy 链只有 gas price。靠「`baseFeeWei` 在不在」隐含地表达模型,要求读者知道这条规则,而且在 **BSC(base fee 为零但仍是 1559)上特别容易误读**。明讲一个字段,比让人从字段的有无去推断便宜得多。 +> +> `feeModel` 由**链上侦测**(§6.1 的同一条规则:`baseFeePerGas` 字段存在即 1559,零也算),`NetworkDescriptor.feeModel` 为覆盖用。 + +> 三个价一律给 **wei 整数字符串**(text 才换算成 gwei,§1.4);`gasPriceWei` 是前两者之和、不是 `eth_gasPrice` 的返回值。非 1559 链只给 `gasPriceWei` 与转账折算两项,`baseFeeWei` / `priorityFeeWei` 不出现(不给 `null`)。 + +> `Transfer cost` 是把单价折算成「一笔原生币转账要花多少」——单看 gwei 数字,多数用户判断不出贵不贵;21,000 gas 是协议固定的转账消耗,折算无歧义。这与 TRON 侧列 `Memo fee` 是同一个意图:把单价翻译成一次实际支出。 +> +> **`Gas price` 是 base + priority 的和,由前两行算出,不是 `eth_gasPrice` 的返回值**——`eth_gasPrice` 给的是节点自己的建议值,与 1559 的两段式定价不是一回事,两者并列会对不上账。`Transfer cost` 按这个和乘 21,000 得出。 +> +> 不支持 EIP-1559 的链没有 base fee,此时 `Gas price` 直接取 `eth_gasPrice`、只显这一行与 `Transfer cost`,`Base fee` / `Priority fee` 不显示。 + +**Help 输出** + +> **相对现状**:描述由 TRON 专属(energy/bandwidth/memo fee)改写为按 family 说差异、TRON 在前;`--network` 示例值;Examples 两族对称。 + +```text +$ wallet-cli chain prices --help + +Usage: + wallet-cli chain prices [options] + +Show what a transaction costs per unit right now. TRON reports energy/bandwidth +unit prices and the memo fee; EVM reports base fee, suggested priority fee and +the resulting gas price. + +Global options: + --output, -o result format [optional, default: config.defaultOutput (built-in: text)] + --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted [optional] + --timeout per node, service, or device call timeout, in milliseconds [optional, default: config.timeoutMs (built-in: 60000)] + --verbose, -v show extra diagnostic output [optional, default: false] + +Examples: + wallet-cli chain prices --network nile + wallet-cli chain prices --network sepolia +``` + +--- + +## 10. help 规格(三层) + +help 分 root / 组 / 命令三层。本章给前两层的完整输出与三层共同的文案规范;**命令层的 help 附在 §3–§9 各命令小节末尾**,与该命令的用法、Options 对照阅读。 + +> ### ⚠️ 本章尚有一项 help 文案未与实作对齐(待办) +> +> 本轮同步已把**旗标集合发生变化**的 help 区块按实测输出重贴(§2.3 `networks`、§2.4 `config`、§3.9 `derive`、§3.10 `backup`、§3.11 `contact` 三条、§6.3 `tx broadcast`、§7.3 `contract deploy`)。**全局旗标文案**(`--network` / `--timeout`)已于修订 4 按实作回贴至全部 **38 个**含 `Global options` 段的命令层区块。**尚未重贴的只剩一项**: +> +> | 项 | 内容 | 规模 | +> | --- | --- | --- | +> | 命令层描述 / Args / Examples | 实作的一行描述与 Examples 多处比规格更长且**多出来的内容是真的** | **38 个命令层 help 区块** | +> +> **已回贴的两条全局旗标文案**(实作版比原文档版准确,故据此改了文档): +> +> ```text +> --network network id or alias, e.g. nile, sepolia, bsc, or eip155:11155111; falls back to config.defaultNetwork when omitted +> --timeout per node, service, or device call timeout, in milliseconds +> ``` +> +> - `--timeout`:原文档版的 `RPC` 是**实作名**,违反 §10.1 规则 3(RPC 方法名/类别名不进 help)。而 `node, service, or device` 也更准确——这条超时同时涵盖节点调用、GasFree 这类外部服务、以及 Ledger 设备。 +> - `--network`:实作版列出 `nile` / `sepolia` / `bsc` / `eip155:11155111` 四种形态(**两族别名 + 规范 id**),比原文档版更能让读者一眼看出「别名与规范 id 都收」。 +> +> **family 标注词表已折叠进本版且实作已追平**:原 v4.13.1 主题 1(`(tron)` / `(evm)` → `(TRON only)` / `(EVM only)`)整体并入 v4.13.0,本文档全部 132 处标注已改(§10.1);实作已于 `e206c00a` 输出全大写,**小写残留为 0**。 +> +> **回贴命令层区块时的两条硬约束**:① 示例网络一律用 `nile` / `sepolia` 等测试网,**不得贴回 `--network tron` / `--network ethereum` 主网形态**;② 实作侧仍有若干处与 §10.1 文案规范不符(见 §10.1 末尾「待实作修正」),那几处**以本文档为准,不要照抄实作**。 + +### 10.1 help 文案规范 + +由实测既有 52 条命令的 help 归纳而来。骨架(`Usage → Args → Requires → Options → Global options → Examples`)由渲染层集中生成、52 条已完全一致,规范只约束**手写文案**。 + +**三层 help 各自的职责** + +| 层 | 描述写什么 | family 差异怎么表达 | +| --- | --- | --- | +| root help | **动词概括**(`Query on-chain account state`),**不列举子命令名** | 整组仅一族可用时标组级 `(TRON only)`;混合组不标 | +| 组 help | 同上,可略具体 | **逐条子命令行尾标 `(TRON only)` / `(EVM only)`** | +| 命令 help | 该命令做什么,两族行为不同时说差异本身 | **逐个 flag 行尾标 `(TRON only)` / `(EVM only)`** | + +**family 标注的词表是封闭的两个值:`(TRON only)` 与 `(EVM only)`。** + +- **全大写、括号包裹**;后续新增 family 按 `( only)` 构词,family 名全大写。 +- **不得使用动词短语形式**(如 `only support TRON`)——标注列是**属性列**,成员一律为名词 / 形容词短语;且该形式超 80 列会打散标注列的对齐。 +- 标注的**适用范围**(哪些项该标、哪些不该标、组级 vs 子命令级 vs flag 级的分工)由上表规定,与词表无关。 + +> **词表的由来**:原定为 `(tron)` / `(evm)`,评审反馈**看不懂**——单看 `(tron)` 读者无从判断这是「只在 TRON 可用」还是「在 TRON 上行为不同」。`only` 把语义补全,全大写让它在一列小写 flag 名里读得出是标注而不是取值。 +> +> ✅ **实作已追平**(`e206c00a`):全量 help 扫描小写 `(tron only)` / `(evm only)` 残留为 **0**,大写标注 **57 处**(root 9 + 组 11 + 命令层 37)。本文档 §3–§9 help 区块的标注列现已是实测值。 + +> **为什么不做按 family 过滤的人类 help**:主流 CLI 的 help 都不随运行时上下文变化——`git` / `docker` / `kubectl` / `aws` 的 help 不因 `--context`、`--region` 而增删条目。需要按某个维度分家时,业内是把该维度**做进命令路径**(`aws s3 …` / `aws ec2 …`),而不是让同一条路径的 help 变形。我方的 family 不是命令路径的一段(`wallet-cli tx send` 两族共用),所以走标注而非过滤;同一条命令的 help 永远只有一个版本,可直接引用、可缓存、可写进文档。 + +> root help 的组描述**不许列举子命令名**——`chain` 原描述 `Query chain params, prices & node info` 点名了 TRON 专属的 `params`,EVM 用户照着找会扑空;且全表只有它在列举,本就是体例偏差。 + +**Requires 段** + +1. 每条是一个**名词短语**,小写开头、句尾无标点;补充说明用 ` — ` 接续。 +2. 冠词统一:主密码类一律带 `the`;硬件与账户类为不定指,用 `a` / `an`。 +3. 顺序固定:命令特有前置 → 主密码 → 账户。 +4. 多条同类前置按**用户输入顺序**排列。 +5. **只列硬前置**(缺了就无法执行);交互确认不属前置,不进 Requires。 +6. 外部服务依赖属硬前置,**必须进 Requires**,不许塞在一行描述的括号里(EVM 侧 `account history` 将来接索引服务时按此办,§4.4)。 + +主密码四种语义的统一写法: + +| 场景 | 规范文案 | +| --- | --- | +| 只能 TTY 交互输入 | `the master password — entered interactively in a TTY` | +| 可 stdin 可交互 | `the master password — pass --password-stdin, or enter it interactively in a TTY` | +| 只能 stdin,从不提示 | `the master password — pass --password-stdin; this command never prompts` | +| **是否需要密码取决于模式** | `the master password only when the selected mode signs — pass --password-stdin then; other modes need no password` | + +> **第四种是本版补入的**(2026-08-28 PM 拍板,按实作)。它覆盖**全部 ✍️ 写命令**——`--dry-run` / `--build-only` 不签名、因而不需要主密码,`--sign-only` 与默认广播路径才需要。把这类命令一律写成第三种(「从不提示,必须给 --password-stdin」)是**错的**:调用方会为一条 `--dry-run` 白准备一次密码。**实测 24 条命令用此文案**(`tx send` / `contract send` / `contract deploy` / `stake` 全组 / `asset` / `exchange` / `vote cast` / `reward withdraw` / `permission update` / `account activate` / `account set` / `gasfree transfer` / `tx multisig`)。 +> +> 它带一个条件从句,形式上比前三种长,但 §10.1 规则 1 约束的是「每条是一个名词短语」——本条主词仍是 `the master password`,条件从句挂在破折号后的补充说明里,与前三种同构。 +> +> `change-password` 的 `the new master password — entered interactively in a TTY` 是第一种的实例,不另立一种。 + +**一行描述** + +1. **祈使动词开头**,描述命令做什么,不描述输出内容。 +2. **标点按层分**:**组 help 的描述是完整句、句尾带句号**(`Query on-chain account state.`);**命令 help 的一行描述单句不加句号**(`Show native balance`),多句时每句都加。组描述是独立段落、命令描述是标题式短语,两者惯例本就不同,别拉平。 +3. **不出现实现细节名**——protobuf 字段名、Java 类名、RPC 方法名(`eth_getBalance` / `triggerConstantContract` / `getAccount`)一律不进 help。需要交代对应关系的写进本文档的「概览」表,那里是设计溯源该待的地方。**旗标名不算实现细节名**——组 help 的子命令描述可以点名本命令的招牌旗标(`tx` 组的 `send Send native coins or tokens with human --amount`,2026-08-28 PM 拍板按实作):`--amount` 是**面向用户的契约**,且它正是这条命令与 `--raw-amount` 路径的分界,点出来比省略更有信息量。规则 3 挡的是**读者用不上的内部名**,不是旗标。 +4. 两族行为不同时,描述里说**差异本身**、不说实现(如 `chain prices` 写 "EVM reports base fee…; TRON reports energy/bandwidth unit prices…",不写调了哪个 RPC)。 + +**family 专属项的标注** + +1. help 静态、不随 `--network` 变化;某个 flag 只属于一族时,**行尾加 `(TRON only)` / `(EVM only)`**,位置在 `[optional, …]` tag 之后。同一条命令的 Options 里**同时出现两族标注**时,其一行描述必须交代标注含义(`Flags marked (TRON only) or (EVM only) are accepted only on networks of that family; using one on the other family is rejected.`)——help 要能被单独读懂,不能依赖读者先看过本规范。 +2. 标注语义是**当前版本仅该族可用**,不承诺未来(`account history` 标 `(TRON only)` 是因为 EVM 侧等索引服务,补齐后摘掉)。 +3. 两族都有的 flag 不标注,且描述必须**族中立**——`--to` 写 "recipient address",不写 "recipient TRON base58 address";`--contract` 写 "token contract address",不写 "TRC20 contract address"。 +4. 组级标注沿用 root help 现状(`stake … (TRON only)`);混合组不标组级,差异下沉到子命令与 flag。 + +**Examples 的 family 配比** + +help 是**两族共用的静态文本**,Examples 因此必须两族兼顾、**TRON 在前**(主推)。适用范围按命令分三档: + +| 档 | 命令 | 要求 | +| --- | --- | --- | +| **吃 `--network` 的命令**(21 条 EVM 绑定命令) | `account` / `token` / `tx` / `contract` / 签名 / 链信息各组 | **必须两族对称**——同一个 flag 组合、只换 `--network` | +| **family 相关的本地命令** | `import ledger`(本版新增 `--app ethereum` 的就是它)、`import watch`(§3.5 明列了 EVM 示例) | **需涵盖两族**,但不要求「只换 `--network`」的对称形式 | +| **其余纯本地命令** | `create` / `derive` / `backup` / `contact` / `encoding` / `address` / `config` / `networks` | **豁免**——它们不吃 `--network`,「只换 `--network`」的对称形式对它们不成立 | + +**这条只约束 help**——本文档 §3–§9 的「示例与输出」段是 EVM 规格正文,示例用 EVM 是必要的,不受此约束。 + +> **示例网络一律用测试网**:本节 Examples 的 `--network` 取值恒为 `nile` / `sepolia` 等测试网。help 的 Examples 是全文档**最可复制**的形态,主网命令不得以可复制形态出现(根 `CLAUDE.md` 示例安全公约)。实作已合规,回贴时不得改回 `--network tron` / `--network ethereum`。 + +**待实作修正(本节规范 vs 当前实作,`e206c00a` 实测)** + +以下两处**以本节为准、需改实作**;回贴命令层 help 区块时**不要照抄实作**: + +| # | 位置 | 本节规定 | 实作当前 | +| :---: | --- | --- | --- | +| 1 | `typed-data sign` 一行描述 | 祈使动词开头、不描述输出内容(规则 1) | 首行是 `Prints the signature, the digest that was signed, and the primary type.`——**整条命令没有祈使动词描述行**,两项都违反。应恢复为 `Sign an EIP-712 / TIP-712 typed-data payload` | +| 2 | `import ledger` 一行描述 | `Register a Ledger account (watch-only; signs on device)` | `Register a Ledger account`——删掉了「设备上签名」这个关键限定 | + +> **另有五处已于 2026-08-28 拍板「按实作」、本文档已同步**,不需改实作:主密码 Requires 第四种写法(已补入本节上方的四行表)· `token info` 描述去掉字段列举(§5.2)· `tx` 组 help 的 `send` 行点名 `--amount`(规则 3 已加注)· §0.3「不提示」正名为「不问主密码」· **flag 标注免责句改用 `are accepted`**(规则 1 的定死文案已同步,见下)。 + +> **免责句为何是 `are accepted` 而非 `apply`**:`apply` 说的是「这个 flag 在另一族不起作用」,读起来像**被忽略**;实际行为是**被拒绝**(EVM 网络上传 `--fee-limit` 报 `invalid_option`)。`are accepted only on…` 与后半句 `using one on the other family is rejected` 同指一件事,语义自洽;`apply` 会让读者以为传了也无妨。 + +### 10.2 root `--help`(完整版) + +> **有两行刻意保留实作版,不照原规格**(其余八处差异实作已照规格,不需再动): +> +> | 组 | 本表采用 | 原规格 | 为什么 | +> | --- | --- | --- | --- | +> | `exchange` | `Create and trade Bancor exchange pairs` | `On-chain Bancor exchange` | 规格版是**名词短语**,违反 §10.1「一行描述以祈使动词开头」,而且全表只有它一列是名词短语 | +> | `contract` | `Call, deploy, govern, and inspect smart contracts` | `Call, send, deploy, and inspect…` | 规格版把 `govern` 换成 `send`,等于**舍弃了对治理四条命令的概括**(`clear-abi` / `set-origin-energy-limit` / `set-user-resource-percent` / `create2`),改成再列一个子命令名;而 `send` 与 `call` 在 root 这一层的区别对读者没有意义。**§10.3 的组 help 同步。** + +```text +$ wallet-cli --help + +Usage: wallet-cli [OPTIONS] COMMAND + +wallet-cli — CLI wallet for TRON and EVM networks. +Agent-first: deterministic exit codes, JSON output. + +Common Commands: + create Create a new HD wallet (BIP39 seed) + import Import a wallet + list List wallets / accounts + +Management Commands: + account Query on-chain account state + permission View / update account permissions (multi-sig) (TRON only) + token Manage the token address book and query tokens + tx Build, send, broadcast, and inspect transactions + gasfree Gas-free token transfers via the GasFree service (TRON only) + contract Call, deploy, govern, and inspect smart contracts + proposal Create / vote on governance proposals (TRON only) + witness Register / operate a super representative (TRON only) + asset Issue & manage TRC10 tokens (TRON only) + exchange Create and trade Bancor exchange pairs (TRON only) + stake Stake / delegate resources & query state (TRON only) + vote Vote for super representatives (TRON only) + reward Query / withdraw voting rewards (TRON only) + chain Query chain and node state + message Sign arbitrary messages + typed-data Sign EIP-712 / TIP-712 structured data + block Get a block (latest if omitted) + +Commands: + use Set the active account + current Show the current (active) account + rename Rename an account label + derive Derive the next HD account from a seed wallet + backup Export an account's secret + metadata (0600) + delete Delete a wallet / account + config Show / get / set configuration values + networks List known networks + change-password Change the master password (re-encrypt keystores) + encoding Convert / validate addresses & encodings + address Generate a random keypair (local, not stored) + contact Manage the recipient address book + +Global Options: + -o, --output string Output format ("text", "json") (default from config) + --network string Network id or alias, e.g. "tron", "ethereum", "sepolia" + --account string Account label or address to act as (overrides active) + --timeout int Request timeout in milliseconds + -v, --verbose Verbose / debug logging + -h, --help Show help + -V, --version Print version information and quit + +Run 'wallet-cli COMMAND --help' for more information on a command. +``` + +**相对现状的三处改动**(其余原样): + +| 项 | 现状(实测) | v4.13.0 | +| --------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| 主描述首行 | `wallet-cli — CLI wallet for TRON.` | `wallet-cli — CLI wallet for TRON and EVM networks.`(第二行 `Agent-first: …` 不变) | +| `chain` 组描述 | `Query chain params, prices & node info` | `Query chain and node state`——**改为动词概括、不列举子命令**:原描述点名了 `params`,而它是 TRON 专属,EVM 用户照着找会扑空;全表也只有它在列举子命令,与 `account` / `tx` / `contract` 的「动词 + 对象」体例不一致 | +| `--network` 示例值 | `"tron:728126428", "tron:3448148188", "tron:2494104990"` | `"tron", "ethereum", "sepolia"`,跨两族各取一个、且用简写别名,让「多链」在第一屏可见 | + +> **`(TRON only)` 标注只给纯 TRON 组**:`permission` / `gasfree` / `proposal` / `witness` / `asset` / `exchange` / `stake` / `vote` / `reward` 九组在 EVM 上整组不适用。**混合组一律不标**——`account`(`activate` / `set` 是 TRON 专属)、`tx`(`approvals` / `multisig`)、`contract`(治理四条)、`chain`(`params` / `prices`)都含 TRON 专属子命令,但组本身两族可用,标了会让人以为整组不可用;差异下沉到子命令与 flag 的 `(TRON only)` / `(EVM only)` 标注(见 §10.1)。这与实现现状一致——实测 root help 里 `chain` 就没有标注。 + +### 10.3 组 `--help`(中间层) + +组 help 是「root → 组 → 命令」三层里的中间层,本版同样要改:现状的子命令描述**写死了 TRON**(`prices` 写 `Energy/bandwidth unit price and memo fee`、`info` 写 `getAccount`、`balance` 写 `(TRX/SUN)`),在 EVM 网络下全不成立。改造两件事:**描述族中立化** + **family 专属子命令行尾标 `(TRON only)` / `(EVM only)`**。 + +> **相对现状**:`balance` / `info` 描述族中立化(去 `TRX/SUN`、去 `getAccount`);`history` 的 `(requires TronGrid)` 移出描述并改标 `(TRON only)`;补 `activate` / `set` 两条并标 `(TRON only)`;`portfolio` 调整到 `info` 之后。 + +```text +$ wallet-cli account --help + +Usage: wallet-cli account COMMAND + +Query on-chain account state. + +Commands: + balance Show the native coin balance + info Show the account's on-chain state + portfolio Show native + token balances with best-effort USD value + history Show transaction history (TRON only) + activate Activate an unactivated account (TRON only) + set Set the on-chain account name / id (TRON only) + +Run 'wallet-cli account COMMAND --help' for more information on a command. +``` + +> **相对现状**:组描述改为动词概括、不列举子命令;`prices` 去掉 `Energy/bandwidth`、`node` 去掉 `(version / sync / peers)`;`params` 标 `(TRON only)` 并移到末位。 + +```text +$ wallet-cli chain --help + +Usage: wallet-cli chain COMMAND + +Query chain and node state. + +Commands: + node Connected node status + prices Current transaction unit prices + params On-chain governance parameters (TRON only) + +Run 'wallet-cli chain COMMAND --help' for more information on a command. +``` + +> **相对现状**:五条子命令描述统一精简,去掉 `(--contract / --asset-id)` 与 `totalSupply` 这类 flag / 字段细节。 + +```text +$ wallet-cli token --help + +Usage: wallet-cli token COMMAND + +Manage the token address book and query tokens. + +Commands: + balance Show a single token balance + info Show token metadata + add Add a token to the address book + list List the address book + remove Remove a user-added token + +Run 'wallet-cli token COMMAND --help' for more information on a command. +``` + +> **相对现状**:`send` / `sign` 描述族中立化;补 `approvals` / `multisig` 两条并标 `(TRON only)`。 + +```text +$ wallet-cli tx --help + +Usage: wallet-cli tx COMMAND + +Build, send, broadcast, and inspect transactions. + +Commands: + send Send native coins or tokens with human --amount + sign Sign a transaction built elsewhere + broadcast Broadcast a presigned transaction + status Show confirmation status of a transaction + info Show full transaction detail + receipt + approvals Show collected signatures on a multi-sig transaction (TRON only) + multisig Create / co-sign a multi-sig transaction (TRON only) + +Run 'wallet-cli tx COMMAND --help' for more information on a command. +``` + +> **相对现状**:`call` / `send` / `deploy` 描述去掉 RPC 方法名;`info` 改标 `(TRON only)`;补 `clear-abi` / `set-origin-energy-limit` / `set-user-resource-percent` / `create2` 四条并标 `(TRON only)`;名列宽随之加宽。 + +```text +$ wallet-cli contract --help + +Usage: wallet-cli contract COMMAND + +Call, deploy, govern, and inspect smart contracts. + +Commands: + call Read-only contract call + send State-changing contract call + deploy Deploy contract bytecode + info Show contract ABI + metadata (TRON only) + clear-abi Clear a contract's on-chain ABI (TRON only) + set-origin-energy-limit Set the deployer's energy cap (TRON only) + set-user-resource-percent Set the caller-paid resource share (TRON only) + create2 Precompute a CREATE2 address (TRON only) + +Run 'wallet-cli contract COMMAND --help' for more information on a command. +``` + +> **相对现状**:组描述精简为 `Import a wallet.`;补 `keystore` 一条;`ledger` / `watch` 描述精简(括号里的说明下沉到各自命令 help)。 + +```text +$ wallet-cli import --help + +Usage: wallet-cli import COMMAND + +Import a wallet. + +Commands: + mnemonic Import a BIP39 mnemonic phrase + private-key Import a raw private key + keystore Import a Web3 keystore file + ledger Register a Ledger account + watch Register a watch-only address + +Run 'wallet-cli import COMMAND --help' for more information on a command. +``` + +> **相对现状**:`list` 的子命令描述补「给出每条的 chain family」;组描述与另两条沿用现状。 + +```text +$ wallet-cli contact --help + +Usage: wallet-cli contact COMMAND + +Manage the recipient address book. + +Commands: + add Add a payee to the address book + list List every contact + remove Remove a contact + +Run 'wallet-cli contact COMMAND --help' for more information on a command. +``` + +> **标注语义**:`(TRON only)` / `(EVM only)` 表示**当前版本仅该族可用**,不承诺未来——`account history` 与 `contract info` 标 `(TRON only)` 是因为 EVM 侧要等索引服务(§4.4、能力矩阵),将来补齐后标注即摘掉。整组仅 TRON 的(`permission` / `gasfree` / `stake` / `vote` / `reward` / `proposal` / `witness` / `asset` / `exchange`)在 root help 标组级 `(TRON only)`,其组 help 内部不再逐条重复。 +> +> **`message` / `typed-data` 两组各只有一个子命令 `sign`,两族均可用**,组 help 无标注、无改造,此处从略。 + +--- + +## 11. 错误码 + +> **「这份表是唯一的错误码索引,不得出现表外的码」这句承诺保留、不弱化**——agent 就是靠 `error.code` 分支的,一个没被文档写过的码等于一个它无法处理的码。 +> +> **会失效的不是承诺,是手工维护的表**:上一版的 §11 列了 9 个从不产生的码、漏了 30 多个真的会产生的码,正是手工维护的结果。因此索引改为与错误定义放在一起、由测试守住,让它结构上不可能再漂移: +> +> - 真理源是 `src/domain/errors/codes.ts` 的 `ERROR_CODES`,逐码附一行语义; +> - 一条测试扫描全部源码**双向**比对——**丢得出来却没登记 → 失败;登记了却没人丢 → 也失败**; +> - **机器可读版本在 `--json-schema` 的 `errorCodes` 键**,agent 一次调用即可取得全量(纯新增,不影响既有键)。 +> +> 下表由该真理源生成,共 **129 条**。各节「概览」的错误行只从这里取值。 + +### 11.1 本版相关的新增与更名 + +| 错误码 | 说明 | +| --- | --- | +| `migration_required` | **本版新增**。注册文件落后于本体,且无法取得主密码(无 TTY 且未给 `--password-stdin`),见 §0 | +| `invalid_config` / `insecure_config` | **本版新增**。config 文件格式错 / 权限或内容不安全(§2.2、§2.4) | +| `unsupported_network` / `missing_network` | 网络解析(§2.1) | +| `family_mismatch` | **本版由 `network_family_mismatch` 更名**——对照旧字符串的 agent/脚本会坏,**进 release note** | +| `missing_wallet_address` | 与 `family_mismatch` **分家**:「账户存在但在另一条链上」先前与「你根本没有账户」共用同一个码,而两者的解法完全不同 | +| EVM 广播拒绝码 | `nonce_too_high` / `replacement_underpriced` / `gas_too_low` / `fee_too_low` / `gas_limit_exceeded`(§6.3 白名单判断的产物) | +| `token_metadata_unavailable` | **取代规格里的 `not_a_token`**——读不到 metadata 的原因不只「不是代币」,新名字更准 | +| `token_already_listed` | **取代规格里的 `token_exists`** | +| `token_not_in_book` | **取代规格里的 `token_not_found`**——说出了「不在地址簿」而不是含糊的「找不到」 | +| `ledger_unsupported` | **规格里的 `app_not_open` 并入此码**:它与「app 版本不支持这条指令」共用同一个 status word(`0x6d00` INS_NOT_SUPPORTED),**单看它分不出是哪一个**,故合并,信息同时涵盖两种原因 | +| `ledger_setting_required` | **本版新增**:TRON app 的设定类状态 | +| `provider_rate_limited` | **外部服务**的限流专用码。**节点限流(HTTP 429)仍归 `rpc_error`**,两者的处置不同 | + +**从 `invalid_value` 这个泛用桶里分出来的六个**(破坏性:比对 `invalid_value` 的脚本会漏接,**进 release note**): + +| 码 | 分出来的理由 | +| --- | --- | +| `account_not_found` | `--account` 打错的下一步是 `list`;而秘密是在隐藏提示下输入的,envelope 连 issue path 都没有,**码是调用者唯一拿得到的东西** | +| `invalid_mnemonic` | 同上。顺带修掉一个真的缺陷:私钥含非十六进制字符时 `hexToBytes` 抛的是自己的 Error,会被归为**信息被 redact 的 `internal_error`**——同一个打字错误的两种形态先前回两个不同的码,其中一个还是错的 | +| `invalid_private_key` | 同上 | +| `seed_not_found` | `--seed-id` 指到一个非 seed 钱包时信息说得清楚、码什么都没说 | +| `invalid_path` | `import ledger --path` 把「这根本不是一条 BIP32 路径」报成 `--path coin_type ? does not match --app tron`——一句在谈 coin_type 的话,而用户的问题不是 coin_type。**同时旧检查只比对 `m/44'/'/` 前缀,`m/44'/195'/garbage` 会通过验证直接送进设备。** 现在路径格式错误报 `invalid_path`,币别不符才留在 `invalid_option`(那是两个旗标之间真正的矛盾) | +| `device_not_found` / `device_locked` | 先前一起归进 `auth_required`——**装置没插时没有任何凭证可以提供,`auth_required` 说的是错的事**;而「没插」与「锁着」的解法一个是插上、一个是输 PIN | + +> **查找「歧义」的情形维持 `invalid_value`**:值是有效的,只是选中多个,解法是缩小范围而不是去找一个不存在的账户。 + +### 11.2 `family_mismatch` 的触发场景(本版扩为六个) + +| # | 场景 | +| :---: | --- | +| 1 | 账户与目标网络 family 不符 | +| 2 | raw tx 与目标网络 family 不符 | +| 3 | **该命令在目标网络的 family 下没有实作**(`stake info --network eip155:1`) | +| 4 | **收款人地址属于另一族**(`--to 0x…` 配 `--network nile`)。先前报 `contact_not_found`——会让用户去找一个他**从没建立过**的通讯录条目 | +| 5 | **通讯录条目的地址属于另一族**。信息刻意描述**地址**而非 family(§3.11),用户不必学会那个词 | +| 6 | **以 family 前缀查询一条该族没有实作的命令**(`evm account history --help`)。命令**存在**,只是没有那一族的实作;报 `unknown_command` 会把读者导向去找不存在的错字 | + +### 11.3 两条跨命令的判定规则 + +**① `unknown_command` 涵盖 meta 路径。** 无法解析的命令路径一律 `unknown_command`(exit 2),**`--help` / `--json-schema` 不例外**。 + +先前 `handleMeta()` 对**任何**无法解析的路径都退回 root help 并 `return 0`——同一个错字,不带 meta 旗标时是 `unknown_command` / exit 2,加上 `--help` 就变成「成功」。**meta 旗标等于在退出码契约上开了一个洞**,而 agent 打错命令名时会拿到一个看似成功的回应。(顺带修掉一个既有缺陷:`tx send --to T... --help` 先前拿到的是**组** help 而非该命令的。)**破坏性,进 release note。** + +**② `--to` 两者皆非时,错误码由值的形状决定。** `--to` 接受地址**或**通讯录名称,所以「解析不出来」有两个可能的原因,而**只讲其中一个会把一半的人送去错的地方找**。 + +| 值的形状 | 码 | +| --- | --- | +| `0x…` 或 `T…` 开头 | `invalid_address` | +| 其余 | `contact_not_found` | + +**两种信息都必须提到另一种可能**: + +```text +invalid_address: 0xnotanaddress is not a valid evm address, and no contact is named that either +contact_not_found: no contact named nosuchname, and it is not an address either +``` + +> 判断用的是最宽的那个问法:**不是「这是不是有效地址」,也不是「这是不是地址形状」**(那两个更早就判掉了),而是**「他是不是想打一个地址」**——没有人会在想打通讯录名称时键入 `0x`。 + +### 11.4 全量索引 + +> 由 `ERROR_CODES` 生成。每条一行:**从调用者这一侧看,发生了什么**;不写该怎么办——那属于 message,message 可以点名涉及的文件、旗标或地址。 + +| 错误码 | 语义 | +| --- | --- | +| `usage_error` | the command line could not be parsed | +| `unknown_command` | no such command path, including under --help / --json-schema | +| `invalid_option` | an option is not accepted here, or contradicts another one | +| `missing_option` | a required option was not given | +| `invalid_value` | an option's value is not of the shape that option takes | +| `unknown_parameter` | no chain parameter by that name | +| `limit_exceeded` | a bounded input (file size, list length, page size) was over its limit | +| `family_mismatch` | the account, recipient, raw transaction or command does not belong to the selected network's chain | +| `missing_network` | the command needs a network and none was selected or configured | +| `unsupported_network` | no network by that id or alias | +| `unsupported_network_capability` | the selected network does not offer what this command needs | +| `missing_wallet_address` | no account is available to act as | +| `account_not_found` | no local account by that id, label or address | +| `seed_not_found` | the reference does not name a seed (HD) wallet | +| `account_exists` | an account with that address is already in the keystore | +| `invalid_account` | the account reference is not well-formed | +| `not_exportable` | the account holds no exportable secret (watch-only or Ledger) | +| `no_software_wallet` | the operation needs a locally stored key and none exists | +| `watch_only_no_signer` | the selected account can be watched but cannot sign | +| `auth_required` | the master password is needed and was not available | +| `auth_failed` | the master password was wrong | +| `weak_password` | the proposed master password does not meet the strength rule | +| `wrong_keystore_password` | the keystore file's own password was wrong | +| `invalid_keystore` | the file is not a valid V3 keystore | +| `invalid_mnemonic` | the phrase is not a valid BIP39 mnemonic | +| `invalid_path` | the value is not a usable BIP44 derivation path | +| `invalid_private_key` | the private key is not 32 bytes of hex | +| `keystore_not_found` | no keystore file at that path | +| `secret_source_error` | a secret channel (stdin / TTY) could not be read | +| `tty_required` | the operation only accepts input from a terminal, and there is none | +| `entropy_failure` | the system random source failed | +| `insecure_permissions` | a wallet file's permissions are wider than 0600 | +| `migration_required` | a registry file is older than this build and must be migrated first | +| `audit_append_failed` | the local export/audit log could not be appended to | +| `file_not_found` | an input file does not exist | +| `output_exists` | the output path is already taken and would be overwritten | +| `io_error` | a local read or write failed | +| `encoding_error` | data on disk or on the wire is not in the form its format requires | +| `invalid_config` | the config file is malformed, or a network in it is missing a required field | +| `insecure_config` | the config file's permissions or contents are unsafe to load | +| `contact_not_found` | no contact by that name, and the value is not an address either | +| `invalid_address` | the value is not a valid address for the relevant chain | +| `already_exists` | a contact with that name or address is already stored | +| `token_not_in_book` | no token by that reference in the local address book | +| `token_already_listed` | that token is already in the local address book | +| `token_is_official` | the entry is a built-in and cannot be edited or removed | +| `token_metadata_unavailable` | the token's on-chain metadata could not be read | +| `unsupported_token` | the token standard is not one this command handles | +| `ambiguous_token_symbol` | the symbol matches more than one token; address it by contract | +| `ambiguous_asset_name` | the TRC10 name matches more than one asset; address it by id | +| `invalid_transaction` | the transaction is malformed, or already carries a signature | +| `invalid_payload` | the payload does not decode as what the flag says it is | +| `invalid_amount` | the amount is not positive, or is finer than the asset's precision | +| `precision_loss` | the amount cannot be represented exactly at the required precision | +| `tx_integrity` | the transaction re-encoded differently than it arrived — it was altered in flight | +| `chain_id_mismatch` | the transaction was built for a different chain than the one selected | +| `signing_rejected` | the signature was declined on the device | +| `dry_run_violation` | a --dry-run path attempted to broadcast; the attempt was barred | +| `invalid_permission` | no such permission group on the account, or it cannot be used here | +| `not_authorized` | the account is not permitted to perform this operation | +| `already_signed` | this account has already signed the transaction | +| `already_approved` | the approval was already recorded | +| `not_approved` | the transaction has not gathered the approvals it needs | +| `tx_expired` | the transaction's expiration has passed | +| `transaction_rejected` | the node refused the transaction, in its own words | +| `nonce_too_low` | nonce already used; the account has moved on | +| `nonce_too_high` | nonce is ahead of the account; an earlier transaction is missing | +| `replacement_underpriced` | replacing a pending transaction needs a higher fee than the original | +| `gas_too_low` | the gas limit is below what this transaction needs | +| `gas_limit_exceeded` | the gas limit exceeds the block gas limit | +| `fee_too_low` | the fee is below what the network is currently accepting | +| `insufficient_balance` | the balance cannot cover the amount plus the maximum fee | +| `insufficient_token_balance` | the token balance cannot cover the amount | +| `execution_reverted` | the contract reverted the call | +| `execution_error` | the transaction ran on-chain and failed | +| `not_found` | the transaction, block or record does not exist at this node | +| `rpc_error` | the node answered with an error | +| `invalid_node_response` | the node's answer was not in the shape the API defines | +| `provider_error` | an external service failed | +| `provider_rate_limited` | an external service is rate-limiting this client | +| `timeout` | the node, service or device did not answer in time | +| `aborted` | the operation was stopped before it finished | +| `cancelled` | the operation was cancelled before it reached the device | +| `history_not_supported` | the selected network exposes no transaction history endpoint | +| `chain_parameter_unavailable` | the node does not report that chain parameter | +| `gasfree_auth_failed` | the GasFree service rejected the request's credentials | +| `gasfree_credentials_missing` | no GasFree credentials are configured | +| `gasfree_integrity` | the GasFree service's answer failed its integrity check | +| `gasfree_rejected` | the GasFree service refused the transfer | +| `tronlink_credentials_missing` | no TronLink multi-sig service credentials are configured | +| `device_not_found` | no Ledger device answered | +| `device_locked` | the Ledger device is connected but locked | +| `ledger_setting_required` | a setting in the Ledger app must be enabled for this operation | +| `ledger_unsupported` | the Ledger app does not implement this operation or cannot decode it | +| `ledger_address_not_found` | the address was not found within the scanned derivation range | +| `wrong_device_seed` | the device holds a different seed than the account was registered with | +| `account_not_active` | the account is not activated on-chain | +| `account_already_active` | the account is already activated on-chain | +| `insufficient_stake` | the staked amount cannot cover this operation | +| `insufficient_voting_power` | the account has less voting power than the votes cast | +| `no_frozen_supply` | there is nothing frozen to act on | +| `not_yet_unfreezable` | the stake is still within its lock-up period | +| `nothing_to_withdraw` | there is nothing available to withdraw | +| `withdraw_too_frequent` | the withdrawal interval has not elapsed yet | +| `no_reward` | there is no reward to claim | +| `not_a_witness` | the address is not a witness | +| `already_witness` | the address is already a witness | +| `asset_not_found` | no TRC10 asset by that id or name | +| `invalid_asset_name` | the TRC10 name is not of an acceptable form | +| `already_issued_asset` | the account has already issued a TRC10 asset | +| `not_an_issuer` | the account did not issue this asset | +| `not_in_ico_window` | the asset's participation window is not open | +| `id_taken` | that id is already in use | +| `proposal_not_found` | no proposal by that id | +| `proposal_expired` | the proposal's voting window has closed | +| `not_proposal_owner` | the account did not create this proposal | +| `already_canceled` | the proposal was already withdrawn | +| `exchange_not_found` | no Bancor exchange pair by that id | +| `exchange_closed` | the exchange pair is not accepting this operation | +| `exchange_trading_disabled` | this network is not accepting Bancor trades | +| `not_exchange_creator` | the account did not create this exchange pair | +| `token_not_in_exchange` | that token is not one of the pair's two sides | +| `same_token` | both sides of the pair would be the same token | +| `insufficient_reserve` | the pair's reserve cannot support the requested amount | +| `self_participation` | the account cannot take both sides of this operation | +| `slippage_exceeded` | the trade would have returned less than the floor set for it | +| `contract_not_found` | no contract at that address | +| `not_contract_deployer` | the account did not deploy this contract | +| `internal_error` | an unexpected internal failure; the message is redacted on purpose | + +> **`rpc_error` 与 `invalid_option` 的退出码不同**:前者是端点侧的失败(重试或换端点即可,exit 1),后者是调用方错误(要改命令行,exit 2)。§6.1 的 gas 估算失败正是按这条界线**由 `invalid_option` 改回节点侧码**的。 + +--- + +## 12. Java 版移除 Standard CLI + +### 12.1 决策 + +Java 版内嵌两套入口:交互式 shell 与 Standard CLI(`org.tron.walletcli.cli`,非交互命令行层)。TS 版已完整覆盖非交互场景(`-o json`、错误码契约、`--*-stdin` 秘密通道),两套并存只会让行为契约长期漂移。**本版一次性移除 Java Standard CLI**,不留弃用过渡版本。 + +移除后 Java 版只剩交互式 shell 一个入口;所有非交互 / 脚本 / CI 场景改用 TS 版。 + +**这是破坏性变更,触达使用者靠四件事**(缺一不可,且都在发版前完成): + +| 交付物 | 内容 | 实测状态 @ `e206c00a` | +| --- | --- | --- | +| 命令映射表 | Standard CLI 全部命令 → TS 版对应命令,逐条列出,含参数与输出差异;进 release note 与 `java/README.md`,移除后继续保留在 docs | ❌ **未做**——全仓不存在;`java/README.md` 无横幅、无 Standard CLI 字样、无迁移指引 | +| 版本号 | Java 版随本版做 **major 级跳跃**,让依赖锁定的构建不会自动升上来 | ❌ **未达成**——`Utils.VERSION = " v4.13.0"`,4.12→4.13 是 **minor**;`java/build.gradle` 仍 `version '1.0-SNAPSHOT'` | +| 启动兜底 | 交互式 shell 收到 Standard CLI 形态的调用(带子命令参数启动)时,打一行**含替代命令与映射表地址**的提示再退出,**而不是**报未知参数 | ⚠️ **半数达成**——`Client.runMain` 已对带参调用打 stderr 提示并 exit 2,文案为 `Standard CLI has been removed in v4.13.0. Use the TypeScript CLI instead: @tron-walletcli/wallet-cli`;**但只给了包名,没有替代命令、没有映射表地址** | +| 社区通知 | GitHub Release note + Discussions 置顶帖 + 官方开发者渠道,随本版发布同步发出 | 🔵 发版动作,仓库内不可核 | + +> 没有弃用版做缓冲,**映射表与启动兜底就是仅有的两条触达渠道**——使用者是脚本与 CI,不读 release note,只在流水线红掉时才发现。映射表必须**逐条覆盖**、可直接照着改脚本,不能只给一句「请改用 TS 版」;启动兜底的那行提示,是他们在故障现场唯一能看到的东西,必须自带出路。 + +> ⚠️ **发版阻塞**:上表两个 ❌ 与一个 ⚠️ 落在同一条链上——**兜底提示之所以只能给包名,正是因为映射表还不存在**(无址可指)。四件交付物「缺一不可、且都在发版前完成」是本节自订的条件,目前仅社区通知一项待发。 + +### 12.2 移除范围(实测量化) + +> **本节的删除工作已在 PR #990 内完成**(`e206c00a`,含 PR #991 合入):`java/…/org/tron/walletcli/cli/` 已不存在,`fbf5362c..HEAD` 的 java 侧净删 **19,329 行 / 77 文件**。下表规模数字已按 `e206c00a` 复核——**「Standard CLI 包」一行逐位吻合**(39 文件 / 6,773 行);测试与文档两行原为 2 / 5,实测为 24 / 1,已订正(原文点名的另外四篇文档在本文档自己的旧基准 `fbf5362c` 上就不存在)。 + +| 项 | 规模 | 位置 | +| --- | --- | --- | +| Standard CLI 包 | **39 个文件 / 6,773 行** | `java/src/main/java/org/tron/walletcli/cli/` | +| 入口挂钩 | 4 个 import + `initRegistry()` 的 9 处 register + main 分支 | `java/src/main/java/org/tron/walletcli/Client.java` | +| 测试 | **24 个测试文件** | 整个 `java/src/test/java/org/tron/walletcli/cli/` 测试树(含 `aliases/` 5 个、`ledger/` 3 个、`commands/` 3 个)+ QA harness(`org/tron/qa/QARunner`、`QASecretImporter`)+ `TransactionUtilsTest` / `UtilsPasswordTest` / `ClearWalletUtilsTest` | +| 文档 | **1 篇** | `java/docs/standard-cli-contract-spec.md` | +| 关联方法族 | `WalletApi` 的 `*ForCli`、`WalletApiWrapper` 的 GasFree 签名分支 | 处置见 §12.3 | + +### 12.3 动工前置(阻塞项,必须先有结论) + +一次性移除没有回退窗口,以下两条**必须先查清调用关系再动手**,否则会删出「Java 版悄悄没了 Ledger」这类回归: + +| # | 前置 | 两种结论各自怎么做 | +| --- | --- | --- | +| 1 | **Ledger 支持的归属**——Java 侧 Ledger 只在 Standard CLI 这条路上做过(`cli/ledger/`、`WalletApi.signTransactionForCli` 的 Ledger 分支、`WalletApiWrapper` 的 GasFree 签名分支) | 若交互式 shell 也要保留 Ledger:先把 Ledger 相关代码**迁出** `cli/` 包并接到交互路,再删其余;若确认放弃:在 release note 里**明写「Java 版不再支持 Ledger」**,不能让它随包静默消失 | +| 2 | **`*ForCli` 方法族**——`processTransactionForCli` / `processTransactionExtentionForCli` / `signTransactionForCli` | 若只有 Standard CLI 调用:随包删;若交互路也在用:只删调用方、方法改名去掉 `ForCli` 后缀 | + +> 第 1 条的两种结论**都可接受,但都必须落在 release note 里**——不可接受的是没结论就删。 diff --git a/ts/src/adapters/outbound/config/builtins.ts b/ts/src/adapters/outbound/config/builtins.ts index 158984ff5..0f1cfc8fa 100644 --- a/ts/src/adapters/outbound/config/builtins.ts +++ b/ts/src/adapters/outbound/config/builtins.ts @@ -135,11 +135,18 @@ export const BUILTIN_NETWORKS: Record = { }, }; -/** The short name a person types, plus the id this CLI carried before its canonical ids became - * CAIP-2. A flat map, so global uniqueness is structural: a duplicate key cannot exist. Each - * short name precedes its legacy spelling because listings show the FIRST entry pointing at an - * id, and the short name is the one worth showing. There is deliberately no bare `evm` entry — - * EVM is a family, not a chain, so it has no mainnet to claim the family name. */ +/** The short name a person types, plus — for TRON only — the id this CLI carried before its + * canonical ids became CAIP-2. A flat map, so global uniqueness is structural: a duplicate key + * cannot exist. Each short name precedes its legacy spelling because listings show the FIRST + * entry pointing at an id, and the short name is the one worth showing. + * + * The EVM networks get no legacy entry: they were never part of a published release, so no + * config.yaml or script can be holding an `evm:56` spelling to keep working. An alias is a + * promise to resolve something forever, and one nobody can have written is only clutter in + * `config aliases`. + * + * There is deliberately no bare `evm` entry either — EVM is a family, not a chain, so it has no + * mainnet to claim the family name. */ export const BUILTIN_ALIASES: Record = { tron: "tron:728126428", "tron:mainnet": "tron:728126428", @@ -148,13 +155,9 @@ export const BUILTIN_ALIASES: Record = { shasta: "tron:2494104990", "tron:shasta": "tron:2494104990", ethereum: "eip155:1", - "evm:1": "eip155:1", sepolia: "eip155:11155111", - "evm:11155111": "eip155:11155111", bsc: "eip155:56", - "evm:56": "eip155:56", "bsc-testnet": "eip155:97", - "evm:97": "eip155:97", }; export const DEFAULT_CONFIG = { diff --git a/ts/src/domain/migration/tokens-v2.ts b/ts/src/domain/migration/tokens-v2.ts index fd7f87fae..1b2309e12 100644 --- a/ts/src/domain/migration/tokens-v2.ts +++ b/ts/src/domain/migration/tokens-v2.ts @@ -10,8 +10,16 @@ import type { TokensFile } from "../types/token.js"; export const TOKENS_VERSION = 2; -/** The ids this CLI carried before its canonical ids became CAIP-2. A network absent from this - * map is user-configured and keeps whatever key it already had. */ +/** + * The ids this CLI carried before its canonical ids became CAIP-2. A network absent from this map + * is user-configured and keeps whatever key it already had. + * + * The EVM ids are listed even though they never reached a published release, which is the reason + * they were dropped from the alias book. The two surfaces fail differently: an unresolvable id in + * config.yaml is an error the user reads and fixes, while an unmatched scope key here just yields + * an empty token list. Covering a branch build costs four lines; a silent loss costs someone their + * token book. + */ const RENAMED_NETWORK_IDS: Record = { "tron:mainnet": "tron:728126428", "tron:shasta": "tron:2494104990", From 6b5d69df6a759c97ecb56341d6e1fd1362663ef8 Mon Sep 17 00:00:00 2001 From: "Leon.Zhang" Date: Tue, 1 Sep 2026 14:51:30 +0800 Subject: [PATCH 7/8] docs: align remaining behavior references --- README.md | 10 +- java/README.md | 16 +-- java/docs/commands/contract.md | 8 +- java/docs/commands/index.md | 3 +- java/docs/commands/multisig.md | 2 + java/docs/commands/stake-v1-legacy.md | 7 +- java/docs/commands/stake-v2.md | 4 +- java/docs/commands/standard-cli.md | 97 +++++++++++++++++++ java/docs/commands/transfer-trc10.md | 2 +- java/docs/commands/vote-reward.md | 6 +- java/docs/commands/wallet.md | 2 +- java/docs/guide/command-flow.md | 8 +- java/docs/guide/getting-started.md | 8 +- java/docs/guide/index.md | 1 + java/docs/reference/config.md | 10 +- java/docs/standard-cli-contract-spec.md | 2 + ts/docs/commands/account/activate.md | 12 +-- ts/docs/commands/account/history.md | 13 ++- ts/docs/commands/account/info.md | 2 +- ts/docs/commands/account/set.md | 6 +- ts/docs/commands/asset/issue.md | 6 +- ts/docs/commands/asset/participate.md | 6 +- ts/docs/commands/asset/unfreeze.md | 6 +- ts/docs/commands/asset/update.md | 6 +- ts/docs/commands/backup.md | 6 +- ts/docs/commands/block.md | 2 +- ts/docs/commands/contact/add.md | 7 +- ts/docs/commands/contact/list.md | 4 +- ts/docs/commands/contract/clear-abi.md | 4 +- ts/docs/commands/contract/create2.md | 2 +- .../contract/set-origin-energy-limit.md | 4 +- .../contract/set-user-resource-percent.md | 4 +- ts/docs/commands/current.md | 3 +- ts/docs/commands/gasfree/info.md | 14 +-- ts/docs/commands/gasfree/trace.md | 12 ++- ts/docs/commands/gasfree/transfer.md | 39 +++++--- ts/docs/commands/import/keystore.md | 6 +- ts/docs/commands/import/ledger.md | 6 +- ts/docs/commands/import/mnemonic.md | 8 +- ts/docs/commands/import/private-key.md | 8 +- ts/docs/commands/import/watch.md | 6 +- ts/docs/commands/index.md | 2 +- ts/docs/commands/permission/show.md | 40 ++------ ts/docs/commands/permission/update.md | 30 +++--- ts/docs/commands/proposal/approve.md | 6 +- ts/docs/commands/proposal/create.md | 6 +- ts/docs/commands/proposal/delete.md | 6 +- ts/docs/commands/token/add.md | 2 +- ts/docs/commands/token/info.md | 33 ++++++- ts/docs/commands/token/remove.md | 2 +- ts/docs/commands/tx/info.md | 6 +- ts/docs/commands/tx/multisig.md | 25 +++-- ts/docs/commands/tx/sign.md | 18 ++-- ts/docs/commands/vote/cast.md | 10 +- ts/docs/commands/vote/list.md | 18 ++-- ts/docs/commands/vote/status.md | 10 +- ts/docs/commands/witness/create.md | 8 +- ts/docs/commands/witness/set-brokerage.md | 4 +- ts/docs/commands/witness/update.md | 4 +- ts/docs/concepts/security.md | 2 +- ts/docs/guide/ledger.md | 6 +- ts/docs/guide/scripting.md | 4 +- ts/docs/machine-interface.md | 14 +-- 63 files changed, 397 insertions(+), 237 deletions(-) create mode 100644 java/docs/commands/standard-cli.md diff --git a/README.md b/README.md index 363833d29..dd0bcd909 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ Both manage TRON wallets, but they are independent implementations rather than i | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **What it is** | The mature, full-feature reference CLI. | A newer rewrite focused on programmatic integration. | | **Runtime** | JVM — built with Gradle, run as a `.jar`. Uses the [Trident](https://github.com/tronprotocol/trident) SDK. | [Node.js](https://nodejs.org) **20+**. | -| **Install** | `git clone` + `./gradlew build` (see [Setup](java/README.md#setup)) | `npm install -g @tron-walletcli/wallet-cli` | +| **Install** | `git clone` + `cd wallet-cli/java && ./gradlew build` (see [Setup](java/README.md#setup)) | `npm install -g @tron-walletcli/wallet-cli` | | **How you drive it** | One-shot standard commands, or an interactive prompt when run without a command / with `--interactive`. | **One-shot subcommands** — `wallet-cli ` from your shell. Interactive prompts only for secret input. | -| **Command style** | PascalCase verbs: `RegisterWallet`, `SendCoin`, `GetBalance`. Amounts in **SUN** (1 TRX = 1,000,000 SUN). | Noun-verb subcommands: `create`, `tx send`, `account balance`, with `--flags`. | +| **Command style** | Kebab-case one-shot commands (`send-coin`) or PascalCase REPL verbs (`SendCoin`). Amounts in **SUN**. | Noun-verb subcommands: `create`, `tx send`, `account balance`, with `--flags`. | | **Output for scripts** | Text by default; standard mode supports `--output json` and structured success/error envelopes. | Stable JSON via `-o json` ([`wallet-cli.result.v1`](ts/docs/machine-interface.md)) + fixed exit codes (`0`/`1`/`2`). | -| **Config / networks** | `config.conf` (net type + full node), or `SwitchNetwork` at runtime. Mainnet · Nile · Shasta · custom. | `--network` flag / `config` command. Three TRON networks plus Ethereum, Sepolia, BNB Smart Chain, and its testnet. | +| **Config / networks** | `config.conf` endpoints, or `SwitchNetwork` at runtime. Mainnet · Nile · Shasta · custom. | `--network` flag / `config` command. Three TRON networks plus Ethereum, Sepolia, BNB Smart Chain, and its testnet. | | **Signing** | Software keystore · Ledger. | Encrypted local keystore · Ledger. Secrets enter via stdin/TTY, never argv or dedicated secret env vars. | | **Feature scope** | **The full surface** — wallets and transfers, staking, voting and rewards, governance, contracts, TRC10, and the on-chain exchange. | **The full surface** — HD wallets, TRX/TRC20/TRC10 transfers, staking & delegation, voting & rewards, governance proposals & super-representative operation, contract call/deploy/governance, TRC10 issuance, the on-chain Bancor exchange, multi-sig, GasFree transfers, message signing, and on-chain queries. | | **Best for** | People at a terminal who want every TRON capability. | Scripting, CI pipelines, and AI agents. | @@ -41,10 +41,10 @@ Build it, then either run a standard command or start the prompt: ```console $ git clone https://github.com/tronprotocol/wallet-cli.git -$ cd wallet-cli && ./gradlew build && cd build/libs +$ cd wallet-cli/java && ./gradlew build && cd build/libs $ java -jar wallet-cli.jar --output json --network nile get-balance --address T... $ java -jar wallet-cli.jar # opens the interactive prompt -> RegisterWallet 123456 # create a keystore (password 123456) +> RegisterWallet # prompts twice for the password, then for mnemonic length > Login # unlock it > GetAddress # your TRON address > GetBalance # TRX balance diff --git a/java/README.md b/java/README.md index 72ec9f94f..58f22f906 100644 --- a/java/README.md +++ b/java/README.md @@ -4,7 +4,7 @@ The original, full-featured implementation of wallet-cli. It supports both one-s > For what wallet-cli is and how this compares to the scriptable, JSON-first [TypeScript implementation](../ts/README.md), see the [repository overview](../README.md). -**Quick links:** [Setup](#setup) · [Quickstart](#quickstart) · [Commands](#commands) · [Understanding TRON mechanics](#understanding-tron-mechanics) · [Configuration](docs/reference/config.md) +**Quick links:** [Setup](#setup) · [Quickstart](#quickstart) · [Commands](#commands) · [Standard CLI](docs/commands/standard-cli.md) · [Understanding TRON mechanics](#understanding-tron-mechanics) · [Configuration](docs/reference/config.md) Need help? Join the [Telegram developer group](https://t.me/TronOfficialDevelopersGroupEn). @@ -18,7 +18,7 @@ git clone https://github.com/tronprotocol/wallet-cli.git ### Configuration -A minimal `config.conf` only needs a network type and a full node to talk to: +A minimal `config.conf` needs a full-node endpoint. `net.type` does not select the network; it only controls whether `grpc.mainnet.apiKey` is applied. The startup network is inferred from the configured node endpoints. ``` net { @@ -40,20 +40,20 @@ You can also switch networks at runtime with the [`SwitchNetwork`](docs/commands - **Compile and run**: ```console - $ cd wallet-cli + $ cd wallet-cli/java $ ./gradlew build $ cd build/libs $ java -jar wallet-cli.jar --help ``` -With no command, wallet-cli opens the legacy interactive prompt. With a command, it uses the standard CLI; `--interactive` selects the prompt explicitly. Standard mode accepts global options such as `--network `, `--wallet`, `--grpc-endpoint`, and `--output `: +With no arguments, wallet-cli opens the legacy interactive prompt. Any command selects the standard CLI; `--interactive` selects the prompt explicitly. Global options without a command, except supported modes such as `--help` and `--version`, are a usage error. Standard mode accepts options such as `--network `, `--wallet`, `--grpc-endpoint`, and `--output `: ```console $ java -jar wallet-cli.jar --output json --network nile get-balance --address T... $ java -jar wallet-cli.jar --interactive ``` -wallet-cli connects to java-tron via the gRPC protocol, which can be deployed locally or remotely. Configure the java-tron node IP and port in `src/main/resources/config.conf`, or use `SwitchNetwork` to switch among mainnet, testnets (Nile and Shasta), and custom networks. +wallet-cli connects to java-tron via gRPC. At startup it first looks for `config.conf` in the current working directory, then falls back to the bundled classpath resource. Use `SwitchNetwork` to switch among mainnet, testnets (Nile and Shasta), and custom networks. ## Quickstart @@ -62,13 +62,13 @@ This quickstart uses the interactive prompt. For automation, pass a standard com ```console # 1. Build $ git clone https://github.com/tronprotocol/wallet-cli.git -$ cd wallet-cli && ./gradlew build && cd build/libs +$ cd wallet-cli/java && ./gradlew build && cd build/libs # 2. Start the interactive wallet $ java -jar wallet-cli.jar # 3. In the wallet prompt: create an account (or ImportWallet), unlock, and inspect it -> RegisterWallet 123456 # create a keystore with password 123456 +> RegisterWallet # prompts twice for the password, then for mnemonic length > Login # unlock the account > GetAddress # show your address > GetBalance # TRX balance @@ -83,7 +83,7 @@ The full first-run walkthrough is in the [getting-started guide](docs/guide/gett ## Commands -Every command is documented on a family page under [docs/commands/](docs/commands/index.md). The **[command index](docs/commands/index.md)** has the full A–Z list linking each command to its section; in the wallet, typing any command shows its built-in usage tips. +Legacy interactive commands are documented on family pages under [docs/commands/](docs/commands/index.md). The **[interactive command index](docs/commands/index.md)** links each PascalCase command to its section. The separate **[standard CLI catalog](docs/commands/standard-cli.md)** lists the 107 one-shot commands and their global invocation contract. ### Wallets & accounts diff --git a/java/docs/commands/contract.md b/java/docs/commands/contract.md index 224143f31..6b47145fb 100644 --- a/java/docs/commands/contract.md +++ b/java/docs/commands/contract.md @@ -111,18 +111,20 @@ Example: ## TriggerConstantContract ```console -> TriggerConstantContract [ownerAddress] contractAddress method args isHex fee_limit value token_value token_id +> TriggerConstantContract ownerAddress contractAddress method args isHex [value token_value token_id] ``` -- `OwnerAddress` — the address of the account that initiated the transaction, optional, default is the address of the login account. +- `ownerAddress` — required. Pass a base58 address, or `#` to use the logged-in account. - `contractAddress` — smart contract address. - `method` — the name of the function and parameters; refer to the example. - `args` — parameter value; if you want to call `receive`, pass `#` instead. - `isHex` — the format of the parameters `method` and `args`; hex string or not. -- `fee_limit` — the most TRX allowed for consumption. +- `value` — optional call value in SUN; when supplied, `token_value` and `token_id` are required too. - `token_value` — number of TRC10. - `token_id` — TRC10 id; if not, use `#` instead. +The command accepts exactly five parameters without value/token fields, or eight parameters with all three optional fields. It does not take `fee_limit`. + Example: ```console diff --git a/java/docs/commands/index.md b/java/docs/commands/index.md index 66aef7619..0dcc215c3 100644 --- a/java/docs/commands/index.md +++ b/java/docs/commands/index.md @@ -2,12 +2,13 @@ Commands are grouped into family pages below; the A–Z index links each command to its owning page. Every family page is populated. Links point to the owning page (open it and jump to the command's section). -Type any command in the interactive wallet to see its built-in usage tips. +This page indexes legacy interactive commands. For one-shot kebab-case commands, global flags, and JSON output, use the [standard CLI reference](standard-cli.md). Type any command in the interactive wallet to see its built-in usage tips. ## By family | Family | Page | |---|---| +| Standard one-shot CLI | [standard-cli.md](standard-cli.md) | | Wallet management | [wallet.md](wallet.md) | | Account commands | [account.md](account.md) | | Network | [network.md](network.md) | diff --git a/java/docs/commands/multisig.md b/java/docs/commands/multisig.md index 2a514d2f1..7046d19da 100644 --- a/java/docs/commands/multisig.md +++ b/java/docs/commands/multisig.md @@ -2,6 +2,8 @@ Configure account permissions, co-sign transactions, inspect signature weight, and use TronLink multi-sign. For the underlying permission model, see [concepts/multisig](../concepts/multisig.md). +Many legacy REPL write commands accept `-m` only as their final token. That switch routes the operation through the interactive multi-sign flow instead of the normal single-signer broadcast. Support is command-specific; use the command's built-in usage text before appending it. The one-shot standard CLI uses command options documented by ` --help` and does not inherit this trailing-token convention. + ## How to use the multi-signature feature of wallet-cli Multi-signature allows other users to access the account in order to better manage it. There are three types of access: diff --git a/java/docs/commands/stake-v1-legacy.md b/java/docs/commands/stake-v1-legacy.md index aab565883..c2ab6e215 100644 --- a/java/docs/commands/stake-v1-legacy.md +++ b/java/docs/commands/stake-v1-legacy.md @@ -11,12 +11,13 @@ After the funds are frozen, the corresponding number of shares and bandwidth wil **Freeze operation is as follows:** ```console -> freezeBalance [OwnerAddress] frozen_balance frozen_duration [ResourceCode:0 BANDWIDTH, 1 ENERGY] [receiverAddress] +> freezeBalance [OwnerAddress] frozen_balance frozen_duration [ResourceCode:0 BANDWIDTH, 1 ENERGY, 2 TRON_POWER] [receiverAddress] ``` - `OwnerAddress` — the address of the account that initiated the transaction, optional, default is the address of the login account. - `frozen_balance` — the amount of frozen funds, the unit is Sun. The minimum value is **1000000 Sun (1 TRX)**. - `frozen_duration` — freeze time, this value is currently only allowed for **3 days**. +- `ResourceCode` — `0` BANDWIDTH; `1` ENERGY; `2` TRON_POWER only when `getAllowNewResourceModel` is enabled. TRON_POWER cannot be delegated, so omit `receiverAddress` when using `2`. For example: @@ -33,7 +34,7 @@ After the freezing time expires, funds can be unfrozen. **Unfreeze operation is as follows:** ```console -> unfreezeBalance [OwnerAddress] ResourceCode(0 BANDWIDTH, 1 CPU) [receiverAddress] +> unfreezeBalance [OwnerAddress] ResourceCode(0 BANDWIDTH, 1 ENERGY, 2 TRON_POWER) [receiverAddress] ``` ## How to delegate resource @@ -55,7 +56,7 @@ The latter two parameters are optional. If not set, the TRX is frozen to obtain ### UnfreezeBalance (undelegate) ```console -> unfreezeBalance [OwnerAddress] ResourceCode(0 BANDWIDTH, 1 CPU) [receiverAddress] +> unfreezeBalance [OwnerAddress] ResourceCode(0 BANDWIDTH, 1 ENERGY) [receiverAddress] ``` The latter two parameters are optional. If they are not set, the BANDWIDTH resource is unfrozen by default; when the `receiverAddress` is set, the delegated resources are unfrozen. diff --git a/java/docs/commands/stake-v2.md b/java/docs/commands/stake-v2.md index 42debeb2f..40e46b545 100644 --- a/java/docs/commands/stake-v2.md +++ b/java/docs/commands/stake-v2.md @@ -12,7 +12,7 @@ FreezeV2-based staking, resource delegation, and unfreeze withdrawal — the cur - `OwnerAddress` — the address of the account that initiated the transaction, optional, default is the address of the login account. - `frozen_balance` — the amount of frozen, the unit is the smallest unit (Sun), the minimum is 1000000 sun. -- `ResourceCode` — 0 BANDWIDTH; 1 ENERGY. +- `ResourceCode` — `0` BANDWIDTH; `1` ENERGY; `2` TRON_POWER only when `getAllowNewResourceModel` is enabled. Example: @@ -60,7 +60,7 @@ wallet> GetTransactionById 82244829971b4235d98a9f09ba67ddb09690ac2f879ad93e09ba - `OwnerAddress` — the address of the account that initiated the transaction, optional, default is the address of the login account. - `unfreezeBalance` — the amount of unfreeze, the unit is the smallest unit (Sun). -- `ResourceCode` — 0 BANDWIDTH; 1 ENERGY. +- `ResourceCode` — `0` BANDWIDTH; `1` ENERGY; `2` TRON_POWER only when `getAllowNewResourceModel` is enabled. Example: diff --git a/java/docs/commands/standard-cli.md b/java/docs/commands/standard-cli.md new file mode 100644 index 000000000..5c063c598 --- /dev/null +++ b/java/docs/commands/standard-cli.md @@ -0,0 +1,97 @@ +# Standard CLI command reference + +The Java jar has a one-shot CLI in addition to the legacy interactive prompt. Any invocation with a command token uses this mode: + +```console +$ java -jar wallet-cli.jar [global options] [command options] +$ java -jar wallet-cli.jar --network nile --output json get-balance --address T... +$ java -jar wallet-cli.jar get-balance --help +``` + +Running the jar with no arguments opens the prompt. Use `--interactive` as a standalone mode selector to open it explicitly. If a command token follows `--interactive`, the prompt still opens and that command is not executed; placing `--interactive` after a command instead passes it to that command and normally produces a usage error. A global option without a command, except `--help`, `--version`, or `--interactive`, returns a usage error with exit `2`. + +## Global options + +| Option | Meaning | +|---|---| +| `--output ` | Output format; default `text` | +| `--network ` | Select a built-in network or custom endpoint set | +| `--wallet ` | Select the wallet used by wallet-bound commands | +| `--grpc-endpoint ` | Override the gRPC endpoint | +| `--quiet` / `--verbose` | Suppress non-essential output or enable diagnostics; mutually exclusive | +| `--password-stdin` | Read the master password from stdin for commands that need one | +| `--help`, `-h`, `--version` | Global help or version when placed before the command | +| `--interactive` | Launch the legacy prompt; use without a command | + +Global execution options may appear before or after the command. Put command-specific options after the command and use ` --help` as the authority for required fields and authentication. + +JSON mode emits `{success, data}` on success or `{success, error, message}` on failure; alias resolution may add `meta.resolved`. Exit codes are `0` for success, `1` for execution failure, and `2` for usage errors. + +For password-bearing commands, keep the secret out of argv: + +```console +$ printf '%s\n' "$PW" | java -jar wallet-cli.jar --network nile --password-stdin send-coin --to T... --amount 1000000 +$ printf '%s\n' "$PW" | java -jar wallet-cli.jar --password-stdin register-wallet --name main --words 12 +``` + +## Wallet and alias commands + +These commands exist only in the standard CLI and do not have equivalent legacy REPL verbs: + +```console +$ java -jar wallet-cli.jar list-wallet +$ java -jar wallet-cli.jar set-active-wallet --name treasury +$ java -jar wallet-cli.jar get-active-wallet +$ java -jar wallet-cli.jar --network nile alias-add --name payroll --type ACCOUNT --address T... --note "operations" +$ java -jar wallet-cli.jar --network nile alias-list --type ACCOUNT +$ java -jar wallet-cli.jar --network nile alias-resolve --name payroll --type ACCOUNT +$ java -jar wallet-cli.jar --network nile alias-remove --name payroll +``` + +`set-active-wallet` requires exactly one of `--name` or `--address`. Alias data is network-scoped. `alias-add --type TOKEN` accepts `--decimals`; `--note` is valid only for `ACCOUNT` aliases. + +## Registered commands + +The registry currently contains 107 primary command names. Aliases are accepted by the parser but omitted here; global help prints the current primary catalog. + +### Wallets and aliases (12) + +`register-wallet`, `list-wallet`, `set-active-wallet`, `get-active-wallet`, `clear-wallet-keystore`, `reset-wallet`, `modify-wallet-name`, `generate-sub-account`, `alias-add`, `alias-remove`, `alias-list`, `alias-resolve` + +### Transactions (12) + +`send-coin`, `transfer-asset`, `transfer-usdt`, `participate-asset-issue`, `asset-issue`, `create-account`, `update-account`, `set-account-id`, `update-asset`, `broadcast-transaction`, `update-account-permission`, `gas-free-transfer` + +### Contracts (7) + +`deploy-contract`, `trigger-contract`, `trigger-constant-contract`, `estimate-energy`, `clear-contract-abi`, `update-setting`, `update-energy-limit` + +### Staking and rewards (10) + +`freeze-balance`, `freeze-balance-v2`, `unfreeze-balance`, `unfreeze-balance-v2`, `withdraw-expire-unfreeze`, `delegate-resource`, `undelegate-resource`, `cancel-all-unfreeze-v2`, `withdraw-balance`, `unfreeze-asset` + +### Witnesses and voting (4) + +`create-witness`, `update-witness`, `vote-witness`, `update-brokerage` + +### Governance proposals (3) + +`create-proposal`, `approve-proposal`, `delete-proposal` + +### Exchange and market (5) + +`exchange-create`, `exchange-inject`, `exchange-withdraw`, `market-sell-asset`, `market-cancel-order` + +### Queries (53) + +`get-address`, `get-balance`, `get-account`, `get-account-by-id`, `get-account-net`, `get-account-resource`, `get-usdt-balance`, `current-network`, `get-block`, `get-block-by-id`, `get-block-by-id-or-num`, `get-block-by-latest-num`, `get-block-by-limit-next`, `get-transaction-by-id`, `get-transaction-info-by-id`, `get-transaction-count-by-block-num`, `get-asset-issue-by-account`, `get-asset-issue-by-id`, `get-asset-issue-by-name`, `get-asset-issue-list-by-name`, `get-chain-parameters`, `get-bandwidth-prices`, `get-energy-prices`, `get-memo-fee`, `get-next-maintenance-time`, `get-contract`, `get-contract-info`, `get-delegated-resource`, `get-delegated-resource-v2`, `get-delegated-resource-account-index`, `get-delegated-resource-account-index-v2`, `get-can-delegated-max-size`, `get-available-unfreeze-count`, `get-can-withdraw-unfreeze-amount`, `get-brokerage`, `get-reward`, `list-nodes`, `list-witnesses`, `list-asset-issue`, `list-asset-issue-paginated`, `list-proposals`, `list-proposals-paginated`, `get-proposal`, `list-exchanges`, `list-exchanges-paginated`, `get-exchange`, `get-market-order-by-account`, `get-market-order-by-id`, `get-market-order-list-by-pair`, `get-market-pair-list`, `get-market-price-by-pair`, `gas-free-info`, `gas-free-trace` + +### Utility (1) + +`help` + +## See also + +- [Interactive command index](index.md) +- [Standard CLI contract](../standard-cli-contract-spec.md) +- [Configuration](../reference/config.md) diff --git a/java/docs/commands/transfer-trc10.md b/java/docs/commands/transfer-trc10.md index 3492cafbe..078476850 100644 --- a/java/docs/commands/transfer-trc10.md +++ b/java/docs/commands/transfer-trc10.md @@ -170,7 +170,7 @@ assetV2 Query the list of all the tokens by pagination. Returns a list of tokens that succeed the token located at offset. ```console -> ListAssetIssuePaginated address code salt +> ListAssetIssuePaginated offset limit ``` Example: diff --git a/java/docs/commands/vote-reward.md b/java/docs/commands/vote-reward.md index 38ff86fff..beec5ec9a 100644 --- a/java/docs/commands/vote-reward.md +++ b/java/docs/commands/vote-reward.md @@ -14,7 +14,7 @@ Voting requires share. Share can be obtained by freezing funds. For example: ```console -> freezeBalance 100000000 3 1 address # Freeze 10TRX and acquire 10 units of shares +> freezeBalance 10000000 3 1 address # Freeze 10 TRX and acquire 10 units of shares > votewitness 123455 witness1 4 witness2 6 # Cast 4 votes for witness1 and 6 votes for witness2 at the same time @@ -97,7 +97,7 @@ Apply to become a super representative candidate. ``` ```console -> CreateWitness TEDapYSVvAZ3aYH7w8N9tMEEFKaNKUD5Bp 007570646174654e616d6531353330363038383733343633 +> CreateWitness TEDapYSVvAZ3aYH7w8N9tMEEFKaNKUD5Bp https://sr.example.com ``` ### UpdateWitness @@ -105,7 +105,7 @@ Apply to become a super representative candidate. Edit the URL of the SR's official website. ```console -> UpdateWitness TEDapYSVvAZ3aYH7w8N9tMEEFKaNKUD5Bp 007570646174654e616d6531353330363038383733343633 +> UpdateWitness TEDapYSVvAZ3aYH7w8N9tMEEFKaNKUD5Bp https://sr.example.com/v2 ``` ## ListWitnesses diff --git a/java/docs/commands/wallet.md b/java/docs/commands/wallet.md index b7a8b4e1f..f24ab3bfc 100644 --- a/java/docs/commands/wallet.md +++ b/java/docs/commands/wallet.md @@ -73,7 +73,7 @@ Import a derived account from a Ledger device into wallet-cli. ```console wallet> ImportWalletByLedger -((Note:This will pair Ledger to user your hardward wallet) +(Note:This will pair Ledger to user your hardware wallet) Only one Ledger device is supported. If you have multiple devices, please ensure only one is connected. Ledger device found: Nano X Please input password. diff --git a/java/docs/guide/command-flow.md b/java/docs/guide/command-flow.md index 8fbbf2ae3..20eda6382 100644 --- a/java/docs/guide/command-flow.md +++ b/java/docs/guide/command-flow.md @@ -3,14 +3,14 @@ A worked end-to-end example of the legacy interactive session: build and run, register, back up, inspect, issue an asset, and transfer it. For one-shot commands and JSON output, see [Getting started](getting-started.md#standard-cli). ```console -$ cd wallet-cli +$ cd wallet-cli/java $ ./gradlew build $ ./gradlew run -> RegisterWallet 123456 (password = 123456) -> login 123456 +> RegisterWallet (prompts twice for the password, then for mnemonic length) +> login (prompts for the password) > getAddress address = TRfwwLDpr4excH4V4QzghLEsdYwkapTxnm' # backup it! -> BackupWallet 123456 +> BackupWallet (prompts for the password) priKey = 1234567890123456789012345678901234567890123456789012345678901234 # backup it!!! (BackupWallet2Base64 option) > getbalance Balance = 0 diff --git a/java/docs/guide/getting-started.md b/java/docs/guide/getting-started.md index 4448b4f1d..a14b0d1cd 100644 --- a/java/docs/guide/getting-started.md +++ b/java/docs/guide/getting-started.md @@ -9,13 +9,13 @@ Build, create an account, and send your first transfer from the interactive prom ```console # 1. Build $ git clone https://github.com/tronprotocol/wallet-cli.git -$ cd wallet-cli && ./gradlew build && cd build/libs +$ cd wallet-cli/java && ./gradlew build && cd build/libs # 2. Start the interactive wallet $ java -jar wallet-cli.jar # 3. In the wallet prompt: create an account (or ImportWallet), unlock, and inspect it -> RegisterWallet 123456 # create a keystore with password 123456 +> RegisterWallet # prompts twice for the password, then for mnemonic length > Login # unlock the account > GetAddress # show your address > GetBalance # TRX balance @@ -32,10 +32,10 @@ Passing a command selects the standard CLI instead of the prompt. It supports te ```console $ java -jar wallet-cli.jar --output json --network nile get-balance --address T... -$ java -jar wallet-cli.jar --network nile send-coin --to T... --amount 1000000 --password-stdin +$ printf '%s\n' "$PW" | java -jar wallet-cli.jar --network nile --password-stdin send-coin --to T... --amount 1000000 ``` -Run `java -jar wallet-cli.jar --help` for the command catalog and ` --help` for command options. The parsing, authentication, JSON envelope, and exit behavior are defined in the [standard CLI contract](../standard-cli-contract-spec.md). +Run `java -jar wallet-cli.jar --help` for the command catalog and ` --help` for command options. The [standard CLI command reference](../commands/standard-cli.md) lists all one-shot commands; parsing, authentication, JSON envelopes, and exit behavior are defined in the [standard CLI contract](../standard-cli-contract-spec.md). ## How to create account diff --git a/java/docs/guide/index.md b/java/docs/guide/index.md index 77c6ff100..22c2bf2d7 100644 --- a/java/docs/guide/index.md +++ b/java/docs/guide/index.md @@ -6,5 +6,6 @@ Task-oriented walkthroughs for wallet-cli (Java). |---|---| | [Getting started](getting-started.md) | Standard CLI and interactive entry modes; build, create an account, and send TRX | | [Command-line operation flow](command-flow.md) | A worked end-to-end legacy interactive session | +| [Standard CLI command reference](../commands/standard-cli.md) | One-shot commands, global flags, JSON mode, and the full registry | For per-command reference, see the [command index](../commands/index.md); for TRON mechanics, see [concepts](../concepts/index.md). diff --git a/java/docs/reference/config.md b/java/docs/reference/config.md index f2b4622bd..0b33975cb 100644 --- a/java/docs/reference/config.md +++ b/java/docs/reference/config.md @@ -1,10 +1,10 @@ # Configuration reference -Full reference for `config.conf`. wallet-cli reads the node config from `src/main/resources/config.conf`. You can also switch networks at runtime with the [`SwitchNetwork`](../commands/network.md) command, so editing `config.conf` is only needed for a custom node or the advanced features below. +Full reference for `config.conf`. At startup, wallet-cli first reads `./config.conf` from the process working directory. If that file does not exist, it loads the bundled classpath resource (`src/main/resources/config.conf` in a source checkout). You can also switch networks at runtime with [`SwitchNetwork`](../commands/network.md). ## Minimal config -A minimal `config.conf` only needs a network type and a full node to talk to: +A minimal `config.conf` needs a full-node endpoint. Keeping `net.type` is useful when configuring a mainnet API key, but it does not select the active network: ``` net { @@ -99,7 +99,7 @@ tronlink = { | Field | Purpose | |---|---| -| `net.type` | Network type (e.g. `mainnet`). | +| `net.type` | Controls whether `grpc.mainnet.apiKey` is loaded. It does not select the active network. | | `fullnode.ip.list` | Full node endpoint(s) `ip : port`. | | `soliditynode.ip.list` | Optional Solidity node endpoint(s). | | `ledger_debug` | Enable Ledger debug output. | @@ -111,7 +111,9 @@ tronlink = { ## Connecting to Java-tron -wallet-cli connects to Java-tron via the gRPC protocol, which can be deployed locally or remotely. Configure the Java-tron node IP and port in `src/main/resources/config.conf` so wallet-cli can talk to the node. You can also use `SwitchNetwork` to switch among mainnet, testnets (Nile and Shasta), and custom networks — see [commands/network](../commands/network.md). +wallet-cli connects to Java-tron via gRPC. The startup network is inferred by comparing `fullnode.ip.list` and `soliditynode.ip.list` with the built-in Mainnet, Nile, and Shasta endpoints; any other pair is `CUSTOM`. Consequently, `net.type = mainnet` with Nile endpoints still starts on Nile. Check the endpoints themselves before sending funds. + +To override the bundled file without rebuilding the jar, place `config.conf` in the directory from which you launch `java -jar`. You can also use `SwitchNetwork` to switch among mainnet, Nile, Shasta, and custom endpoints at runtime — see [commands/network](../commands/network.md). ## See also diff --git a/java/docs/standard-cli-contract-spec.md b/java/docs/standard-cli-contract-spec.md index 02a4f9e46..4a5ac7c54 100644 --- a/java/docs/standard-cli-contract-spec.md +++ b/java/docs/standard-cli-contract-spec.md @@ -113,6 +113,7 @@ Supported global options are: - `--version` - `--quiet` - `--verbose` + - `--password-stdin` - valued options: - `--output ` - `--network ` @@ -129,6 +130,7 @@ For Contract 1, this applies to valued global options only. ### Boundary Rules - Execution modifier global options are recognized before and after the command token. +- `--password-stdin` is an execution modifier and is recognized in either position. - Top-level mode selectors `--version` and `--interactive` are recognized only before the command token. - The first token before command resolution that does not begin with `-` is the command token. - The command token is normalized to lowercase for registry lookup. diff --git a/ts/docs/commands/account/activate.md b/ts/docs/commands/account/activate.md index 0810db993..9aeaaaff1 100644 --- a/ts/docs/commands/account/activate.md +++ b/ts/docs/commands/account/activate.md @@ -46,11 +46,11 @@ echo "$PW" | wallet-cli account activate --address TNewAddr9k2fP7cW4bXm1sV8dRj6e ``` ```console -⏳ Submitted — activate account - TxID a1b... +⏳ Account activated Address TNewAddr9k2fP7cW4bXm1sV8dRj6eL3aQz - Payer TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw (main) - Status pending + Payer TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw + TxID a1b... + Status pending — not yet on-chain ! Track it: wallet-cli tx info --network tron:nile --txid a1b... ``` @@ -66,9 +66,9 @@ echo "$PW" | wallet-cli account activate --address TNewAddr9k2fP7cW4bXm1sV8dRj6e ```console ✅ Account activated - TxID e7a... Address TNewAddr9k2fP7cW4bXm1sV8dRj6eL3aQz - Payer TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw (main) + Payer TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw + TxID e7a... Block #84,340,277 Fee 1.1 TRX Status success diff --git a/ts/docs/commands/account/history.md b/ts/docs/commands/account/history.md index 10eb675b8..cdb3cccac 100644 --- a/ts/docs/commands/account/history.md +++ b/ts/docs/commands/account/history.md @@ -10,14 +10,17 @@ wallet-cli account history [--limit ] [--only ] [options] ## Description -Lists recent transfers touching the account, newest first. TRON only — there is no EVM binding, so on an EVM network the command fails with `family_mismatch` rather than returning an empty list. History is served by **TronGrid**, not plain node RPC, so on TRON networks/endpoints without TronGrid it fails while `balance`/`info` still work. +Lists recent activity touching the account, newest first. TRON only — there is no EVM binding, so on an EVM network the command fails with `family_mismatch` rather than returning an empty list. History is served by **TronGrid**, not plain node RPC, so on TRON networks/endpoints without TronGrid it fails while `balance`/`info` still work. + +`--only token` selects TronGrid's TRC20 transfer endpoint. The current `--only native` path uses the general transactions endpoint and does not post-filter its records, so it may include non-native contract activity; omitting `--only` uses that same endpoint. Do not treat `only: "native"` in JSON as proof that every returned record is a TRX transfer. ## Options | Option | Description | |---|---| | `--limit ` | Max records, 1–200 (default 20) | -| `--only ` | Filter by transfer type; omit for all | +| `--only token` | Query TRC20 transfer history | +| `--only native` | Select the general transaction endpoint; currently not a strict native-transfer filter | Plus the [global options](../index.md#global-options-every-command). @@ -41,14 +44,14 @@ wallet-cli account history --limit 2 --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"account.history","data":{"address":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ","only":"all","count":2,"records":[{"txId":"fb7f8e6b44cd9100f6d1133acea341a2f3d53ab140a93c95b8f2bd74d3a2b366","time":1783780503000,"type":"Transfer","amount":"1","symbol":"TRX","from":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ","to":"TGkbaCYB4kRBc3Q6wjqkACefUvRwf2KzkH","counterparty":"TGkbaCYB4kRBc3Q6wjqkACefUvRwf2KzkH","status":"ok"},…]},"meta":{"durationMs":1556,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"account.history","data":{"address":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ","only":"all","count":2,"records":[{"txId":"fb7f8e6b44cd9100f6d1133acea341a2f3d53ab140a93c95b8f2bd74d3a2b366","time":1783780503000,"type":"Transfer","amount":"1","symbol":"TRX","from":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ","to":"TGkbaCYB4kRBc3Q6wjqkACefUvRwf2KzkH","counterparty":"TGkbaCYB4kRBc3Q6wjqkACefUvRwf2KzkH","status":"ok"},{"txId":"aa9c6d96b582201bda4ca1f7f35eff597371f5ca8e99db0df78d02d78f668a31","time":1783779301000,"type":"Transfer","amount":"2","symbol":"TRX","from":"TGkbaCYB4kRBc3Q6wjqkACefUvRwf2KzkH","to":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ","counterparty":"TGkbaCYB4kRBc3Q6wjqkACefUvRwf2KzkH","status":"ok"}]},"meta":{"durationMs":1556,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output | Field | Type | Meaning | |---|---|---| -| `address` / `only` / `count` | — | Query echo and record count | +| `address` / `only` / `count` | — | Query echo and returned record count; `only` echoes the selector and does not strengthen the filtering guarantee above | | `records[].txId` | string | Feed to [`tx info`](../tx/info.md) for detail | | `records[].time` | number | Epoch ms | | `records[].type` | string | Transaction type (e.g. `Transfer`, `CreateSmart`) | @@ -59,7 +62,7 @@ wallet-cli account history --limit 2 --network tron:nile -o json ## Exit status -`0` · `1` execution failure (incl. TronGrid unavailable) · `2` usage error (limit out of 1–200). +`0` · `1` execution failure (`history_not_supported`, including a missing or incompatible TronGrid endpoint) · `2` usage error (limit out of 1–200). ## See also diff --git a/ts/docs/commands/account/info.md b/ts/docs/commands/account/info.md index 22d302b26..fa875fdf5 100644 --- a/ts/docs/commands/account/info.md +++ b/ts/docs/commands/account/info.md @@ -40,7 +40,7 @@ wallet-cli account info --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"account.info","data":{"address":"TNmoJ3Be59WFEq5dsW6eCkZjveiL3G8HVB","account":{"account_name":"71612d74657374","balance":"9915803110","create_time":1753860222000,"owner_permission":{…},"active_permission":[…],"frozenV2":[{},{"type":"ENERGY"},{"type":"TRON_POWER"}],…},"resources":{"bandwidth":{"used":325,"limit":600},"energy":{"used":0,"limit":0}}},"meta":{"durationMs":746,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"account.info","data":{"address":"TNmoJ3Be59WFEq5dsW6eCkZjveiL3G8HVB","account":{"account_name":"71612d74657374","balance":"9915803110","create_time":1753860222000,"owner_permission":{},"active_permission":[{}],"frozenV2":[{},{"type":"ENERGY"},{"type":"TRON_POWER"}]},"resources":{"bandwidth":{"used":325,"limit":600},"energy":{"used":0,"limit":0}}},"meta":{"durationMs":746,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` On an EVM network the same command reports the EVM account state: diff --git a/ts/docs/commands/account/set.md b/ts/docs/commands/account/set.md index d10ee2b4c..5ef073392 100644 --- a/ts/docs/commands/account/set.md +++ b/ts/docs/commands/account/set.md @@ -48,7 +48,7 @@ echo "$PW" | wallet-cli account set --name "Acme Treasury" --network tron:nile - ```console ✅ On-chain name set - Account TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw (main) + Address TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw Name Acme Treasury TxID f2b... Block #84,341,590 @@ -72,8 +72,8 @@ echo "$PW" | wallet-cli account set --id acme-treasury-01 --network tron:nile -- ```console ✅ Account id set - Account TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw (main) - Id acme-treasury-01 + Address TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw + ID acme-treasury-01 TxID 3d9... Block #84,341,730 Fee 0.3 TRX diff --git a/ts/docs/commands/asset/issue.md b/ts/docs/commands/asset/issue.md index 55be9419d..d31bf4d9a 100644 --- a/ts/docs/commands/asset/issue.md +++ b/ts/docs/commands/asset/issue.md @@ -96,7 +96,7 @@ echo "$PW" | wallet-cli asset issue --name MyToken --abbr MTK --supply 100000000 ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"asset.issue","data":{"kind":"asset-issue","stage":"confirmed","txId":"7d1...","confirmed":true,"blockNumber":57883010,"failed":false,"assetId":"1000123","name":"MyToken","abbr":"MTK","totalSupply":1000000000000000,"precision":6,"price":"1:100","trxNum":1,"num":100,"startTime":1785542400000,"endTime":1788134400000,"url":"https://mytoken.io","description":"Demo TRC10","freeAssetNetLimit":0,"publicFreeAssetNetLimit":0,"frozenSupply":[{"amount":100000000000000,"days":30},{"amount":50000000000000,"days":90}],"feeSun":1024000000,"resource":{"netUsage":312,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6720,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"asset.issue","data":{"kind":"asset-issue","stage":"confirmed","txId":"7d1...","confirmed":true,"blockNumber":57883010,"feeSun":1024000000,"netUsed":312,"netFeeSun":0,"failed":false,"assetId":"1000123","issuerAddress":"TQkXm4vN...","name":"MyToken","abbr":"MTK","totalSupply":"1000000000000000","precision":6,"price":"1:100","trxNum":1,"num":100,"startTime":1785542400000,"endTime":1788134400000,"url":"https://mytoken.io","description":"Demo TRC10","freeAssetNetLimit":0,"publicFreeAssetNetLimit":0,"frozenSupply":[{"amount":"100000000000000","days":30},{"amount":"50000000000000","days":90}]},"meta":{"durationMs":6720,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -106,9 +106,9 @@ echo "$PW" | wallet-cli asset issue --name MyToken --abbr MTK --supply 100000000 | Stage | Fields | |---|---| | default (submit) | `kind: "asset-issue"`, `stage: "submitted"`, `txId`, and the token definition below except `assetId` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed`, and `assetId` — assigned by the chain, so known only once confirmed | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), `failed`, and `assetId` — assigned by the chain, so known only once confirmed | -Definition fields: `name`, `abbr`, `totalSupply` (raw), `precision`, `price` (the `trx:tokens` string as given) with the stored `trxNum` / `num` pair, `startTime` / `endTime` (ms since epoch), `url`, `description`, `freeAssetNetLimit`, `publicFreeAssetNetLimit`, and `frozenSupply[]` (`amount` raw, `days`). +Definition fields: `issuerAddress`, `name`, `abbr`, `totalSupply` (raw decimal string), `precision`, `price` (the `trx:tokens` string as given) with the stored `trxNum` / `num` pair, `startTime` / `endTime` (ms since epoch), `url`, `description`, `freeAssetNetLimit`, `publicFreeAssetNetLimit`, and `frozenSupply[]` (`amount` raw decimal string, `days`). Confirmation resource fields are flat; there is no `resource` object and the bandwidth field is `netUsed`, not `netUsage`. ## Exit status diff --git a/ts/docs/commands/asset/participate.md b/ts/docs/commands/asset/participate.md index 7b82ab30e..43a86efb8 100644 --- a/ts/docs/commands/asset/participate.md +++ b/ts/docs/commands/asset/participate.md @@ -66,7 +66,7 @@ echo "$PW" | wallet-cli asset participate 1000124 --pay 100 --network tron:nile ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"asset.participate","data":{"kind":"asset-participate","stage":"confirmed","txId":"4c8...","confirmed":true,"blockNumber":57883402,"failed":false,"assetId":"1000124","name":"BetaToken","issuerAddress":"TBeta9mR...","participantAddress":"TQkXm4vN...","paidSun":100000000,"receivedAmount":10000000000,"feeSun":0,"resource":{"netUsage":301,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6450,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"asset.participate","data":{"kind":"asset-participate","stage":"confirmed","txId":"4c8...","confirmed":true,"blockNumber":57883402,"feeSun":0,"netUsed":301,"netFeeSun":0,"failed":false,"assetId":"1000124","name":"BetaToken","issuerAddress":"TBeta9mR...","participantAddress":"TQkXm4vN...","paidSun":"100000000","receivedAmount":"10000000000","precision":6},"meta":{"durationMs":6450,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -76,9 +76,9 @@ echo "$PW" | wallet-cli asset participate 1000124 --pay 100 --network tron:nile | Stage | Fields | |---|---| | default (submit) | `kind: "asset-participate"`, `stage: "submitted"`, `txId`, `assetId`, `name`, `issuerAddress`, `participantAddress`, `paidSun`, `receivedAmount` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), and `failed` | -`paidSun` is the TRX spent in sun; `receivedAmount` is the token amount in its smallest unit (text shows both in human units). +`paidSun` is the TRX spent in sun; `receivedAmount` is the token amount in its smallest unit. Both are decimal strings; `precision` is included so text and machine consumers can scale the token amount. ## Exit status diff --git a/ts/docs/commands/asset/unfreeze.md b/ts/docs/commands/asset/unfreeze.md index 25a2cc4eb..0bd748d1e 100644 --- a/ts/docs/commands/asset/unfreeze.md +++ b/ts/docs/commands/asset/unfreeze.md @@ -65,7 +65,7 @@ echo "$PW" | wallet-cli asset unfreeze --network tron:nile --wait --password-std ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"asset.unfreeze","data":{"kind":"asset-unfreeze","stage":"confirmed","txId":"6a5...","confirmed":true,"blockNumber":57883560,"failed":false,"assetId":"1000123","name":"MyToken","issuerAddress":"TQkXm4vN...","releasedAmount":100000000000000,"stillFrozenAmount":50000000000000,"feeSun":0,"resource":{"netUsage":288,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6410,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"asset.unfreeze","data":{"kind":"asset-unfreeze","stage":"confirmed","txId":"6a5...","confirmed":true,"blockNumber":57883560,"feeSun":0,"netUsed":288,"netFeeSun":0,"failed":false,"assetId":"1000123","name":"MyToken","issuerAddress":"TQkXm4vN...","releasedAmount":"100000000000000","stillFrozenAmount":"50000000000000","precision":6},"meta":{"durationMs":6410,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -75,9 +75,9 @@ echo "$PW" | wallet-cli asset unfreeze --network tron:nile --wait --password-std | Stage | Fields | |---|---| | default (submit) | `kind: "asset-unfreeze"`, `stage: "submitted"`, `txId`, `assetId`, `name`, `issuerAddress` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed`, `releasedAmount`, `stillFrozenAmount` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), `failed`, `releasedAmount`, `stillFrozenAmount` | -`releasedAmount` and `stillFrozenAmount` are raw amounts (smallest unit) and reflect what the confirmed transaction actually did. +`releasedAmount` and `stillFrozenAmount` are raw decimal strings (smallest unit); `precision` is included for scaling. The confirmed `releasedAmount` reflects what the receipt reports. ## Exit status diff --git a/ts/docs/commands/asset/update.md b/ts/docs/commands/asset/update.md index 61f3dc9ba..152144a7d 100644 --- a/ts/docs/commands/asset/update.md +++ b/ts/docs/commands/asset/update.md @@ -68,7 +68,7 @@ echo "$PW" | wallet-cli asset update --url https://mytoken.io/v2 --network tron: ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"asset.update","data":{"kind":"asset-update","stage":"confirmed","txId":"9e3...","confirmed":true,"blockNumber":57883190,"failed":false,"assetId":"1000123","name":"MyToken","issuerAddress":"TQkXm4vN...","url":"https://mytoken.io/v2","description":"Demo TRC10","freeAssetNetLimit":0,"publicFreeAssetNetLimit":0,"feeSun":0,"resource":{"netUsage":295,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6480,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"asset.update","data":{"kind":"asset-update","stage":"confirmed","txId":"9e3...","confirmed":true,"blockNumber":57883190,"feeSun":0,"netUsed":295,"netFeeSun":0,"failed":false,"assetId":"1000123","name":"MyToken","issuerAddress":"TQkXm4vN...","url":"https://mytoken.io/v2","description":"Demo TRC10","freeAssetNetLimit":0,"publicFreeAssetNetLimit":0},"meta":{"durationMs":6480,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -78,9 +78,9 @@ echo "$PW" | wallet-cli asset update --url https://mytoken.io/v2 --network tron: | Stage | Fields | |---|---| | default (submit) | `kind: "asset-update"`, `stage: "submitted"`, `txId`, `assetId`, `name`, `issuerAddress`, and the four fields as submitted | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), and `failed` | -The four fields are `url`, `description`, `freeAssetNetLimit`, and `publicFreeAssetNetLimit` — always all four, including the ones read back unchanged. +The four fields are `url`, `description`, `freeAssetNetLimit`, and `publicFreeAssetNetLimit` — always all four, including the ones read back unchanged. Confirmation resource fields are flat; there is no nested `resource` object. ## Exit status diff --git a/ts/docs/commands/backup.md b/ts/docs/commands/backup.md index bbdc48b70..6609ecbd0 100644 --- a/ts/docs/commands/backup.md +++ b/ts/docs/commands/backup.md @@ -100,7 +100,7 @@ printf '%s' "$PW" | wallet-cli backup main --keystore --out ./main.keystore.json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"backup","data":{"accountId":"wlt_d1qbj2fb.0","label":"main","type":"seed","index":0,"active":true,"addresses":{"tron":"TQkXm4vN...5Zt7Uw","evm":"0x7B28FE10...46C9C"},"seedId":"wlt_d1qbj2fb","derivationPath":{"tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0"},"family":"tron","secretType":"privateKey","format":"keystore","out":"./main.keystore.json","fileMode":"0600","bytes":491},"meta":{"durationMs":1420,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"backup","data":{"accountId":"wlt_d1qbj2fb.0","label":"main","type":"seed","index":0,"active":true,"addresses":{"tron":"TQkXm4vN...5Zt7Uw","evm":"0x7B28FE10...46C9C"},"seedId":"wlt_d1qbj2fb","derivationPath":{"tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0"},"family":"tron","secretType":"privateKey","format":"keystore","out":"./main.keystore.json","fileMode":"0600","bytes":491},"meta":{"durationMs":1420,"warnings":[]},"chain":{"family":"tron","network":"tron:mainnet","chainId":"mainnet"}} ``` The audit log: @@ -123,12 +123,12 @@ wallet-cli backup --records --limit 3 -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"backup.records","data":{"records":[{"operation":"backup --keystore","accountId":"wlt_d1qbj2fb.0","account":"TQkXm4vN...5Zt7Uw","label":"main","out":"./wlt_d1qbj2fb.0-1785930000.keystore.json","timestamp":"2026-08-05T11:40:00Z"},{"operation":"backup","accountId":"wlt_d1qbj2fb.0","account":"TQkXm4vN...5Zt7Uw","label":"main","out":"./wlt_d1qbj2fb.0-1785834720.json","timestamp":"2026-08-04T09:12:00Z"},{"operation":"backup","accountId":"wlt_9x3k2m7p.0","account":"TBeta9mR...8pLx","label":null,"out":"./tbeta-seed.json","timestamp":"2026-07-30T22:03:00Z"}]},"meta":{"durationMs":8,"warnings":[],"pagination":{"offset":0,"limit":3,"total":12}}} +{"schema":"wallet-cli.result.v1","success":true,"command":"backup.records","data":{"records":[{"operation":"backup --keystore","accountId":"wlt_d1qbj2fb.0","account":"TQkXm4vN...5Zt7Uw","label":"main","out":"./wlt_d1qbj2fb.0-1785930000.keystore.json","timestamp":"2026-08-05T11:40:00Z"},{"operation":"backup","accountId":"wlt_d1qbj2fb.0","account":"TQkXm4vN...5Zt7Uw","label":"main","out":"./wlt_d1qbj2fb.0-1785834720.json","timestamp":"2026-08-04T09:12:00Z"},{"operation":"backup","accountId":"wlt_9x3k2m7p.0","account":"TBeta9mR...8pLx","label":null,"out":"./tbeta-seed.json","timestamp":"2026-07-30T22:03:00Z"}]},"meta":{"durationMs":8,"warnings":[],"pagination":{"offset":0,"limit":3,"total":12}},"chain":{"family":"tron","network":"tron:mainnet","chainId":"mainnet"}} ``` ## Output -Both forms are local commands — no `chain` block — and they carry different `command` ids: `backup` for an export, `backup.records` for the log. +Both forms are local and contact no node, but `backup` has an optional network display selector: the selected or default network chooses which family `--keystore` exports. The result therefore includes a `chain` block, including for `--records`. The forms carry different `command` ids: `backup` for an export, `backup.records` for the log. `data` for an export is the account plus the file's details: diff --git a/ts/docs/commands/block.md b/ts/docs/commands/block.md index 5925eabf5..bd7a64d9a 100644 --- a/ts/docs/commands/block.md +++ b/ts/docs/commands/block.md @@ -37,7 +37,7 @@ wallet-cli block 70433745 --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"block","data":{"block":{"blockID":"0000000041e6a3c3…","block_header":{"raw_data":{"number":69093315,"txTrieRoot":"…","witness_address":"41…","parentHash":"…","version":31,"timestamp":1783783761000},"witness_signature":"…"},"transactions":[{…}]}},"meta":{"durationMs":126,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"block","data":{"block":{"blockID":"0000000041e6a3c3…","block_header":{"raw_data":{"number":69093315,"txTrieRoot":"…","witness_address":"41…","parentHash":"…","version":31,"timestamp":1783783761000},"witness_signature":"…"},"transactions":[{}]}},"meta":{"durationMs":126,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` On an EVM network the text summary carries the gas and fee figures a block actually has: diff --git a/ts/docs/commands/contact/add.md b/ts/docs/commands/contact/add.md index d6bded037..4706b0f96 100644 --- a/ts/docs/commands/contact/add.md +++ b/ts/docs/commands/contact/add.md @@ -12,7 +12,7 @@ wallet-cli contact add
[--note ] Saves a recipient (name → address) to the local address book. The name can then be used wherever a recipient is expected — [`tx send --to`](../tx/send.md) and [`gasfree transfer --to`](../gasfree/transfer.md). The address is validated locally against the family it belongs to (`T…` = TRON, `0x…` = EVM), which is also the family recorded on the entry; no node access. -A contact belongs to **one family**. Filing a TRON address under EVM (or the reverse) is refused with `invalid_address`, because a name that resolved to an address that does not exist on the selected network would be worse than no name at all. +A contact belongs to **one family**, inferred directly from its address; this command has no family or network selector. A malformed address, or one that belongs to no supported family, is refused with `invalid_address`. Family compatibility is checked later when a contact is used by a chain command. The name must be 1–64 safe characters (no control or formatting characters) and must not **resemble** an address. The resemblance check is deliberately loose — it matches a near miss too, a checksum typo or a truncated paste — so that a mistyped address can never silently fall through to a name lookup and pay whoever registered that name. Names are compared case-insensitively after Unicode NFKC normalization. @@ -37,7 +37,7 @@ wallet-cli contact add alice TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --note "Alice ma Note Alice mainnet ``` -An EVM address is filed the same way, under `family: "evm"`: +An EVM address is filed the same way. The family is used internally for routing but is not exposed in the public contact object: ```bash wallet-cli contact add alice-eth 0x742d35Cc6634C0532925a3b844Bc454e4438f44e --note "Alice mainnet" @@ -48,7 +48,7 @@ wallet-cli contact add alice TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --note "Alice ma ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"contact.add","data":{"name":"alice","address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","note":"Alice mainnet","family":"tron"},"meta":{"durationMs":4,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"contact.add","data":{"name":"alice","address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","note":"Alice mainnet"},"meta":{"durationMs":4,"warnings":[]}} ``` ## Output @@ -58,7 +58,6 @@ wallet-cli contact add alice TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --note "Alice ma | `name` | string | Contact name | | `address` | string | Recipient address | | `note` | string \| null | The note, or `null` | -| `family` | string | Chain family the address belongs to — `tron` or `evm`, detected from the address | ## Exit status diff --git a/ts/docs/commands/contact/list.md b/ts/docs/commands/contact/list.md index 800d98c9a..8b1f643e6 100644 --- a/ts/docs/commands/contact/list.md +++ b/ts/docs/commands/contact/list.md @@ -33,14 +33,14 @@ wallet-cli contact list -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"contact.list","data":{"contacts":[{"name":"alice","address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","note":"Alice mainnet","family":"tron"},{"name":"bob","address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","note":null,"family":"tron"}]},"meta":{"durationMs":3,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"contact.list","data":{"contacts":[{"name":"alice","address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","note":"Alice mainnet"},{"name":"bob","address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","note":null}]},"meta":{"durationMs":3,"warnings":[]}} ``` ## Output | Field | Type | Meaning | |---|---|---| -| `contacts[]` | array | Recipients, each `{name, address, note, family}` — `note` is `null` when unset | +| `contacts[]` | array | Recipients, each `{name, address, note}` — `note` is `null` when unset. Family remains an internal routing detail and is not returned | ## Exit status diff --git a/ts/docs/commands/contract/clear-abi.md b/ts/docs/commands/contract/clear-abi.md index fc92f5781..5cf458d41 100644 --- a/ts/docs/commands/contract/clear-abi.md +++ b/ts/docs/commands/contract/clear-abi.md @@ -62,7 +62,7 @@ echo "$PW" | wallet-cli contract clear-abi TQ5nJ8mV...4wRe --network tron:nile - ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"contract.clear-abi","data":{"kind":"contract-clear-abi","stage":"confirmed","txId":"3f7...","confirmed":true,"blockNumber":57882140,"failed":false,"contractAddress":"TQ5nJ8mV...","deployerAddress":"TQkXm4vN...","feeSun":0,"resource":{"netUsage":287,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6510,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"contract.clear-abi","data":{"kind":"contract-clear-abi","stage":"confirmed","txId":"3f7...","confirmed":true,"blockNumber":57882140,"failed":false,"contractAddress":"TQ5nJ8mV...","deployerAddress":"TQkXm4vN...","feeSun":0,"energyUsed":0,"netUsed":287,"energyFeeSun":0,"netFeeSun":0,"resource":{"netUsage":287,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6510,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -72,7 +72,7 @@ echo "$PW" | wallet-cli contract clear-abi TQ5nJ8mV...4wRe --network tron:nile - | Stage | Fields | |---|---| | default (submit) | `kind: "contract-clear-abi"`, `stage: "submitted"`, `txId`, `contractAddress`, `deployerAddress` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), and `failed` | ## Exit status diff --git a/ts/docs/commands/contract/create2.md b/ts/docs/commands/contract/create2.md index 22511572a..4e255421e 100644 --- a/ts/docs/commands/contract/create2.md +++ b/ts/docs/commands/contract/create2.md @@ -75,7 +75,7 @@ This is a local command, so the envelope carries no `chain` block. ## Exit status -`0` success · `1` execution failure (`io_error` — `--code-file` cannot be read) · `2` usage error (`missing_option` — no `--deployer` / `--salt`, or neither code source; `invalid_option` — both `--code` and `--code-file`; `invalid_value` — malformed deployer address, non-hex code, or a salt outside the 64-bit signed range). +`0` success · `1` execution failure · `2` usage error (`missing_option` — no `--deployer` or `--salt`; `file_not_found` — `--code-file` does not exist; `invalid_value` — neither or both code sources, an unreadable code file, malformed deployer address, non-hex code, or a salt outside the signed 64-bit range). ## See also diff --git a/ts/docs/commands/contract/set-origin-energy-limit.md b/ts/docs/commands/contract/set-origin-energy-limit.md index 0c96fd486..041b64829 100644 --- a/ts/docs/commands/contract/set-origin-energy-limit.md +++ b/ts/docs/commands/contract/set-origin-energy-limit.md @@ -66,7 +66,7 @@ echo "$PW" | wallet-cli contract set-origin-energy-limit TQ5nJ8mV...4wRe 5000000 ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"contract.set-origin-energy-limit","data":{"kind":"contract-set-origin-energy-limit","stage":"confirmed","txId":"3a9...","confirmed":true,"blockNumber":57882265,"failed":false,"contractAddress":"TQ5nJ8mV...","deployerAddress":"TQkXm4vN...","originEnergyLimit":50000000,"feeSun":0,"resource":{"netUsage":290,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6530,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"contract.set-origin-energy-limit","data":{"kind":"contract-set-origin-energy-limit","stage":"confirmed","txId":"3a9...","confirmed":true,"blockNumber":57882265,"failed":false,"contractAddress":"TQ5nJ8mV...","deployerAddress":"TQkXm4vN...","originEnergyLimit":50000000,"feeSun":0,"energyUsed":0,"netUsed":290,"energyFeeSun":0,"netFeeSun":0,"resource":{"netUsage":290,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6530,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -76,7 +76,7 @@ echo "$PW" | wallet-cli contract set-origin-energy-limit TQ5nJ8mV...4wRe 5000000 | Stage | Fields | |---|---| | default (submit) | `kind: "contract-set-origin-energy-limit"`, `stage: "submitted"`, `txId`, `contractAddress`, `deployerAddress`, `originEnergyLimit` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), and `failed` | `originEnergyLimit` is the value now in effect. diff --git a/ts/docs/commands/contract/set-user-resource-percent.md b/ts/docs/commands/contract/set-user-resource-percent.md index ea5e503d4..130d67bec 100644 --- a/ts/docs/commands/contract/set-user-resource-percent.md +++ b/ts/docs/commands/contract/set-user-resource-percent.md @@ -68,7 +68,7 @@ echo "$PW" | wallet-cli contract set-user-resource-percent TQ5nJ8mV...4wRe 100 - ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"contract.set-user-resource-percent","data":{"kind":"contract-set-user-resource-percent","stage":"confirmed","txId":"8b2...","confirmed":true,"blockNumber":57882388,"failed":false,"contractAddress":"TQ5nJ8mV...","deployerAddress":"TQkXm4vN...","consumeUserResourcePercent":100,"feeSun":0,"resource":{"netUsage":289,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6470,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"contract.set-user-resource-percent","data":{"kind":"contract-set-user-resource-percent","stage":"confirmed","txId":"8b2...","confirmed":true,"blockNumber":57882388,"failed":false,"contractAddress":"TQ5nJ8mV...","deployerAddress":"TQkXm4vN...","consumeUserResourcePercent":100,"feeSun":0,"energyUsed":0,"netUsed":289,"energyFeeSun":0,"netFeeSun":0,"resource":{"netUsage":289,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6470,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -78,7 +78,7 @@ echo "$PW" | wallet-cli contract set-user-resource-percent TQ5nJ8mV...4wRe 100 - | Stage | Fields | |---|---| | default (submit) | `kind: "contract-set-user-resource-percent"`, `stage: "submitted"`, `txId`, `contractAddress`, `deployerAddress`, `consumeUserResourcePercent` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), and `failed` | `consumeUserResourcePercent` is the value now in effect — the caller's share. diff --git a/ts/docs/commands/current.md b/ts/docs/commands/current.md index db6bd61ce..cdae2ff12 100644 --- a/ts/docs/commands/current.md +++ b/ts/docs/commands/current.md @@ -52,7 +52,7 @@ The QR encodes **one** address — the receive address for the selected network. wallet-cli current --qr --network evm:11155111 ``` -The QR is a terminal rendering only and scans from a real terminal (where the block characters line up); `-o json` is unchanged by `--qr` (machine consumers take the address and generate their own code). If the terminal is non-interactive or too narrow to fit it, it degrades to printing the addresses with a warning: +The QR is a terminal rendering only and scans from a real terminal (where the block characters line up). In JSON mode no QR pixels are rendered; `--qr` validates that the selected account has an address for the selected network and adds that value as `data.receiveAddress`. If the text terminal is non-interactive or too narrow to fit the QR, it degrades to printing the addresses with a warning: ```console warning: terminal is non-interactive or too narrow for a complete QR code; showing the full address only @@ -91,6 +91,7 @@ error [missing_wallet_address]: no active account; import one first | `derivationPath` | object \| null | Per-family BIP32 path for `seed` accounts; `null` otherwise | | `seedId` | string | Owning seed wallet id (`seed` accounts only) | | `family` | string | Chain family this account is bound to — single-family accounts (`watch`, `ledger`) only | +| `receiveAddress` | string | Present in JSON only when `--qr` was requested; address selected by `--network` | The `chain` block echoes the network selected for display; the command contacts no node. diff --git a/ts/docs/commands/gasfree/info.md b/ts/docs/commands/gasfree/info.md index 33aeed700..9d99a1e0d 100644 --- a/ts/docs/commands/gasfree/info.md +++ b/ts/docs/commands/gasfree/info.md @@ -27,14 +27,14 @@ wallet-cli gasfree info --account main --network tron:nile ``` ```console -Account main (TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw) +Owner TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw GasFree address TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw Status active Nonce 4 -Supported tokens (1) - Token Activation fee Transfer fee - USDT 1 USDT 0.5 USDT +| Token | Balance | Activation fee | Transfer fee | +| ----- | ------- | -------------- | ------------ | +| USDT | 125 USDT | 1 USDT | 0.5 USDT | ``` ```bash @@ -42,7 +42,7 @@ wallet-cli gasfree info --account main --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.info","data":{"ownerAddress":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","gasFreeAddress":"TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw","active":true,"nonce":4,"tokens":[{"symbol":"USDT","address":"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t","decimals":6,"activateFee":"1000000","transferFee":"500000"}]},"meta":{"durationMs":380,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.info","data":{"ownerAddress":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","gasFreeAddress":"TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw","active":true,"nonce":"4","tokens":[{"symbol":"USDT","address":"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t","decimals":6,"activateFee":"1000000","transferFee":"500000","balance":"125000000"}]},"meta":{"durationMs":380,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -52,8 +52,8 @@ wallet-cli gasfree info --account main --network tron:nile -o json | `ownerAddress` | string | The account's own TRON address | | `gasFreeAddress` | string | Derived GasFree address (receive/pay here) | | `active` | boolean | Whether the GasFree address is activated on-chain | -| `nonce` | number | Current per-address nonce | -| `tokens[]` | array | Supported tokens: `{symbol, address, decimals, activateFee, transferFee}` — fees in the token's base units | +| `nonce` | string | Current per-address nonce, as an unsigned decimal string | +| `tokens[]` | array | Supported tokens: `{symbol, address, decimals, activateFee, transferFee, balance}` — fees and balance are decimal strings in the token's base units | ## Exit status diff --git a/ts/docs/commands/gasfree/trace.md b/ts/docs/commands/gasfree/trace.md index 3aa688581..688d78593 100644 --- a/ts/docs/commands/gasfree/trace.md +++ b/ts/docs/commands/gasfree/trace.md @@ -32,7 +32,9 @@ Status succeed TxID d2e... Token USDT Amount 25 USDT -Fee 0.5 USDT +Service fee 0.5 USDT +Activation fee 0 USDT +Total 25.5 USDT To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub ``` @@ -41,7 +43,7 @@ wallet-cli gasfree trace 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 --network tron:nil ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.trace","data":{"traceId":"7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527","state":"SUCCEED","txId":"d2e...","token":"USDT","amount":"25000000","serviceFee":"500000","activateFee":"0","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub"},"meta":{"durationMs":290,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.trace","data":{"traceId":"7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527","state":"SUCCEED","txId":"d2e...","token":"USDT","tokenAddress":"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t","decimals":6,"amount":"25000000","serviceFee":"500000","activateFee":"0","totalDeducted":"25500000","from":"TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER","owner":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","nonce":"8"},"meta":{"durationMs":290,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -52,9 +54,15 @@ wallet-cli gasfree trace 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 --network tron:nil | `state` | string | Raw state enum: `WAITING` / `INPROGRESS` / `CONFIRMING` / `SUCCEED` / `FAILED` | | `txId` | string | On-chain transaction id (once submitted) | | `token` | string | Token symbol | +| `tokenAddress` | string | TRC20 contract address | +| `decimals` | number | Token decimals used to render amounts | | `amount` | string | Amount, in token base units | | `serviceFee` / `activateFee` | string | Fees charged, in token base units | +| `totalDeducted` | string | Amount plus the settled service and activation fees, in token base units | +| `from` / `owner` | string | GasFree holding address / owning account address | | `to` | string | Recipient address | +| `nonce` | string | Authorization nonce | +| `failureReason` | string | Provider explanation, only when supplied for a failed transfer | ## Exit status diff --git a/ts/docs/commands/gasfree/transfer.md b/ts/docs/commands/gasfree/transfer.md index cf5da6e07..66efeec91 100644 --- a/ts/docs/commands/gasfree/transfer.md +++ b/ts/docs/commands/gasfree/transfer.md @@ -43,16 +43,18 @@ echo "$PW" | wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub ```console ⏳ Submitted to GasFree — send 25 USDT Trace ID 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 - From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER (GasFree address) + From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub - Fee 0.5 USDT - Total 25.5 USDT - Status accepted + Service fee 0.5 USDT + Activation fee 0 USDT + Authorized max fee 1.5 USDT + Total 25.5 USDT + Status waiting ! Track it: wallet-cli gasfree trace 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.transfer","data":{"kind":"gasfree-transfer","stage":"submitted","traceId":"7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527","token":"USDT","tokenAddress":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","decimals":6,"amount":"25000000","serviceFee":"500000","activateFee":"0","authorizedMaxFee":"500000","totalDeducted":"25500000","owner":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC","from":"TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","serviceProvider":"TKtWbdzEq5ss9vTS9kwRhBp5mXmBfBns3E","nonce":"8","deadline":"1700000060"},"meta":{"durationMs":650,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.transfer","data":{"kind":"gasfree-transfer","stage":"submitted","traceId":"7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527","state":"WAITING","token":"USDT","tokenAddress":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","decimals":6,"amount":"25000000","serviceFee":"500000","activateFee":"0","authorizedMaxFee":"1500000","totalDeducted":"25500000","owner":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC","from":"TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","serviceProvider":"TKtWbdzEq5ss9vTS9kwRhBp5mXmBfBns3E","nonce":"8","deadline":"1700000060"},"meta":{"durationMs":650,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` Add `--wait` to poll to a terminal state, with the on-chain txid and actual deduction: @@ -65,11 +67,13 @@ echo "$PW" | wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub ✅ Sent 25 USDT via GasFree Trace ID a41b6c88-0d2f-4e73-9a05-3c7d81f2b964 TxID d2e... - From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER (GasFree address) + From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub - Fee 0.5 USDT - Total 25.5 USDT - Status succeed + Service fee 0.5 USDT + Activation fee 0 USDT + Authorized max fee 1.5 USDT + Total 25.5 USDT + Status succeed ``` On a first transfer the GasFree address isn't activated yet, so the fee itemises the service fee and the one-time activation fee, and `Total` includes activation: @@ -80,10 +84,13 @@ wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --amount 25 ```console ⏳ Dry run — GasFree transfer 25 USDT (not submitted) - From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER (GasFree address, not activated) + From TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub - Fee 1.5 USDT (0.5 service + 1.0 activation) - Total 26.5 USDT + Service fee 0.5 USDT + Activation fee 1 USDT + Authorized max fee 1.5 USDT + Total 26.5 USDT + Status not submitted ``` ```json @@ -96,12 +103,12 @@ wallet-cli gasfree transfer --to TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub --amount 25 | Mode | Fields | |---|---| -| default (submit) | `kind: "gasfree-transfer"`, `stage: "submitted"`, `traceId`, `token`, `tokenAddress`, `decimals`, `amount`, `serviceFee`, `activateFee`, `authorizedMaxFee`, `totalDeducted`, `owner`, `from`, `to`, `nonce`, `deadline`, `serviceProvider`, plus `toContact` when `--to` was a contact name | -| `--wait` (confirmed) | the above, but `stage: "confirmed"`, plus `confirmed`, `state` (`SUCCEED` / `FAILED`), `failed`, and `txId` | -| `--wait` (failed) | the same fields, but `stage: "failed"`, `failed: true`, `state: "FAILED"`, and `failureReason` carrying the provider's explanation | +| default (submit) | `kind: "gasfree-transfer"`, `stage: "submitted"`, `traceId`, provider `state`, `token`, `tokenAddress`, `decimals`, `amount`, `serviceFee`, `activateFee`, `authorizedMaxFee`, `totalDeducted`, `owner`, `from`, `to`, `nonce`, `deadline`, `serviceProvider`, plus `toContact` when `--to` was a contact name | +| `--wait` (confirmed) | the above, but `stage: "confirmed"`, `state: "SUCCEED"`, and `txId` when supplied by the provider | +| `--wait` (failed) | the same fields, but `stage: "failed"`, `state: "FAILED"`, and optional `failureReason` / `txId` from the provider | | `--dry-run` | the default fields except `traceId`, with `stage: "dry-run"`; no signature or submission | -A provider-side failure still leaves the envelope at `success: true` and exit `0` — the command completed; the transfer did not. Branch on `data.stage` / `data.state`, not on the exit code. See [script safety](../../machine-interface.md#script-safety-never-mistake-submitted-for-confirmed). +A provider-side failure still leaves the envelope at `success: true` and exit `0` — the command completed; the transfer did not. There are no `confirmed` or `failed` booleans in this view; branch on `data.stage` / `data.state`, not on the exit code. See [script safety](../../machine-interface.md#script-safety-never-mistake-submitted-for-confirmed). ## Exit status diff --git a/ts/docs/commands/import/keystore.md b/ts/docs/commands/import/keystore.md index 96a8e7f9c..9ae10a2f3 100644 --- a/ts/docs/commands/import/keystore.md +++ b/ts/docs/commands/import/keystore.md @@ -44,6 +44,7 @@ wallet-cli import keystore ./tronlink-export.json --label imported Account ID wlt_7h2k9m1a Type private key TRON address TZx9kP2m...7bWq + EVM address 0xe4aAd11792F7E74f1B5cbce65f9a1E207c952961 Active yes ⚠️ The keystore password was read from hidden input and was not printed. @@ -56,7 +57,7 @@ wallet-cli import keystore ./tronlink-export.json --label imported -o json ```console ? Master password (hidden): ? Keystore file password (hidden): -{"schema":"wallet-cli.result.v1","success":true,"command":"import.keystore","data":{"status":"created","accountId":"wlt_7h2k9m1a","label":"imported","type":"privateKey","index":null,"active":true,"addresses":{"tron":"TZx9kP2m...7bWq"}},"meta":{"durationMs":44,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"import.keystore","data":{"status":"created","accountId":"wlt_7h2k9m1a","label":"imported","type":"privateKey","index":null,"active":true,"addresses":{"tron":"TZx9kP2m...7bWq","evm":"0xe4aAd11792F7E74f1B5cbce65f9a1E207c952961"},"derivationPath":null},"meta":{"durationMs":44,"warnings":[]}} ``` ## Output @@ -71,7 +72,8 @@ wallet-cli import keystore ./tronlink-export.json --label imported -o json | `type` | string | `"privateKey"` (standalone, no seed) | | `index` | number \| null | Non-HD account, always `null` | | `active` | boolean | Became the active account | -| `addresses.tron` | string | Base58 TRON address | +| `addresses` | object | Both encodings of the imported key: `tron` (base58) and `evm` (EIP-55) | +| `derivationPath` | null | A Web3 keystore contains one raw key and has no derivation path | ## Exit status diff --git a/ts/docs/commands/import/ledger.md b/ts/docs/commands/import/ledger.md index 5854bbf27..7c09a9198 100644 --- a/ts/docs/commands/import/ledger.md +++ b/ts/docs/commands/import/ledger.md @@ -14,7 +14,7 @@ wallet-cli import ledger --app (--index | --path | - | Option | Description | |---|---| | `--app ` | **Required.** Ledger app to open on the device; this is what selects the chain family and the derivation scheme | -| `--index ` | Account index under the app's default path; omit with no `--path`/`--address` to use index 0. Mutually exclusive with `--path` / `--address` | +| `--index ` | Account index under wallet-cli's family path template. Mutually exclusive with `--path` / `--address` | | `--path ` | Explicit derivation path, e.g. `m/44'/195'/0'/0/0` (TRON) or `m/44'/60'/0'/0/0` (Ethereum) | | `--address ` | Known address to locate by bounded scan | | `--scan-limit ` | Indexes to scan with `--address` (default 20) | @@ -26,6 +26,10 @@ Plus [global options](../index.md). Creates a watch-only entry; no secret is stored. Requires the device unlocked with the selected app open. +When all three locators are omitted, an attached TTY opens a paged account selector (five derived addresses at a time). In non-interactive use there is no selector and the command falls back to index 0; pass `--index`, `--path`, or `--address` explicitly in scripts. + +For Ethereum, `--index ` uses wallet-cli's MetaMask-style path `m/44'/60'/0'/0/`. Ledger Live commonly uses `m/44'/60'/'/0/0`; use an explicit `--path` when importing an account created under that scheme. + `--app` is what makes a Ledger account **single-family**: the TRON app registers a `tron` account and the Ethereum app an `evm` one, and the resulting account has only that one address. Import the same device twice, once per app, to hold both. See [Ledger guide](../../guide/ledger.md). ## Examples diff --git a/ts/docs/commands/import/mnemonic.md b/ts/docs/commands/import/mnemonic.md index 60e4b57dc..029c20479 100644 --- a/ts/docs/commands/import/mnemonic.md +++ b/ts/docs/commands/import/mnemonic.md @@ -45,6 +45,7 @@ wallet-cli import mnemonic --label restored Account ID wlt_d66fvems.0 Type HD TRON address TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH + EVM address 0x7B28FE10FBccE88c3967ff0Fd64f1ffB46b46C9C Active yes ⚠️ Recovery phrase was read from hidden input and was not printed. @@ -58,7 +59,7 @@ wallet-cli import mnemonic --label restored -o json ? Set master password (hidden): ? Confirm master password: ? Paste recovery phrase (hidden): -{"schema":"wallet-cli.result.v1","success":true,"command":"import.mnemonic","data":{"status":"created","accountId":"wlt_d66fvems.0","label":"restored","type":"seed","index":0,"active":true,"addresses":{"tron":"TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH"},"seedId":"wlt_d66fvems"},"meta":{"durationMs":38,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"import.mnemonic","data":{"status":"created","accountId":"wlt_d66fvems.0","label":"restored","type":"seed","index":0,"active":true,"addresses":{"tron":"TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH","evm":"0x7B28FE10FBccE88c3967ff0Fd64f1ffB46b46C9C"},"seedId":"wlt_d66fvems","derivationPath":{"tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0"}},"meta":{"durationMs":38,"warnings":[]}} ``` ## Output @@ -67,14 +68,15 @@ wallet-cli import mnemonic --label restored -o json | Field | Type | Meaning | |---|---|---| -| `status` | string | `"created"` | +| `status` | string | `"created"`, or `"existing"` when the mnemonic's account #0 was already present (the existing account is selected) | | `accountId` | string | Stable id `.` | | `label` | string | Account label | | `type` | string | `"seed"` (HD-derived) | | `index` | number | HD derivation index (0 for the first account) | | `active` | boolean | Became the active account | -| `addresses.tron` | string | Base58 TRON address | +| `addresses` | object | Both derived addresses: `tron` (base58) and `evm` (EIP-55) | | `seedId` | string | Owning seed wallet id | +| `derivationPath` | object | Per-family BIP44 path for account index 0 | ## Exit status diff --git a/ts/docs/commands/import/private-key.md b/ts/docs/commands/import/private-key.md index 3337f5b97..6d6450acc 100644 --- a/ts/docs/commands/import/private-key.md +++ b/ts/docs/commands/import/private-key.md @@ -38,6 +38,7 @@ wallet-cli import private-key --label hot Account ID wlt_2qnr6j1f Type private key TRON address TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC + EVM address 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC Active yes ⚠️ Private key was read from hidden input and was not printed. @@ -51,7 +52,7 @@ wallet-cli import private-key --label hot -o json ? Set master password (hidden): ? Confirm master password: ? Paste private key (hidden): -{"schema":"wallet-cli.result.v1","success":true,"command":"import.private-key","data":{"status":"created","accountId":"wlt_2qnr6j1f","label":"hot","type":"privateKey","index":null,"active":true,"addresses":{"tron":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC"}},"meta":{"durationMs":38,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"import.private-key","data":{"status":"created","accountId":"wlt_2qnr6j1f","label":"hot","type":"privateKey","index":null,"active":true,"addresses":{"tron":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC","evm":"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"},"derivationPath":null},"meta":{"durationMs":38,"warnings":[]}} ``` ## Output @@ -60,13 +61,14 @@ wallet-cli import private-key --label hot -o json | Field | Type | Meaning | |---|---|---| -| `status` | string | `"created"` | +| `status` | string | `"created"`, or `"existing"` when the same key was already present (the existing account is selected) | | `accountId` | string | Stable account id | | `label` | string | Account label | | `type` | string | `"privateKey"` (standalone, no seed) | | `index` | number \| null | Non-HD account, always `null` | | `active` | boolean | Became the active account | -| `addresses.tron` | string | Base58 TRON address | +| `addresses` | object | Both encodings of the imported key: `tron` (base58) and `evm` (EIP-55) | +| `derivationPath` | null | A raw private key has no derivation path | ## Exit status diff --git a/ts/docs/commands/import/watch.md b/ts/docs/commands/import/watch.md index 4f86ef56c..72b08a996 100644 --- a/ts/docs/commands/import/watch.md +++ b/ts/docs/commands/import/watch.md @@ -46,7 +46,7 @@ wallet-cli import watch --address TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ --label col ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"import.watch","data":{"status":"created","accountId":"wlt_jsyq8fxe","label":"cold","type":"watch","index":null,"active":true,"addresses":{"tron":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ"},"family":"tron"},"meta":{"durationMs":36,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"import.watch","data":{"status":"created","accountId":"wlt_jsyq8fxe","label":"cold","type":"watch","index":null,"active":false,"addresses":{"tron":"TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ"},"family":"tron"},"meta":{"durationMs":36,"warnings":[]}} ``` ## Output @@ -55,12 +55,12 @@ wallet-cli import watch --address TMSgJxtPw29AFEHMXsjGo4kWV7UwbCToHJ --label col | Field | Type | Meaning | |---|---|---| -| `status` | string | `"created"` | +| `status` | string | `"created"`, or `"existing"` if the same watch address was already registered | | `accountId` | string | Stable account id | | `label` | string | Account label | | `type` | string | `"watch"` (read-only, cannot sign) | | `index` | number \| null | Non-HD account, always `null` | -| `active` | boolean | Became the active account | +| `active` | boolean | Whether this account is already the current active account. Registering a watch-only account does not select it; use [`use`](../use.md) explicitly | | `addresses` | object | The single address, keyed by its family — `{"tron":"T…"}` or `{"evm":"0x…"}` | | `family` | string | Chain family detected from the address — `tron` or `evm` | diff --git a/ts/docs/commands/index.md b/ts/docs/commands/index.md index 4aafee713..37d805a23 100644 --- a/ts/docs/commands/index.md +++ b/ts/docs/commands/index.md @@ -185,7 +185,7 @@ Individual flags are family-scoped the same way. `--help` tags them `(tron only) -h, --help / -V, --version ``` -Broadcast (✍️) commands additionally take `--wait` / `--wait-timeout ` (cap default: config `waitTimeoutMs`, built-in 60000). Early-exit modes are command-specific: transaction-building commands expose `--dry-run` / `--sign-only` / `--build-only`, while submit-only commands such as `tx broadcast` do not rebuild or sign and therefore omit `--sign-only` / `--build-only`. +Commands whose schema enables post-broadcast polling take `--wait` / `--wait-timeout ` (cap default: config `waitTimeoutMs`, built-in 60000). Early-exit modes are also command-specific: transaction-building commands may expose `--dry-run` / `--sign-only` / `--build-only`, while submit-only commands such as `tx broadcast` do not rebuild or sign and therefore omit `--sign-only` / `--build-only`. Fee and multi-sig flags are **family-scoped**, so they are not global: diff --git a/ts/docs/commands/permission/show.md b/ts/docs/commands/permission/show.md index cb155f06a..fed2087dc 100644 --- a/ts/docs/commands/permission/show.md +++ b/ts/docs/commands/permission/show.md @@ -27,33 +27,7 @@ No command-specific options; the [global options](../index.md#global-options-eve ## Examples -**A never-modified account** shows the chain-default structure — the active group covers every ordinary operation type: - -```bash -wallet-cli permission show --account solo --network tron:nile -``` - -```console -Account solo (TWfd2K9nP4rH7gL3jM6cV1bN8yS5aQ0eXt) - -Permission Name owner (id 0) -Threshold 1 -Authorized To Address Weight - TWfd2K9nP4rH7gL3jM6cV1bN8yS5aQ0eXt 1 (this wallet: solo) - -Permission Name active (id 2, active) -Operation(s) Activate Account · Transfer TRX · Transfer TRC10 - Vote · Issue TRC10 · Update Account Name - TRX Stake (1.0) · TRX Unstake (1.0) - Claim Voting Rewards · Create Smart Contract - Trigger Smart Contract · TRX Stake (2.0) - TRX Unstake (2.0) · Withdraw Unstaked TRX - Delegate Resources · Reclaim Resources - Cancel Unstake · … (40 total) -Threshold 1 -Authorized To Address Weight - TWfd2K9nP4rH7gL3jM6cV1bN8yS5aQ0eXt 1 (this wallet: solo) -``` +**A never-modified account** shows the chain-default owner and active groups. The active group's complete operation set is line-wrapped to fit the terminal; labels are never replaced with an ellipsis. Unknown bitmap bits are printed as `Unknown contract type `. **A multi-sig account** — here the owner group is a 2-of-3 and a scoped `finance` active group handles day-to-day transfers. This wallet holds only one of the keys (`main`); the other two are held by external co-signers, so they carry no annotation: @@ -72,7 +46,7 @@ Authorized To Address Weight TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz 1 Permission Name finance (id 2, active) -Operation(s) Transfer TRX · Transfer TRC10 · Trigger Smart Contract +Operation(s) Transfer TRX · Transfer TRC10 · Trigger Smart Contract (3 total) Threshold 2 Authorized To Address Weight TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw 1 (this wallet: main) @@ -85,7 +59,7 @@ wallet-cli permission show --account main --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"permission.show","data":{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","owner":{"id":0,"threshold":2,"keys":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"local":"main"},{"address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","weight":1,"local":null},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"local":null}]},"witness":null,"actives":[{"id":2,"name":"finance","threshold":2,"operations":["TransferContract","TransferAssetContract","TriggerSmartContract"],"operationsHex":"0600008000000000000000000000000000000000000000000000000000000000","keys":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"local":"main"},{"address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","weight":1,"local":null},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"local":null}]}]},"meta":{"durationMs":21,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"permission.show","data":{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","owner":{"id":0,"name":"owner","threshold":2,"keys":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"local":"main"},{"address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","weight":1,"local":null},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"local":null}]},"witness":null,"actives":[{"id":2,"name":"finance","threshold":2,"operations":["TransferContract","TransferAssetContract","TriggerSmartContract"],"operationLabels":["Transfer TRX","Transfer TRC10","Trigger Smart Contract"],"operationsHex":"0600008000000000000000000000000000000000000000000000000000000000","unknownOperationIds":[],"keys":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"local":"main"},{"address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","weight":1,"local":null},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"local":null}]}]},"meta":{"durationMs":21,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -93,11 +67,13 @@ wallet-cli permission show --account main --network tron:nile -o json | Field | Type | Meaning | |---|---|---| | `address` | string | Queried account | -| `owner` | object | Owner group `{id, threshold, keys[]}` | -| `witness` | object \| null | Witness group (SRs only), else `null` | -| `actives[]` | array | Active groups, each `{id, name, threshold, operations[], operationsHex, keys[]}` | +| `owner` | object | Owner group `{id, name, threshold, keys[]}` | +| `witness` | object \| null | Witness group with `{id, name, threshold, keys[]}` for SRs, else `null` | +| `actives[]` | array | Active groups, each `{id, name, threshold, operations[], operationLabels[], operationsHex, unknownOperationIds[], keys[]}` | | `…operations[]` | string[] | Contract-type names the active group may perform | +| `…operationLabels[]` | string[] | Human-readable labels corresponding to known operation ids | | `…operationsHex` | string | Raw 32-byte operations bitmap, hex | +| `…unknownOperationIds[]` | number[] | Set bits this build cannot map to a known contract type; empty when all operations are known | | `…keys[]` | array | Group keys: `{address, weight, local}` — `local` is the wallet label if held locally, else `null` | ## Exit status diff --git a/ts/docs/commands/permission/update.md b/ts/docs/commands/permission/update.md index ecfc09498..c534fdfa6 100644 --- a/ts/docs/commands/permission/update.md +++ b/ts/docs/commands/permission/update.md @@ -29,10 +29,11 @@ wallet-cli permission show -o json --network tron:nile | jq '.data' > perms.json Changing only `keys`, `threshold` or `name` needs no such deletion. -⚠️ **The chain applies no safety checks.** Even if the new structure contains no key you can sign with, the transaction still succeeds and the account is permanently locked, with no on-chain recovery. This CLI surfaces two **local** warnings but does **not** block the submission (in JSON they go to `meta.warnings`, and `success` stays `true`): +⚠️ **The chain applies no safety checks.** Even if the new structure contains no key you can sign with, the transaction still succeeds and the account is permanently locked, with no on-chain recovery. This CLI can surface four **local warning codes** but does **not** block the submission (in JSON they go to `meta.warnings`, and `success` stays `true`): - **Lockout risk** — when the combined weight of your locally-signable owner keys (software / Ledger) is below the new owner threshold, a `!` line spells out that you can no longer meet the owner threshold on your own (`owner_lockout` if you hold no weight, `owner_lockout_partial` if you now need co-signers). Multi-party custody legitimately means "I alone can't reach the threshold", so this is a notice, not a block. - **Dangerous operations** — when an active group includes `Update Account Permissions` (that group could then change the permissions themselves, effectively owner-level), a `!` line flags it (`active_can_update_permission`). +- **Unknown operations** — when an active bitmap grants contract-type ids this build cannot name, the ids are preserved and reported as `active_unknown_operations` rather than silently dropped. ## Options @@ -65,20 +66,22 @@ wallet-cli permission show --network tron:nile -o json | jq '.data' > perms.json $EDITOR perms.json ``` -Submit with `--wait`. The receipt is the transaction record plus the resulting on-chain structure (read back after confirmation, same cards as `permission show`), with any `!` warnings appended: +Submit with `--wait`. Safety warnings are written to stderr as `warning: ...` before the stdout receipt. After confirmation, the receipt includes the resulting on-chain structure when the follow-up read succeeds, using the same cards as `permission show`: ```bash echo "$PW" | wallet-cli permission update --file perms.json --network tron:nile --wait --password-stdin ``` ```console +warning: local keys hold 1 of 2 owner weight; co-signers are required for owner-level operations ✅ Permissions updated - Account main (TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw) TxID b3c... Block #84,335,102 Fee 100.268 TRX Status success +Account TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw + Permission Name owner (id 0) Threshold 2 Authorized To Address Weight @@ -87,25 +90,22 @@ Authorized To Address Weight TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz 1 Permission Name finance (id 2, active) -Operation(s) Transfer TRX · Transfer TRC10 · Trigger Smart Contract +Operation(s) Transfer TRX · Transfer TRC10 · Trigger Smart Contract (3 total) Threshold 2 Authorized To Address Weight TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw 1 (this wallet: main) TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub 1 TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz 1 - -! Your local keys now hold 1 of 2 owner weight — co-signers are required - for owner-level operations from now on. ``` -The JSON receipt's `data.permissions` is **structurally identical** to `permission show`'s `data`, so you can diff it against the pre-change export; the lockout warning is in `meta.warnings` with `success` still `true`: +When present, the JSON receipt's `data.permissions` is **structurally identical** to `permission show`'s `data`, so you can diff it against the pre-change export. If the confirmed post-check cannot be read, the field is omitted and `meta.warnings` contains `permission_postcheck_unavailable`; the confirmed transaction still has `success: true`. The lockout warning is also in `meta.warnings`: ```bash echo "$PW" | wallet-cli permission update --file perms.json --network tron:nile --wait --password-stdin -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"permission.update","data":{"kind":"permission-update","stage":"confirmed","txId":"b3c...","confirmed":true,"blockNumber":84335102,"feeSun":100268000,"failed":false,"permissions":{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","owner":{"id":0,"threshold":2,"keys":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"local":"main"},{"address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","weight":1,"local":null},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"local":null}]},"witness":null,"actives":[{"id":2,"name":"finance","threshold":2,"operations":["TransferContract","TransferAssetContract","TriggerSmartContract"],"operationsHex":"0600008000000000000000000000000000000000000000000000000000000000","keys":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"local":"main"},{"address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","weight":1,"local":null},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"local":null}]}]}},"meta":{"durationMs":6810,"warnings":[{"code":"owner_lockout_partial","message":"local keys hold 1 of 2 owner weight; co-signers are required for owner-level operations"}]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"permission.update","data":{"kind":"permission-update","stage":"confirmed","txId":"b3c...","confirmed":true,"blockNumber":84335102,"feeSun":100268000,"failed":false,"permissions":{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","owner":{"id":0,"name":"owner","threshold":2,"keys":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"local":"main"},{"address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","weight":1,"local":null},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"local":null}]},"witness":null,"actives":[{"id":2,"name":"finance","threshold":2,"operations":["TransferContract","TransferAssetContract","TriggerSmartContract"],"operationLabels":["Transfer TRX","Transfer TRC10","Trigger Smart Contract"],"operationsHex":"0600008000000000000000000000000000000000000000000000000000000000","unknownOperationIds":[],"keys":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"local":"main"},{"address":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","weight":1,"local":null},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"local":null}]}]}},"meta":{"durationMs":6810,"warnings":[{"code":"owner_lockout_partial","message":"local keys hold 1 of 2 owner weight; co-signers are required for owner-level operations"}]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -115,12 +115,14 @@ echo "$PW" | wallet-cli permission update --file perms.json --network tron:nile | Mode | Fields | |---|---| | default (submit) | `kind: "permission-update"`, `stage: "submitted"`, `txId` | -| `--wait` (confirmed) | the above, but `stage: "confirmed"`, plus `confirmed`, `blockNumber`, `feeSun`, `failed`, and `permissions` (same shape as `permission show` data, read back from chain) | -| `--dry-run` | `kind`, `mode: "dry-run"`, `fee` (the 100 TRX change fee), and `permissions` (the resulting structure); no `txId` | -| `--sign-only` | `kind`, `mode: "sign-only"`, `hex` (signed tx hex — feed `tx broadcast --hex`), `fee` | -| `--build-only` | `kind`, `mode: "build-only"`, `hex` (unsigned tx hex — feed `tx multisig --create`), `fee` | +| `--wait` (confirmed) | the above, but `stage: "confirmed"`, plus `confirmed`, `blockNumber`, `feeSun`, `failed`, and optional `permissions` (same shape as `permission show` data when the post-check read succeeds) | +| `--dry-run` | `kind`, `mode: "dry-run"`, `tx`, `fee` (the account-permission fee), and `permissions` (the resulting structure); no `txId` | +| `--sign-only` | `kind`, `mode: "sign-only"`, `signed`, `hex` (signed tx hex — feed `tx broadcast --hex`), `fee`, `address`, `txId`, and `permissions` | +| `--build-only` | `kind`, `mode: "build-only"`, `tx`, `hex` (unsigned tx hex — feed `tx multisig --create`), `fee`, and `permissions` | + +Local warnings (`owner_lockout`, `owner_lockout_partial`, `active_can_update_permission`, `active_unknown_operations`) are emitted before the transaction is built, appear in `meta.warnings` as `{code, message}` objects, and do not affect `success` — see [reading `meta.warnings`](../../machine-interface.md#reading-metawarnings). -Local warnings (`owner_lockout`, `owner_lockout_partial`, `active_can_update_permission`) are emitted before the transaction is built, appear in `meta.warnings` as `{code, message}` objects, and do not affect `success` — see [reading `meta.warnings`](../../machine-interface.md#reading-metawarnings). +Post-confirmation warnings use `permission_postcheck_unavailable` when the read-back fails and `permission_postcheck_mismatch` when the returned structure differs. In either case the transaction is already confirmed, so the command remains successful and callers must treat `permissions` as optional. ## Exit status diff --git a/ts/docs/commands/proposal/approve.md b/ts/docs/commands/proposal/approve.md index 3bf38c916..59e95973a 100644 --- a/ts/docs/commands/proposal/approve.md +++ b/ts/docs/commands/proposal/approve.md @@ -75,7 +75,7 @@ echo "$PW" | wallet-cli proposal approve 47 --network tron:nile --wait --passwor ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"proposal.approve","data":{"kind":"proposal-approve","stage":"confirmed","txId":"b1e...","confirmed":true,"blockNumber":57880240,"failed":false,"proposalId":47,"addApproval":true,"feeSun":0,"resource":{"netUsage":267,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6410,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"proposal.approve","data":{"kind":"proposal-approve","stage":"confirmed","txId":"b1e...","confirmed":true,"blockNumber":57880240,"failed":false,"proposalId":47,"voterAddress":"TSRmq8kP...","addApproval":true,"approvals":13,"approvalThreshold":18,"feeSun":0,"energyUsed":0,"netUsed":267,"energyFeeSun":0,"netFeeSun":0,"resource":{"netUsage":267,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6410,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -84,8 +84,8 @@ echo "$PW" | wallet-cli proposal approve 47 --network tron:nile --wait --passwor | Stage | Fields | |---|---| -| default (submit) | `kind: "proposal-approve"`, `stage: "submitted"`, `txId`, `proposalId`, `addApproval` (`false` with `--cancel`) | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| default (submit) | `kind: "proposal-approve"`, `stage: "submitted"`, `txId`, `proposalId`, `voterAddress`, `addApproval` (`false` with `--cancel`), `approvals`, and `approvalThreshold` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), and `failed` | ## Exit status diff --git a/ts/docs/commands/proposal/create.md b/ts/docs/commands/proposal/create.md index 21c07ddb8..08f8a46b4 100644 --- a/ts/docs/commands/proposal/create.md +++ b/ts/docs/commands/proposal/create.md @@ -81,7 +81,7 @@ echo "$PW" | wallet-cli proposal create --set getTransactionFee=15 --network tro ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"proposal.create","data":{"kind":"proposal-create","stage":"confirmed","txId":"9c4...","confirmed":true,"blockNumber":57880102,"feeSun":0,"resource":{"netUsage":268,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0},"failed":false,"proposalId":48,"changes":[{"id":3,"name":"getTransactionFee","currentValue":10,"proposedValue":15,"unit":"sun/byte"}]},"meta":{"durationMs":6480,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"proposal.create","data":{"kind":"proposal-create","stage":"confirmed","txId":"9c4...","confirmed":true,"blockNumber":57880102,"feeSun":0,"energyUsed":0,"netUsed":268,"energyFeeSun":0,"netFeeSun":0,"failed":false,"proposerAddress":"TSRmq8kP...","proposalId":48,"changes":[{"id":3,"name":"getTransactionFee","currentValue":10,"proposedValue":15,"unit":"sun/byte"}],"resource":{"netUsage":268,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6480,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -90,8 +90,8 @@ echo "$PW" | wallet-cli proposal create --set getTransactionFee=15 --network tro | Stage | Fields | |---|---| -| default (submit) | `kind: "proposal-create"`, `stage: "submitted"`, `txId`, `changes[]` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed`, and `proposalId` — the new proposal's id, known only once it is on chain | +| default (submit) | `kind: "proposal-create"`, `stage: "submitted"`, `txId`, `proposerAddress`, `changes[]` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), `failed`, and optional `proposalId` — the new proposal's id, known only once it is on chain | `proposalId` is **omitted** when the id cannot be established beyond doubt. The chain does not report it, so it is recognised by comparing the proposal list against a snapshot taken before diff --git a/ts/docs/commands/proposal/delete.md b/ts/docs/commands/proposal/delete.md index 4802c9302..36f35285e 100644 --- a/ts/docs/commands/proposal/delete.md +++ b/ts/docs/commands/proposal/delete.md @@ -58,7 +58,7 @@ echo "$PW" | wallet-cli proposal delete 48 --network tron:nile --wait --password ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"proposal.delete","data":{"kind":"proposal-delete","stage":"confirmed","txId":"c7d...","confirmed":true,"blockNumber":57880355,"failed":false,"proposalId":48,"feeSun":0,"resource":{"netUsage":265,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6390,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"proposal.delete","data":{"kind":"proposal-delete","stage":"confirmed","txId":"c7d...","confirmed":true,"blockNumber":57880355,"failed":false,"proposalId":48,"proposerAddress":"TSRmq8kP...","feeSun":0,"energyUsed":0,"netUsed":265,"energyFeeSun":0,"netFeeSun":0,"resource":{"netUsage":265,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6390,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -67,8 +67,8 @@ echo "$PW" | wallet-cli proposal delete 48 --network tron:nile --wait --password | Stage | Fields | |---|---| -| default (submit) | `kind: "proposal-delete"`, `stage: "submitted"`, `txId`, `proposalId` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| default (submit) | `kind: "proposal-delete"`, `stage: "submitted"`, `txId`, `proposalId`, `proposerAddress` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), and `failed` | ## Exit status diff --git a/ts/docs/commands/token/add.md b/ts/docs/commands/token/add.md index 3268f8ec3..8634cc83c 100644 --- a/ts/docs/commands/token/add.md +++ b/ts/docs/commands/token/add.md @@ -67,7 +67,7 @@ wallet-cli token add --contract 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 --net ## Exit status -`0` added · `1` execution failure (`token_metadata_unavailable` — metadata could not be fetched, nothing is stored) · `2` usage error (`token_already_listed` — already in the official layer; `invalid_value`; `invalid_option` — `--asset-id` on an EVM network). +`0` added · `1` execution failure (`token_metadata_unavailable` — metadata could not be fetched, nothing is stored; `encoding_error` / `io_error` — the local token book could not be decoded or written) · `2` usage error (`token_already_listed` — already in the official layer; `invalid_value`; `invalid_option` — `--asset-id` on an EVM network). ## See also diff --git a/ts/docs/commands/token/info.md b/ts/docs/commands/token/info.md index f8fcba0af..4eaa9281b 100644 --- a/ts/docs/commands/token/info.md +++ b/ts/docs/commands/token/info.md @@ -12,7 +12,7 @@ wallet-cli token info (--contract
| --asset-id ) [options] Fetches a token's metadata straight from the chain — a pure RPC read that never touches your accounts. Pass exactly one selector: `--contract` for a contract-based token (TRC20 on TRON, ERC20 on EVM), `--asset-id` for a TRC10 asset. -TRON additionally reports `totalSupply`; the EVM read returns `name`, `symbol` and `decimals` only. +Contract-token reads (TRC20/ERC20) return normalized metadata. The TRC10 `--asset-id` branch keeps the node record's snake_case keys, but decodes its text fields (`name`, `abbr`, `url`, `description`) to UTF-8 and serializes int64 quantities such as `total_supply` as decimal strings. Do not apply the contract-token field set to a TRC10 response. ## Options @@ -53,15 +53,42 @@ wallet-cli token info --contract 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 --ne {"schema":"wallet-cli.result.v1","success":true,"command":"token.info","data":{"contract":"0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238","symbol":"USDC","decimals":6,"name":"USDC"},"meta":{"durationMs":409,"warnings":[]},"chain":{"family":"evm","network":"evm:11155111","chainId":"11155111"}} ``` +A TRC10 lookup keeps the node's key names while decoding text and preserving quantities exactly: + +```bash +wallet-cli token info --asset-id 1002000 --network tron:nile -o json +``` + +```json +{"schema":"wallet-cli.result.v1","success":true,"command":"token.info","data":{"id":"1002000","owner_address":"418225f3aa48a2d30643a64410abb1e914dfa0bd2f","name":"MyToken","abbr":"MTK","description":"Demo TRC10","url":"https://mytoken.example","total_supply":"1000000000","trx_num":1,"num":100,"precision":6,"start_time":1785542400000,"end_time":1788134400000,"free_asset_net_limit":0,"public_free_asset_net_limit":0,"frozen_supply":[]},"meta":{"durationMs":210,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +``` + ## Output +For `--contract` (TRC20/ERC20): + | Field | Type | Meaning | |---|---|---| -| `contract` | string | Token contract address (or `assetId` for TRC10) | +| `contract` | string | Token contract address | | `name` | string | Token name | | `symbol` | string | Token symbol | | `decimals` | number | Token decimals | -| `totalSupply` | string | Total supply, raw integer in base units; **TRON only** | +| `totalSupply` | string | Total supply when the TRON contract adapter returns it; not returned by the EVM service | + +For `--asset-id` (TRC10): + +| Field | Type | Meaning | +|---|---|---| +| `id` / `owner_address` | string | Asset id and the node's hex owner address | +| `name` / `abbr` / `description` / `url` | string | UTF-8 text decoded from the node response | +| `total_supply` | string | Exact int64 supply in minimal units | +| `trx_num` / `num` | number | On-chain ICO rate pair | +| `precision` | number? | Asset precision; absent means `0` | +| `start_time` / `end_time` | number | ICO window, epoch milliseconds | +| `free_asset_net_limit` / `public_free_asset_net_limit` | number? | Free-bandwidth limits when present | +| `frozen_supply` | array? | Frozen tranches; each `frozen_amount` is a decimal string and `frozen_days` is a number | + +The TRC10 shape does not contain normalized `contract`, `symbol`, or `decimals` keys. ## Exit status diff --git a/ts/docs/commands/token/remove.md b/ts/docs/commands/token/remove.md index 648ce60d9..cac6ec928 100644 --- a/ts/docs/commands/token/remove.md +++ b/ts/docs/commands/token/remove.md @@ -53,7 +53,7 @@ wallet-cli token remove --contract TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf --network ## Exit status -`0` removed · `1` execution failure (`encoding_error`, `insecure_permissions`, `io_error`) · `2` usage error (`token_is_official` — official-layer tokens can't be removed; `token_not_in_book` — not in the user layer; `invalid_value`; `invalid_option` — `--asset-id` on an EVM network). +`0` removed · `1` execution failure (`encoding_error` / `io_error` — the local token book could not be decoded or written) · `2` usage error (`token_is_official` — official-layer tokens can't be removed; `token_not_in_book` — not in the user layer; `invalid_value`; `invalid_option` — `--asset-id` on an EVM network). ## See also diff --git a/ts/docs/commands/tx/info.md b/ts/docs/commands/tx/info.md index 9bc2731c5..60bd5f7b0 100644 --- a/ts/docs/commands/tx/info.md +++ b/ts/docs/commands/tx/info.md @@ -41,16 +41,16 @@ Confirmations 2 Fee 2.1 TRX ``` -`-o json` returns the full detail (`transaction` is the raw tx, `info` is the receipt; elided as `{…}` here): +`-o json` returns the full detail (`transaction` is the raw tx, `info` is the receipt; shown as empty objects here): ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"tx.info","data":{"txid":"34d9da372cd7fa9d4e7384744c0925af9d682eef4c9410fb831e0b87b355171b","from":"TR66PwBkGtktmiRhGjP9C6o8ts2ndDo4sP","to":"TVMV1gstFzkDyBfrpNc1Sa72Az2dMgDCLY","amount":"1","symbol":"TRX","status":"success","blockNumber":70433563,"confirmations":5,"feeSun":2100000,"transaction":{…},"info":{…}},"meta":{"durationMs":1396,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"tx.info","data":{"txid":"34d9da372cd7fa9d4e7384744c0925af9d682eef4c9410fb831e0b87b355171b","from":"TR66PwBkGtktmiRhGjP9C6o8ts2ndDo4sP","to":"TVMV1gstFzkDyBfrpNc1Sa72Az2dMgDCLY","amount":"1","symbol":"TRX","status":"success","blockNumber":70433563,"confirmations":5,"feeSun":2100000,"transaction":{},"info":{}},"meta":{"durationMs":1396,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` On an EVM network the summary adds `type` and `nonce`, prices the fee in wei, and nests `receipt` instead of `info`: ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"tx.info","data":{"txid":"0x55b0068ef31bce39bbf5b06d456eaef307fd77f96d85ea291f48c1ae4b900d80","type":"contract-call","from":"0x88878d9250e68C574912f5618ad3b43f675B8888","nonce":342,"to":"0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E","rawAmount":"0","amount":"0","symbol":"ETH","blockTime":1787817996,"status":"success","blockNumber":11576586,"gasUsed":"127165","feeWei":"635825000000000","effectiveGasPriceWei":"5000000000","confirmations":0,"transaction":{…},"receipt":{…}},"meta":{"durationMs":706,"warnings":[]},"chain":{"family":"evm","network":"evm:11155111","chainId":"11155111"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"tx.info","data":{"txid":"0x55b0068ef31bce39bbf5b06d456eaef307fd77f96d85ea291f48c1ae4b900d80","type":"contract-call","from":"0x88878d9250e68C574912f5618ad3b43f675B8888","nonce":342,"to":"0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E","rawAmount":"0","amount":"0","symbol":"ETH","blockTime":1787817996,"status":"success","blockNumber":11576586,"gasUsed":"127165","feeWei":"635825000000000","effectiveGasPriceWei":"5000000000","confirmations":0,"transaction":{},"receipt":{}},"meta":{"durationMs":706,"warnings":[]},"chain":{"family":"evm","network":"evm:11155111","chainId":"11155111"}} ``` An unknown txid errors out (exit 1) — unlike `tx status`'s `not_found` (exit 0): diff --git a/ts/docs/commands/tx/multisig.md b/ts/docs/commands/tx/multisig.md index aa404e5ef..3467a402a 100644 --- a/ts/docs/commands/tx/multisig.md +++ b/ts/docs/commands/tx/multisig.md @@ -88,10 +88,10 @@ wallet-cli tx multisig --account cosigner --network tron:nile ```console Multi-sig transactions — TronLink service (1 total) -| TxID | Type | Amount | State | Progress | Expires | -| ------ | ------------ | --------- | ------------ | -------- | ---------------- | -| 9c1... | Transfer TRX | 1,000 TRX | awaiting you | 1 / 2 | 2026-07-14 15:32 | -! Co-sign it: wallet-cli tx multisig --sign 9c1... +| TxID | Type | Amount | State | Validation | Progress | Expires | +| ------ | ---------------- | --------- | ------------ | ---------- | -------- | ---------------- | +| 9c1... | TransferContract | 1,000 TRX | awaiting you | verified | 1 / 2 | 2026-07-14 15:32 | +! Co-sign one with: wallet-cli tx multisig --sign ``` ```bash @@ -123,7 +123,7 @@ Progress 2 / 2 — threshold reached The list mode as JSON: ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"tx.multisig","data":{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","total":1,"unreadable":0,"transactions":[{"txId":"9c1...","state":"pending","verified":true,"contractType":"TransferContract","operation":"Transfer TRX","rawAmount":"1000000000","originator":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","owner":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","permission":{"id":2,"name":"finance","threshold":2},"currentWeight":1,"missingWeight":1,"thresholdReached":false,"awaitingMySignature":true,"signedByCurrentAccount":false,"expiration":1784388720000}]},"meta":{"durationMs":420,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"tx.multisig","data":{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","total":1,"unreadable":0,"transactions":[{"verified":true,"txId":"9c1...","state":"pending","contractType":"TransferContract","originator":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","owner":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","permission":{"id":2,"name":"finance","threshold":2},"currentWeight":1,"missingWeight":1,"thresholdReached":false,"awaitingMySignature":true,"signedByCurrentAccount":false,"createdAt":1784385120000,"expiration":1784388720000,"expired":false,"signatures":1,"signatureProgress":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1,"signed":true,"signedAt":1784385130000},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1,"signed":false,"signedAt":null}],"from":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","rawAmount":"1000000000"}]},"meta":{"durationMs":420,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` Optionally, a WebSocket nudge (count only — list them to see details): @@ -135,6 +135,10 @@ wallet-cli tx multisig --watch --account cosigner --network tron:nile ```console Watching TronLink multi-sig service for tron:nile … (Ctrl-C to stop) 🔔 You have 1 transaction(s) to sign — view them with: wallet-cli tx multisig + +✅ Stopped watching TronLink multi-sig service + Address TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz + Notifications 1 ``` ## Output @@ -152,13 +156,18 @@ Watching TronLink multi-sig service for tron:nile … (Ctrl-C to stop) | `transactions[].state` | string | `pending` \| `signed` \| `success` \| `failed` | | `transactions[].verified` | boolean | Whether the record reconciled with the chain | | `transactions[].unverifiedReason` | string? | Present only when `verified` is `false` | -| `transactions[].contractType` / `operation` | string | Machine enum / human operation name | -| `transactions[].rawAmount` | string | Raw integer amount; units follow the contract type | +| `transactions[].contractType` | string | Contract type reported by the service | +| `transactions[].from` / `to` | string? | Decoded sender and recipient when the contract type exposes them | +| `transactions[].rawAmount` | string? | Decoded raw integer amount when available; units follow the contract type | | `transactions[].originator` / `owner` | string | Who created it / whose account it acts on | | `transactions[].permission` | object | `id`, `name`, `threshold` | | `transactions[].currentWeight` / `missingWeight` / `thresholdReached` | — | Approval progress | | `transactions[].awaitingMySignature` | boolean | Whether it is waiting on the selected account | | `transactions[].signedByCurrentAccount` | boolean | Whether this account already signed | +| `transactions[].createdAt` / `expiration` | number | Service creation time and transaction expiry, in Unix milliseconds | +| `transactions[].expired` | boolean | Whether the transaction is already expired | +| `transactions[].signatures` | number | Number of signatures currently attached | +| `transactions[].signatureProgress` | array | Per-key `address`, `weight`, `signed`, and nullable `signedAt` | A record the client cannot reconcile with the chain stays visible and is labelled rather than failing the whole page. @@ -170,7 +179,7 @@ A record the client cannot reconcile with the chain stays visible and is labelle | `hex` | string | The transaction hex including all signatures gathered so far | | `transaction` | object | Transaction summary + approval progress | -`--watch` streams count nudges and emits no terminal JSON frame. +`--watch` streams count nudges. When stopped, its terminal result is `{action: "watch", address, notifications}` in JSON mode; text mode prints the same address and notification count. ## Exit status diff --git a/ts/docs/commands/tx/sign.md b/ts/docs/commands/tx/sign.md index 133047ac1..0d64cce5e 100644 --- a/ts/docs/commands/tx/sign.md +++ b/ts/docs/commands/tx/sign.md @@ -32,7 +32,7 @@ Payload integrity is checked in every mode, offline included. A TRON transaction That three-way check is TRON's; an EVM transaction hashes its own bytes, so there is nothing to disagree. -Four contract types cannot be re-encoded by the bundled decoder — `UnfreezeAssetContract`, `ShieldedTransferContract`, `MarketSellAssetContract`, `MarketCancelOrderContract`. `--hex` / `--file` input carrying one is refused with `invalid_transaction`; sign those through `--transaction` JSON instead. +Three contract types cannot be field-by-field re-encoded by the bundled decoder — `ShieldedTransferContract`, `MarketSellAssetContract`, and `MarketCancelOrderContract`. They are not refused: the command still verifies `txID = sha256(raw_data_hex)` and binds the declared contract type to the protobuf envelope, but it cannot independently prove that the human-readable fields inside `raw_data` match the executed fields. Treat those fields as unverified and inspect the artifact with tooling that understands the contract type before signing. `UnfreezeAssetContract` is fully re-encoded by the bundled TRC10 codec. ## Options @@ -42,7 +42,7 @@ Four contract types cannot be re-encoded by the bundled decoder — `UnfreezeAss | `--file ` | **Required** (one of). File containing the transaction hex (prefer this for long hex) | | `--transaction ` | **Required** (one of). **TRON only.** Unsigned TRON transaction JSON; compatibility path, never checked online | | `--offline` | Sign locally without contacting a node; skips the signer-permission and approval-weight checks. Only meaningful on TRON — EVM signing contacts no node either way | -| `--out ` | Write the resulting hex to a file (mode 0644, written atomically) instead of stdout | +| `--out ` | **TRON artifact path only.** Atomically write the resulting co-signed protobuf hex to a mode-0644 file instead of stdout. Do not use on EVM: the current EVM binding accepts but ignores this option | Plus the [global options](../index.md#global-options-every-command) and `--password-stdin` for software accounts. @@ -117,6 +117,12 @@ echo "$PW" | wallet-cli tx sign --file tx.hex --account cosigner --out tx.signed {"schema":"wallet-cli.result.v1","success":true,"command":"tx.sign","data":{"kind":"tx-sign","signer":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","hex":"0a02...9f31","checked":true,"transaction":{"txId":"9c1...","contractType":"TransferContract","operation":"Transfer TRX","from":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","rawAmount":"1000000000","permissionId":2,"expiration":1784388720000,"expired":false,"signatures":2},"signerWeight":1,"approval":{"txId":"9c1...","contractType":"TransferContract","operation":"Transfer TRX","from":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","rawAmount":"1000000000","permission":{"id":2,"name":"finance","threshold":2},"currentWeight":2,"missingWeight":0,"thresholdReached":true,"approved":[{"address":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","weight":1},{"address":"TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz","weight":1}],"expiration":1784388720000,"expired":false,"signatures":2}},"meta":{"durationMs":310,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` +An EVM artifact-signing result has the single-signature shape: + +```json +{"schema":"wallet-cli.result.v1","success":true,"command":"tx.sign","data":{"kind":"sign","mode":"sign-only","signed":{"raw":"0x02f86b...","hash":"0x55b0068ef31bce39bbf5b06d456eaef307fd77f96d85ea291f48c1ae4b900d80"},"address":"0x88878d9250e68C574912f5618ad3b43f675B8888","txId":"0x55b0068ef31bce39bbf5b06d456eaef307fd77f96d85ea291f48c1ae4b900d80"},"meta":{"durationMs":84,"warnings":[]},"chain":{"family":"evm","network":"evm:11155111","chainId":"11155111"}} +``` + ## Output The two input modes return different shapes. @@ -133,11 +139,11 @@ The two input modes return different shapes. | `signerWeight` | number | The signer's weight in the group. TRON, and only when `checked` is `true` | | `approval` | object | Authoritative online approval state, same shape as [`tx approvals`](approvals.md) `data`. TRON, and only when `checked` is `true` | -On EVM the result is the single-signature shape instead — `kind: "sign"`, `mode: "sign-only"`, `signed` (`{raw, hash}`), `hex`, `address`, `txId` — the same shape `tx send --sign-only` emits, since one signature completes the transaction. +On EVM the result is the single-signature shape instead — `kind: "sign"`, `mode: "sign-only"`, `signed` (`{raw, hash}`), `address`, and `txId`. There is no top-level `hex`; the raw signed transaction is `signed.raw`. The accepted `--out` option is currently ignored by the EVM binding, so write `data.signed.raw` yourself or omit the flag. -`transaction` is always present and identical in both modes, so a consumer can read it unconditionally; test `checked` before reaching for `approval`. +For TRON `--hex` / `--file` results, `transaction` is always present in online and offline modes, so a consumer can read it unconditionally; test `checked` before reaching for `approval`. EVM results do not contain `transaction` or `checked`. -`--transaction` (direct JSON signing) returns the same shape `tx send --sign-only` emits, so consumers need no branch: +`--transaction` is the TRON-only direct JSON path and returns the same shape that TRON `tx send --sign-only` emits: | Field | Type | Meaning | |---|---|---| @@ -145,7 +151,7 @@ On EVM the result is the single-signature shape instead — `kind: "sign"`, `mod | `mode` | string | `"sign-only"` | | `address` | string | Address that produced the signature | | `txId` | string | Transaction id | -| `signed` | object | The signed transaction — exactly what [`tx broadcast`](broadcast.md) accepts. A TRON transaction object here; `{raw, hash}` on EVM | +| `signed` | object | Signed TRON transaction object — exactly what TRON [`tx broadcast`](broadcast.md) accepts through `--transaction` / `--tx-stdin` | No `fee` is reported for `--transaction`: nothing was estimated, because the transaction was not built here. diff --git a/ts/docs/commands/vote/cast.md b/ts/docs/commands/vote/cast.md index 3f0cd0f25..d311310f8 100644 --- a/ts/docs/commands/vote/cast.md +++ b/ts/docs/commands/vote/cast.md @@ -48,9 +48,9 @@ echo "$PW" | wallet-cli vote cast --for TZ4...=600 --for TT5...=400 --network tr ``` ```console -⏳ Submitted — vote 1,000 TP across 2 SRs - TxID e5f... +⏳ Voted 1,000 TP across 2 witnesses Votes TZ4...=600, TT5...=400 + TxID e5f... Status pending — tallied at next maintenance cycle (~6h) ! Track it: wallet-cli tx info --network tron:nile --txid e5f... ``` @@ -70,10 +70,10 @@ echo "$PW" | wallet-cli vote cast --for TZ4...=600 --for TT5...=400 --network tr ``` ```console -✅ Voted 1,000 TP across 2 SRs - TxID f8a... +✅ Voted 1,000 TP across 2 witnesses Votes TZ4...=600, TT5...=400 - Block 84,121,055 + TxID f8a... + Block #84,121,055 Fee 0 TRX Status success — tallied at next maintenance cycle (~6h) ``` diff --git a/ts/docs/commands/vote/list.md b/ts/docs/commands/vote/list.md index 933b0bc6a..e3c4b55d0 100644 --- a/ts/docs/commands/vote/list.md +++ b/ts/docs/commands/vote/list.md @@ -34,11 +34,11 @@ wallet-cli vote list --limit 3 --network tron:nile ``` ```console -| Rank | Name | Votes | APR | Reward ratio | Address | -| ---- | --------------- | ------------- | ---- | ------------ | ---------------------------------- | -| 1 | TRONSCAN | 1,203,456,789 | — | 80% | TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g | -| 2 | Binance Staking | 998,765,432 | — | 0% | TT5W8MPbYJih9R586kTszb4LoybzUvCYm2 | -| 3 | JustLend | 876,543,210 | — | 80% | TWxkzUeAiKcFvzXvJEcaTQCQqCuMednAtN | +| Rank | Name | Votes | APR | Reward ratio | Address | +| ---- | ---------------- | ------------- | ---- | ------------ | ---------------------------------- | +| 1 | tronscan.org | 1,203,456,789 | — | 80% | TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g | +| 2 | binance.com | 998,765,432 | — | 0% | TT5W8MPbYJih9R586kTszb4LoybzUvCYm2 | +| 3 | justlend.org | 876,543,210 | — | 80% | TWxkzUeAiKcFvzXvJEcaTQCQqCuMednAtN | ``` ```bash @@ -46,7 +46,7 @@ wallet-cli vote list --limit 3 --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"vote.list","data":{"witnesses":[{"rank":1,"name":"TRONSCAN","address":"TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g","voteCount":"1203456789","rewardRatioPct":80,"brokeragePct":20,"aprPct":null}]},"meta":{"durationMs":40,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"vote.list","data":{"witnesses":[{"rank":1,"name":"tronscan.org","address":"TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g","voteCount":"1203456789","rewardRatioPct":80,"brokeragePct":20,"aprPct":null}]},"meta":{"durationMs":40,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -56,11 +56,11 @@ wallet-cli vote list --limit 3 --network tron:nile -o json | Field | Type | Meaning | |---|---|---| | `rank` | number | Rank by vote count (1 = most votes) | -| `name` | string | SR name | +| `name` | string | Hostname derived from the witness URL; falls back to the URL text or address | | `address` | string | SR base58 address | | `voteCount` | string | Total votes, raw integer | -| `rewardRatioPct` | number | % of rewards passed to voters (on-chain) | -| `brokeragePct` | number | SR's cut (= 100 − `rewardRatioPct`) | +| `rewardRatioPct` | number \| null | % of rewards passed to voters; `null` when brokerage cannot be read | +| `brokeragePct` | number \| null | SR's cut (= 100 − `rewardRatioPct`); `null` when unavailable | | `aprPct` | null | Reserved field; always `null` in the current implementation | ## Exit status diff --git a/ts/docs/commands/vote/status.md b/ts/docs/commands/vote/status.md index b06d8e41b..933fe00cd 100644 --- a/ts/docs/commands/vote/status.md +++ b/ts/docs/commands/vote/status.md @@ -35,9 +35,9 @@ Claimable 12.345678 TRX Current votes (2) | Name | Votes | APR | Reward ratio | Address | | --------------- | ----- | ---- | ------------ | ---------------------------------- | -| TRONSCAN | 600 | — | 80% | TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g | -| Binance Staking | 400 | — | 0% | TT5W8MPbYJih9R586kTszb4LoybzUvCYm2 | -! 400 votes on Binance Staking earn nothing — 0% reward ratio +| tronscan.org | 600 | — | 80% | TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g | +| binance.com | 400 | — | 0% | TT5W8MPbYJih9R586kTszb4LoybzUvCYm2 | +! 400 votes on binance.com earn nothing — 0% reward ratio ``` ```bash @@ -45,7 +45,7 @@ wallet-cli vote status --account main --network tron:nile -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"vote.status","data":{"address":"TQk...","votingPower":{"total":1500,"used":1000,"available":500},"claimableRewardSun":"12345678","votes":[{"witness":"TZ4...","name":"TRONSCAN","count":600,"rewardRatioPct":80,"brokeragePct":20,"aprPct":null},{"witness":"TT5...","name":"Binance Staking","count":400,"rewardRatioPct":0,"brokeragePct":100,"aprPct":null}]},"meta":{"durationMs":16,"warnings":["400 votes on TT5... (Binance Staking) earn nothing: reward ratio is 0%"]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"vote.status","data":{"address":"TQk...","votingPower":{"total":1500,"used":1000,"available":500},"claimableRewardSun":"12345678","votes":[{"witness":"TZ4...","name":"tronscan.org","count":600,"rewardRatioPct":80,"brokeragePct":20,"aprPct":null},{"witness":"TT5...","name":"binance.com","count":400,"rewardRatioPct":0,"brokeragePct":100,"aprPct":null}]},"meta":{"durationMs":16,"warnings":["400 votes on TT5... (binance.com) earn nothing: reward ratio is 0%"]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -55,7 +55,7 @@ wallet-cli vote status --account main --network tron:nile -o json | `address` | string | Queried account | | `votingPower.total` / `.used` / `.available` | number | TP total / spent / spendable | | `claimableRewardSun` | string | Currently claimable reward, in SUN | -| `votes[]` | array | Current distribution: `witness`, `name`, `count`, `rewardRatioPct`, `brokeragePct`, and reserved `aprPct` (always `null`) | +| `votes[]` | array | Current distribution: `witness`, URL-hostname `name`, `count`, nullable `rewardRatioPct` / `brokeragePct`, and reserved `aprPct` (always `null`) | Zero-reward-ratio warnings appear in `meta.warnings` as plain strings — see [reading `meta.warnings`](../../machine-interface.md#reading-metawarnings). diff --git a/ts/docs/commands/witness/create.md b/ts/docs/commands/witness/create.md index 2dc6865dc..f17de3dfe 100644 --- a/ts/docs/commands/witness/create.md +++ b/ts/docs/commands/witness/create.md @@ -60,7 +60,7 @@ echo "$PW" | wallet-cli witness create --url https://sr.acme.io --network tron:n ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"witness.create","data":{"kind":"witness-create","stage":"confirmed","txId":"d3a...","confirmed":true,"blockNumber":57881020,"failed":false,"witnessAddress":"TSRmq8kP...","url":"https://sr.acme.io","feeSun":9999000000,"resource":{"netUsage":285,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0},"registrationFeeSun":9999000000},"meta":{"durationMs":6620,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"witness.create","data":{"kind":"witness-create","stage":"confirmed","txId":"d3a...","confirmed":true,"blockNumber":57881020,"failed":false,"witnessAddress":"TSRmq8kP...","url":"https://sr.acme.io","feeSun":"9999000000","energyUsed":0,"netUsed":285,"energyFeeSun":0,"netFeeSun":0,"registrationFeeSun":"9999000000","resource":{"netUsage":285,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6620,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -69,10 +69,10 @@ echo "$PW" | wallet-cli witness create --url https://sr.acme.io --network tron:n | Stage | Fields | |---|---| -| default (submit) | `kind: "witness-create"`, `stage: "submitted"`, `txId`, `witnessAddress`, `url` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed`, `registrationFeeSun` | +| default (submit) | `kind: "witness-create"`, `stage: "submitted"`, `txId`, `witnessAddress`, `url`, `feeSun`, and `registrationFeeSun` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), `failed`, and `registrationFeeSun` | -`registrationFeeSun` is the burned registration fee on its own; `feeSun` is the transaction's total cost, which includes it. +`registrationFeeSun` and `feeSun` are decimal strings containing the same irreversible registration burn. The command deliberately overwrites the node receipt's bandwidth/energy fee with that economically relevant amount; do not add the two fields together. ## Exit status diff --git a/ts/docs/commands/witness/set-brokerage.md b/ts/docs/commands/witness/set-brokerage.md index 40f247eab..e71ba4acf 100644 --- a/ts/docs/commands/witness/set-brokerage.md +++ b/ts/docs/commands/witness/set-brokerage.md @@ -62,7 +62,7 @@ echo "$PW" | wallet-cli witness set-brokerage 20 --network tron:nile --wait --pa ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"witness.set-brokerage","data":{"kind":"witness-set-brokerage","stage":"confirmed","txId":"f8c...","confirmed":true,"blockNumber":57881402,"failed":false,"witnessAddress":"TSRmq8kP...","brokerage":20,"feeSun":0,"resource":{"netUsage":269,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6470,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"witness.set-brokerage","data":{"kind":"witness-set-brokerage","stage":"confirmed","txId":"f8c...","confirmed":true,"blockNumber":57881402,"failed":false,"witnessAddress":"TSRmq8kP...","brokerage":20,"feeSun":0,"energyUsed":0,"netUsed":269,"energyFeeSun":0,"netFeeSun":0,"resource":{"netUsage":269,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6470,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -72,7 +72,7 @@ echo "$PW" | wallet-cli witness set-brokerage 20 --network tron:nile --wait --pa | Stage | Fields | |---|---| | default (submit) | `kind: "witness-set-brokerage"`, `stage: "submitted"`, `txId`, `witnessAddress`, `brokerage` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), and `failed` | `brokerage` is the value now in effect, as a number. diff --git a/ts/docs/commands/witness/update.md b/ts/docs/commands/witness/update.md index f50a01b0b..42f36539e 100644 --- a/ts/docs/commands/witness/update.md +++ b/ts/docs/commands/witness/update.md @@ -58,7 +58,7 @@ echo "$PW" | wallet-cli witness update --url https://sr.acme.io/v2 --network tro ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"witness.update","data":{"kind":"witness-update","stage":"confirmed","txId":"e5b...","confirmed":true,"blockNumber":57881190,"failed":false,"witnessAddress":"TSRmq8kP...","url":"https://sr.acme.io/v2","feeSun":0,"resource":{"netUsage":270,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6440,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"witness.update","data":{"kind":"witness-update","stage":"confirmed","txId":"e5b...","confirmed":true,"blockNumber":57881190,"failed":false,"witnessAddress":"TSRmq8kP...","url":"https://sr.acme.io/v2","feeSun":0,"energyUsed":0,"netUsed":270,"energyFeeSun":0,"netFeeSun":0,"resource":{"netUsage":270,"netFeeSun":0,"energyUsage":0,"energyFeeSun":0}},"meta":{"durationMs":6440,"warnings":[]},"chain":{"family":"tron","network":"tron:nile","chainId":"nile"}} ``` ## Output @@ -68,7 +68,7 @@ echo "$PW" | wallet-cli witness update --url https://sr.acme.io/v2 --network tro | Stage | Fields | |---|---| | default (submit) | `kind: "witness-update"`, `stage: "submitted"`, `txId`, `witnessAddress`, `url` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, `feeSun`, `resource`, `failed` | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), their governance compatibility view `resource` (`netUsage`, `netFeeSun`, `energyUsage`, `energyFeeSun`), and `failed` | ## Exit status diff --git a/ts/docs/concepts/security.md b/ts/docs/concepts/security.md index 79254f4b3..bb02d9cb4 100644 --- a/ts/docs/concepts/security.md +++ b/ts/docs/concepts/security.md @@ -46,7 +46,7 @@ Unexpected internal exceptions are collapsed to a generic `internal_error` messa | Software key | `create` / `import` | Convenient; host compromise = key compromise | | Ledger | `import ledger` | Key never on host; every send confirmed on-device. `--app` fixes the account to one chain family — import once per app to cover both — see [Ledger guide](../guide/ledger.md) | | Watch-only | `import watch` | No signing at all; safe for monitoring balances of cold storage. Bound to the pasted address's family | -| Split sign/broadcast | `tx send --build-only` → `tx sign --offline` → `tx broadcast` | Signing machine can stay offline; `--sign-only` still builds and estimates online — see [Scripting](../guide/scripting.md#sign-here-broadcast-there) | +| Split sign/broadcast | TRON: `tx send --build-only --expiration ` → `tx sign --offline` → `tx broadcast`; EVM: the same flow without `--expiration` | Signing machine can stay offline; `--sign-only` still builds and estimates online. TRON defaults to about 60 seconds and permits at most 24 hours, so set the shortest window that covers artifact transfer — see [Scripting](../guide/scripting.md#sign-here-broadcast-there) | ## What wallet-cli cannot do for you diff --git a/ts/docs/guide/ledger.md b/ts/docs/guide/ledger.md index 2591dad7b..71e3bab2d 100644 --- a/ts/docs/guide/ledger.md +++ b/ts/docs/guide/ledger.md @@ -18,12 +18,14 @@ Locally this creates a **watch-only** entry — no secret is stored; signing hap | Flag | Use when | |---|---| -| `--index ` | You know the account index under the app's default path (omit everything for index 0) | +| `--index ` | You know the account index under wallet-cli's family path template | | `--path ` | You need an explicit derivation path, e.g. `m/44'/195'/0'/0/0` (TRON) or `m/44'/60'/0'/0/0` (Ethereum) | | `--address ` | You know the address; wallet-cli scans indexes to find it (`--scan-limit`, default 20) | **`--app` fixes the account to one chain family.** Unlike a software account — which holds a TRON *and* an EVM address from the same seed — a Ledger account has exactly the one address its app derives, and only works on networks of that family. Selecting it elsewhere fails with `family_mismatch`. Import the same device twice, once per app, to cover both. +With no locator, a TTY presents a paged account selector; a non-interactive invocation falls back to index 0. For Ethereum, wallet-cli's `--index ` template is `m/44'/60'/0'/0/` (MetaMask style), while Ledger Live commonly uses `m/44'/60'/'/0/0`. Use `--path` to register the exact Ledger Live account instead of assuming the indexes are interchangeable. + Confirm with `wallet-cli list` — the account appears alongside your software accounts and works with `use`, `--account`, and every query command. `list` shows one family at a time, so a TRON-app account is invisible under `--network sepolia` and vice versa; `-o json` shows every account regardless. ## 2. Sign and send @@ -50,7 +52,7 @@ More remedies: [Troubleshooting](../troubleshooting.md#timeout-exit-1). ## Offline pattern -Ledger already isolates keys, but you can still split build/sign/broadcast. For a device machine with no chain access, build unsigned hex with `--build-only` on a connected machine, sign it with `tx sign --offline` where the Ledger is attached, then broadcast the signed hex from a connected machine. See [Scripting → Sign here, broadcast there](scripting.md#sign-here-broadcast-there). +Ledger already isolates keys, but you can still split build/sign/broadcast. For a device machine with no chain access, build TRON unsigned hex with an explicit signing window, for example `--build-only --expiration 3600000`, on a connected machine; sign it with `tx sign --offline` where the Ledger is attached; then broadcast the signed hex from a connected machine. The default TRON expiry is about 60 seconds and is usually too short for a cross-machine workflow; the maximum is 24 hours. EVM artifacts have no expiration flag. See [Scripting → Sign here, broadcast there](scripting.md#sign-here-broadcast-there). ## See also diff --git a/ts/docs/guide/scripting.md b/ts/docs/guide/scripting.md index c2e5035f5..4288b80fa 100644 --- a/ts/docs/guide/scripting.md +++ b/ts/docs/guide/scripting.md @@ -62,7 +62,7 @@ Or decouple: capture `data.txId`, then poll [`tx status`](../commands/tx/status. ```bash # on the connected build machine wallet-cli tx send --to T... --amount 1 --network tron:nile \ - --build-only -o json | jq -r '.data.hex' > unsigned.hex + --build-only --expiration 3600000 -o json | jq -r '.data.hex' > unsigned.hex # on the offline signing machine printf '%s' "$PW" | wallet-cli tx sign --file unsigned.hex --network tron:nile \ @@ -72,7 +72,7 @@ printf '%s' "$PW" | wallet-cli tx sign --file unsigned.hex --network tron:nile \ wallet-cli tx broadcast --file signed.hex --network tron:nile -o json ``` -The **hex** form above works on both chain families — protobuf on TRON, RLP on EVM. If the signing machine does have RPC access and you only want to withhold broadcast, `tx send --sign-only` emits signed hex directly. +The **hex** form above works on both chain families — protobuf on TRON, RLP on EVM. `--expiration` is TRON-only; its value above gives the transfer one hour for file movement and signing (maximum 24 hours). The node default is about 60 seconds, and `tx sign --offline` refuses an expired artifact, so choose the shortest practical window and rebuild after it expires. Omit the flag for EVM, whose transaction format has no expiration field. If the signing machine does have RPC access and you only want to withhold broadcast, `tx send --sign-only` emits signed hex directly. TRON also accepts signed transaction JSON, but JSON must go through `--transaction` or `--tx-stdin`; `--file` and `--hex` are hex-only: diff --git a/ts/docs/machine-interface.md b/ts/docs/machine-interface.md index f0633bdd3..3fb394391 100644 --- a/ts/docs/machine-interface.md +++ b/ts/docs/machine-interface.md @@ -21,7 +21,7 @@ wallet-cli --json-schema tron # scoped to one chain family wallet-cli --json-schema # one command ``` -One call returns the whole surface: `tool`, `version`, `globalFlags`, `errorCodes`, and `commands[]` — each with `id`, `path`, `usage`, `families`, `requires` (network / auth / wallet), `capability`, `examples`, and a JSON Schema for its input. This is the intended way for an agent to learn the CLI; do not scrape `--help`. +One call returns the whole surface: `tool`, `version`, `globalFlags`, `errorCodes`, and `commands[]`. Command entries carry `id`, `path`, `usage`, `requires` (network / auth / wallet), `capability`, `examples`, and a JSON Schema for their input; chain commands also declare `families`. This is the intended way for an agent to learn the CLI; do not scrape `--help`. ### Chain families @@ -31,7 +31,7 @@ A network belongs to one **chain family**, `tron` or `evm`, and that is what dec - A flag may belong to one family too (`--asset-id` and `--permission-id` are TRON's, `--gas-limit` and `--nonce` are EVM's). Using one on the other family is **`invalid_option`** at exit `2`. `--help` tags them `(tron only)` / `(evm only)`. - An **account** is not family-bound when it holds a key — a seed or private-key account has both a TRON and an EVM address. Watch-only and Ledger accounts hold one address and therefore one family; selecting one on a mismatched network is also `family_mismatch`. -Both checks are static: they depend on the command, the flags and the selected network only, so an agent can decide them from the catalog without a call. +Command-family and flag-family checks are static and can be decided from the catalog. Account-family compatibility also depends on the selected wallet account: key-backed accounts serve both families, while watch-only and Ledger accounts are family-bound. ### Startup wallet-data upgrades @@ -106,10 +106,12 @@ Schema id: `wallet-cli.result.v1`. | `error.details` | object | optional | Structured extras when available | | `meta.durationMs` | number | always | Wall time | | `meta.warnings` | `(string \| {code, message})[]` | always | Non-fatal notices; **elements are not uniformly typed** — see below | -| `meta.pagination` | object | paginated commands only | `offset` / `limit` / `total`; present where `--limit` / `--offset` apply — see [pagination](#pagination) | -| `chain` | object | when a network was selected | `family` / `network` / `chainId`. Present on every chain command, and on the local commands that take `--network` as a display selector (`list`, `current`). Commands that never take one (`config`, `networks`, `contact`, `encoding`, `address`, `create`, `import`, …) omit it — its presence does **not** mean a node was contacted | +| `meta.pagination` | object | windowed commands only | `offset` / `limit` / `total`; present when the command returns a pagination window — see [pagination](#pagination) | +| `chain` | object | when a network was selected | `family` / `network` / `chainId`. Present on every chain command and on local commands whose policy resolves a network. Commands with `network: "none"` omit it; its presence does **not** mean a node was contacted | -Encoding rules: `bigint` values are serialized as decimal **strings** (e.g. `"balance": "1976489000"`), binary as hex. Treat every on-chain amount as a string. +The current local network-aware commands are `backup`, `current`, and `list`. They use the selected or default network as a family/display selector without contacting a node. + +Encoding rules: `bigint` values are serialized as decimal **strings** (e.g. `"balance": "1976489000"`), and binary values are hex. Amounts represented by `bigint` or protocol int64 values are strings; bounded counters and fees such as `feeSun`, `multiSignFeeSun`, `energyUsed`, and `netUsed` may be JSON numbers. Follow each command's field table instead of coercing every amount to one type. ### Reading `meta.warnings` @@ -127,7 +129,7 @@ Helpers that assume strings (`.meta.warnings | join("\n")`, `Array.prototype.joi ### Pagination -Every command that takes `--limit` / `--offset` reports the window it returned in `meta.pagination`, never inside `data`: +Commands that return an offset/limit window report it in `meta.pagination`, never inside `data`. The current set is `asset list`, `exchange list`, `proposal list`, and `backup --records`; a command may accept `--limit` merely as a result cap and then omit pagination metadata. | Key | Type | Meaning | |---|---|---| From c318824f185d0f99785166a59f2b8224e4715360 Mon Sep 17 00:00:00 2001 From: jizhen181-dot Date: Tue, 1 Sep 2026 18:10:55 +0800 Subject: [PATCH 8/8] docs: align docs with implementation across five audit rounds Documentation-only pass over ts/docs, java/docs and both READMEs, reconciling every documented behaviour, error code, field type and terminal transcript against the current code. No source file changes. Behaviour and contract: - asset issue: local validation limited to what the service checks; chain-side bounds attributed to the node (transaction_rejected) - asset info/issue: ICO rate reported as the reduced trxNum/num pair - contract create2 keeps a chain block; deploy documents per-family contractAddress derivation (EVM nonce vs TRON prepared txID) - gasfree renamed to TIP-712; provider_error / provider_rate_limited scoped to what each adapter actually raises - tx info: EVM not_found is exit 2, TRON rpc_error exit 1 - machine-interface: migration_required moved to the exit-2 table, token_metadata_unavailable marked as crossing exit codes, usage_error narrowed to parser failures - secret handling: prompts documented as an interactive-command allowlist; signing commands never prompt Transcripts recomputed from the render layer (kv/table widths, row order, Block "#" prefix, relative-time suffixes, blank lines), and example data made self-consistent (txids, block numbers, pagination totals, per-seed EVM addresses). Java side: wallet selector tables, resource/stake-v2 outputs that are plain text rather than JSON, prompt wording, TxId casing, and examples that could not execute as written (votewitness placeholders, quoted method signatures, GetBlockByLimitNext arguments). Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 2 +- java/README.md | 2 +- java/docs/commands/account.md | 14 ++--- java/docs/commands/chain-data.md | 4 +- java/docs/commands/contract.md | 12 ++--- java/docs/commands/gasfree.md | 8 +-- java/docs/commands/index.md | 4 +- java/docs/commands/multisig.md | 8 +-- java/docs/commands/network.md | 2 +- java/docs/commands/proposals.md | 2 +- java/docs/commands/resources.md | 12 ++--- java/docs/commands/stake-v1-legacy.md | 2 +- java/docs/commands/stake-v2.md | 28 +++++----- java/docs/commands/transfer-trc10.md | 34 +++++++----- java/docs/commands/usdt.md | 56 ++------------------ java/docs/commands/vote-reward.md | 8 +-- java/docs/commands/wallet.md | 64 ++++++++++++----------- java/docs/guide/command-flow.md | 8 +-- ts/README.md | 13 +++-- ts/docs/commands/account/activate.md | 2 +- ts/docs/commands/account/info.md | 2 +- ts/docs/commands/account/portfolio.md | 2 +- ts/docs/commands/account/set.md | 8 +-- ts/docs/commands/asset/index.md | 2 +- ts/docs/commands/asset/info.md | 4 +- ts/docs/commands/asset/issue.md | 26 ++++----- ts/docs/commands/asset/list.md | 2 +- ts/docs/commands/asset/participate.md | 10 ++-- ts/docs/commands/asset/unfreeze.md | 12 ++--- ts/docs/commands/asset/update.md | 8 +-- ts/docs/commands/backup.md | 22 ++++---- ts/docs/commands/block.md | 2 +- ts/docs/commands/chain/node.md | 2 +- ts/docs/commands/chain/params.md | 2 +- ts/docs/commands/chain/prices.md | 4 +- ts/docs/commands/change-password.md | 7 ++- ts/docs/commands/config.md | 23 ++++++-- ts/docs/commands/contact/list.md | 7 +-- ts/docs/commands/contract/call.md | 4 +- ts/docs/commands/contract/create2.md | 6 +-- ts/docs/commands/contract/deploy.md | 2 +- ts/docs/commands/contract/send.md | 16 +++--- ts/docs/commands/create.md | 4 +- ts/docs/commands/current.md | 11 ++-- ts/docs/commands/delete.md | 2 +- ts/docs/commands/derive.md | 4 +- ts/docs/commands/encoding/convert.md | 14 ++--- ts/docs/commands/exchange/index.md | 4 +- ts/docs/commands/exchange/list.md | 6 +-- ts/docs/commands/exchange/trade.md | 6 +-- ts/docs/commands/gasfree/index.md | 2 +- ts/docs/commands/gasfree/info.md | 10 ++-- ts/docs/commands/gasfree/trace.md | 16 +++--- ts/docs/commands/gasfree/transfer.md | 54 +++++++++---------- ts/docs/commands/import/index.md | 2 +- ts/docs/commands/import/ledger.md | 2 +- ts/docs/commands/import/mnemonic.md | 6 +-- ts/docs/commands/index.md | 10 ++-- ts/docs/commands/list.md | 16 +++--- ts/docs/commands/message/sign.md | 2 +- ts/docs/commands/networks.md | 2 +- ts/docs/commands/permission/show.md | 14 ++--- ts/docs/commands/permission/update.md | 24 ++++----- ts/docs/commands/proposal/create.md | 10 ++-- ts/docs/commands/proposal/list.md | 2 +- ts/docs/commands/reward/balance.md | 4 +- ts/docs/commands/reward/withdraw.md | 18 +++---- ts/docs/commands/stake/delegated.md | 4 +- ts/docs/commands/stake/unfreeze.md | 7 +-- ts/docs/commands/tx/approvals.md | 9 ++-- ts/docs/commands/tx/broadcast.md | 7 ++- ts/docs/commands/tx/info.md | 12 ++--- ts/docs/commands/tx/multisig.md | 24 ++++----- ts/docs/commands/tx/sign.md | 17 +++--- ts/docs/commands/tx/status.md | 2 +- ts/docs/commands/typed-data/sign.md | 2 +- ts/docs/commands/vote/cast.md | 18 +++---- ts/docs/commands/vote/list.md | 12 ++--- ts/docs/commands/vote/status.md | 14 ++--- ts/docs/commands/witness/set-brokerage.md | 2 +- ts/docs/concepts/accounts-and-hd.md | 6 +-- ts/docs/concepts/energy-bandwidth.md | 2 +- ts/docs/concepts/networks.md | 4 +- ts/docs/guide/getting-started.md | 10 ++-- ts/docs/guide/scripting.md | 2 +- ts/docs/machine-interface.md | 17 +++--- ts/docs/troubleshooting.md | 10 ++-- 87 files changed, 439 insertions(+), 445 deletions(-) diff --git a/README.md b/README.md index 541204100..ceebd7ce2 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Both manage TRON wallets, but they are independent implementations rather than i | **What it is** | The mature, full-feature reference CLI. | A newer rewrite focused on programmatic integration. | | **Runtime** | JVM — built with Gradle, run as a `.jar`. Uses the [Trident](https://github.com/tronprotocol/trident) SDK. | [Node.js](https://nodejs.org) **20+**. | | **Install** | `git clone` + `cd wallet-cli/java && ./gradlew build` (see [Setup](java/README.md#setup)) | `npm install -g @tron-walletcli/wallet-cli` | -| **How you drive it** | An **interactive prompt only** — start it, then type commands at `>`. | **One-shot subcommands** — `wallet-cli ` from your shell. Interactive prompts only for secret input. | +| **How you drive it** | An **interactive prompt only** — start it, then type commands at `>`. | **One-shot subcommands** — `wallet-cli ` from your shell. Prompts appear only on a short allowlist (`create`, `import *`, `backup`, `change-password`, `delete`); every other command errors instead of asking. | | **Command style** | PascalCase verbs: `RegisterWallet`, `SendCoin`, `GetBalance`. Amounts in **SUN** (1 TRX = 1,000,000 SUN). | Noun-verb subcommands: `create`, `tx send`, `account balance`, with `--flags`. | | **Output for scripts** | Human-readable text. | Stable JSON via `-o json` ([`wallet-cli.result.v1`](ts/docs/machine-interface.md)) + fixed exit codes (`0`/`1`/`2`). | | **Config / networks** | `config.conf` endpoints, or `SwitchNetwork` at runtime. Mainnet · Nile · Shasta · custom. | `--network` flag / `config` command. Three TRON networks plus Ethereum, Sepolia, BNB Smart Chain, and its testnet. | diff --git a/java/README.md b/java/README.md index 64c954e48..254c05d8b 100644 --- a/java/README.md +++ b/java/README.md @@ -76,7 +76,7 @@ The full first-run walkthrough is in the [getting-started guide](docs/guide/gett ## Commands -Every command is documented on a family page under [docs/commands/](docs/commands/index.md). The **[command index](docs/commands/index.md)** has the full A–Z list linking each command to its section; in the wallet, typing any command shows its built-in usage tips. +Every command is documented on a family page under [docs/commands/](docs/commands/index.md). The **[command index](docs/commands/index.md)** has the full A–Z list linking each command to its section; in the wallet, `help ` shows a command's built-in usage tips (`help` alone prints the full table). ### Wallets & accounts diff --git a/java/docs/commands/account.md b/java/docs/commands/account.md index f81ea3fc4..8aa3e982a 100644 --- a/java/docs/commands/account.md +++ b/java/docs/commands/account.md @@ -43,12 +43,14 @@ Before sign transaction hex string is 0a84010a0291a422082bfcd3bb597f3d4040e0cff9 Please confirm and input your permission id, if input y/Y means default 0, other non-numeric characters will cancel transaction. y Please choose your key for sign. -The 1th keystore file name is TJEEKTmaVTYSpJAxahtyuofnDSpe2seajB.json -The 2th keystore file name is TX1L9xonuUo1AHsjUZ3QzH8wCRmKm56Xew.json -The 3th keystore file name is TVuVqnJFuuDxN36bhEbgDQS7rNGA5dSJB7.json -The 4th keystore file name is Ledger-TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe.json -The 5th keystore file name is TYXFDtn86VPFKg4mkwMs45DKDcpAyqsada.json -Please choose between 1 and 5 + +No. Address Name +1 TJEEKTmaVTYSpJAxahtyuofnDSpe2seajB TJEEKTmaVTYSpJAxahtyuofnDSpe2seajB.json +2 TX1L9xonuUo1AHsjUZ3QzH8wCRmKm56Xew TX1L9xonuUo1AHsjUZ3QzH8wCRmKm56Xew.json +3 TVuVqnJFuuDxN36bhEbgDQS7rNGA5dSJB7 TVuVqnJFuuDxN36bhEbgDQS7rNGA5dSJB7.json +4 TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe Ledger-TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe.json +5 TYXFDtn86VPFKg4mkwMs45DKDcpAyqsada TYXFDtn86VPFKg4mkwMs45DKDcpAyqsada.json +Please choose No. between 1 and 5, or enter search to search wallets 1 After sign transaction hex string is 0a84010a0291a422082bfcd3bb597f3d404083bd9cfae5325a6612640a32747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e4163636f756e74437265617465436f6e7472616374122e0a15419d9c2bb5ee381a4396dd49ce42292e756b2e5e4b12154124764e4674179d4578cfc4c833c1ac1a09f6ce56708e8df6efe5321241ce53add4f75fe1838aa7e0a4e2411b3bbfce1d2164d68dac18507ed87e22ae503f65592a1161640834b3c0cef43c28f20b2d335120cc78b6f745a82ea95e451100 TxId is 26d6fcdfdc0018097ec4166eb140e19ebd597bea2212579d2f6d921b0ad6e56f diff --git a/java/docs/commands/chain-data.md b/java/docs/commands/chain-data.md index 0c118029d..0ed3b52da 100644 --- a/java/docs/commands/chain-data.md +++ b/java/docs/commands/chain-data.md @@ -57,10 +57,10 @@ Get the latest `n` blocks, where 0 < n < 100. ### GetBlockByLimitNext ```console -> GetBlockByLimitNext startBlockId endBlockId +> GetBlockByLimitNext start_block_number end_block_number ``` -Get the block in the range [startBlockId, endBlockId). +Get the blocks in the block-height range [start_block_number, end_block_number). Both arguments are block **numbers**, not block ids. ## Chain parameters & nodes diff --git a/java/docs/commands/contract.md b/java/docs/commands/contract.md index 6b47145fb..0b37b16b0 100644 --- a/java/docs/commands/contract.md +++ b/java/docs/commands/contract.md @@ -5,7 +5,7 @@ Deploy, trigger, and inspect smart contracts. ## DeployContract ```console -> DeployContract [ownerAddress] contractName ABI byteCode constructor params isHex fee_limit consume_user_resource_percent origin_energy_limit value token_value token_id(e.g: TRXTOKEN, use # if don't provided) library:address,...> +> DeployContract [ownerAddress] contractName ABI byteCode constructor params isHex fee_limit consume_user_resource_percent origin_energy_limit value token_value token_id(e.g: TRXTOKEN, use # if don't provided) ``` - `OwnerAddress` — the address of the account that initiated the transaction, optional, default is the address of the login account. @@ -23,8 +23,7 @@ Deploy, trigger, and inspect smart contracts. Example: ```console -> deployContract normalcontract544 [{"constant":false,"inputs":[{"name":"i","type":"uint256"}],"name": "findArgsByIndexTest","outputs":[{"name":"z","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] -608060405234801561001057600080fd5b50610134806100206000396000f3006080604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663329000b58114610045575b600080fd5b34801561005157600080fd5b5061005d60043561006f565b60408051918252519081900360200190f35b604080516003808252608082019092526000916060919060208201838038833901905050905060018160008151811015156100a657fe5b602090810290910101528051600290829060019081106100c257fe5b602090810290910101528051600390829060029081106100de57fe5b6020908102909101015280518190849081106100f657fe5b906020019060200201519150509190505600a165627a7a72305820b24fc247fdaf3644b3c4c94fcee380aa610ed83415061ff9e65d7fa94a5a50a00029 # # false 1000000000 75 50000 0 0 # +> deployContract normalcontract544 [{"constant":false,"inputs":[{"name":"i","type":"uint256"}],"name": "findArgsByIndexTest","outputs":[{"name":"z","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] 608060405234801561001057600080fd5b50610134806100206000396000f3006080604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663329000b58114610045575b600080fd5b34801561005157600080fd5b5061005d60043561006f565b60408051918252519081900360200190f35b604080516003808252608082019092526000916060919060208201838038833901905050905060018160008151811015156100a657fe5b602090810290910101528051600290829060019081106100c257fe5b602090810290910101528051600390829060029081106100de57fe5b6020908102909101015280518190849081106100f657fe5b906020019060200201519150509190505600a165627a7a72305820b24fc247fdaf3644b3c4c94fcee380aa610ed83415061ff9e65d7fa94a5a50a00029 # # false 1000000000 75 50000 0 0 # ``` Get the result of the contract execution with the `getTransactionInfoById` command: @@ -84,8 +83,7 @@ Takes 5 parameters, or 8 when the trailing `value token_value token_id` group is Example: ```console -> triggerContract TGdtALTPZ1FWQcc5MW7aK3o1ASaookkJxG findArgsByIndexTest(uint256) 0 false -1000000000 0 0 # +> triggerContract TGdtALTPZ1FWQcc5MW7aK3o1ASaookkJxG findArgsByIndexTest(uint256) 0 false 1000000000 0 0 # # Get the result of the contract execution with the getTransactionInfoById command > getTransactionInfoById 7d9c4e765ea53cf6749d8a89ac07d577141b93f83adc4015f0b266d8f5c2dec4 { @@ -128,7 +126,7 @@ The command accepts exactly five parameters without value/token fields, or eight Example: ```console -> TriggerConstantContract TSNEe5Tf4rnc9zPMNXfaTF5fZfHDDH8oyW TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs "balanceOf(address)" 000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c true +> TriggerConstantContract TSNEe5Tf4rnc9zPMNXfaTF5fZfHDDH8oyW TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs balanceOf(address) 000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c true ``` ## ClearContractABI @@ -242,7 +240,7 @@ Estimate the energy required for the successful execution of a smart contract tr Example: ```console -> EstimateEnergy TSNEe5Tf4rnc9zPMNXfaTF5fZfHDDH8oyW TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs "balanceOf(address)" 000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c true +> EstimateEnergy TSNEe5Tf4rnc9zPMNXfaTF5fZfHDDH8oyW TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs balanceOf(address) 000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c true ``` ## See also diff --git a/java/docs/commands/gasfree.md b/java/docs/commands/gasfree.md index 4b0b74ff5..669b5d406 100644 --- a/java/docs/commands/gasfree.md +++ b/java/docs/commands/gasfree.md @@ -14,7 +14,6 @@ For the current wallet address: ```console wallet> gasfreeinfo -balanceOf(address):70a08231 { "gasFreeAddress":"TCtSt8fCkZcVdrGpaVHUr6P8EmdjysswMF", "active":true, @@ -30,7 +29,6 @@ For a specified address: ```console wallet> gasfreeinfo TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe -balanceOf(address):70a08231 { "gasFreeAddress":"TCtSt8fCkZcVdrGpaVHUr6P8EmdjysswMF", "active":true, @@ -49,7 +47,8 @@ Submit a gas-free token transfer request. ```console wallet> gasfreetransfer TEkj3ndMVEmFLYaFrATMwMjBRZ1EAZkucT 100000 -GasFreeTransfer result: { +GasFreeTransfer result: +{ "code":200, "data":{ "amount":100000, @@ -81,7 +80,8 @@ Track transfer status — check the progress of a GasFree transfer using the `id ```console wallet> gasfreetrace 6c3ff67e-0bf4-4c09-91ca-0c7c254b01a0 -GasFreeTrace result: { +GasFreeTrace result: +{ "code":200, "data":{ "amount":100000, diff --git a/java/docs/commands/index.md b/java/docs/commands/index.md index 66aef7619..377975bce 100644 --- a/java/docs/commands/index.md +++ b/java/docs/commands/index.md @@ -2,7 +2,7 @@ Commands are grouped into family pages below; the A–Z index links each command to its owning page. Every family page is populated. Links point to the owning page (open it and jump to the command's section). -Type any command in the interactive wallet to see its built-in usage tips. +Run `help ` in the interactive wallet to see a command's built-in usage tips; `help` on its own prints the full table. Typing a command bare does not reliably show usage — most commands that need no argument simply run. ## By family @@ -15,7 +15,7 @@ Type any command in the interactive wallet to see its built-in usage tips. | USDT & TRC20 | [usdt.md](usdt.md) | | Staking (Stake 2.0) | [stake-v2.md](stake-v2.md) | | Staking (Stake 1.0, legacy) | [stake-v1-legacy.md](stake-v1-legacy.md) | -| Resource prices & withdrawals | [resources.md](resources.md) | +| Resource prices & memo fee | [resources.md](resources.md) | | Voting, rewards & witnesses | [vote-reward.md](vote-reward.md) | | Smart contracts | [contract.md](contract.md) | | Proposals | [proposals.md](proposals.md) | diff --git a/java/docs/commands/multisig.md b/java/docs/commands/multisig.md index 69e9ade41..5fb9cbe42 100644 --- a/java/docs/commands/multisig.md +++ b/java/docs/commands/multisig.md @@ -24,7 +24,7 @@ or wallet> updateAccountPermission === UpdateAccountPermission Interactive Mode === -Select permission to modify: +Please enter the index(1-7) to operate: 1. owner_permission 2. witness_permission 3. active_permissions @@ -45,7 +45,7 @@ If the account is not a witness, it's not necessary to set `witness_permission`, > SendCoin TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW 10000000000000000 ``` -Will show "Please confirm and input your permission id, if input y or Y means default 0, other non-numeric characters will cancel transaction." +Will show "Please confirm and input your permission id, if input y/Y means default 0, other non-numeric characters will cancel transaction." This will require the transfer authorization of active access. Enter: 2 @@ -53,7 +53,7 @@ Then select accounts and put in the local password, i.e. TNhXo1GbRNCuorvYu5JFWN3 Select another account and enter the local password, i.e. TKwhcDup8L2PH5r6hxp5CQvQzZqJLmKvZP will need a private key of TKwhcDup8L2PH5r6hxp5CQvQzZqJLmKvZP to sign a transaction. -The weight of each account is 1, threshold of access is 2. When the requirements are met, users will be notified with "Send 10000000000000000 Sun to TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW successful !!". +The weight of each account is 1, threshold of access is 2. When the requirements are met, users will be notified with "Send 10000000000000000 Sun to TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW successful !!". This is how multiple accounts use multi-signature when using the same cli. Use the instruction `addTransactionSign` according to the obtained transaction hex string if signing at multiple cli. After signing, the users will need to broadcast final transactions manually. @@ -190,7 +190,7 @@ wallet> tronlinkmultisign 1. Multi-sign transaction list 2. Create multi-sign transaction 0. Exit -Please enter the number to operate: +Please enter to operate: ``` ## See also diff --git a/java/docs/commands/network.md b/java/docs/commands/network.md index 1d084c1a0..e280501a2 100644 --- a/java/docs/commands/network.md +++ b/java/docs/commands/network.md @@ -41,7 +41,7 @@ View the current network. ```console wallet> currentnetwork -currentNetwork: NILE +current network: NILE ``` For a custom network, the node endpoints are shown: diff --git a/java/docs/commands/proposals.md b/java/docs/commands/proposals.md index 64c58ec8c..a109f1fcf 100644 --- a/java/docs/commands/proposals.md +++ b/java/docs/commands/proposals.md @@ -14,7 +14,7 @@ Initiate a proposal. - `id0` — the serial number of the parameter. Every parameter of the TRON network has a serial number. Please refer to `http://tronscan.org/#/sr/committee`. - `Value0` — the modified value. -In the example, modification No.4 (modifying token issuance fee) costs 1000 TRX as follows: +Values are passed to the chain verbatim — the CLI does no unit conversion, so a SUN-denominated parameter must be given in SUN. In the example, proposal No.4 (the token issuance fee) is set to the raw value `1000`, which is 1000 SUN: ```console > createProposal 4 1000 diff --git a/java/docs/commands/resources.md b/java/docs/commands/resources.md index 769dfc481..2a70e656e 100644 --- a/java/docs/commands/resources.md +++ b/java/docs/commands/resources.md @@ -8,9 +8,7 @@ Get the historical unit price of bandwidth. ```console wallet> getBandwidthPrices -{ - "prices": "0:10,1606537680000:40,1614238080000:140,1626581880000:1000,1626925680000:140,1627731480000:1000" -} +The BandwidthPrices is 0:10,1606537680000:40,1614238080000:140,1626581880000:1000,1626925680000:140,1627731480000:1000 ``` ## GetEnergyPrices @@ -19,9 +17,7 @@ Get the historical unit price of energy. ```console wallet> getEnergyPrices -{ - "prices": "0:100,1575871200000:10,1606537680000:40,1614238080000:140,1635739080000:280,1681895880000:420" -} +The EnergyPrices is 0:100,1575871200000:10,1606537680000:40,1614238080000:140,1635739080000:280,1681895880000:420 ``` ## GetMemoFee @@ -30,9 +26,7 @@ Get the memo fee. ```console wallet> getMemoFee -{ - "prices": "0:0,1675492680000:1000000" -} +The MemoFee is 0:0,1675492680000:1000000 ``` ## See also diff --git a/java/docs/commands/stake-v1-legacy.md b/java/docs/commands/stake-v1-legacy.md index c2ab6e215..af12aa9a2 100644 --- a/java/docs/commands/stake-v1-legacy.md +++ b/java/docs/commands/stake-v1-legacy.md @@ -22,7 +22,7 @@ After the funds are frozen, the corresponding number of shares and bandwidth wil For example: ```console -> freezeBalance 100000000 3 1 address +> freezeBalance 100000000 3 1 TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW ``` After the freeze operation, frozen funds will be transferred from Account Balance to Frozen. You can view frozen funds from your account information. After being unfrozen, it is transferred back to Balance from Frozen, and the frozen funds cannot be used for trading. diff --git a/java/docs/commands/stake-v2.md b/java/docs/commands/stake-v2.md index 40e46b545..891b403dc 100644 --- a/java/docs/commands/stake-v2.md +++ b/java/docs/commands/stake-v2.md @@ -2,6 +2,8 @@ FreezeV2-based staking, resource delegation, and unfreeze withdrawal — the current staking model. For the difference from the legacy model, see [concepts/staking-models](../concepts/staking-models.md). +The transaction examples below are abridged: every signing command also prints the permission-id prompt, the key selector, the before/after signing hex strings, and a trailing ` successful !!!` line. Only the `TxId is …` line is kept here, because it is the part you carry to `GetTransactionById`. + ## FreezeBalanceV2 / UnfreezeBalanceV2 ### FreezeBalanceV2 @@ -18,7 +20,7 @@ Example: ```console wallet> FreezeBalanceV2 TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh 1000000000000000 0 -txid is 82244829971b4235d98a9f09ba67ddb09690ac2f879ad93e09ba3ec1ab29177d +TxId is 82244829971b4235d98a9f09ba67ddb09690ac2f879ad93e09ba3ec1ab29177d wallet> GetTransactionById 82244829971b4235d98a9f09ba67ddb09690ac2f879ad93e09ba3ec1ab29177d { "ret":[ @@ -66,7 +68,7 @@ Example: ```console wallet> UnFreezeBalanceV2 TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh 9000000 0 -txid is dcfea1d92fc928d24c88f7f71a03ae8105d0b5b112d6d48be93d3b9c73bea634 +TxId is dcfea1d92fc928d24c88f7f71a03ae8105d0b5b112d6d48be93d3b9c73bea634 wallet> GetTransactionById dcfea1d92fc928d24c88f7f71a03ae8105d0b5b112d6d48be93d3b9c73bea634 { "ret":[ @@ -118,7 +120,7 @@ Example: ```console wallet> DelegateResource TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh 10000000 0 TQ4gjjpAjLNnE67UFbmK5wVt5fzLfyEVs3 true -txid is 363ac0b82b6ad3e0d3cad90f7d72b3eceafe36585432a3e013389db36152b6ed +TxId is 363ac0b82b6ad3e0d3cad90f7d72b3eceafe36585432a3e013389db36152b6ed wallet> GetTransactionById 363ac0b82b6ad3e0d3cad90f7d72b3eceafe36585432a3e013389db36152b6ed { "ret":[ @@ -170,7 +172,7 @@ Example: ```console wallet> UnDelegateResource TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh 1000000 0 TQ4gjjpAjLNnE67UFbmK5wVt5fzLfyEVs3 -txid is feb334794cf361fd351728026ccf7319e6ae90eba622b9eb53c626cdcae4965c +TxId is feb334794cf361fd351728026ccf7319e6ae90eba622b9eb53c626cdcae4965c wallet> GetTransactionById feb334794cf361fd351728026ccf7319e6ae90eba622b9eb53c626cdcae4965c { "ret":[ @@ -217,7 +219,7 @@ Example: ```console wallet> withdrawexpireunfreeze TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh -txid is e5763ab8dfb1e7ed076770d55cf3c1ddaf36d75e23ec8330f99df7e98f54a147 +TxId is e5763ab8dfb1e7ed076770d55cf3c1ddaf36d75e23ec8330f99df7e98f54a147 wallet> GetTransactionById e5763ab8dfb1e7ed076770d55cf3c1ddaf36d75e23ec8330f99df7e98f54a147 { "ret":[ @@ -262,7 +264,7 @@ Example: ```console wallet> cancelAllUnfreezeV2 TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh -txid is e5763ab8dfb1e7ed076770d55cf3c1ddaf36d75e23ec8330f99df7e98f54a147 +TxId is e5763ab8dfb1e7ed076770d55cf3c1ddaf36d75e23ec8330f99df7e98f54a147 wallet> GetTransactionById e5763ab8dfb1e7ed076770d55cf3c1ddaf36d75e23ec8330f99df7e98f54a147 { "ret":[ @@ -360,9 +362,8 @@ Example: ```console wallet> getCanDelegatedMaxSize TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh 0 -{ - "max_size": 999999978708334 -} +GetCanDelegatedMaxSize=999999978708334 +GetCanDelegatedMaxSize successful !!! ``` ### GetAvailableUnfreezeCount @@ -379,9 +380,8 @@ Example: ```console wallet> getAvailableUnfreezeCount TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh -{ - "count": 31 -} +GetAvailableUnfreezeCount=31 +GetAvailableUnfreezeCount successful!!! ``` ### GetCanWithdrawUnfreezeAmount @@ -399,9 +399,7 @@ Example: ```console wallet> getCanWithdrawUnfreezeAmount TJAVcszse667FmSNCwU2fm6DmfM5D4AyDh 1671100335000 -{ - "amount": 9000000 -} +GetCanWithdrawUnfreezeAmount successful amount:9000000 !!! ``` ## See also diff --git a/java/docs/commands/transfer-trc10.md b/java/docs/commands/transfer-trc10.md index 078476850..07e5711bf 100644 --- a/java/docs/commands/transfer-trc10.md +++ b/java/docs/commands/transfer-trc10.md @@ -125,14 +125,22 @@ Example: ```console > TransferAsset TN3zfjYUmMFK3ZsHSsrdJoNRtGkQmZLBLz 1000001 1000 > getaccount TN3zfjYUmMFK3ZsHSsrdJoNRtGkQmZLBLz # View target account information after the transfer -address: TN3zfjYUmMFK3ZsHSsrdJoNRtGkQmZLBLz - assetV2 +{ + "address": "TN3zfjYUmMFK3ZsHSsrdJoNRtGkQmZLBLz", + "balance": 9999900000, + "assetV2": [ + { + "key": "1000001", + "value": 1000 + } + ], + "free_asset_net_usageV2": [ { - id: 1000001 - balance: 1000 - latest_asset_operation_timeV2: null - free_asset_net_usageV2: 0 + "key": "1000001", + "value": 0 } + ] +} ``` ### ParticipateAssetIssue @@ -155,14 +163,16 @@ Example: ```console > ParticipateAssetIssue TRGhNNfnmgLegT4zHNjEqDSADjgmnHvubJ 1000001 1000 > getaccount TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW # View remaining balance -address: TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW -assetV2 +{ + "address": "TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW", + "balance": 9999900000, + "assetV2": [ { - id: 1000001 - balance: 1000 - latest_asset_operation_timeV2: null - free_asset_net_usageV2: 0 + "key": "1000001", + "value": 1000 } + ] +} ``` ### ListAssetIssuePaginated diff --git a/java/docs/commands/usdt.md b/java/docs/commands/usdt.md index b78968fcf..d8252138e 100644 --- a/java/docs/commands/usdt.md +++ b/java/docs/commands/usdt.md @@ -14,17 +14,6 @@ Get the USDT balance of the login account, or of `Address` when one is given. ```console wallet> getusdtbalance -balanceOf(address):70a08231 -Execution result = { - "constant_result": [ - "0000000000000000000000000000000000000000000000000000000000000000" - ], - "result": { - "result": true - }, - "energy_used": 4062, - "energy_penalty": 3127 -} USDT balance = 0 ``` @@ -38,39 +27,8 @@ Make a USDT transfer. ```console wallet> transferusdt TR311sD6KasRnofj5RnFiFBA2rH8RH2kYk 1 -balanceOf(address):70a08231 -Execution result = { - "constant_result": [ - "000000000000000000000000000000000000000000000000000000006544ae57" - ], - "result": { - "result": true - }, - "energy_used": 935 -} USDT balance = 1698999895 -transfer(address,uint256):a9059cbb It is estimated that 345 bandwidth and 29650 energy will be consumed. -Execution result = { - "constant_result": [ - "0000000000000000000000000000000000000000000000000000000000000000" - ], - "result": { - "result": true - }, - "energy_used": 29650, - "logs": [ - { - "address": "NaMomAhUzuFzMNFzzQHVNsR8xbmP3A5LT", - "topics": [ - "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "000000000000000000000000caf9798d70a3c609b600f163e53cfe8f586e1b9f", - "000000000000000000000000a5418b8da12e73075abb46375e7a15c758ea21fc" - ], - "data": "0000000000000000000000000000000000000000000000000000000000000001" - } - ] -} { "raw_data":{ "contract":[ @@ -98,13 +56,9 @@ Before sign transaction hex string is 0ad4010a0208c72208c02252c2ae3b92e140b8c896 Please confirm and input your permission id, if input y/Y means default 0, other non-numeric characters will cancel transaction. y Please choose your key for sign. - -No. Address Name -1 TUUSMd58eC3fKx3fn7whxJyr1FR56tgaP8 test -Please choose No. between 1 and 1, or enter search to search wallets -1 +The keystore file TUUSMd58eC3fKx3fn7whxJyr1FR56tgaP8.json is loaded. Please input your password. -******** +password: After sign transaction hex string is 0ad4010a0208c72208c02252c2ae3b92e1409fb0b9d9a2335aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541caf9798d70a3c609b600f163e53cfe8f586e1b9f121541eca9bc828a3005b9a3b909f2cc5c2a54794de05f2244a9059cbb000000000000000000000041a5418b8da12e73075abb46375e7a15c758ea21fc000000000000000000000000000000000000000000000000000000000000000170938293cfa23390018094ebdc031241a776830e5cd054c6a94631b6d62704e249e7587ab3f036e5e4fac15cbf49e671262532e094e1a32ad858272da3e101958102df61b0f72f26756a94b608883a6f01 TxId is 9c8d4b84e9a71ccaad86b0a96f790067d3fc7ea85c26b425e5d748b81d31a8b8 Transfer 1 to TR311sD6KasRnofj5RnFiFBA2rH8RH2kYk broadcast successful. @@ -133,7 +87,7 @@ wallet> GetUsdtTransferById b0044dcb188568d11e77da926d96630f3e878583c5d5f4b3a72d ## AddressBook -Add, delete, modify, and search the address book. +Add, edit, and delete address-book entries. The interactive menu has no search action. ```console wallet> AddressBook @@ -141,8 +95,8 @@ wallet> AddressBook MAIN MENU: 1. addAddress 2. editAddress -3. delAddress -4. getAddressBook +3. deleteAddress +4. exit Select option: 1 ``` diff --git a/java/docs/commands/vote-reward.md b/java/docs/commands/vote-reward.md index beec5ec9a..9b2b610a1 100644 --- a/java/docs/commands/vote-reward.md +++ b/java/docs/commands/vote-reward.md @@ -14,14 +14,14 @@ Voting requires share. Share can be obtained by freezing funds. For example: ```console -> freezeBalance 10000000 3 1 address # Freeze 10 TRX and acquire 10 units of shares +> freezeBalance 10000000 3 1 # Freeze 10 TRX and acquire 10 units of shares -> votewitness 123455 witness1 4 witness2 6 # Cast 4 votes for witness1 and 6 votes for witness2 at the same time +> votewitness TJmka325yjJKeFpQDwKSQAoNwEyNGhsaEV 4 TFFLWM7tmKiwGtbh2mcz2rBssoFjHjSShG 6 # Cast 4 votes for the first SR and 6 for the second at the same time -> votewitness 123455 witness1 10 # Voted 10 votes for witness1 +> votewitness TJmka325yjJKeFpQDwKSQAoNwEyNGhsaEV 10 # Vote 10 for the first SR only ``` -The final result of the above command was 10 votes for witness1 and 0 vote for witness2. +Each SR must be given as a Base58Check address; a placeholder name is not accepted. The final result of the above commands was 10 votes for `TJmka325…` and 0 for `TFFLWM7t…`. ## Brokerage diff --git a/java/docs/commands/wallet.md b/java/docs/commands/wallet.md index f24ab3bfc..5855cefe5 100644 --- a/java/docs/commands/wallet.md +++ b/java/docs/commands/wallet.md @@ -26,7 +26,7 @@ Please input password. password: Please input password again. password: -Please enter 12 words (separated by spaces) [Attempt 1/3]: +Please enter 12 or 24 words (separated by spaces) [Attempt 1/3]: ``` ## ExportWalletMnemonic @@ -37,8 +37,8 @@ Export the mnemonic of the address in the wallet. wallet> ExportWalletMnemonic Please input your password. password: -exportWalletMnemonic successful !! -a*ert tw*st co*rect mat*er pa*s g*ther p*t p*sition s*op em*ty coc*nut aband*n +exportWalletMnemonic successful !! +alert twist correct matter pass gather pit position stop empty coconut abandon ``` ## ExportWalletKeystore @@ -50,7 +50,7 @@ wallet> ExportWalletKeystore tronlink /tmp Please input your password. password: exported keystore file : /tmp/TYdhEg8b7tXm92UDbRDXPtJNU6T9xVGbbo.json -exportWalletKeystore successful !! +exportWalletKeystore successful !! ``` ## ImportWalletByKeystore @@ -59,12 +59,14 @@ Import a TronLink-format keystore file into wallet-cli. ```console wallet> ImportWalletByKeystore tronlink /tmp/tronlink.json +Please enter the password for the keystore file, enter it once. +password: Please input password. password: Please input password again. password: fileName = TYQq6zp51unQDNELmT4xKMWh5WLcwpCDZJ.json -importWalletByKeystore successful !! +importWalletByKeystore successful !! ``` ## ImportWalletByLedger @@ -74,7 +76,6 @@ Import a derived account from a Ledger device into wallet-cli. ```console wallet> ImportWalletByLedger (Note:This will pair Ledger to user your hardware wallet) -Only one Ledger device is supported. If you have multiple devices, please ensure only one is connected. Ledger device found: Nano X Please input password. password: @@ -87,8 +88,8 @@ Default Path: m/44'/195'/0'/0/0 1. Import Default Account 2. Change Path 3. Custom Path -Select an option: 1 -Import a wallet by Ledger successful, keystore file : ./Wallet/Ledger-TAT1dA8F9HXGqmhvMCjxCKAD29YxDRw81y.json +Please select an option, other inputs will exit this operation: 1 +Import a wallet by Ledger successful, keystore file : ./Wallet/Ledger-TAT1dA8F9HXGqmhvMCjxCKAD29YxDRw81y.json You are now logged in, and you can perform operations using this account. ``` @@ -113,11 +114,11 @@ wallet> GenerateSubAccount Please input your password. password: -=== Sub Account Generator === ------------------------------ +=== GenerateSubAccount Generator === +------------------------------- Default Address: TYEhEg7b7tXm92UDbRDXPtJNU6T9xVGbbo Default Path: m/44'/195'/0'/0/1 ------------------------------ +------------------------------- 1. Generate Default Path 2. Change Account @@ -125,8 +126,8 @@ Default Path: m/44'/195'/0'/0/1 Enter your choice (1-3): 1 mnemonic file : ./Mnemonic/TYEhEg7b7tXm92UDbRDXPtJNU6T9xVGbbo.json -Generate a sub account successful, keystore file name is TYEhEg7b7tXm92UDbRDXPtJNU6T9xVGbbo.json -generateSubAccount successful. +GenerateSubAccount successful, keystore file name is TYEhEg7b7tXm92UDbRDXPtJNU6T9xVGbbo.json +generateSubAccount successful. ``` ## ClearWalletKeystore @@ -139,16 +140,17 @@ wallet> ClearWalletKeystore Warning: Dangerous operation! This operation will permanently delete the Wallet&Mnemonic files of the Address: TABWx7yFhWrvZHbwKcCmFLyPLWjd2dZ2Rq Warning: The private key and mnemonic words will be permanently lost and cannot be recovered! -Continue? (y/Y to proceed):y +Continue? (y/Y to proceed, c/C to cancel): +y Final confirmation: Please enter: 'DELETE' to confirm the delete operation: Confirm: (DELETE): DELETE -File deleted successfully: +Delete File successful: - /wallet-cli/Wallet/TABWx8yFhWrvZHbwKcCmFLyPLWjd2dZ2Rq.json - /wallet-cli/Mnemonic/TABWx8yFhWrvZHbwKcCmFLyPLWjd2dZ2Rq.json -ClearWalletKeystore successful !!! +ClearWalletKeystore successful !!! ``` ## ResetWallet @@ -157,7 +159,6 @@ Delete all local wallet keystore files and mnemonic files, and follow the prompt ```console wallet> resetwallet -User defined config file doesn't exists, use default config file in jar Warning: Dangerous operation! This operation will permanently delete the Wallet&Mnemonic files @@ -180,14 +181,15 @@ Log in to multiple keystore accounts with a unified password. wallet> loginall Please input your password. password: -Use user defined config file in current dir [========================================] 100% -The 1th keystore file name is TJEEKTmaVTYSpJAxahtyuofnDSpe2seajB.json -The 2th keystore file name is TX1L9xonuUo1AHsjUZ3QzH8wCRmKm56Xew.json -The 3th keystore file name is TVuVqnJFuuDxN36bhEbgDQS7rNGA5dSJB7.json -The 4th keystore file name is Ledger-TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe.json -The 5th keystore file name is TYXFDtn86VPFKg4mkwMs45DKDcpAyqsada.json -Please choose between 1 and 5 + +No. Address Name +1 TJEEKTmaVTYSpJAxahtyuofnDSpe2seajB main +2 TX1L9xonuUo1AHsjUZ3QzH8wCRmKm56Xew cold +3 TVuVqnJFuuDxN36bhEbgDQS7rNGA5dSJB7 test +4 TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe Ledger-TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe.json +5 TYXFDtn86VPFKg4mkwMs45DKDcpAyqsada backup +Please choose No. between 1 and 5, or enter search to search wallets 5 LoginAll successful !!! ``` @@ -227,12 +229,14 @@ After logging in with `LoginAll`, switch between wallets. ```console wallet> switchwallet -The 1th keystore file name is TJEEKTmaVTYSpJAxahtyuofnDSpe2seajB.json -The 2th keystore file name is TX1L9xonuUo1AHsjUZ3QzH8wCRmKm56Xew.json -The 3th keystore file name is TVuVqnJFuuDxN36bhEbgDQS7rNGA5dSJB7.json -The 4th keystore file name is Ledger-TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe.json -The 5th keystore file name is TYXFDtn86VPFKg4mkwMs45DKDcpAyqsada.json -Please choose between 1 and 5 + +No. Address Name +1 TJEEKTmaVTYSpJAxahtyuofnDSpe2seajB main +2 TX1L9xonuUo1AHsjUZ3QzH8wCRmKm56Xew cold +3 TVuVqnJFuuDxN36bhEbgDQS7rNGA5dSJB7 test +4 TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe Ledger-TRvVXgqddDGYRMx3FWf2tpVxXQQXDZxJQe.json +5 TYXFDtn86VPFKg4mkwMs45DKDcpAyqsada backup +Please choose No. between 1 and 5, or enter search to search wallets 5 SwitchWallet successful !!! ``` diff --git a/java/docs/guide/command-flow.md b/java/docs/guide/command-flow.md index 63defc19c..cbd528811 100644 --- a/java/docs/guide/command-flow.md +++ b/java/docs/guide/command-flow.md @@ -9,11 +9,13 @@ $ ./gradlew run > RegisterWallet (prompts twice for the password, then for mnemonic length) > login (prompts for the password) > getAddress -address = TRfwwLDpr4excH4V4QzghLEsdYwkapTxnm' # backup it! +GetAddress successful !! +address = TRfwwLDpr4excH4V4QzghLEsdYwkapTxnm # backup it! > BackupWallet (prompts for the password) -priKey = 1234567890123456789012345678901234567890123456789012345678901234 # backup it!!! (BackupWallet2Base64 option) +BackupWallet successful !! +1234567890123456789012345678901234567890123456789012345678901234 # backup it!!! (BackupWallet2Base64 prints the same key base64-encoded) > getbalance -Balance = 0 +Balance = 0 SUN = 0.000000 TRX > AssetIssue TestTRX TRX 75000000000000000 1 1 2 "2019-10-02 15:10:00" "2020-07-11" "just for test121212" www.test.com 100 100000 10000 10 10000 1 > getaccount TRfwwLDpr4excH4V4QzghLEsdYwkapTxnm (Print balance: 9999900000 diff --git a/ts/README.md b/ts/README.md index 51aa90792..4556438a2 100644 --- a/ts/README.md +++ b/ts/README.md @@ -1,6 +1,6 @@ # wallet-cli — TypeScript implementation -The agent-first implementation of wallet-cli, built for automation: every command has a stable JSON envelope, deterministic exit codes, and discoverable schemas; interactive prompts are kept only for secret input (import / backup / delete). For what wallet-cli is and how the two implementations compare, see the [repository overview](../README.md); for the original, see the [Java implementation](../java/README.md). +The agent-first implementation of wallet-cli, built for automation: every command has a stable JSON envelope, deterministic exit codes, and discoverable schemas; interactive prompts are kept to a short allowlist — `create`, the `import` variants, `backup`, `change-password` and `delete` — and everywhere else a missing credential is an error, never a prompt. For what wallet-cli is and how the two implementations compare, see the [repository overview](../README.md); for the original, see the [Java implementation](../java/README.md). ## Key features @@ -27,7 +27,7 @@ The agent-first implementation of wallet-cli, built for automation: every comman ## Supported chains -Seven built-in networks are supported. Networks use a canonical `family:chain` id: +Seven built-in networks are supported. Networks use a canonical [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) `namespace:reference` id. The namespace is not the family: `eip155` is CAIP-2's namespace for EVM chains, while the family this CLI branches on is `evm`. | Network id | Family | Native coin | Environment | |---|---|---|---| @@ -83,8 +83,13 @@ wallet-cli create --label main ```console ✅ Created wallet "main" Account ID wlt_2dbv24de.0 + Type HD TRON address TTVdGTBXY5mmY3nJFGUp7Vo898kUJ6gtFQ + EVM address 0x5c8e1b04A7f39d62C0B3e85A1d47F9028b6ce713 Active yes + +⚠️ Recovery phrase is encrypted locally and was not printed. +⚠️ Run `backup` soon and store the file offline. ``` ```bash @@ -184,7 +189,7 @@ Every command supports `-o json` and then prints **exactly one** terminal JSON f TRON differs a lot from EVM chains in fees, accounts, and key permissions — these are worth understanding up front to avoid surprises: -- [Networks](docs/concepts/networks.md) — built-in TRON/EVM networks and the `family:chain` id +- [Networks](docs/concepts/networks.md) — built-in TRON/EVM networks and the CAIP-2 `namespace:reference` id - [Accounts & HD](docs/concepts/accounts-and-hd.md) — mnemonics, derivation paths, account activation - [Energy & bandwidth](docs/concepts/energy-bandwidth.md) — TRON's resource-based fee model (in place of EVM gas) - [Security](docs/concepts/security.md) — keystore encryption, secret handling, multi-sig permissions @@ -193,4 +198,4 @@ TRON differs a lot from EVM chains in fees, accounts, and key permissions — th A command errored or behaved unexpectedly? Common issues and how to diagnose them are in [troubleshooting.md](docs/troubleshooting.md). -> All copy-pasteable examples in this documentation run against the **Nile testnet** (`--network tron:3448148188`). Mainnet commands move real funds; they appear only as annotated, non-copyable descriptions. +> Copy-pasteable examples that spend anything target a testnet — **Nile** (`--network tron:3448148188`) on TRON, **Sepolia** (`--network eip155:11155111`) on EVM. Mainnet ids (`tron:728126428`, `eip155:1`) also appear: in read-only examples such as token-book listings and config paths, and in a few illustrations of mainnet token contracts. Those last ones carry placeholder recipients (`T...` / `0x...`) and are not runnable as written. diff --git a/ts/docs/commands/account/activate.md b/ts/docs/commands/account/activate.md index ee5b4b645..7186fa376 100644 --- a/ts/docs/commands/account/activate.md +++ b/ts/docs/commands/account/activate.md @@ -12,7 +12,7 @@ wallet-cli account activate --address ## Description -A TRON address doesn't exist on-chain until it receives its first asset or is explicitly created — until then queries return `not_found` and it cannot initiate a transaction. This command creates (activates) such an account **without transferring any asset**; the payer account covers the on-chain account-creation fee. +A TRON address doesn't exist on-chain until it receives its first asset or is explicitly created — until then `account set` refuses it with `not_found` (a plain `account info` still succeeds, returning an empty `account` object) and it cannot initiate a transaction. This command creates (activates) such an account **without transferring any asset**; the payer account covers the on-chain account-creation fee. Use it only when an address needs to *exist* on its own — to be queryable, or able to initiate its own transactions. If you're sending it funds anyway, [`tx send`](../tx/send.md) activates the recipient automatically in one step; and adding an address to a multi-sig permission does **not** require activation. diff --git a/ts/docs/commands/account/info.md b/ts/docs/commands/account/info.md index 773ddf208..6746554c7 100644 --- a/ts/docs/commands/account/info.md +++ b/ts/docs/commands/account/info.md @@ -40,7 +40,7 @@ wallet-cli account info --network tron:3448148188 -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"account.info","data":{"address":"TNmoJ3Be59WFEq5dsW6eCkZjveiL3G8HVB","account":{"account_name":"71612d74657374","balance":"9915803110","create_time":1753860222000,"owner_permission":{},"active_permission":[{}],"frozenV2":[{},{"type":"ENERGY"},{"type":"TRON_POWER"}]},"resources":{"bandwidth":{"used":325,"limit":600},"energy":{"used":0,"limit":0}}},"meta":{"durationMs":746,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"account.info","data":{"address":"TNmoJ3Be59WFEq5dsW6eCkZjveiL3G8HVB","account":{"account_name":"71612d74657374","balance":"9915803110","create_time":1753860222000,"owner_permission":{"threshold":1,"keys":[{},{}]},"active_permission":[{}],"frozenV2":[{},{"type":"ENERGY"},{"type":"TRON_POWER"}]},"resources":{"bandwidth":{"used":325,"limit":600},"energy":{"used":0,"limit":0}}},"meta":{"durationMs":746,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} ``` On an EVM network the same command reports the EVM account state: diff --git a/ts/docs/commands/account/portfolio.md b/ts/docs/commands/account/portfolio.md index b779b219a..b4c4c2e41 100644 --- a/ts/docs/commands/account/portfolio.md +++ b/ts/docs/commands/account/portfolio.md @@ -45,7 +45,7 @@ wallet-cli account portfolio --network tron:3448148188 -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"account.portfolio","data":{"network":"tron:3448148188","account":"wlt_gd2x8vyk","address":"TNmoJ3Be59WFEq5dsW6eCkZjveiL3G8HVB","priceSource":"coingecko","holdings":[{"kind":"native","symbol":"TRX","decimals":6,"rawBalance":"9915803110","balance":"9915.80311","priceUsd":0,"valueUsd":0},{"kind":"trc20","symbol":"USDT","decimals":6,"rawBalance":"17061463423","balance":"17061.463423","priceUsd":0,"valueUsd":0,"id":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","name":"Tether USD","source":"official"}],"totalValueUsd":0},"meta":{"durationMs":724,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"account.portfolio","data":{"network":"tron:3448148188","account":"wlt_gd2x8vyk","address":"TNmoJ3Be59WFEq5dsW6eCkZjveiL3G8HVB","priceSource":"coingecko","holdings":[{"kind":"native","symbol":"TRX","decimals":6,"rawBalance":"9915803110","balance":"9915.80311","priceUsd":0,"valueUsd":0},{"kind":"trc20","symbol":"USDT","decimals":6,"rawBalance":"17061463423","balance":"17061.463423","priceUsd":0,"valueUsd":0,"id":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","name":"Tether USD","source":"official"},{"kind":"trc20","symbol":"USDD","decimals":18,"rawBalance":"0","balance":"0","priceUsd":0,"valueUsd":0,"id":"TYQF9cAeJ3Faq8QXpHxTcFco72DRCQbgFt","name":"Usdd Stablecoin","source":"official"}],"totalValueUsd":0},"meta":{"durationMs":724,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} ``` The same command on an EVM network, with `kind` reporting `erc20` instead of `trc20`: diff --git a/ts/docs/commands/account/set.md b/ts/docs/commands/account/set.md index 4bc9840a1..1209bb3cb 100644 --- a/ts/docs/commands/account/set.md +++ b/ts/docs/commands/account/set.md @@ -14,7 +14,7 @@ wallet-cli account set (--name | --id ) Sets the account's on-chain **name** (a display alias, up to 32 bytes) or its **account id** (a globally unique identifier, 8–32 bytes). One at a time — `--name` and `--id` are mutually exclusive; to set both, run it twice. -⚠️ **On mainnet each can be set only once and can never be changed** — the value is permanent, and there is no confirmation prompt. This is different from [`rename`](../rename.md), which changes the local label and can be redone anytime. +⚠️ **Each can be set only once and can never be changed** — the value is permanent, and there is no confirmation prompt. This is not a mainnet-only rule: the CLI refuses a second write with `name_already_set` / `id_already_set` on every network, Nile and Shasta included, so a testnet run is not a rehearsal you can repeat. This is different from [`rename`](../rename.md), which changes the local label and can be redone anytime. Requires the account. The master password via `--password-stdin` is needed only when the selected mode signs — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. The account id's uniqueness is enforced on-chain — a taken id fails with `id_taken`. @@ -24,8 +24,8 @@ Ledger support differs by field: the TRON app can sign `--name`, but cannot sign | Option | Description | |---|---| -| `--name ` | **Required** (one of). On-chain account name, up to 32 bytes; mainnet allows setting it once | -| `--id ` | **Required** (one of). Account id, 8–32 bytes, globally unique; can be set once | +| `--name ` | **Required** (one of). On-chain account name, up to 32 bytes; can be set once, on any network | +| `--id ` | **Required** (one of). Account id, 8–32 bytes, globally unique; can be set once, on any network | | `--dry-run` | Build and estimate only; no signature/broadcast, no password. Excludes `--sign-only` / `--build-only` | | `--sign-only` | Build and sign, output the signed hex (feed [`tx broadcast`](../tx/broadcast.md)). Excludes `--dry-run` / `--build-only`; pairs with `--expiration` | | `--build-only` | Build and estimate, output the **unsigned** hex (feed [`tx multisig --create`](../tx/multisig.md)). Excludes `--dry-run` / `--sign-only`; pairs with `--expiration` | @@ -71,7 +71,7 @@ echo "$PW" | wallet-cli account set --id acme-treasury-01 --network tron:3448148 ``` ```console -✅ Account id set +✅ On-chain id set Address TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw ID acme-treasury-01 TxID 3d9... diff --git a/ts/docs/commands/asset/index.md b/ts/docs/commands/asset/index.md index 6034f805e..aafe87065 100644 --- a/ts/docs/commands/asset/index.md +++ b/ts/docs/commands/asset/index.md @@ -30,7 +30,7 @@ wallet-cli asset COMMAND | `asset participate` | [participate.md](participate.md) | Buy into a token's ICO with TRX | | `asset unfreeze` | [unfreeze.md](unfreeze.md) | Release matured frozen supply | | `asset info` | [info.md](info.md) | Full detail of one TRC10 | -| `asset list` | [list.md](list.md) | List every TRC10 on chain | +| `asset list` | [list.md](list.md) | List TRC10 tokens, one page at a time | ## See also diff --git a/ts/docs/commands/asset/info.md b/ts/docs/commands/asset/info.md index 103320046..c5ffc06b1 100644 --- a/ts/docs/commands/asset/info.md +++ b/ts/docs/commands/asset/info.md @@ -101,7 +101,7 @@ wallet-cli asset info 1000123 --network tron:3448148188 -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"asset.info","data":{"kind":"asset-info","assetId":"1000123","name":"MyToken","abbr":"MTK","issuerAddress":"TQkXm4vN...","totalSupply":"1000000000000000","precision":6,"price":"1:100","trxNum":1000000,"num":100000000,"startTime":1785542400000,"endTime":1788134400000,"url":"https://mytoken.io","description":"Demo TRC10","freeAssetNetLimit":0,"publicFreeAssetNetLimit":0,"frozenSupply":[{"amount":"100000000000000","days":30,"expireTime":1788134400000},{"amount":"50000000000000","days":90,"expireTime":1793318400000}]},"meta":{"durationMs":26,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"asset.info","data":{"kind":"asset-info","assetId":"1000123","name":"MyToken","abbr":"MTK","issuerAddress":"TQkXm4vN...","totalSupply":"1000000000000000","precision":6,"price":"1:100","trxNum":1,"num":100,"startTime":1785542400000,"endTime":1788134400000,"url":"https://mytoken.io","description":"Demo TRC10","freeAssetNetLimit":0,"publicFreeAssetNetLimit":0,"frozenSupply":[{"amount":"100000000000000","days":30,"expireTime":1788134400000},{"amount":"50000000000000","days":90,"expireTime":1793318400000}]},"meta":{"durationMs":26,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} ``` The ambiguous-name failure, in json: @@ -126,7 +126,7 @@ wallet-cli asset info MyToken --network tron:3448148188 -o json | `totalSupply` | string | Total supply, raw (whole tokens × 10^`precision`). A **string**: supplies reach int64 and would lose precision as a JSON number | | `precision` | number | Decimal places, 0–6 | | `price` | string | The issued rate as `trx:tokens`, in whole units — what text renders as `1 TRX = 100 MyToken` | -| `trxNum` / `num` | number | The same rate exactly as stored on chain, in sun and minimal units. For a `precision` of 6, `1:100` is stored as `1000000` / `100000000` | +| `trxNum` / `num` | number | The rate exactly as the chain stores it, in sun and minimal units, reduced to lowest terms when the token was issued. At a `precision` of 6, `1:100` is stored as `trxNum=1` / `num=100` | | `startTime` / `endTime` | number | ICO window, ms since epoch | | `url` / `description` | string | Project page and description | | `freeAssetNetLimit` / `publicFreeAssetNetLimit` | number | Free bandwidth per holder, and the shared pool | diff --git a/ts/docs/commands/asset/issue.md b/ts/docs/commands/asset/issue.md index 6033c3c43..0d3b5d341 100644 --- a/ts/docs/commands/asset/issue.md +++ b/ts/docs/commands/asset/issue.md @@ -24,9 +24,9 @@ Creates a TRC10 token and, in the same transaction, fixes the terms of its ICO: Amounts (`--supply`, `--freeze`) are in **whole tokens** — `--supply 1000000000 --precision 6` becomes an on-chain `total_supply` of `1000000000000000`. -Dates are read as **UTC**, as `YYYY-MM-DD` or `YYYY-MM-DD HH:mm:ss`; a bare date means `00:00:00`. `--start` must be later than the chain's current time, so a bare date is at the earliest tomorrow — to start a sale the same day, give the time as well. +Dates are read as **UTC**, as `YYYY-MM-DD` or `YYYY-MM-DD HH:mm:ss`; a bare date means `00:00:00`. `--start` must be later than **this machine's** clock at build time (the check is local, not a node read), so a bare date is at the earliest tomorrow — to start a sale the same day, give the time as well. The chain applies its own window check on top. -Constraints are checked locally before broadcast: `--name` and `--abbr` are 1–32 visible ASCII characters (`0x21`–`0x7E`, so no spaces and no non-ASCII); `--url` is required and at most 256 bytes; `--description` at most 200 bytes; `--precision` 0–6; `--end` after `--start`; each `--freeze` tranche's days within `getMinFrozenSupplyTime`…`getMaxFrozenSupplyTime`, the number of tranches within `getMaxFrozenSupplyNumber`, and their sum within the total supply; both free-bandwidth limits below `getOneDayNetLimit`. +Constraints are checked locally before broadcast: `--name` and `--abbr` are 1–32 visible ASCII characters (`0x21`–`0x7E`, so no spaces and no non-ASCII); `--url` is required and at most 256 bytes; `--description` at most 200 bytes; `--precision` 0–6; `--start` in the future and `--end` after `--start`; each `--freeze` tranche's amount and days greater than zero. One extra pre-flight read refuses an account that has already issued a TRC10, because the fee is burned either way. Every other bound belongs to the node — the chain's limits on frozen tranches (`getMinFrozenSupplyTime`, `getMaxFrozenSupplyTime`, `getMaxFrozenSupplyNumber`, and the tranche sum against the total supply) and on the free-bandwidth limits (`getOneDayNetLimit`) are enforced at broadcast, and violating one comes back as `transaction_rejected` in the node's own words. **By default the command returns at submission** (`stage: "submitted"`), not confirmation — add `--wait` to block until confirmed/failed. Requires an account. The master password (via `--password-stdin`) is needed only by the modes that sign — `--dry-run` and `--build-only` do not unlock the wallet and run without it. Watch-only accounts fail with `watch_only_no_signer` in a signing mode. @@ -71,28 +71,28 @@ echo "$PW" | wallet-cli asset issue --name MyToken --abbr MTK --supply 100000000 ```console ✅ Asset issued Asset MyToken (id 1000123) - Issuer TQkXm4vN...5Zt7Uw (main) + Issuer TQkXm4vN...5Zt7Uw Total supply 1,000,000,000 Precision 6 Price 1 TRX = 100 MyToken - ICO start time 2026-08-01 00:00 UTC - ICO end time 2026-08-31 00:00 UTC + ICO start time 2026-08-01 00:00:00 UTC + ICO end time 2026-08-31 00:00:00 UTC Url https://mytoken.io Description Demo TRC10 Free net/account 0 Public free net 0 - Frozen (2) - 100,000,000 for 30 days - 50,000,000 for 90 days + 100,000,000 for 30 days + 50,000,000 for 90 days TxID 7d1... - Block 57,883,010 - Fee 1,024 TRX (312 bandwidth) + Block #57,883,010 + Fee 1,024 TRX Status success ``` ```bash echo "$PW" | wallet-cli asset issue --name MyToken --abbr MTK --supply 1000000000 --price 1:100 --precision 6 \ - --start 2026-08-01 --end 2026-08-31 --url https://mytoken.io --network tron:3448148188 --wait --password-stdin -o json + --start 2026-08-01 --end 2026-08-31 --url https://mytoken.io --description "Demo TRC10" \ + --freeze 100000000:30 --freeze 50000000:90 --network tron:3448148188 --wait --password-stdin -o json ``` ```json @@ -108,11 +108,11 @@ echo "$PW" | wallet-cli asset issue --name MyToken --abbr MTK --supply 100000000 | default (submit) | `kind: "asset-issue"`, `stage: "submitted"`, `txId`, and the token definition below except `assetId` | | `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), `failed`, and `assetId` — assigned by the chain, so known only once confirmed | -Definition fields: `issuerAddress`, `name`, `abbr`, `totalSupply` (raw decimal string), `precision`, `price` (the `trx:tokens` string as given) with the stored `trxNum` / `num` pair, `startTime` / `endTime` (ms since epoch), `url`, `description`, `freeAssetNetLimit`, `publicFreeAssetNetLimit`, and `frozenSupply[]` (`amount` raw decimal string, `days`). Confirmation resource fields are flat; there is no `resource` object and the bandwidth field is `netUsed`, not `netUsage`. +Definition fields: `issuerAddress`, `name`, `abbr`, `totalSupply` (raw decimal string), `precision`, `price` (a `trx:tokens` string derived back from the stored pair, so it is the **reduced** rate, not necessarily what you typed — `--price 2:200 --precision 6` reports `"1:100"`) with the stored `trxNum` / `num` pair, `startTime` / `endTime` (ms since epoch), `url`, `description`, `freeAssetNetLimit`, `publicFreeAssetNetLimit`, and `frozenSupply[]` (`amount` raw decimal string, `days`). Confirmation resource fields are flat; there is no `resource` object and the bandwidth field is `netUsed`, not `netUsage`. ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`already_issued_asset` — this account already issued one, `insufficient_balance` — below the issuance fee, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — a required flag is absent; `invalid_asset_name` — name or abbreviation outside 1–32 visible ASCII; `invalid_value` — rate, precision, dates, bandwidth limits, or frozen tranches out of range, or the rate exceeding int32 after conversion). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`already_issued_asset` — this account already issued one, `transaction_rejected` — the node refused it, e.g. the balance cannot cover the issuance fee or a chain-side bound was exceeded, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — a required flag is absent; `invalid_asset_name` — name or abbreviation outside 1–32 visible ASCII; `invalid_value` — a malformed or non-positive rate, precision, supply or frozen tranche, a malformed date, `--start` not in the future, `--end` not after `--start`, or the rate exceeding int32 after reduction). ## See also diff --git a/ts/docs/commands/asset/list.md b/ts/docs/commands/asset/list.md index c42de534d..d69d576ae 100644 --- a/ts/docs/commands/asset/list.md +++ b/ts/docs/commands/asset/list.md @@ -1,6 +1,6 @@ # wallet-cli asset list -List every TRC10 on chain. +List TRC10 tokens on chain, one page at a time. ## Synopsis diff --git a/ts/docs/commands/asset/participate.md b/ts/docs/commands/asset/participate.md index e7d43f676..b2beac98e 100644 --- a/ts/docs/commands/asset/participate.md +++ b/ts/docs/commands/asset/participate.md @@ -14,7 +14,7 @@ wallet-cli asset participate --pay Buys from a token's issuance inside its funding window, at the fixed rate set when it was issued. This is participation in the ICO, not a market trade — the tokens come out of the issuer's remaining supply, and the price is not negotiable. The issuer's address is resolved from the token, so there is nothing to pass for it. -**`--pay` is the TRX you spend, not the tokens you receive.** You get `floor(pay × tokens ÷ trx)` where `trx:tokens` is the token's issued rate — the amount paid times the unit price, rounded down, since the chain multiplies before dividing on integers. The TRX is transferred in full, so any truncated remainder is not refunded; the loss is under 1 sun and cannot occur at all when the rate's `trxNum` is 1. If `--pay` is too small to buy even one unit, the command fails locally rather than broadcasting. +**`--pay` is the TRX you spend, not the tokens you receive.** You get `floor(pay × tokens ÷ trx)` where `trx:tokens` is the token's issued rate — the amount paid times the unit price, rounded down, since the chain multiplies before dividing on integers. The TRX is transferred in full, so any truncated remainder is not refunded. What is lost is under one **token minimal unit**, and the chain stores that unit's price as the pair `trxNum:num` — `trxNum` sun buys `num` minimal units — so the discarded amount is under `trxNum ÷ num` sun. At `--price 1:100 --precision 6` that is 0.01 sun, i.e. nothing; at `--price 1:100 --precision 0` the pair reduces to `10000:1` and the worst case is 9,999 sun. If `--pay` is too small to buy even one unit, the command fails locally rather than broadcasting. The acting account cannot be the token's own issuer. @@ -52,12 +52,12 @@ echo "$PW" | wallet-cli asset participate 1000124 --pay 100 --network tron:34481 ✅ Participated in ICO Asset BetaToken (id 1000124) Issuer TBeta9mR...8pLx - Participant TQkXm4vN...5Zt7Uw (main) + Participant TQkXm4vN...5Zt7Uw Paid 100 TRX Received 10,000 BetaToken TxID 4c8... - Block 57,883,402 - Fee 0 TRX (301 bandwidth) + Block #57,883,402 + Fee 0 TRX Status success ``` @@ -82,7 +82,7 @@ echo "$PW" | wallet-cli asset participate 1000124 --pay 100 --network tron:34481 ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`asset_not_found` — no such token, `not_in_ico_window` — outside the funding window, `self_participation` — you issued this token, `insufficient_balance`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — no `--pay`; `invalid_amount` — `--pay` is not a decimal number, or has more than 6 decimal places; `invalid_value` — `--pay` ≤ 0, or too small to buy one unit). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`asset_not_found` — no such token, `not_in_ico_window` — outside the funding window, `self_participation` — you issued this token, `transaction_rejected` — the node refused it, e.g. the balance cannot cover `--pay`, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — no `--pay`; `invalid_amount` — `--pay` is not a decimal number, or has more than 6 decimal places; `invalid_value` — `--pay` ≤ 0, or too small to buy one unit). ## See also diff --git a/ts/docs/commands/asset/unfreeze.md b/ts/docs/commands/asset/unfreeze.md index 4dc186f01..8e9c15640 100644 --- a/ts/docs/commands/asset/unfreeze.md +++ b/ts/docs/commands/asset/unfreeze.md @@ -51,12 +51,12 @@ echo "$PW" | wallet-cli asset unfreeze --network tron:3448148188 --wait --passwo ```console ✅ Frozen supply released Asset MyToken (id 1000123) - Issuer TQkXm4vN...5Zt7Uw (main) + Issuer TQkXm4vN...5Zt7Uw Released 100,000,000 MyToken Still frozen 50,000,000 MyToken TxID 6a5... - Block 57,883,560 - Fee 0 TRX (288 bandwidth) + Block #57,883,560 + Fee 0 TRX Status success ``` @@ -74,10 +74,10 @@ echo "$PW" | wallet-cli asset unfreeze --network tron:3448148188 --wait --passwo | Stage | Fields | |---|---| -| default (submit) | `kind: "asset-unfreeze"`, `stage: "submitted"`, `txId`, `assetId`, `name`, `issuerAddress` | -| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), `failed`, `releasedAmount`, `stillFrozenAmount` | +| default (submit) | `kind: "asset-unfreeze"`, `stage: "submitted"`, `txId`, `assetId`, `name`, `issuerAddress`, `precision`, and `releasedAmount` / `stillFrozenAmount` — present here too, but as the amounts this command *intends* to release | +| `--wait` (confirmed) | above, plus `stage: "confirmed"`, `confirmed` (boolean), `blockNumber`, flat settlement fields when returned (`feeSun`, `energyUsed`, `netUsed`, `energyFeeSun`, `netFeeSun`), and `failed`; `releasedAmount` is then taken from the receipt | -`releasedAmount` and `stillFrozenAmount` are raw decimal strings (smallest unit); `precision` is included for scaling. The confirmed `releasedAmount` reflects what the receipt reports. +`releasedAmount` and `stillFrozenAmount` are raw decimal strings (smallest unit); `precision` is included for scaling. Both are always present — before confirmation they are this command's own computation from the asset's frozen tranches, and only the confirmed `releasedAmount` is the receipt's number. ## Exit status diff --git a/ts/docs/commands/asset/update.md b/ts/docs/commands/asset/update.md index 7cd60e6fa..94d39a4a3 100644 --- a/ts/docs/commands/asset/update.md +++ b/ts/docs/commands/asset/update.md @@ -52,14 +52,14 @@ echo "$PW" | wallet-cli asset update --url https://mytoken.io/v2 --network tron: ```console ✅ Asset updated Asset MyToken (id 1000123) - Issuer TQkXm4vN...5Zt7Uw (main) + Issuer TQkXm4vN...5Zt7Uw Url https://mytoken.io/v2 Description Demo TRC10 Free net/account 0 Public free net 0 TxID 9e3... - Block 57,883,190 - Fee 0 TRX (295 bandwidth) + Block #57,883,190 + Fee 0 TRX Status success ``` @@ -84,7 +84,7 @@ The four fields are `url`, `description`, `freeAssetNetLimit`, and `publicFreeAs ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_an_issuer` — this account has not issued a TRC10, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`missing_option` — no field given; `invalid_value` — URL or description too long, bandwidth limits out of range). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`not_an_issuer` — this account has not issued a TRC10, `watch_only_no_signer`, `ledger_unsupported`, `auth_failed`) · `2` usage error (`invalid_value` — no field given at all, or a URL / description that is too long; the command has no required flag, so a bare call is `invalid_value`, not `missing_option`). ## See also diff --git a/ts/docs/commands/backup.md b/ts/docs/commands/backup.md index 572279c2c..93c667915 100644 --- a/ts/docs/commands/backup.md +++ b/ts/docs/commands/backup.md @@ -69,7 +69,7 @@ printf '%s' "$PW" | wallet-cli backup main --password-stdin ``` ```console -⚠️ Backup written ./wlt_d1qbj2fb.0-1783751611076.json +⚠️ Backup written /home/you/wlt_d1qbj2fb.0-1783751611076.json Account ID wlt_d1qbj2fb.0 Secret recovery phrase File mode 0600 @@ -85,7 +85,7 @@ printf '%s' "$PW" | wallet-cli backup main --keystore --password-stdin ``` ```console -⚠️ Keystore written ./wlt_d1qbj2fb.0-1785930000.keystore.json +⚠️ Keystore written /home/you/wlt_d1qbj2fb.0-1785930000.keystore.json Account ID wlt_d1qbj2fb.0 Family tron Secret private key @@ -100,7 +100,7 @@ printf '%s' "$PW" | wallet-cli backup main --keystore --out ./main.keystore.json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"backup","data":{"accountId":"wlt_d1qbj2fb.0","label":"main","type":"seed","index":0,"active":true,"addresses":{"tron":"TQkXm4vN...5Zt7Uw","evm":"0x7B28FE10...46C9C"},"seedId":"wlt_d1qbj2fb","derivationPath":{"tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0"},"family":"tron","secretType":"privateKey","format":"keystore","out":"./main.keystore.json","fileMode":"0600","bytes":491},"meta":{"durationMs":1420,"warnings":[]},"chain":{"family":"tron","network":"tron:728126428","chainId":"728126428"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"backup","data":{"accountId":"wlt_d1qbj2fb.0","label":"main","type":"seed","index":0,"active":true,"addresses":{"tron":"TQkXm4vN...5Zt7Uw","evm":"0x86B3D0f2...f4106"},"seedId":"wlt_d1qbj2fb","derivationPath":{"tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0"},"family":"tron","secretType":"privateKey","format":"keystore","out":"/home/you/main.keystore.json","fileMode":"0600","bytes":491},"meta":{"durationMs":1420,"warnings":[]},"chain":{"family":"tron","network":"tron:728126428","chainId":"728126428"}} ``` The audit log: @@ -111,11 +111,11 @@ wallet-cli backup --records --limit 3 ```console Backup records (showing 3 of 12) -| Time (UTC) | Exported account | Operation | File | -| ---------------- | ------------------------ | ----------------- | ----------------------------------------- | -| 2026-08-05 11:40 | TQkXm4vN...5Zt7Uw (main) | backup --keystore | ./wlt_d1qbj2fb.0-1785930000.keystore.json | -| 2026-08-04 09:12 | TQkXm4vN...5Zt7Uw (main) | backup | ./wlt_d1qbj2fb.0-1785834720.json | -| 2026-07-30 22:03 | TBeta9mR...8pLx | backup | ./tbeta-seed.json | +| Time (UTC) | Exported account | Operation | File | +| ---------------- | ------------------------ | ----------------- | ------------------------------------------------- | +| 2026-08-05 11:40 | TQkXm4vN...5Zt7Uw (main) | backup --keystore | /home/you/wlt_d1qbj2fb.0-1785930000.keystore.json | +| 2026-08-04 09:12 | TQkXm4vN...5Zt7Uw (main) | backup | /home/you/wlt_d1qbj2fb.0-1785834720.json | +| 2026-07-30 22:03 | TBeta9mR...8pLx | backup | /home/you/tbeta-seed.json | ``` ```bash @@ -123,7 +123,7 @@ wallet-cli backup --records --limit 3 -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"backup.records","data":{"records":[{"operation":"backup --keystore","accountId":"wlt_d1qbj2fb.0","account":"TQkXm4vN...5Zt7Uw","label":"main","out":"./wlt_d1qbj2fb.0-1785930000.keystore.json","timestamp":"2026-08-05T11:40:00Z"},{"operation":"backup","accountId":"wlt_d1qbj2fb.0","account":"TQkXm4vN...5Zt7Uw","label":"main","out":"./wlt_d1qbj2fb.0-1785834720.json","timestamp":"2026-08-04T09:12:00Z"},{"operation":"backup","accountId":"wlt_9x3k2m7p.0","account":"TBeta9mR...8pLx","label":null,"out":"./tbeta-seed.json","timestamp":"2026-07-30T22:03:00Z"}]},"meta":{"durationMs":8,"warnings":[],"pagination":{"offset":0,"limit":3,"total":12}},"chain":{"family":"tron","network":"tron:728126428","chainId":"728126428"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"backup.records","data":{"records":[{"operation":"backup --keystore","accountId":"wlt_d1qbj2fb.0","account":"TQkXm4vN...5Zt7Uw","family":"tron","label":"main","out":"/home/you/wlt_d1qbj2fb.0-1785930000.keystore.json","timestamp":"2026-08-05T11:40:00Z"},{"operation":"backup","accountId":"wlt_d1qbj2fb.0","account":"TQkXm4vN...5Zt7Uw","label":"main","out":"/home/you/wlt_d1qbj2fb.0-1785834720.json","timestamp":"2026-08-04T09:12:00Z"},{"operation":"backup","accountId":"wlt_9x3k2m7p.0","account":"TBeta9mR...8pLx","label":null,"out":"/home/you/tbeta-seed.json","timestamp":"2026-07-30T22:03:00Z"}]},"meta":{"durationMs":8,"warnings":[],"pagination":{"offset":0,"limit":3,"total":12}},"chain":{"family":"tron","network":"tron:728126428","chainId":"728126428"}} ``` ## Output @@ -145,7 +145,7 @@ Both forms are local and contact no node, but `backup` has an optional network d | `seedId` | string | Owning seed wallet id (`seed` accounts only) | | `secretType` | string | Kind of exported secret — `mnemonic`, or `privateKey` with `--keystore` | | `format` | string | `keystore` when `--keystore` was used | -| `out` | string | Path written | +| `out` | string | **Absolute** path written — a relative `--out` is resolved against the working directory before it is reported | | `fileMode` | string | File permissions, always `0600` | | `bytes` | number | File size in bytes | @@ -155,7 +155,7 @@ Both forms are local and contact no node, but `backup` has an optional network d |---|---|---| | `operation` | string | `backup` or `backup --keystore` | | `accountId` / `account` / `label` | string \| null | The account whose secret was exported; `label` is `null` when unset | -| `out` | string | File the secret went to | +| `out` | string | File the secret went to, as an **absolute** path | | `timestamp` | string | Export time, UTC | `meta.pagination` carries `offset`, `limit` (`null` = unlimited), and `total`. diff --git a/ts/docs/commands/block.md b/ts/docs/commands/block.md index b89366f42..edb9296bb 100644 --- a/ts/docs/commands/block.md +++ b/ts/docs/commands/block.md @@ -37,7 +37,7 @@ wallet-cli block 70433745 --network tron:3448148188 -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"block","data":{"block":{"blockID":"0000000041e6a3c3…","block_header":{"raw_data":{"number":69093315,"txTrieRoot":"…","witness_address":"41…","parentHash":"…","version":31,"timestamp":1783783761000},"witness_signature":"…"},"transactions":[{}]}},"meta":{"durationMs":126,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"block","data":{"block":{"blockID":"0000000041e6a3c3…","block_header":{"raw_data":{"number":70433745,"txTrieRoot":"…","witness_address":"41…","parentHash":"…","version":31,"timestamp":1787818674000},"witness_signature":"…"},"transactions":[{}]}},"meta":{"durationMs":126,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} ``` On an EVM network the text summary carries the gas and fee figures a block actually has: diff --git a/ts/docs/commands/chain/node.md b/ts/docs/commands/chain/node.md index 6524bb903..913a13dc1 100644 --- a/ts/docs/commands/chain/node.md +++ b/ts/docs/commands/chain/node.md @@ -12,7 +12,7 @@ wallet-cli chain node [options] Shows the connected node's version, head/solid block heights, sync state, and peer counts, on TRON and EVM networks alike. Its job in troubleshooting: separate "the node is out of sync" from "something is wrong with my transaction" before you start debugging the latter. -How the numbers are made: on TRON, version, block heights and peers come from the node's `getnodeinfo`, and the sync verdict is a freshness check — the head block header's timestamp against the local clock, within 3 block intervals (TRON produces a block every 3 s, so 9 s). On EVM the fields come from `web3_clientVersion`, `eth_chainId`, `eth_syncing`, `net_peerCount` and the latest block, and the verdict is the node's own `eth_syncing` answer rather than a guess from timestamps — the text `Syncing` row is that same verdict read the other way round. Public gateways (TronGrid, public RPC providers) may hide some fields (peers, machine info); those rows show `—` (json `null`). +How the numbers are made: on TRON, version, the solid block height and peers come from the node's `getnodeinfo`, while the **head** block number and timestamp come from a separate `getBlock()` read; the sync verdict is a freshness check — the head block header's timestamp against the local clock, within 3 block intervals (TRON produces a block every 3 s, so 9 s). On EVM the fields come from `web3_clientVersion`, `eth_chainId`, `eth_syncing`, `net_peerCount` and the latest block, and the verdict is the node's own `eth_syncing` answer rather than a guess from timestamps — the text `Syncing` row is that same verdict read the other way round. Public gateways (TronGrid, public RPC providers) may hide some fields (peers, machine info); those rows show `—` (json `null`). The `endpoint` is reported as a **host only**, never the full URL — a commercial RPC endpoint often carries its API key in the path, and this is output people paste into issues and CI logs. Read the full value with `config networks..httpEndpoint`. diff --git a/ts/docs/commands/chain/params.md b/ts/docs/commands/chain/params.md index 0fb05b32f..a9fabc104 100644 --- a/ts/docs/commands/chain/params.md +++ b/ts/docs/commands/chain/params.md @@ -76,7 +76,7 @@ wallet-cli chain params --network tron:3448148188 -o json | Field | Type | Meaning | |---|---|---| | `key` | string | Parameter name, verbatim from the chain | -| `value` | number | Raw chain value, no unit suffix (text adds SUN / ms) | +| `value` | number | Raw chain value, no unit suffix (text adds SUN / ms). Absent altogether for a parameter the node reports without one. The port that carries it is typed `number \| string`, but the TronWeb gateway behind it only ever yields numbers | ## Exit status diff --git a/ts/docs/commands/chain/prices.md b/ts/docs/commands/chain/prices.md index a37c43286..7da7de8c7 100644 --- a/ts/docs/commands/chain/prices.md +++ b/ts/docs/commands/chain/prices.md @@ -80,8 +80,8 @@ EVM: | Field | Type | Meaning | |---|---|---| | `feeModel` | string | `eip1559` or `legacy` | -| `baseFeeWei` | string | The latest block's base fee per gas; EIP-1559 chains only | -| `priorityFeeWei` | string | The node's suggested tip per gas | +| `baseFeeWei` | string | The latest block's base fee per gas; EIP-1559 chains only. A zero base fee is reported as `"0"`, not omitted | +| `priorityFeeWei` | string \| null | The node's suggested tip per gas; `null` when the node suggests none. Present on EIP-1559 chains only, alongside `baseFeeWei` | | `gasPriceWei` | string | Price per gas at those numbers | | `transferGas` | number | `21000` — the gas a plain native transfer costs | | `transferCostWei` | string | `transferGas × gasPriceWei`, i.e. what that transfer would cost now | diff --git a/ts/docs/commands/change-password.md b/ts/docs/commands/change-password.md index dd63cc452..e7ac2c2f0 100644 --- a/ts/docs/commands/change-password.md +++ b/ts/docs/commands/change-password.md @@ -36,14 +36,13 @@ wallet-cli change-password ``` ```console -? Current master password (hidden): +? Master password (hidden): ? New master password (hidden): -? Confirm new password (hidden): +? Confirm new password: ? Re-encrypt 3 software wallet(s) with the new password? [y/N]: y ✅ Master password changed — re-encrypted 3 software wallet(s) Wallets wallet1, wallet2, imported-1 - -⚠️ Ledger / watch-only accounts have no secrets and are unaffected. + Note Ledger / watch-only accounts are unaffected ``` ## Output diff --git a/ts/docs/commands/config.md b/ts/docs/commands/config.md index b6615b2d3..d30edfba5 100644 --- a/ts/docs/commands/config.md +++ b/ts/docs/commands/config.md @@ -25,7 +25,7 @@ Known keys: |---|---|---|---| | `defaultNetwork` | network id | `tron:728126428` | Network used when `--network` is omitted | | `defaultOutput` | `text` \| `json` | `text` | Output format when `-o` is omitted | -| `timeoutMs` | integer ms | `60000` | Default per RPC/device call timeout (`--timeout` overrides) | +| `timeoutMs` | ms, any finite number > 0 | `60000` | Default per node, service, or device call timeout (`--timeout` overrides). Unlike `waitTimeoutMs` it is not required to be an integer | | `waitTimeoutMs` | integer ms ≥ 0 | `60000` | Default `--wait` polling cap for broadcast commands | | `gasfreeApiKey` | string | (unset) | GasFree API key ([`gasfree`](gasfree/index.md)) | | `gasfreeApiSecret` | string | (unset) | GasFree API secret | @@ -71,12 +71,27 @@ networks httpEndpoint api.trongrid.io tron:3448148188 httpEndpoint nile.trongrid.io + tron:2494104990 + httpEndpoint api.shasta.trongrid.io + eip155:1 + httpEndpoint ethereum-rpc.publicnode.com eip155:11155111 httpEndpoint ethereum-sepolia-rpc.publicnode.com + eip155:56 + httpEndpoint bsc-dataseed.bnbchain.org + eip155:97 + httpEndpoint bsc-testnet-dataseed.bnbchain.org aliases - tron tron:728126428 - nile tron:3448148188 - sepolia eip155:11155111 + tron tron:728126428 + tron:mainnet tron:728126428 + nile tron:3448148188 + tron:nile tron:3448148188 + shasta tron:2494104990 + tron:shasta tron:2494104990 + ethereum eip155:1 + sepolia eip155:11155111 + bsc eip155:56 + bsc-testnet eip155:97 tronlinkSecretId TEST tronlinkSecretKey ******** tronlinkChannel test diff --git a/ts/docs/commands/contact/list.md b/ts/docs/commands/contact/list.md index 8b1f643e6..e6f04ae84 100644 --- a/ts/docs/commands/contact/list.md +++ b/ts/docs/commands/contact/list.md @@ -23,9 +23,10 @@ wallet-cli contact list ``` ```console -Name Address Note -alice TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub Alice mainnet -bob TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz — +| Name | Address | Note | +| ----- | ---------------------------------- | ------------- | +| alice | TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub | Alice mainnet | +| bob | TXe4Kd8nP2rF9gH5jL3mV6cW1bN7yS0aQz | — | ``` ```bash diff --git a/ts/docs/commands/contract/call.md b/ts/docs/commands/contract/call.md index e2fecbb59..c3a6e3b99 100644 --- a/ts/docs/commands/contract/call.md +++ b/ts/docs/commands/contract/call.md @@ -43,7 +43,7 @@ wallet-cli contract call --contract TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf --method {"schema":"wallet-cli.result.v1","success":true,"command":"contract.call","data":{"contract":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","method":"balanceOf(address)","result":["0000000000000000000000000000000000000000000000000000000000000000"]},"meta":{"durationMs":15,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} ``` -The same call on an EVM network. Note the shape of `result`: the TRON node returns the return data split into words, the EVM node returns it as one `0x` blob: +The same call on an EVM network. Note the shape of `result`: TRON passes the node's `constant_result` array through untouched, while the EVM node returns one `0x` blob: ```bash wallet-cli contract call --contract 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 --method "balanceOf(address)" --params '[{"type":"address","value":"0x541B10b92b45C08513e67bb8209f035D810212B6"}]' --network eip155:11155111 @@ -64,7 +64,7 @@ Result 0x0000000000000000000000000000000000000000000000000000000000000000 (raw) |---|---|---| | `contract` | string | Contract address called | | `method` | string | Method signature invoked | -| `result` | string[] \| string | Raw ABI-encoded return data; an array of 32-byte words on TRON, a single `0x` string on EVM. Decode per the method's return type | +| `result` | string[] \| string | Raw ABI-encoded return data. On TRON it is the node's `constant_result` array verbatim — the CLI does no splitting or re-chunking; on EVM, a single `0x` string. Decode per the method's return type | ## Exit status diff --git a/ts/docs/commands/contract/create2.md b/ts/docs/commands/contract/create2.md index 6318b0461..207eb53a6 100644 --- a/ts/docs/commands/contract/create2.md +++ b/ts/docs/commands/contract/create2.md @@ -58,7 +58,7 @@ wallet-cli contract create2 --deployer TQkXm4vN...5Zt7Uw --code 6080604052... -- ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"contract.create2","data":{"deployerAddress":"TQkXm4vN...","salt":255,"saltHex":"0x00000000000000000000000000000000000000000000000000000000000000ff","codeHash":"c8f4a1...b91b","address":"TWq8dK3n...2mHb"},"meta":{"durationMs":3,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"contract.create2","data":{"deployerAddress":"TQkXm4vN...","salt":255,"saltHex":"0x00000000000000000000000000000000000000000000000000000000000000ff","codeHash":"c8f4a1...b91b","address":"TWq8dK3n...2mHb"},"meta":{"durationMs":3,"warnings":[]},"chain":{"family":"tron","network":"tron:728126428","chainId":"728126428"}} ``` ## Output @@ -66,12 +66,12 @@ wallet-cli contract create2 --deployer TQkXm4vN...5Zt7Uw --code 6080604052... -- | Field | Type | Meaning | |---|---|---| | `deployerAddress` | string | The deployer as given, base58 | -| `salt` | number | The salt as given, decimal | +| `salt` | number \| string | The salt as given, decimal. A **string** when it falls outside the safe-integer range, so a full int64 salt survives JSON | | `saltHex` | string | The zero-padded 32 bytes that actually enter the hash | | `codeHash` | string | `keccak256` of the creation bytecode | | `address` | string | The resulting contract address, base58 | -This is a local command, so the envelope carries no `chain` block. +The command never contacts a node, but it is still a TRON chain command: the envelope carries the usual `chain` block for the selected network. `--network` is optional here and only picks which network the block names. ## Exit status diff --git a/ts/docs/commands/contract/deploy.md b/ts/docs/commands/contract/deploy.md index 8a1dae8b6..80dd8add3 100644 --- a/ts/docs/commands/contract/deploy.md +++ b/ts/docs/commands/contract/deploy.md @@ -137,7 +137,7 @@ echo "$PW" | wallet-cli contract deploy --artifact ./build/contracts/Token.json | `--dry-run` | `kind`, `mode: "dry-run"`, `contractAddress`, `fee`, the unsigned `tx` (plus `nonce` on EVM) | | `--sign-only` / `--build-only` | `kind`, `mode`, `hex`, `fee`, the transaction object | -`contractAddress` is computed locally from the deployer and nonce, so it is known before the transaction confirms. +`contractAddress` is known before the transaction confirms, but each family derives it differently. On EVM it is computed locally from the deployer and the nonce. On TRON the address is derived from the final txID, so it is read back from the prepared transaction — after `--permission-id` / `--expiration` have been bound and the txID is settled — rather than computed from the builder's output. ## Exit status diff --git a/ts/docs/commands/contract/send.md b/ts/docs/commands/contract/send.md index 0c3aff0ee..eb34b777a 100644 --- a/ts/docs/commands/contract/send.md +++ b/ts/docs/commands/contract/send.md @@ -72,8 +72,9 @@ echo "$PW" | wallet-cli contract send --contract TXYZopYRdj2D9XRtbG411XZZ3kM5VkA ```console ⏳ Called transfer - TxID c8d... - Status pending — not yet on-chain + Contract TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf + TxID c8d... + Status pending — not yet on-chain ! Track it: wallet-cli tx info --network tron:3448148188 --txid c8d... ``` @@ -109,11 +110,12 @@ echo "$PW" | wallet-cli contract send --contract TXYZopYRdj2D9XRtbG411XZZ3kM5VkA ```console ❌ Called transfer - TxID c8d... - Block #66,000,123 - Energy 31,200 - Status failed - Reason OUT_OF_ENERGY + Contract TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf + TxID c8d... + Block #66,000,123 + Energy 31,200 + Status failed + Reason OUT_OF_ENERGY ``` ## Output diff --git a/ts/docs/commands/create.md b/ts/docs/commands/create.md index e76d7691c..9a69a4b77 100644 --- a/ts/docs/commands/create.md +++ b/ts/docs/commands/create.md @@ -40,7 +40,7 @@ wallet-cli create --label main Account ID wlt_2dbv24de.0 Type HD TRON address TTVdGTBXY5mmY3nJFGUp7Vo898kUJ6gtFQ - EVM address 0x7B28FE10FBccE88c3967ff0Fd64f1ffB46b46C9C + EVM address 0x5c8e1b04A7f39d62C0B3e85A1d47F9028b6ce713 Active yes ⚠️ Recovery phrase is encrypted locally and was not printed. @@ -54,7 +54,7 @@ printf '%s' "$PW" | wallet-cli create --label main --password-stdin -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"create","data":{"status":"created","accountId":"wlt_2dbv24de.0","label":"main","type":"seed","index":0,"active":true,"addresses":{"tron":"TTVdGTBXY5mmY3nJFGUp7Vo898kUJ6gtFQ","evm":"0x7B28FE10FBccE88c3967ff0Fd64f1ffB46b46C9C"},"seedId":"wlt_2dbv24de","derivationPath":{"tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0"}},"meta":{"durationMs":38,"warnings":[]}} +{"schema":"wallet-cli.result.v1","success":true,"command":"create","data":{"status":"created","accountId":"wlt_2dbv24de.0","label":"main","type":"seed","index":0,"active":true,"addresses":{"tron":"TTVdGTBXY5mmY3nJFGUp7Vo898kUJ6gtFQ","evm":"0x5c8e1b04A7f39d62C0B3e85A1d47F9028b6ce713"},"seedId":"wlt_2dbv24de","derivationPath":{"tron":"m/44'/195'/0'/0/0","evm":"m/44'/60'/0'/0/0"}},"meta":{"durationMs":38,"warnings":[]}} ``` ## Output diff --git a/ts/docs/commands/current.md b/ts/docs/commands/current.md index c3fbe4a6f..9826c0d38 100644 --- a/ts/docs/commands/current.md +++ b/ts/docs/commands/current.md @@ -12,7 +12,7 @@ wallet-cli current [options] | Option | Description | |---|---| -| `--qr` | Also render the receive address for the selected network as a scannable QR code in the terminal, with the full address printed above it for manual verification; text output only | +| `--qr` | Also render the receive address for the selected network as a scannable QR code in the terminal, with the full address printed below it for manual verification; text output only | Plus the [global options](index.md) (`--account` overrides which account is shown). @@ -30,9 +30,9 @@ Active account: main EVM address 0x7B28FE10FBccE88c3967ff0Fd64f1ffB46b46C9C ``` -With `--account`, the header reads `Selected account:` instead of `Active account:`. +The header follows `data.active`, not the flag: it reads `Selected account:` when `--account` names an account other than the active one, and stays `Active account:` otherwise — including when `--account` happens to name the active account. -Add `--qr` to also render the active account's address as a scannable receive QR code, drawn with block characters below the address. Purely local — the address comes from local keystore metadata, no node access: +Add `--qr` to also render the active account's address as a scannable receive QR code, drawn with block characters after the address list and followed by a `Receive address` line carrying the full value. Purely local — the address comes from local keystore metadata, no node access: ```bash wallet-cli current --qr @@ -43,7 +43,8 @@ Active account: main TRON address TE9kPMtaMjfZN95CuPRsCHUQGWwx9EcJW8 EVM address 0x7B28FE10FBccE88c3967ff0Fd64f1ffB46b46C9C - [ scannable QR code of the TRON address, drawn in the terminal ] +[ scannable QR code of the TRON address, drawn in the terminal ] +Receive address TE9kPMtaMjfZN95CuPRsCHUQGWwx9EcJW8 ``` The QR encodes **one** address — the receive address for the selected network. Pass `--network` to choose which: @@ -88,7 +89,7 @@ error [missing_wallet_address]: no active account; import one first | `index` | number \| null | HD derivation index; `null` for non-HD accounts | | `active` | boolean | `true` for the active account; `false` when `--account` selected a different one | | `addresses` | object | One entry per family the account can produce: `tron` (base58) and/or `evm` (`0x`, EIP-55 checksummed) | -| `derivationPath` | object \| null | Per-family BIP32 path for `seed` accounts; `null` otherwise | +| `derivationPath` | object \| null | The BIP32 path behind each address: every family for a `seed` account, the single chosen path for a `ledger` account; `null` for `privateKey` and `watch`, which were never derived | | `seedId` | string | Owning seed wallet id (`seed` accounts only) | | `family` | string | Chain family this account is bound to — single-family accounts (`watch`, `ledger`) only | | `receiveAddress` | string | Present in JSON only when `--qr` was requested; address selected by `--network` | diff --git a/ts/docs/commands/delete.md b/ts/docs/commands/delete.md index db92b1ad1..901d6c862 100644 --- a/ts/docs/commands/delete.md +++ b/ts/docs/commands/delete.md @@ -67,7 +67,7 @@ wallet-cli delete main-1 --yes -o json |---|---|---| | `accountId` | string | Id of the deleted account/wallet (`wlt_….N` for a sub-account, the wallet id `wlt_…` for a wallet) | | `scope` | string | `account` (only that account) or `wallet` (cascaded whole wallet) | -| `secretRemoved` | boolean | Whether the key was removed (deleting an HD sub-account keeps the seed = `false`; deleting a wallet = `true`) | +| `secretRemoved` | boolean | Whether encrypted secret material was removed. Deleting an HD sub-account keeps the seed, so `false`. Deleting a wallet reports whether that wallet held a secret at all: `true` for seed and private-key wallets, `false` for Ledger and watch-only ones, which never stored one | | `newActive` | string \| null | New active account id after deletion; `null` if none remain | ## Exit status diff --git a/ts/docs/commands/derive.md b/ts/docs/commands/derive.md index 5e6224452..980c6256a 100644 --- a/ts/docs/commands/derive.md +++ b/ts/docs/commands/derive.md @@ -13,7 +13,7 @@ wallet-cli derive --seed-id [--index ] [--label ] [options] | Option | Description | |---|---| | `--seed-id ` | seed id of the HD wallet to derive from — the HD group header in `list` [required] | -| `--index ` | explicit HD account index; omit to use the next free index | +| `--index ` | explicit HD account index; omit to use the next free index. An index that already exists is not re-derived — the existing account is made active and `status` comes back `"existing"` | | `--label ` | label for the new account, 1-64 chars; omit to auto-generate | | `--password-stdin` | read the master password from stdin (fd 0) | @@ -55,7 +55,7 @@ printf '%s' "$PW" | wallet-cli derive --seed-id wlt_y8cz6xda --password-stdin -o | Field | Type | Meaning | |---|---|---| -| `status` | string | `"created"` | +| `status` | string | `"created"` for a newly derived index, `"existing"` when `--index` names an index this wallet already holds — that account is simply made active again, and no new key is derived | | `accountId` | string | Stable id `.` | | `label` | string | Account label (default `-`, e.g. `main-1`) | | `type` | string | Always `"seed"` | diff --git a/ts/docs/commands/encoding/convert.md b/ts/docs/commands/encoding/convert.md index 891ed5f71..76c9ca626 100644 --- a/ts/docs/commands/encoding/convert.md +++ b/ts/docs/commands/encoding/convert.md @@ -30,9 +30,9 @@ wallet-cli encoding convert TBhCfAytTEh52WFL6HYr64i2nmc3u3TCUp ``` ```console -TRON TBhCfAytTEh52WFL6HYr64i2nmc3u3TCUp -TRON hex 4112e94f5a3c88b17d2f6e0b9a45cd310f8e7a6d29 -EVM 0x12E94f5a3c88b17d2F6E0b9a45Cd310f8E7a6D29 +TRON TBhCfAytTEh52WFL6HYr64i2nmc3u3TCUp +TRON hex 4112e94f5a3c88b17d2f6e0b9a45cd310f8e7a6d29 +EVM 0x12E94f5a3c88b17d2F6E0b9a45Cd310f8E7a6D29 ``` ```bash @@ -50,9 +50,9 @@ wallet-cli encoding convert 04a1b2c3d4e5...f6a7b8c9d0 ``` ```console -TRON TBhCfAytTEh52WFL6HYr64i2nmc3u3TCUp -TRON hex 4112e94f5a3c88b17d2f6e0b9a45cd310f8e7a6d29 -EVM 0x12E94f5a3c88b17d2F6E0b9a45Cd310f8E7a6D29 +TRON TBhCfAytTEh52WFL6HYr64i2nmc3u3TCUp +TRON hex 4112e94f5a3c88b17d2f6e0b9a45cd310f8e7a6d29 +EVM 0x12E94f5a3c88b17d2F6E0b9a45Cd310f8E7a6D29 ``` A non-address-shaped input converts across encodings, in both directions: @@ -84,7 +84,7 @@ wallet-cli encoding convert TBhCfAytTEh52WFL6HYr64i2nmc3u3TCUX ``` ```console -Error: invalid_value — base58 checksum mismatch (typo in the address?) +error [invalid_value]: base58 checksum mismatch (typo in the address?) ``` ## Output diff --git a/ts/docs/commands/exchange/index.md b/ts/docs/commands/exchange/index.md index 902eb2f5e..bf79aa2bb 100644 --- a/ts/docs/commands/exchange/index.md +++ b/ts/docs/commands/exchange/index.md @@ -2,7 +2,7 @@ TRON's protocol-level Bancor exchange. -Pairs trade **TRX against TRC10** — never TRC20 — and settle instantly against a bonding curve: no order book, no counterparty, no matching. Four properties differ from the AMMs most people are used to, and all four matter before you touch this group: +Pairs trade **TRX and TRC10 assets** — never TRC20 — and settle instantly against a bonding curve. Either side may be TRX or a TRC10 id, so a TRC10-against-TRC10 pair is legal too; the only rule is that the two sides differ: no order book, no counterparty, no matching. Four properties differ from the AMMs most people are used to, and all four matter before you touch this group: - **A pair is private to its creator.** Only the account that created a pair can inject or withdraw its liquidity, and that binding cannot be transferred. There are no LP tokens and no outside liquidity providers. - **Anyone can trade**, though — trading is open even though liquidity is not. @@ -35,7 +35,7 @@ wallet-cli exchange COMMAND | `exchange withdraw` | [withdraw.md](withdraw.md) | Take liquidity out in proportion to reserves | | `exchange trade` | [trade.md](trade.md) | Swap one side for the other | | `exchange show` | [show.md](show.md) | One pair's creator, creation time, and reserves | -| `exchange list` | [list.md](list.md) | List every pair on chain | +| `exchange list` | [list.md](list.md) | List exchange pairs, one page at a time | ## See also diff --git a/ts/docs/commands/exchange/list.md b/ts/docs/commands/exchange/list.md index e2997d5d1..23118e19b 100644 --- a/ts/docs/commands/exchange/list.md +++ b/ts/docs/commands/exchange/list.md @@ -1,6 +1,6 @@ # wallet-cli exchange list -List every exchange pair on chain. +List exchange pairs on chain, one page at a time. ## Synopsis @@ -16,13 +16,13 @@ Lists pairs with their id, both tokens, reserves, and creator. Read-only, no acc **Reserves here are in minimal units, not whole tokens.** This command makes a single RPC and so has no token precisions to divide by; `exchange show` fetches them and prints whole tokens instead. The same pair therefore reads `6,672` here and `66.72` there. -Paging happens on the node, and **there is no total**: the chain exposes no count of exchange pairs. The title reports the window it asked for — `Exchanges (limit 3, offset 0)` — not `showing 3 of N`, and `meta.pagination.total` is always `null`. To get everything, pass a `--limit` large enough to cover it. +Paging happens on the node, and **there is no total**: the chain exposes no count of exchange pairs. The title reports the window it asked for — `Exchanges (limit 3, offset 0)` — not `showing 3 of N`, and `meta.pagination.total` is always `null`. To get everything, page with `--offset` until a short page comes back: `--limit` caps at `1000`, and a larger value is rejected with `invalid_value`. ## Options | Option | Description | |---|---| -| `--limit ` | Max pairs to return (default `10`) | +| `--limit ` | Max pairs to return, 1–1000 (default `10`) | | `--offset ` | Pagination offset (default `0`) | Plus the [global options](../index.md#global-options-every-command). diff --git a/ts/docs/commands/exchange/trade.md b/ts/docs/commands/exchange/trade.md index fe1f123ca..4b644cd92 100644 --- a/ts/docs/commands/exchange/trade.md +++ b/ts/docs/commands/exchange/trade.md @@ -76,14 +76,14 @@ echo "$PW" | wallet-cli exchange trade 12 --sell TRX --amount 100 --min-received Status success ``` -The same trade via `--slippage 1`: the CLI computes 4,950 from the current reserves, takes 1 % off, and sends 4,900 as the floor. +The same trade via `--slippage 1`: the CLI computes 4,950 from the current reserves, takes 1 % off, and sends 4,900.5 as the floor. The percentage is first converted to basis points, **rounded to the nearest** one — so `--slippage 1.006` tolerates 1.01 % — and the floor itself is then integer-divided, i.e. rounded down. ```bash echo "$PW" | wallet-cli exchange trade 12 --sell TRX --amount 100 --slippage 1 --network tron:3448148188 --wait --password-stdin -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"exchange.trade","data":{"kind":"exchange-trade","stage":"confirmed","txId":"d9a...","confirmed":true,"blockNumber":57884455,"failed":false,"exchangeId":12,"pair":"TRX:1000123","traderAddress":"TQkXm4vN...","soldTokenId":"_","soldQuant":"100000000","soldLabel":"TRX","soldDecimals":6,"receivedTokenId":"1000123","receivedLabel":"MyToken","receivedDecimals":6,"receivedQuant":"4950000000","estimatedReceivedQuant":"4950000000","minReceivedQuant":"4900000000","feeSun":0},"meta":{"durationMs":6490,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"exchange.trade","data":{"kind":"exchange-trade","stage":"confirmed","txId":"d9a...","confirmed":true,"blockNumber":57884455,"failed":false,"exchangeId":12,"pair":"TRX:1000123","traderAddress":"TQkXm4vN...","soldTokenId":"_","soldQuant":"100000000","soldLabel":"TRX","soldDecimals":6,"receivedTokenId":"1000123","receivedLabel":"MyToken","receivedDecimals":6,"receivedQuant":"4950000000","estimatedReceivedQuant":"4950000000","minReceivedQuant":"4900500000","feeSun":0},"meta":{"durationMs":6490,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} ``` ## Output @@ -102,7 +102,7 @@ TRX is identified as `"_"`; every quantity is a **string** in minimal units. Bef ## Exit status -`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`exchange_not_found` — no such pair, `token_not_in_exchange`, `exchange_closed` — a side holds zero, `exchange_trading_disabled` — the network is not accepting Bancor trades, `slippage_exceeded` — the return fell below the floor, `transaction_rejected` — the node refused it, for example for lack of balance, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`missing_option` — no `--sell`; `invalid_option` — both or neither of `--amount` / `--raw-amount`, or more than one floor flag; `invalid_amount` — the amount or `--min-received` is not a decimal number, or has more decimal places than that token allows; `invalid_value` — amount ≤ 0, or a `--slippage` outside 0–100). +`0` submitted (or built/signed in early-exit modes) · `1` execution failure (`exchange_not_found` — no such pair, `token_not_in_exchange`, `exchange_closed` — a side holds zero, `exchange_trading_disabled` — the network is not accepting Bancor trades, `slippage_exceeded` — the return fell below the floor, `transaction_rejected` — the node refused it, for example for lack of balance, `watch_only_no_signer`, `auth_failed`) · `2` usage error (`missing_option` — no `--sell`; `invalid_option` — both or neither of `--amount` / `--raw-amount`, or more than one floor flag; `invalid_amount` — the amount or `--min-received` is not a decimal number, or has more decimal places than that token allows; `invalid_value` — amount ≤ 0, or a `--slippage` that is not both greater than 0 and less than 100; `0` and `100` are themselves rejected). ## See also diff --git a/ts/docs/commands/gasfree/index.md b/ts/docs/commands/gasfree/index.md index 5f298e1f5..dc683adfc 100644 --- a/ts/docs/commands/gasfree/index.md +++ b/ts/docs/commands/gasfree/index.md @@ -2,7 +2,7 @@ Gas-free token transfers via the GasFree service. -`gasfree` moves tokens without holding any TRX: you sign a transfer with EIP-712 structured-data signing and the GasFree service ([open.gasfree.io](https://open.gasfree.io)) puts it on-chain for you. The fee is charged in the transferred token itself — a per-transfer service fee, plus a one-time activation fee on your first transfer — so **no TRX is needed**. +`gasfree` moves tokens without holding any TRX: you sign a transfer with TIP-712 structured-data signing (TRON's EIP-712 analogue) and the GasFree service ([open.gasfree.io](https://open.gasfree.io)) puts it on-chain for you. The fee is charged in the transferred token itself — a per-transfer service fee, plus a one-time activation fee on your first transfer — so **no TRX is needed**. **TRON only.** GasFree is a TRON service; every subcommand here fails with `family_mismatch` on an EVM network. diff --git a/ts/docs/commands/gasfree/info.md b/ts/docs/commands/gasfree/info.md index e67822d5a..5c030384b 100644 --- a/ts/docs/commands/gasfree/info.md +++ b/ts/docs/commands/gasfree/info.md @@ -32,9 +32,9 @@ GasFree address TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw Status active Nonce 4 -| Token | Balance | Activation fee | Transfer fee | -| ----- | ------- | -------------- | ------------ | -| USDT | 125 USDT | 1 USDT | 0.5 USDT | +| Token | Balance | Activation fee | Transfer fee | +| ----- | -------- | -------------- | ------------ | +| USDT | 125 USDT | 1 USDT | 0.5 USDT | ``` ```bash @@ -42,7 +42,7 @@ wallet-cli gasfree info --account main --network tron:3448148188 -o json ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.info","data":{"ownerAddress":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","gasFreeAddress":"TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw","active":true,"nonce":"4","tokens":[{"symbol":"USDT","address":"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t","decimals":6,"activateFee":"1000000","transferFee":"500000","balance":"125000000"}]},"meta":{"durationMs":380,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.info","data":{"ownerAddress":"TQkXm4vN8pR2sD6fWbYc3LhJa9Ee5Zt7Uw","gasFreeAddress":"TVjsyZ7fYF3qCcNaMxN5PMWmSgYcCyqZfw","active":true,"nonce":"4","tokens":[{"symbol":"USDT","address":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","decimals":6,"activateFee":"1000000","transferFee":"500000","balance":"125000000"}]},"meta":{"durationMs":380,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} ``` ## Output @@ -57,7 +57,7 @@ wallet-cli gasfree info --account main --network tron:3448148188 -o json ## Exit status -`0` success · `1` execution failure (`gasfree_integrity` — the provider's fee metadata disagreed between the token list and the address response, `provider_error` — service error / rate limit) · `2` usage error (`gasfree_credentials_missing`, `unsupported_network`, `invalid_value`). +`0` success · `1` execution failure (`gasfree_integrity` — the provider's fee metadata disagreed between the token list and the address response, `provider_error` — the service failed, answered with malformed or oversized JSON, returned a field this CLI will not act on, or returned any non-429 error status; `provider_rate_limited` — the service returned 429, with `details.retryAfter` when it sent one) · `2` usage error (`gasfree_credentials_missing`, `unsupported_network`, `invalid_value`). ## See also diff --git a/ts/docs/commands/gasfree/trace.md b/ts/docs/commands/gasfree/trace.md index 30ca201cf..0a423d3ae 100644 --- a/ts/docs/commands/gasfree/trace.md +++ b/ts/docs/commands/gasfree/trace.md @@ -27,15 +27,15 @@ wallet-cli gasfree trace 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 --network tron:344 ``` ```console -Trace ID 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 -Status succeed -TxID d2e... -Token USDT -Amount 25 USDT +Trace ID 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 +Status succeed +TxID d2e... +Token USDT +Amount 25 USDT Service fee 0.5 USDT Activation fee 0 USDT Total 25.5 USDT -To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub +To TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub ``` ```bash @@ -43,7 +43,7 @@ wallet-cli gasfree trace 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 --network tron:344 ``` ```json -{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.trace","data":{"traceId":"7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527","state":"SUCCEED","txId":"d2e...","token":"USDT","tokenAddress":"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t","decimals":6,"amount":"25000000","serviceFee":"500000","activateFee":"0","totalDeducted":"25500000","from":"TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER","owner":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","nonce":"8"},"meta":{"durationMs":290,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} +{"schema":"wallet-cli.result.v1","success":true,"command":"gasfree.trace","data":{"traceId":"7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527","state":"SUCCEED","txId":"d2e...","token":"USDT","tokenAddress":"TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf","decimals":6,"amount":"25000000","serviceFee":"500000","activateFee":"0","totalDeducted":"25500000","from":"TNER12mMVWruqopsW9FQtKxCGfZcEtb3ER","owner":"TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC","to":"TBy6mQ7Y3nJ8sD2fWpXk4LhVc9Ra1Zt5Ub","nonce":"8"},"meta":{"durationMs":290,"warnings":[]},"chain":{"family":"tron","network":"tron:3448148188","chainId":"3448148188"}} ``` ## Output @@ -66,7 +66,7 @@ wallet-cli gasfree trace 7f3e9a02-58c1-4d2e-b6a4-91d0c3f8e527 --network tron:344 ## Exit status -`0` success · `1` execution failure (`not_found` — no such trace id, `gasfree_integrity`, `provider_error`) · `2` usage error (`gasfree_credentials_missing`, `unsupported_network`, `invalid_value`). +`0` success · `1` execution failure (`not_found` — no such trace id, `gasfree_integrity`, `provider_error` — the service failed, answered with malformed or oversized JSON, returned a field this CLI will not act on, or returned any non-429 error status; `provider_rate_limited` — the service returned 429) · `2` usage error (`gasfree_credentials_missing`, `unsupported_network`, `invalid_value`). A `FAILED` transfer is a successful query: the envelope stays `success: true` at exit `0`, and `data.failureReason` carries the provider's explanation. diff --git a/ts/docs/commands/gasfree/transfer.md b/ts/docs/commands/gasfree/transfer.md index 9253a8cf0..0038ed48c 100644 --- a/ts/docs/commands/gasfree/transfer.md +++ b/ts/docs/commands/gasfree/transfer.md @@ -11,7 +11,7 @@ wallet-cli gasfree transfer --to --amount [--token

- A command-line wallet for the TRON network — interactive in Java, agent-first in TypeScript + A command-line wallet for TRON and selected EVM networks — dual-mode in Java, agent-first in TypeScript