Release v4.9.7#944
Merged
Merged
Conversation
…ded-op51 fix(permission): exclude disabled ContractType 51 and surface unknown ops in UI
…op-labels test(permission): cover default active operation bitmap
Align resource-code handling across REPL and standard CLI with the node's actuator behavior, per the discussion in #939: - freeze/unfreeze accept 2 (TRON_POWER) only when the chain parameter getAllowNewResourceModel is enabled; delegate/undelegate reject 2 unconditionally (BANDWIDTH/ENERGY only). - Fail-open: when chain params can't be fetched (offline/timeout), skip the client-side pre-check and defer to the node's validate() at broadcast; text mode warns on stderr, JSON/quiet stays silent. - Only query chain params when resource == 2 (0/1 short-circuit, no RPC). REPL previously had no resource-code range check (permissive pass-through); standard CLI hardcoded 0/1. Reuses the existing getChainParameters() plumbing. Docs updated for consistency. Refs: #939
v1 freezeBalance/unfreezeBalance with a receiver is a delegated freeze, where TRON_POWER is not delegatable. Both the standard CLI and the REPL previously passed freezeContext=true unconditionally, so resource=2 could be let through to the node when getAllowNewResourceModel was on. Derive the context from the receiver instead (freezeContext = receiver == null); v2 self-stake and delegate/undelegate paths are unchanged. Add StakingResourceGuardTest covering the tri-state getAllowNewResourceModel behavior (enabled / disabled / unknown fail-open), unconditional TRON_POWER rejection for delegation, and the receiver-aware v1 semantics. Docs: fix stale "1 CPU" to "1 ENERGY" and drop TRON_POWER from the delegate-resource section (not delegatable). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…code fix(staking): network-aware, fail-open resource-code validation
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>
Message-sign's Ledger path never applied --timeout: MessageService.sign awaited signer.signMessage with no bound, so an unresponsive device or an un-tapped on-device prompt hung the CLI. Root cause was that device timeout was a per-caller responsibility (the tx pipeline opted in, the message service did not) while RPC calls are bounded inside their adapter. Make the Ledger adapter self-bounding like TronChain: route every device call through withTimeout(this.timeoutMs). Every caller now inherits the bound, and precheck's appConfig/getAddress are covered too. --timeout is already documented as 'per RPC/device call'; this brings the device family in line with that contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The deployed address is deterministic at build time (createSmartContract returns it), but nothing wired it to TxReceiptView.contractAddress, so the text "Address" row was always empty and JSON never carried it. Capture it in deploy()'s build callback and expose it (base58) across all stages. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Scrub URLs (to scheme://host), home-dir prefixes (to ~), and long hex runs (to [redacted]) from free-text error messages before they reach the user layer, via a new redactErrorMessage helper. Apply it to TRON RPC/broadcast/ constant-call errors. In account portfolio, stop leaking raw per-token and price-provider errors into the success payload — degrade to stable reasons (balanceUnavailable/priceUnavailable + reason) and render those instead of the raw text (audit I-06). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…root - derive now takes a strict seed id (wlt_…), rejecting labels/sub-account refs, and auto-names new accounts <wallet-name>-<index> - expose seedId on AccountDescriptor and in list/render output - deleting the HD root (index 0) cascades to the whole wallet; only non-root sub-accounts (N>0) are forgotten individually - clearer --wait/--dry-run/--sign-only conflict message - drop stale one-off scripts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ke/deploy in --help The tsup ESM bundle force-bundled @LedgerHQ (needed: their lib-es has extensionless imports Node's loader rejects), which dragged in axios's CJS require("util")/require("http") as esbuild's __require shim — throwing `Dynamic require of "util" is not supported` whenever the Speculos transport opened, so the dist binary couldn't drive the software Ledger. Externalize axios (like tronweb already is) so Node loads it natively at runtime; this removes the shim entirely — no createRequire banner needed. axios is declared in dependencies and kept pinned via overrides to ^1.18.1 (post-2026-03 supply-chain-safe). Verified via dist against Speculos: import ledger, message sign, tx send, stake, and the full 31-check ledger suite all pass with no banner. Also add a --help note to `stake cancel-unfreeze` and `contract deploy`: the Ledger TRON app firmware rejects CancelAllUnfreezeV2Contract / CreateSmartContract (APDU 0x6a80, even with blind-signing enabled). Verified both sign and broadcast fine from software accounts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A timed-out Ledger call (sign/getAddress/signMessage/appConfig) rejected
with ChainError("timeout") but never closed the transport: the pending
HID APDU kept the native handle open, pinning libuv so the process hung
after exit code was set — breaking the standard CLI's deterministic-exit
contract. The happy path (tap/reject) was unaffected.
Fold withTrx into #bound so withTimeout's onTimeout can close the handle.
Closing the HID transport rejects the pending APDU, so the run unwinds and
the native handle is released. close() may run twice (onTimeout + finally);
both swallow errors. Covers all four device ops, including those not routed
through the pipeline's abort signal.
Other no-op-onTimeout withTimeout sites (TronRpcClient, history-reader,
coingecko) are unaffected: their fetch/tronweb calls self-cancel via
AbortSignal.timeout or tronweb's HTTP timeout, and HTTP sockets don't pin
libuv the way node-hid's native handle does.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(ts): implement TypeScript wallet CLI
Will-Guan
previously approved these changes
Jul 3, 2026
zerodevblock-cyber
approved these changes
Jul 3, 2026
matrix-agent116
approved these changes
Jul 3, 2026
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.
New Features
block, network, and configuration operations across Mainnet, Nile, and Shasta. The implementation also provides stable
wallet-cli.result.v1JSON output, deterministic exit codes, transaction dry-runs and confirmation waiting, securesecret input, comprehensive automated test coverage, and architecture documentation. (#941)
Bug Fixes
Fix staking resource-code validation to follow the node's network configuration. Freeze and unfreeze operations accept
TRON_POWERonly when the new resource model is enabled, while delegate and undelegate operations support onlyBANDWIDTHandENERGY. When chain parameters cannot be retrieved, wallet-cli now defers validation to the node instead of rejecting the transaction locally. (#939, [fix(staking): network-aware, fail-open resource-code validation #943](fix(staking): network-aware, fail-open resource-code validation #943))
Fix the interactive account permission update flow by excluding the disabled
ShieldedTransferContractoperation from the default active-permission bitmap. Unsupported, disabled, and unknown operations are now visible andremovable in the permission UI, with regression coverage for the corrected bitmap. (#934, #935, [test(permission): cover default active operation bitmap #936](https://github.com/
test(permission): cover default active operation bitmap #936))