Conversation
Add a non-interactive standard CLI framework (--output json, --private-key, --mnemonic flags) alongside the existing interactive REPL. Includes a bash harness that verifies all 120+ commands across help, text, JSON, on-chain transactions, REPL parity, and wallet management (321 tests, 315 pass, 0 fail). Key changes: - New cli/ package: StandardCliRunner, OutputFormatter, CommandRegistry, and per-domain command files (Query, Transaction, Staking, etc.) - JSON mode suppresses stray System.out/err from WalletApi layer so only structured OutputFormatter output reaches stdout - Remove debug print from AbiUtil.parseMethod() that contaminated stdout - Harness scripts (harness/) for automated three-way parity verification - Updated .gitignore for runtime artifacts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ments Rename harness/ → qa/ across shell scripts, Java classes, docs, and build config. Fix vote-witness QA test that extracted keystore address instead of witness address by filtering "keystore" lines from list-witnesses output. Add lock/unlock commands, improve GlobalOptions parsing, and update CLAUDE.md baseline. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d auth options Move JSON stream suppression earlier in StandardCliRunner to cover network init and authentication (not just command execution), remove --private-key and --mnemonic global options, and update QA/plan docs to reflect current test baseline (321 tests, 314 passed, 1 failed, 6 skipped). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Simplify protobuf formatting to use Utils.formatMessageString for both modes, suppress info messages in JSON mode, and update spec/plan docs to clarify the strict JSON-only contract. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add text+JSON parity, JSON field validation, round-trip verification (set then get-active-wallet), and error case tests (no args, both args, invalid address) for wallet management commands.
Replace login/logout/backup/export commands with list-wallet, set-active-wallet, and get-active-wallet for multi-wallet support. Implement transfer-usdt with automatic energy estimation. Update CLAUDE.md docs and add QA tests for new transaction commands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skip unit tests and QA verification when no code files (src/, build.gradle, *.java) have changed. Consolidates two separate hook blocks into one.
… to JSON errors WalletApi.listWallets filtered to skip the .active-wallet config file, which was incorrectly treated as a wallet keystore. OutputFormatter.error() and usageError() now include "success": false in JSON output for consistent envelope format.
…defaults
- OutputFormatter.success() now wraps jsonData in {"success":true,"data":{...}}
envelope for consistent API response format
- OutputFormatter.protobuf() outputs single-line valid JSON via
JsonFormat.printToString in JSON mode (was using formatJson which
introduced illegal newlines inside string values)
- deploy-contract defaults token-id to empty string (matching REPL behavior)
and origin-energy-limit to 1 (TRON requires > 0)
- add --force support for reset-wallet and clear-wallet-keystore - de-interactivize standard CLI transaction signing with permission-id support - align wallet command JSON schema with success/data envelope - add standard CLI change-password command and QA coverage - improve QA command counting, skip reasons, and stale result cleanup - add --case support to qa/run.sh for targeted reruns - strengthen transaction QA side-effect checks - make send-coin JSON return txid and verify send-coin-balance via tx receipt fallback - update QA reports and fix report documentation
Wipe temporary secret buffers in the standard CLI import-wallet path after keystore creation. - add try/finally around private-key import flow - clear private key bytes with Arrays.fill(priKey, (byte) 0) - clear derived password bytes after use for consistency with existing secret handling
- add command-level auto-auth policy for standard CLI - keep list-wallet usable when active wallet config is malformed - retire stale QARunner baseline/verify modes - remove unused QA helper code - fix remaining parser/config review follow-ups
- route trigger-constant-contract through OutputFormatter so --output json always emits an envelope - move constant-call normalization into WalletApi to avoid duplicated logic in ContractCommands - add typed command errors for trigger-constant-contract, gas-free-info, and gas-free-trace - fix gas-free-info / gas-free-trace standard CLI error envelopes - return auth_required instead of false success for tronlink-multi-sign auth failures - extend QA coverage for trigger-constant-contract json parity - fix gas-free-trace QA case to use --id in shell and batch runner - strengthen semantic parity checks for text/json validation
- route trigger-constant-contract through OutputFormatter and return stable JSON envelopes - move constant-call normalization into shared WalletApi/WalletApiWrapper logic - add typed command-error handling for trigger-constant-contract, gas-free-info, and gas-free-trace - fix tronlink-multi-sign auth failures to return an error envelope instead of false success - scope MASTER_PASSWORD fallback to standard CLI so REPL password prompts keep legacy behavior - harden transfer-usdt fee-limit calculation with exact arithmetic and overflow protection in both CLI and REPL - clear wallet command sensitive buffers with try/finally cleanup - tighten BOOLEAN option parsing to avoid implicit flag/value ambiguity - retire stale Java QA verification paths and fix QA semantic-parity / query-runner regressions - extend tests and QA coverage for JSON parity and command error handling
Text-mode --help was bypassing OutputFormatter with a direct realOut.println(), while JSON mode correctly used formatter.help(). Unify both modes through the same formatter path for consistency. No observable behavior change — text mode still prints plain help text, JSON mode still emits the success envelope. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Contract 5 says command-specific machine-readable payload belongs under
data. Most transaction commands only returned {message: '...'} — now
commands that go through the standard sign-and-broadcast path include
txid via WalletApi.getLastBroadcastTxId(), and gas-free-transfer
includes gas_free_id from the GasFree API response. Commands where
txid is not reliably available (broadcast-transaction, estimate-energy)
are unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
trigger-contract goes through the standard sign-and-broadcast path (callContract with isConstant=false) so LAST_BROADCAST_TX_ID is set. Include it in JSON data for consistency with other broadcast commands. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- tighten global and command option parsing with explicit usage errors, inline --key=value support, repeat/conflict detection, and stricter help handling - introduce command-level auth policies so no-auth and conditional-auth commands skip auto-login correctly while auth-required commands fail explicitly - normalize standard CLI output semantics, including JSON-friendly main entry errors, structured estimate-energy results, and clearer unsupported interactive command handling - harden wallet/config handling and gas-free error mapping for standard CLI execution - validate vote-witness input earlier and improve command result consistency - replace legacy QA flow with manifest-driven standard CLI contract verification, parallel task execution, and coverage/audit reporting - add standard CLI contract spec, audit notes, and compliance plan documentation - expand unit test coverage for parser, auth, formatter, runner, and active wallet edge cases
Add qa_assert_text_stderr_clean to verify stderr is empty on success for text mode, closing the gap where JSON mode already enforced this via qa_assert_json_stderr_clean. Applied to success, stateful_replay_execution, help_text, and help_dual branches. Intentionally not added to usage/execution since those expect error messages in stderr.
Master merge into develop
resolveSigningWalletFile()/resolveSigningPassword() were shared between REPL and Standard CLI signing paths, which bypassed selectWalletFileE() and relaxed the 3-condition password reuse guard (lockAccount + isUnifiedExist + address match) to just isUnifiedExist(). This prevented REPL multi-wallet users from temporarily switching signing wallets. Restored inline selectWalletFileE() + original password logic in: - signTransaction(tx) - signTransaction(tx, multi) - addTransactionSign(tx) resolve* helpers are now only used by signTransactionForCli().
- Remove deprecated exchange-transaction command from standard CLI and user manual (REPL path unchanged). - Split GasFreeApi.getMessage into REPL-facing (prints + swallows) and getMessageOrThrow (standard-CLI-facing, throws IllegalStateException); WalletApiWrapper maps the exception to a query_failed CommandErrorException so JSON output stays structured. - gas-free-info now always requires an authenticated wallet, even when --address targets another account — the handler's triggerContract call requires a non-null wallet. Updated StandardCliRunnerTest accordingly. - Restore non-paginated WalletApi.listProposals / listExchanges for list-proposals and list-exchanges standard CLI commands. - Expand qa/ harness: +660 lines in qa/lib/cli.sh, manifest/case_resolver updates, and task_runner tweaks for broader parity coverage.
W1: reset-wallet dry-run now includes ledger_files and config_files
to match actual deletion scope
W2: --grpc-endpoint validation failure no longer destroys the
existing ApiClient; uses try-finally guard to close the new
client and preserve the original connection
W3: corrupt keystore files no longer crash list-wallet,
set-active-wallet, or get-active-wallet; list-wallet marks
them with an error field, findWalletFileByAddress/Name skips
and continues scanning
W4: clear-wallet-keystore no longer wipes the entire Ledger/
directory; blanket cleanup is now exclusive to reset-wallet
W5: replaced logger.warn with System.err.println in CLI code
paths to prevent logback STDOUT appender from polluting
--output json responses
- Unify applyNetwork() + applyGrpcEndpointOverride() into
resolveConnection()
that validates before mutating global state
- Use correct solidityNode for known networks instead of reusing
grpc-endpoint
- Zero sensitive key material after copy in deploy/trigger paths
- Add permissionId range validation (0-2)
- Migrate always-true emitBooleanResult() to emitSuccess()
- Thread masterPasswordProvider through CommandContext
- Strengthen mismatch test with assertSame
…A exit code
- QA: qa/run.sh now exits non-zero when tests fail (was always exit 0)
- WalletApi: clearContractAbiForCli/updateBrokerageForCli record gRPC error
before returning false, matching all other *ForCli methods
- WalletApi: voteWitnessForCli catches NumberFormatException on vote count
- WalletApi: remove unused voteScore param from createAssetIssueForCli chain
- gas-free-info: add getGasFreeInfoDataForCli using static constant call,
allowing --address queries without wallet auth (REPL method unchanged)
- Update QA manifest and unit test for gas-free-info auth policy change
- Change deploy-contract --origin-energy-limit default from 1 to 10,000,000
to match realistic contract energy allowance
- Return success with empty list instead of error when no wallets exist,
as an empty wallet directory is a valid state
- Add unit test for list-wallet empty directory case
…sistency, and fail-fast on missing chain params
- M8: Add missing space before version string in global help output
- M9: Add requirePositive validation for exchange-inject, exchange-withdraw,
and market-sell-asset quantity parameters
- M10: Add 0-100 range validation for update-brokerage
- M12: Unify send-coin non-multi output to use emitSuccess pattern while
preserving to/amount/txid fields
- M13: Replace hardcoded 420 SUN energy price fallback with explicit abort
when getEnergyFee chain parameter is absent
…ling, and code hygiene - #2: Preserve specific Ledger signing error messages instead of overwriting with generic Transaction signing failed - #3: Add requirePositive validation for exchange-create balances - #4: Add requireNonNegative validation for update-asset limits - #5: Use validated address bytes via encode58Check instead of discarding getAddress() return values in delegated-resource queries - #7: Validate witness addresses in vote-witness before submission - #8: Validate mnemonic word count (12/24) in register-wallet using existing MnemonicUtils utility - #9: Specify StandardCharsets.UTF_8 in all String.getBytes() calls (47 occurrences across 12 files) - #13: Make resolveActiveWalletFileStrict throw IOException instead of returning null, matching its Strict contract - #17: Add requireNonNegative check for permission-id on 6 commands
…late QA classes - Move QA classes to src/test and build separate qaJar to keep production JAR clean - Add sanitizePermissionJson() to strip @type keys (fastjson deserialization defense) - Block absolute/traversal paths in wallet override resolution - Validate address format for gasfree-transfer --to - Add missing bounds checks: origin-energy-limit, value, token-value, duration, lock-period, proposal id, resource type - Simplify --help detection to work anywhere in args - Return descriptive message instead of null from extractTransactionReturnMessage - Make ActiveWalletConfig.clear() return boolean instead of printing to stderr - Replace JSON.parse() + unsafe cast with JSON.parseObject() in GasFree handlers
…ode stdout pollution
- H1+H11: AbiUtil.encodeInput throws IllegalArgumentException instead of
printStackTrace + return null, eliminating NPE chain in parseMethod callers
- H2: wrap all 4 parseMethod calls in ContractCommands with try-catch,
classifying malformed input as usage_error (exit 2)
- H3: fix reference equality tokenId != → in
WalletApi.triggerCallContract
- H4: validate tokenId as numeric at ContractCommands handler level (3 sites)
with defense-in-depth catch in WalletApi
- H5: add WalletApi.broadcastTransactionForCli returning error string instead
of printing to stdout; update broadcast-transaction command to use it
- H6+H12: remove System.out.println from addressValid() and
decodeFromBase58Check() — callers already handle null/false returns
- H7: gas-free-info now uses getAddress() + encode58Check() for Base58
validation, consistent with all other address-accepting commands
- broadcast-transaction: InvalidProtocolBufferException classified as
usage_error (malformed input) not execution_error
…tion, and error handling
- M6: deploy-contract constructor encoding uses encodeInput instead of
parseMethod to avoid prepending 4-byte selector (StandardCLI-only bug)
- H6: getTransactionCountByBlockNum now throws on gRPC failure instead
of silently returning 0 with success status
- H9: participate-asset-issue adds requireNonBlank for asset parameter
- H10: TODO comments for update-account/set-account-id/update-asset
field length limits (node enforces, not yet validated client-side)
- H11: TODO for deploy-contract to include contract_address in JSON
- H13: null-guard e.getMessage() in 5 catch blocks (fallback to toString)
- H14: exchange-inject/withdraw add requireNonBlank for token-id
- C9: comment clarifying definite-assignment safety on uninitialized Triple
…handling
- JsonFormatUtil: track inString/escaped state so structural characters
inside JSON string values are no longer misinterpreted as structural
tokens; replace custom formatter in Utils with Gson pretty-printer
- ActiveWalletConfig: validate Base58Check address in setActiveAddress();
enforce .json extension when resolving wallet file by selection
- GlobalOptions: use Locale.ROOT for toLowerCase() to avoid locale traps
- CommandSupport: add requirePermissionId() (0–2 range), requireMaxBytes(),
and requireByteRange() helpers
- Commands: replace requireNonNegative with requirePermissionId for
permission-id across TransactionCommands, StakingCommands,
ContractCommands, and WitnessCommands
- TransactionCommands: resolve three open TODOs — enforce name ≤ 200 B,
account-id 8–32 B, and contract description/url byte limits; fix
transfer-usdt energy estimation to throw CommandErrorException instead
of returning a false flag
- QueryCommands: validate block number is non-negative before querying
- WalletApiWrapper: ensure fee-limit buffer is at least 1 via Math.max
- MiscCommands: remove duplicate help alias
- Tests: use valid Base58Check addresses; isolate wallet-dir tests with
TemporaryFolder; update TransactionCommandsTest for CommandErrorException
…validation, and UX
- Remove auth requirement for constant/view calls (trigger-constant-contract,
estimate-energy): these are read-only gRPC calls that don't need signing,
so null owner is now passed through to the node directly
- Standardize error code missing_env → execution_error in register-wallet
- Detect duplicate witness addresses in vote-witness instead of silent overwrite
- Add null guard to CommandDefinition.Builder.authPolicy()
- Remove unused OptionDef.Type.ADDRESS enum value
- Make help command delegate to registry.formatGlobalHelp() and support
--command for per-command help, matching --help behavior
- Improve text-mode output: structured data now renders as aligned Metadata table
- Add design-intent comments for --help scanning and broad exception catch
…ant calls auth-free
- deploy-contract: WalletApi.deployContractForCli now returns the base58
contract address (derived via generateContractAddress) instead of boolean;
ContractCommands includes it as contract_address in the JSON response data
- trigger-constant-contract / estimate-energy: remove the conditional auth
requirement — neither command broadcasts a transaction, so no wallet is
needed; reflect this in the user manual and QA expectations
- transfer-usdt: drop redundant boolean result variable; callContractForCli
throws on failure, so emitSuccess is always the correct path
- docs: correct origin-energy-limit default (1 → 10,000,000) and tighten the
--grpc-endpoint validation comment to explain why we delegate to gRPC
…OADCAST_TX_ID ThreadLocal
Replace the thread-local side-channel (LAST_BROADCAST_TX_ID) with direct
return values — all *ForCli methods now return the txid String (null on
failure) instead of void/boolean. deployContractForCli returns
Pair<contractAddress, txid>; callContractForCli returns
Triple<String, Long, Long> with txid as the left element.
Other fixes in this commit:
- Use lenient JSON parsing in Utils.formatMessageString to tolerate
malformed on-chain data (e.g. asset #436 description on Nile)
- Add QA manifest entry for the help command (noauth-help type)
- Fix test compilation: update overrides to match new signatures
Keep wallet address validation on the standard TRON prefix instead of switching by net.typ
… docs GlobalOptions parses the full argv: after the command token, recognized globals (--network, --output, --wallet, --grpc-endpoint, etc.) are applied and removed from command args; --help/-h after the command are left for the command parser; unknown trailing flags pass through unchanged. Malformed or invalid values for known globals still fail at the global layer (validated globally). Client.requestsJsonOutput no longer stops scanning at the first non-flag so post-command --output json is respected. OutputFormatter adds queryResult(): text mode renders small flat primitive maps as Metadata, larger/nested/array payloads as indented Result: JSON; success/formatMessage-only paths wrap plain strings for TEXT JSON payloads. QueryCommands and ContractCommands use queryResult for RPC-style successes; several protobuf paths now emit not_found when the RPC returns null. Updates standard-cli-contract-spec and qa/contracts.tsv (post-command network succeeds; unknown post-command option stays command-local usage). Adds GlobalOptions, OutputFormatter, and ClientMain tests for the above.
Prevent text query results from dropping message-only or empty payloads, and make Gson-formatted result indentation platform-independent. Treat --version and --interactive as pre-command top-level modes only, with post-command usage now handled by command parsing; update the standard CLI contract, QA cases, and regression tests accordingly.
…bal-flag feat(cli): allow known globals after command; query output + contract…
fix(cli): use a single default address prefix
zerodevblock-cyber
approved these changes
Apr 29, 2026
matrix-agent116
approved these changes
Apr 29, 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.
Key Features
Standard CLI architecture — StandardCliRunner, CommandRegistry, CommandDefinition, and handlers under org.tron.walletcli.cli.commands.*; global vs command-local options (GlobalOptions, ParsedOptions), as documented in docs/standard-cli-contract-spec.md.
Structured output — --output text|json; unified OutputFormatter; JSON envelope for success/errors; txid (and related data) enriched in JSON for on-chain transaction commands.
Environment & auth — --network, --grpc-endpoint, etc.; non-interactive flows via MASTER_PASSWORD + keystore; StandardCliRunner command-level auto-auth policies.
Wallet / address UX — Active wallet (set-active-wallet); list-wallet excludes .active-file entry; behavior when active-wallet config is invalid; unified default address prefix.
API alignment for scripting — ForCli-style paths: txid returned from methods (removing reliance on LAST_BROADCAST_TX_ID ThreadLocal); deploy-contract exposes contract address; constant contract calls without auth where intended; multiple audit-driven fixes (ABI, validation, JSON stdout pollution).
Security & consistency — Stronger input validation (e.g. hex, permission JSON); removal of unsafe Standard CLI wallet commands / unsafe address generation paths; sensitive buffer clearing on private-key import.
Testing & QA — qa/ shell parity (REPL vs standard CLI text/JSON); witness-related serial stateful test flow; expanded unit coverage (StandardCliRunner, routing, WalletApiWrapper, etc.).
REPL compatibility — Keep Standard CLI development independent of the legacy REPL, while remaining compatible with legacy REPL behavior.
Misc — gasFree info contract null-check; CI updates.