feat(ts): implement TypeScript wallet CLI#941
Open
gummy789j wants to merge 13 commits into
Open
Conversation
Zeuscaponee
approved these changes
Jun 30, 2026
Add nativeSymbol/nativeDecimals to FamilyMeta and route account/token balance rendering through a shared humanBalance helper. Refresh help text, golden tests, and related command wiring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A hostile wallet label or remote token/RPC metadata value could carry ANSI/OSC escape bytes that spoof or corrupt terminal display. Sanitize C0/C1/DEL control bytes at the text-output boundary (success/error/event frames); JSON mode stays byte-exact. (CLI-OUT-001) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…them TronWeb's triggerSmartContract defaults to having the node assemble the unsigned tx, letting a malicious/compromised RPC return a different contract/recipient/amount than requested. Switch TRC20 transfer and generic contract calls to txLocal so the calldata is ABI-encoded client-side; every build path now assembles raw_data locally (the node only supplies the ref block and broadcasts). Add a fail-closed tripwire (assertBuiltTx) that rejects a tx whose contract count/type drifts from the request, so a future re-route back through the node fails closed instead of signing silently. (TRON-RPC-TX-SUBST-001/002/003) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump tronweb to ^6.4.0 and add overrides for axios (^1.18.1), ws (^8.18.3), and esbuild (^0.28.1) to pull patched versions through the tronweb, ethers, and Speculos transport paths. npm audit now reports 0 vulnerabilities (was 6: 4 high, 1 moderate, 1 low). (DEP-001) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address boundary-audit items with client-side guards, best-effort
degradation, and consistent error classification:
- big-integer precision: chain base-unit amounts (feeLimit, callValue,
lockPeriod, block number) carry as strings and reject values past
MAX_SAFE_INTEGER at the #safeNumber chokepoint, before TronWeb.
- contract deploy: drop the dead --constructor-sig flag; --params are raw
positional constructor values (types come from the ABI). call/send keep
{type,value} and now validate that shape at the command layer.
- tx info: transaction is the source of truth; a missing/failed info
degrades instead of sinking the command (mirrors getContractMetadata).
- buildNativeTransfer: wrap the sendTrx build so node failures surface as
rpc_error, not a redacted internal_error, matching sibling builders.
- --wait: a confirmation that never lands now records a meta.warnings
entry instead of silently returning submitted.
- amounts: send/stake amounts must be positive (reject zero); contract
call-value stays non-negative.
- account portfolio: per-token best-effort — one unreadable token
degrades to a balanceError row without failing the whole portfolio.
Adds golden error-contract cases, a hermetic tron-confirmation unit test,
a per-token portfolio test, and a Nile contract-deploy test (live tiers
gated behind RUN_LIVE / RUN_LIVE_BROADCAST).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously --timeout only applied to write commands via TxPipeline; read
commands used a hardcoded 30s (TRON getAccount) or no bound at all (account
history, CoinGecko price). Hung reads could hang the CLI indefinitely.
Timeout work:
- Extract withTimeout into src/domain/async and reuse it across layers.
- TronRpcClient: take timeoutMs; wrap every #wrap'd read/build and broadcast,
and drive the getAccount fetch AbortSignal off timeoutMs (was 30s). Timeout
surfaces as ChainError("timeout"), not a remapped rpc_error.
- TronGridHistoryReader + CoinGeckoPriceProvider: take timeoutMs and pass an
AbortSignal.timeout to fetch (history had no timeout before).
- Thread the effective timeout (globals.timeoutMs ?? config.timeoutMs) through
composition -> gateway registry/factory, family deps, and price provider.
- TxPipeline: drop the now-redundant per-step withTimeout on build/estimate/
broadcast/software-sign (adapters self-bound); keep only the device-sign
bound + abort, which no RPC timeout covers.
- Reject a non-positive --timeout at the boundary: per-flag `min` (timeout=1)
in the globals coercion, and config set timeoutMs must be positive. So 0ms
(instant-abort) never reaches withTimeout/AbortSignal.
Tests: new adapter timeout tests (tron, history, coingecko), a pipeline
device-sign timeout+abort pin test, and config-service validation tests.
Also folds in in-progress parallel work present in the tree: price-provider
apiKey removal + per-contract CoinGecko pricing, keystore changes, wallet
import-watch promptHints + shell default-label text, wallet-service no longer
auto-activating a newly derived account, and doc updates.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/not_found)
getTransactionInfo alone can't tell "unconfirmed" from "never existed" — it
returns {} for both. Pair it with getTransactionById (parallel) so a broadcast
tx reads as pending and an unknown hash reads as not_found instead of an
ambiguous "unknown". The renderer switches on the new `state`; `confirmed`/
`failed` are kept for back-compat.
Also resolve a unique chain leaf directly in help/--json-schema so a
single-family multi-segment path (e.g. `tx info`) emits its own input schema
rather than the family catalog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- generalize command `positional` → `positionals[]`; config now binds `[key] [value]` through the shared path, dropping the shell special-case - rename leaf help sections Flags/Global flags → Options/Global options and usage placeholder [flags] → [options], aligning with root's Global Options - fold each command's stdin channel (--*-stdin) into Options instead of a standalone Input options section; machine --json-schema catalog keeps inputFlags as a distinct key (typed args vs stdin channel stay separate) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The active chain is already known from the request — the renderer reads
`ctx.net.family` and the envelope carries `chain.family` — so duplicating
`family` inside each command's `data` was redundant. Remove it from the
TxStatusView/TxReceiptView/TxInfoView contracts (and the tx/stake/contract
service returns); the text renderer now dispatches on `ctx.net.family` via a
`renderFamily(ctx)` helper, keeping the FAMILY_RENDER table intact.
Also stop leaking the raw TronWeb broadcast response: the adapter's
`broadcast()` returned `{ txId, raw: res }`, and `raw` rode the BroadcastResult
index signature all the way into the output JSON. Return `{ txId }` only, so the
submitted/confirmed receipt exposes named fields, not the SDK's raw shape.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduce a TypeScript implementation of the TRON wallet CLI with a hexagonal architecture, secure local wallet management, interactive TTY workflows, and software/Ledger signing.
Highlights
wallet-cli.result.v1JSON envelopes and deterministic exit codes.Architecture
The implementation follows a hexagonal architecture:
domain: wallet and TRON value objects and domain rulesapplication: use cases, services, contracts, and portsadapters/inbound: CLI parsing, prompting, rendering, and outputadapters/outbound: TRON RPC, keystore, Ledger, persistence, pricing, and configurationbootstrap: dependency composition and family registrationTRON-specific behavior is registered through family definitions without introducing a universal-chain abstraction.
CLI Guarantees
wallet-cli.result.v1JSON output0,1, and2exit codes