Skip to content

Feat/ts v4.11.0 - #963

Open
gummy789j wants to merge 16 commits into
release_v4.11.0from
feat/ts-v4.11.0
Open

Feat/ts v4.11.0#963
gummy789j wants to merge 16 commits into
release_v4.11.0from
feat/ts-v4.11.0

Conversation

@gummy789j

Copy link
Copy Markdown
Collaborator

Summary

Brings the TypeScript CLI to the 4.11.0 release line: TRON multi-signature support end to end, gas-free token transfers, and local utilities (contacts, keypair generation, encoding conversion, account lifecycle, receive QR).

Command surface grows 52 → 66, and all 66 now have a reference page.

Scope is ts/ only, apart from a one-line version constant in Java.

Base: release_v4.11.0 · 15 commits · 159 files, +12,991 / −216

Multi-signature

The CLI can now participate in a multi-sig account for the whole lifecycle: configure permissions, collect signatures from several keys, broadcast once the threshold is met.

Command Purpose
permission show / update Inspect groups with decoded operation bitmaps; replace the full structure
tx sign --hex/--file Append a signature, preserving prior ones (--check verifies online)
tx approvals Who signed, accumulated weight, missing weight, expiry
tx multisig Collect signatures via the TronLink service (--create / --sign / --watch)
tx broadcast --hex/--file Validates threshold and expiry before submitting

Signatures travel either offline as a hex artifact passed between signers, or through the TronLink service as a shared queue.

Safety properties worth review:

  • tx sign on an artifact is offline by default; permission membership is only claimed with --check.
  • tx broadcast refuses a transaction below threshold (not_authorized) or expired (tx_expired) — it
  • permission update rejects rather than normalizes (invalid_permission), emits lockout warnings, and re-reads after confirmation. A bitmap cannot silently widen a permission: unnamed set bits must be declared in unknownOperationIds.
  • TronLink responses are untrusted — the tx hash is re-derived and contract type, owner, weights, and signature progress cross-checked (provider_error). --watch receives a count only, never content.

GasFree

gasfree info / transfer / trace — move USDT and other supported tokens with no TRX, by signing a TIP-712 PermitTransfer and letting the provider broadcast for a fee in the transferred token.
Balance and fees are checked up front; the signed digest is recomputed and the recovered signer verified against the selected account (signing_rejected); provider metadata is validated (gasfree_integrity). --dry-run prices without unlocking, --wait reports settled rather than estimated amounts. Mainnet and Nile only; Shasta returns

Local utilities

Command Purpose
contact add / list / remove Recipient names usable as tx send --to and `gasfree transfer --to
address generate Keypair generated locally, not added to the wallet
encoding convert base58 ⇄ 41… hex ⇄ 0x EVM, and hex / Base64 / Base58Check
account activate / set Activate an account; set the one-time on-chain name or ID
current --qr Terminal receive QR for the selected address
  • The address book is re-validated on every read (regular file, owner, mode 0600); names resembling an to` can't be reinterpreted.
  • address generate writes the key to an exclusively created 0600 file and never overwrites; stdout requires explicit --print-secret.
  • encoding convert refuses any 32-byte input — indistinguishable from a private key, and argv is visibl
  • current --qr encodes exactly the address and never draws a partial QR (a truncated code could scan as a different address).

Architecture & config

New ports keep hexagonal boundaries intact: contact-repository, gasfree-provider, keypair-writer, qr-encoder, tronlink-collaboration. New pure-domain modules: contact, encoding, gasfree, permission.

Five new optional config keys — tronlinkSecretId / SecretKey / Channel and gasfreeApiKey / ApiSecret. Both secrets are masked on read and write (********, including the echoed input), so a config write is safe to log;
unset keys are omitted entirely.

Documentation

19 new pages, 9 updated, following the existing layout. New groups permission/, gasfree/, contact/,new pages tx/approvals, tx/multisig, account/activate, account/set; updated tx/index (multi-siglifecycle), tx/broadcast, tx/send, current, config, README, and the agent SKILL.md.

Written against the command registry and domain rules with examples taken from real output. Verified mechanically: all 66 commands have a page, and every relative link and anchor resolves.

Version

0.2.04.11.0 for the ts package, aligning it with the Java release line. Java Utils.VERSION moves v4.10.0v4.11.0.

Verification

tsc --noEmit     clean
vitest run       98 files, 675 passed, 2 skipped
depcruise src    no violations (303 modules, 817 dependencies)

41 test files added or updated.

Behaviour changes

  • tx send --to accepts a contact name; resolution is surfaced in the receipt and data.toContact.
  • tx send auth is now conditional--build-only builds unsigned without unlocking.
  • tx broadcast validates before submitting, so some transactions now fail locally instead of on-chain.
  • current honours --account, so data.active is no longer always true.
  • Signing commands gain --build-only, --permission-id, --expiration.

Leon.Zhang and others added 15 commits July 27, 2026 18:06
Backfill reference pages for every command added since release_v4.11.0,
verified against the command registry rather than the drafted command
doc: all 66 registered commands now have a page.

New pages:
  permission/ (index, show, update)  multi-sig permission structure
  gasfree/    (index, info, transfer, trace)
  contact/    (index, add, list, remove)
  address/    (index, generate)
  encoding/   (index, convert)
  tx/approvals, tx/multisig
  account/activate, account/set

Updated: commands/index, account/index, tx/index (multi-sig lifecycle),
tx/broadcast (--hex/--file/--dry-run, threshold validation), tx/send
(contact names), current (--qr), config (TronLink/GasFree credentials and
secret masking), README, and the agent SKILL command map.

Examples and error codes were taken from actual command output and the
domain rules, so contact limits, the permission operation bitmaps, and the
config masking behaviour match the implementation.

Also included in this commit:
- bump version to 4.11.0 (ts package, runner, java Utils)
- name TRON contract types 3/20/32/51 and require unnamed operation bits
  to be declared via unknownOperationIds
- add USDD to mainnet and seed the Nile builtin token book

@zerodevblock-cyber zerodevblock-cyber left a comment

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.

Review summary

I validated the 15 selected findings against PR #963 at commit 1e068529da4bf2535618acb228fa5597da5c49de. Each finding was rechecked against the aggregate diff, its source-to-sink call path, existing guards, tests, and a safe local or in-memory reproduction where feasible. The detailed findings are attached as inline comments.

Findings

  • P1 (2)
    • CR-020: transaction preparation can change the deployment txID while the CLI keeps reporting the address derived from the pre-prepare txID.
    • CR-016: documented service-secret configuration passes the expanded credential through OS argv before output masking applies.
  • P2 (5)
    • CR-002: post-confirmation read failures overwrite an irreversible success result.
    • CR-003: a polling error after GasFree acceptance discards the trace ID and submitted receipt.
    • CR-007: wallet-cli.result.v1 changes warning elements from strings to objects without versioning.
    • CR-008: the transaction codec rejects protocol type 51 despite the published support surface.
    • CR-021: insecure-config bootstrap failures bypass the JSON envelope and UsageError exit code.
  • P3 (8)
    • CR-005, CR-009, CR-010, CR-012, CR-013, CR-014, CR-015, and CR-022 cover file-write atomicity, dependency direction, error classification, documentation drift, machine-error consistency, GasFree failure semantics, terminal bidi safety, and TronLink signer metadata validation.

The highest-priority fixes are to bind the reported deployment address to the final prepared transaction (CR-020) and move service secrets off argv (CR-016). The P2 items should also be resolved before release because they affect irreversible-operation recovery, machine compatibility, supported transaction types, and deterministic CLI error handling.

Validation checks completed:

  • the PR head and reviewed HEAD are identical;
  • all 15 inline anchors are added lines in the current aggregate PR diff;
  • the relevant targeted suite passed (6 files, 50 tests);
  • npm run typecheck passed;
  • no external service was called, no real credential was used, and no transaction was signed or broadcast.

account: scope.activeAccount,
broadcaster: gateway,
...transactionMode(input),
...tronTransactionHooks(gateway),

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.

[P1][CR-020] Recompute the deployment address after transaction preparation

tronTransactionHooks() runs after build(). A non-zero permission ID or a custom expiration changes raw_data and recomputes the txID, but contractAddress is still the value captured from the builder transaction. TronWeb derives the contract address from the owner and txID, so the CLI can report a different address from the contract that is actually deployed.

Please derive the address from the final prepared transaction, or have the pipeline return the final derived address. A regression test should cover non-zero permission IDs and custom expirations.

Configure a service credential — note that the value never appears in the output:

```bash
wallet-cli config gasfreeApiSecret "$SECRET" -o json

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.

[P1][CR-016] Do not pass service credentials through process arguments

This example expands $SECRET before the process starts, so the credential is present in OS argv even though the command masks its output. Process inspection, endpoint telemetry, CI tracing, or command logging can capture it; this also contradicts the machine-interface promise that secrets never travel via argv.

Please provide a dedicated stdin, hidden TTY, or file-descriptor secret input and update this example to use that channel.

estimate: async () => ({ ...fee, balanceSun }),
});
if (outcome.stage === "confirmed" && !outcome.failed) {
const activated = await gateway.getAccount(input.address);

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.

[P2][CR-002] Preserve the confirmed receipt when this post-check cannot be read

At this point the transaction has already returned confirmed. If this follow-up RPC times out, is rate-limited, or observes a lagging node, the exception escapes the use case and replaces the confirmed txid with a command failure. Callers can then retry an operation that already succeeded.

Please retain the confirmed/submitted outcome and report an observation warning for read failures. The account-set and permission-update post-checks have the same partial-success boundary and should follow the same rule.

const remaining = deadline - this.clock();
if (remaining <= 0) return undefined;
await this.sleep(Math.min(POLL_MS, remaining));
const current = await this.provider.trace(network, initial.id);

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.

[P2][CR-003] Preserve the accepted GasFree receipt when polling fails

submitTransfer() has already succeeded and the trace ID has been captured before this request runs. A transient timeout, 429, or provider 5xx escapes from here, causing the entire command to fail without returning that trace ID. Only normal deadline exhaustion currently preserves the submitted receipt.

Please retry retryable polling failures within the wait deadline, or return the existing accepted result with a warning so callers can reconcile the original submission instead of resubmitting it.

export interface Meta {
durationMs: number;
warnings: string[];
warnings: WarningItem[];

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.

[P2][CR-007] Keep the v1 warning element type stable

The envelope still declares wallet-cli.result.v1, while the published machine contract defines meta.warnings as string[]. Allowing WarningView here serializes {code, message} objects for permission warnings and can break existing v1 consumers that parse, join, or display every element as a string.

Please keep warnings as strings in v1, or introduce the structured form through a versioned field/schema with an explicit migration path.

signOnly: z.boolean().default(false).describe("sign and output the transaction without broadcasting; mutually exclusive with --dry-run; broadcast later with tx broadcast"),
dryRun: z.boolean().default(false).describe("build and estimate only, with no signature and no broadcast"),
signOnly: z.boolean().default(false).describe("sign and output complete transaction hex without broadcasting"),
buildOnly: z.boolean().default(false).describe("build and output unsigned complete transaction hex without unlocking"),

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.

[P3][CR-012] Update every leaf command page for the new transaction modes

--build-only, --permission-id, and --expiration are now shared by 11 transaction commands, but their individual reference pages still document only the previous dry-run/sign-only surface; some also still imply that every non-dry-run mode unlocks the wallet.

Please update the affected leaf pages and consider validating each page's option set against the command registry in CI so shared schema changes cannot leave command documentation behind.

const code = (error as NodeJS.ErrnoException).code;
if (code === "EEXIST" || code === "ELOOP") {
throw new ExecutionError(
"file_exists",

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.

[P3][CR-013] Reuse the existing output-conflict error contract

An existing target is reported here as file_exists via ExecutionError (exit 1), while the existing backup writer reports the equivalent O_EXCL conflict as output_exists via UsageError (exit 2). Automation can therefore treat the same deterministic conflict as retryable in one command and malformed input in another.

Please use one stable code, error class, and exit-code policy for exclusive-output conflicts, with a compatibility plan if the older code must remain supported.

terminal.txnActivateFee ?? accepted.activateFee;
return {
...accepted,
stage: terminal.state === "SUCCEED" ? "confirmed" : "failed",

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.

[P3][CR-014] Do not expose a FAILED transfer as top-level success

This branch resolves normally with stage: "failed", so the shared shell formats it as success: true and exits 0. The published machine interface tells automation to branch on exit code/top-level success first and does not define a GasFree-specific secondary check, so a failed transfer can be recorded as successful.

Please either return a failure envelope/non-zero exit for the FAILED terminal state, or explicitly version and document a stable two-level status contract. Add an end-to-end assertion for exit code, top-level success, stage, state, and failure reason.

function permissionCard(permission: PermissionGroupView, active = false): string {
const activePermission = active ? permission as AccountPermissionsView["actives"][number] : undefined;
const lines = [
`Permission Name ${permission.name} (id ${permission.id}${active ? ", active" : ""})`,

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.

[P3][CR-015] Escape Unicode bidi controls in untrusted display fields

Permission names are chain-controlled and reach this security-sensitive terminal summary. The shared text sanitizer removes ANSI/C0/C1 bytes but leaves Unicode bidi controls such as U+202E and U+2066–U+2069, allowing the visible order of names, IDs, or adjacent labels to differ from the underlying string. GasFree and multisig renderers expose the same class of input.

Please escape or visibly mark bidi/default-ignorable controls in text mode while preserving the original value in JSON, and add terminal golden tests for the affected renderers.

|| approval.signatures !== remote.view.signatures
|| approval.expiration !== remote.view.expiration
|| approved.size !== signedProgress.length
|| signedProgress.some((address) => !approved.has(address))

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.

[P3][CR-022] Validate every progress entry against the on-chain permission

This comparison only binds the signed-address set to the chain-approved set. Unsigned progress entries and all per-signer weights remain provider-controlled, so TronLink can add the current account as an unsigned signer or redistribute individual weights while preserving the signed set and aggregate currentWeight. The list can then report a false awaitingMySignature state or incorrect weights.

Please compare every progress address and weight with the selected permission's on-chain keys and render chain-derived weights. signChecked() still prevents unauthorized signing, but the collaboration list should not present unverified signer metadata as chain-validated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants