Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ts/docs/machine-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ retry, exactly as the four-state model below requires.

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 two strings (`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.

The catalog deliberately does not prescribe retry behavior. A transport `timeout` or `rpc_error`
only says that the CLI did not observe a successful response. For a read-only call, retrying the same
request is normally harmless. On a submit path, however, the node may have accepted the transaction
before the response was lost. Treat that outcome as unknown: reconcile by `txId` when available, or
by sender and nonce for EVM, before building and signing another transaction. Re-running a send does
not retry the same transaction; it creates a second transaction and can duplicate a payment.

Comment on lines +202 to +208

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There is a section more clearly to explain the same thing

retry describes the error, not the command. timeout and rpc_error are same because
for most calls that is correct — the node never acted, so resending is free. But a command that
may have already broadcast a transaction (tx send and anything else on the submit path) can hit
timeout or rpc_error after the node accepted the transaction and before the response made it
back. In that case the outcome is unknown, not failed, and resending does not retry the original
request — it builds and signs a new transaction, which on TRON is a second, distinct transfer.
retry: "same" is correct for a timeout/rpc_error that happens while resolving a network id or
reading a balance; it is not a license to resend a broadcast blind. Reconcile with
[tx status](https://github.com/tronprotocol/wallet-cli/pull/997/changes#script-safety-never-mistake-submitted-for-confirmed) before deciding whether to
retry, exactly as the four-state model below requires.

Common codes at exit **2** (usage — fix the call):

| Code | Meaning |
Expand Down
77 changes: 52 additions & 25 deletions ts/skills/wallet-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,65 @@
---
name: wallet-cli
description: Operate a TRON wallet from the command line — create/import wallets, query balances and history, send TRX/TRC20/TRC10, check transaction status, stake resources, and sign via software or Ledger. Use for any TRON wallet operation when the wallet-cli binary is available.
description: Operate a TRON or EVM wallet from the command line — create/import wallets, query balances and history, send native coins and TRC20/ERC20/TRC10 tokens, use GasFree and multi-sig flows, stake resources, vote and govern, manage TRC10 assets and Bancor exchanges, deploy and call contracts, and sign via software or Ledger. Use for TRON or EVM wallet operations when the wallet-cli binary is available.
---

# wallet-cli — agent usage

TRON CLI wallet with a machine contract: stable JSON envelopes, deterministic exit codes, secrets via stdin only.
CLI wallet for TRON and EVM chains, with a machine contract: stable JSON envelopes, deterministic exit codes, and secrets accepted only through stdin flags or hidden TTY prompts.

If `wallet-cli` is not on PATH: `npm install -g @tron-walletcli/wallet-cli` (requires Node.js 20+). Never install the bare npm name `wallet-cli` — that is an unrelated third-party package.

## Invocation rules (always)

1. Always pass `-o json`. Parse stdout as a single JSON object, schema `wallet-cli.result.v1`.
2. Branch on exit code first (`0` ok, `1` execution failure, `2` your call was malformed), then on `error.code`. Never parse `error.message`.
3. Always pass `--network tron:nile` for tests; `tron:mainnet` moves real funds.
4. Secrets go through stdin flags (`--password-stdin`), never argv/env. Only one `*-stdin` flag per run. Mnemonic/private-key import and `change-password` are interactive-only (hidden TTY input) — hand those to the human; an agent cannot drive them.
5. All on-chain amounts in JSON are decimal strings — do not treat as numbers.
3. Always pass a canonical test network in automation: `--network tron:3448148188` (Nile) or `--network eip155:11155111` (Sepolia). Short aliases such as `nile` and `sepolia` are accepted, but can be re-pointed locally; output always reports the canonical id. `tron:728126428`, `eip155:1`, and `eip155:56` move real funds.
4. The CLI does not read secrets from argv or dedicated secret environment variables. Use stdin flags such as `--password-stdin`; only one `*-stdin` flag may consume stdin per run. Mnemonic/private-key import and `change-password` are interactive-only (hidden TTY input) — hand those to the human; an agent cannot drive them.
5. Do not infer JSON number types. Values backed by `bigint` or protocol int64 amounts are decimal strings; bounded counters and fees such as `blockNumber`, `feeSun`, `energyUsed`, and `netUsed` may be numbers. Follow the command's field table.
6. `--timeout <ms>` bounds every RPC/device call (default 60000).
7. **Discover, don't guess**: `wallet-cli --json-schema` returns the command catalog, including `families`, `inputSchema`, and the maintained `errorCodes` discovery index. The index maps codes to descriptions and is not a closed enum; branch on exit code first and tolerate unknown codes.
8. **Chain families decide what runs.** TRON protocol features (stake, vote, reward, proposal, witness, permission, asset, exchange, GasFree, `account history|activate|set`, `chain params`, `tx approvals|multisig`, and TRON contract governance) fail on EVM with `family_mismatch` (exit 2). Family-scoped flags fail with `invalid_option`: `--asset-id`, `--fee-limit`, `--permission-id`, `--expiration`, `--transaction`, and `--tx-stdin` are TRON-specific; `--gas-limit`, `--max-fee`, `--priority-fee`, and `--nonce` are EVM-specific.
9. A software key-backed account has one address per family (TRON base58 and EVM `0x`); the selected network chooses which one a command uses. Watch-only and Ledger accounts are single-family.
10. Human token and exchange amounts are scaled with decimals or precision supplied by the selected node. When the exact base-unit quantity matters, use `--raw-amount` or the command's `--raw-*` option and inspect the raw quantity before signing.

## Command map

```
create --label <l> new HD wallet (BIP39)
import mnemonic|private-key|ledger|watch bring in existing accounts
import mnemonic|private-key|keystore|ledger|watch bring in existing accounts
list / use <acct> / current enumerate & select active account
derive / rename / backup / delete account lifecycle (backup: secret, file mode 0600)
derive / rename / backup / delete account lifecycle (backup: secret -> 0600 file in cwd)
backup <acct> --keystore export one private key as a Web3 keystore
backup --records inspect the local export audit log
address generate keypair made locally; NOT added to the wallet
account balance|info|history|portfolio on-chain state (history needs TronGrid)
account activate --address <addr> activate a new account (payer = active account)
account set --name <n> | --id <n> one-time on-chain name/ID — effectively immutable
tx send --to <addr|contact> --amount <n> TRX; add --token SYM | --contract Txx | --asset-id N for tokens
[--dry-run|--sign-only|--build-only] estimate only / sign / build unsigned, no broadcast
tx sign --transaction <json> sign JSON built elsewhere
tx sign --file <hex> [--check] [--out] append a signature to a multi-sig artifact (--check verifies online)
tx approvals --file <hex> who signed, weight so far, missing weight, expiry
account activate --address <T..> activate a TRON account without transferring funds
account set --name|--id <v> set a one-time TRON name or id
tx send --to <addr|contact> --amount <n> native coin; add --token SYM | --contract <addr> | --asset-id N (TRON) for tokens
[--dry-run|--sign-only|--build-only] estimate / sign to hex / build unsigned hex; never broadcast in these modes
[--permission-id N] [--expiration ms] TRON permission group / longer signing window
[--gas-limit N] [--max-fee gwei] [--priority-fee gwei] [--nonce N] EVM fee controls
tx sign --hex|--file sign an artifact (TRON co-signing; one EVM signature)
tx sign --transaction <json> sign a TRON JSON transaction
tx approvals --hex|--file TRON signing weight, missing weight, and expiry
tx multisig [--create|--sign <id>|--watch] collect signatures via the TronLink service
tx broadcast --tx-stdin|--hex|--file broadcast a presigned tx; --dry-run validates only
tx broadcast --hex|--file|--transaction|--tx-stdin presigned hex for either family; JSON is TRON-only
tx status --txid <id> state: confirmed|failed|pending|not_found
tx info --txid <id> full detail + receipt
permission show owner/witness/active groups, thresholds, decoded ops
permission update --file <json> replace the WHOLE permission structure (lockout risk)
gasfree info|transfer|trace send tokens with no TRX; fee paid in the token
permission show|update inspect / replace TRON permissions (lockout risk)
gasfree info|transfer|trace TRON token transfers with fees paid in the token
stake freeze|unfreeze|withdraw|cancel-unfreeze|delegate|undelegate resource staking
token / contract / message / block address book, smart contracts, signing, blocks
proposal list|show|create|approve|delete TRON governance proposals
witness create|update|set-brokerage TRON SR candidacy
asset issue|update|participate|unfreeze|info|list TRC10 lifecycle
exchange create|inject|withdraw|trade|show|list TRON protocol Bancor pairs
contract call|send|deploy read / write / deploy contracts on both families
contract clear-abi|set-origin-energy-limit|set-user-resource-percent TRON deployer controls
contract create2 compute a TRON CREATE2 address locally
token / message / typed-data / block address book, signing, and chain queries
contact add|list|remove recipient names usable as --to <name>
encoding convert <value> base58 / 41-hex / 0x-EVM / hex / base64 conversions
config / networks local config, known networks
config / networks local config, known networks and aliases
```

Multi-sig: build unsigned (`--build-only`) → each signer `tx sign --file … --out` → check with
Expand All @@ -57,24 +71,37 @@ Details for any command: `wallet-cli <command> --help`.
## Transaction safety (mandatory)

- `tx send` returns at **submission** (`data.stage: "submitted"`, `data.txId`) — that is NOT confirmation.
- Either add `--wait` (blocks until confirmed/failed, cap `--wait-timeout`), or poll `tx status` until `data.state` is `confirmed`; abort on `failed`; `pending`/`not_found` mean keep polling within your own deadline.
- Before any mainnet send: confirm with the user; consider `--dry-run` first (builds + estimates, no signature, no broadcast).
- Either add `--wait` (blocks until confirmed/failed, capped by `--wait-timeout`), or poll `tx status` until `data.state` is `confirmed`; abort on `failed`; keep polling `pending`/`not_found` within a deadline.
- `confirmed` means included in a block with an execution result or receipt; it does not mean finalized. Use a TRON SolidityNode view or an EVM finalized-block check when finality matters.
- A deadline ending in `pending` or `not_found` is an unknown outcome, not failure. Reconcile it externally and never auto-resend.
- `timeout` or `rpc_error` on a submit path can occur after the node accepted the transaction. Retry read-only calls as appropriate, but reconcile a broadcast by txid (or sender/nonce on EVM) before creating another transaction.
- Chain-assigned ids such as `proposalId`, `assetId`, and `exchangeId` are absent at submission; use `--wait` or query later before consuming them.
- `exchange trade` permits an omitted price floor by submitting `expected = 1` with a warning. An agent must pass `--min-received`, `--raw-min-received`, or `--slippage` unless the user explicitly accepts an effectively unprotected trade.
- Before any mainnet send, confirm with the user and consider `--dry-run` first (builds + estimates, no signature, no broadcast).

## Dangerous commands — require explicit user confirmation

`tx send` / `tx broadcast` / `contract send|deploy` / `gasfree transfer` on `tron:mainnet` (moves funds) · `delete` (removes accounts; HD delete cascades from the seed root) · `backup` (writes secret material to disk) · `address generate --print-secret` (writes a private key to stdout).
`tx send` / `tx broadcast` / `contract send|deploy` / `gasfree transfer` / `exchange trade|create|inject|withdraw` / `asset participate` on a mainnet (moves funds or burns fees) · `delete` (removes accounts; HD delete cascades from the seed root) · `backup` (writes secret material to disk) · `address generate --print-secret` (writes a private key to stdout).

**`permission update` is the most dangerous command in the CLI** — it replaces the account's entire permission structure, and a structure whose owner group excludes your keys locks the account permanently, with no recovery. Never run it without `--dry-run` first and explicit user confirmation of the rendered structure. Heed `owner_lockout` / `owner_lockout_partial` warnings.

`account activate` and `account set` are one-shot: an account activates once, and the on-chain name and ID can each be set once.

Irreversible TRON operations also require confirmation: `witness create` burns the registration fee; `asset issue` burns the issuance fee and an account can issue only one TRC10; `exchange create` permanently binds liquidity management to the creator; `contract clear-abi` cannot restore the removed ABI.

## Error handling

| exit | error.code | action |
|---|---|---|
| 2 | `usage_error`, `invalid_value` | fix flags; re-read `--help` |
| 1 | `timeout` | retry with higher `--timeout`; check network/proxy |
| 1 | `rpc_error` | node rejected: inspect message for context, verify txid/address/funds |
| 1 | `timeout` | retry read-only calls as appropriate; reconcile submit paths before retrying |
| 1 | `rpc_error` | inspect context; reconcile submit paths because the node may have accepted the transaction |
| 2 | `family_mismatch` | switch to a network family supported by the command/account/recipient |
| 2 | `invalid_option` | remove an invalid combination or a flag scoped to the other family |
| 1 | `chain_id_mismatch`, `nonce_too_low` | EVM artifact targets another chain or its nonce is spent; rebuild it |
| 2 | `migration_required` | re-run with an available password source, then re-issue the original command |
| 1 | `internal_error` | do not retry blindly; report |

`wallet-cli --json-schema | jq '.errorCodes'` is the maintained discovery index. Its values are descriptions, not retry instructions. For an unknown code, preserve the exit-code classification.

Full contract: `docs/machine-interface.md` in the wallet-cli repository.