Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
9 changes: 6 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ FileTest
bin

# Wallet keystore files created at runtime
Wallet/
Mnemonic/
wallet_data/
/Wallet/
/Mnemonic/
/wallet_data/

# QA runtime output
qa/results/
Expand All @@ -38,3 +38,6 @@ qa/.verify.lock/
graphify-out/
.vscode/
docs/superpowers


/ts-deprecated/
49 changes: 49 additions & 0 deletions ts/.dependency-cruiser.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Enforced dependency direction:
*
* bootstrap (composition) -> inbound/outbound adapters -> application -> domain
* inbound adapters -> application -> domain
*
* Inbound and outbound adapters are peers. They may meet only in bootstrap/composition.
*/
module.exports = {
forbidden: [
{
name: "no-circular",
severity: "error",
from: {},
to: { circular: true },
},
{
name: "domain-is-independent",
severity: "error",
from: { path: "^src/domain/" },
to: { path: "^src/(application|adapters|bootstrap)/" },
},
{
name: "application-owns-ports",
severity: "error",
comment: "production application code depends on domain and its own ports, never adapters",
from: { path: "^src/application/", pathNot: "\\.test\\.ts$" },
to: { path: "^src/(adapters|bootstrap)/" },
},
{
name: "inbound-does-not-know-outbound",
severity: "error",
comment: "CLI adapters call application ports/use-cases; bootstrap/composition supplies outbound implementations",
from: { path: "^src/adapters/inbound/", pathNot: "\\.test\\.ts$" },
to: { path: "^src/(adapters/outbound|bootstrap)/" },
},
{
name: "outbound-does-not-know-inbound",
severity: "error",
from: { path: "^src/adapters/outbound/", pathNot: "\\.test\\.ts$" },
to: { path: "^src/(adapters/inbound|bootstrap)/" },
},
],
options: {
doNotFollow: { path: "node_modules" },
tsConfig: { fileName: "tsconfig.json" },
enhancedResolveOptions: { extensions: [".ts", ".js"] },
},
};
6 changes: 6 additions & 0 deletions ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
dist/
*.log
.wallet-cli/
.env
.private/
52 changes: 52 additions & 0 deletions ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# wallet-cli

TypeScript implementation of the TRON wallet CLI. This project owns its source, dependencies,
tests and live-test artifacts independently.

The CLI contract is:

- stable `wallet-cli.result.v1` JSON envelopes;
- deterministic `0 / 1 / 2` exit codes;
- exactly one terminal stdout frame in JSON mode;
- encrypted local wallet storage;
- stdin/TTY secret handling without secret argv/env values;
- TRON mainnet, Nile and Shasta targets;
- software and Ledger signing;
- extensible family plugins without a universal-chain abstraction.

## Commands

```bash
npm ci
npm run typecheck
npm run depcruise
npm test
npm run build
```

The live Nile suite reads the test identity from `../ts/.private/.env.test`, uses an isolated
`WALLET_CLI_HOME`, and never copies or logs the private material:

```bash
npm run test:live:nile
```

Its raw output is written to
`docs/nile-full-command-test-2026-06-29-run2-rawlogs.md`.

## Architecture

```mermaid
flowchart LR
ENTRY[index.ts] --> BOOTSTRAP[bootstrap<br/>runner + composition + family plugins]
BOOTSTRAP --> IN[inbound CLI adapter]
BOOTSTRAP --> OUT[outbound adapters]
IN --> APPLICATION[application<br/>contracts + use cases + ports]
OUT --> APPLICATION
APPLICATION --> DOMAIN[domain<br/>wallet + chain facts + value types]
```

Dependency direction is enforced by dependency-cruiser. Inbound and outbound adapters are peers
and may meet only in `bootstrap/composition.ts`. See the
[architecture source of truth](./docs/typescript-wallet-cli-architecture-source-of-truth.zh-TW.md)
for the complete contract; `docs/architecture.md` is its compact English overview.
230 changes: 230 additions & 0 deletions ts/docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
# wallet-cli architecture — English summary

This document is a non-normative English overview. The single, authoritative architecture and
behavior contract is
[typescript-wallet-cli-architecture-source-of-truth.zh-TW.md](./typescript-wallet-cli-architecture-source-of-truth.zh-TW.md).

## 1. Dependency model

```mermaid
flowchart LR
BOOTSTRAP[bootstrap<br/>process lifecycle + composition root] --> INBOUND[adapters/inbound<br/>CLI]
BOOTSTRAP --> OUTBOUND[adapters/outbound<br/>filesystem / TRON / Ledger / price]
INBOUND --> APPLICATION[application<br/>contracts / use cases / ports]
OUTBOUND --> APPLICATION
APPLICATION --> DOMAIN[domain<br/>pure rules and values]
```

Rules:

1. `domain` imports neither application, adapters nor bootstrap.
2. Production `application` imports neither adapters nor bootstrap.
3. Inbound and outbound adapters never import each other or bootstrap.
4. `bootstrap/composition.ts` is the only general composition root.
5. Chain-specific adapter and use-case construction belongs to a family plugin.
6. Circular dependencies are forbidden.
7. Type-only dependencies must follow the same conceptual direction even when the dependency
checker would ignore their runtime edge.

## 2. Package tree

```text
src/
├── index.ts
├── bootstrap/
│ ├── argv.ts
│ ├── runner.ts
│ ├── composition.ts
│ ├── family-registry.ts
│ └── families/
│ ├── types.ts
│ └── tron.ts
├── domain/
│ ├── address/
│ ├── amounts/
│ ├── derivation/
│ ├── errors/
│ ├── family/
│ ├── resources/
│ ├── sources/
│ ├── types/
│ └── wallet/
├── application/
│ ├── contracts/
│ │ ├── execution-policy.ts
│ │ ├── execution-scope.ts
│ │ └── progress.ts
│ ├── ports/
│ │ ├── chain/
│ │ ├── network-registry.ts
│ │ └── prompt.ts
│ ├── services/
│ │ ├── capability/
│ │ ├── pipeline/
│ │ ├── signer/
│ │ └── target/
│ └── use-cases/
│ └── tron/
└── adapters/
├── inbound/cli/
│ ├── commands/
│ ├── contracts/
│ ├── context/
│ ├── help/
│ ├── input/
│ ├── output/
│ ├── registry/
│ ├── render/
│ └── shell/
└── outbound/
├── chain/tron/
├── config/
├── keystore/
├── ledger/
├── persistence/
├── price/
└── tokenbook/
```

## 3. Responsibilities

### Domain

Contains pure wallet, account, address, amount, derivation, source, family and transaction value
rules. Domain code performs no filesystem, network, device, terminal or process I/O.

### Application

Defines what the product does and which external capabilities it requires.

- `contracts`: adapter-neutral execution policy, scopes and progress events.
- `ports`: capabilities required by application workflows, implemented by inbound or outbound adapters.
- `services`: reusable orchestration such as signer resolution and transaction pipeline.
- `use-cases`: wallet/config and family-specific workflows.

Application services consume ports such as `WalletRepository`, `ChainGatewayProvider`,
`LedgerDevice`, `TokenRepository`, `PriceProvider`, `NetworkRegistry`, `PromptPort` and
`BackupWriter`. They never construct or import filesystem, TronWeb, Ledger transport, CoinGecko,
Zod, yargs, CLI rendering or output-envelope implementations.

### Inbound CLI adapter

Owns yargs, zod command schemas, help/catalog generation, TTY input, stream discipline, output
envelopes and human rendering. A command definition translates CLI input into a use-case call; it
does not implement persistence or provider transport. CLI-only contracts (`CommandDefinition`,
`ExecutionContext`, globals, session and output envelopes) live under `inbound/cli/contracts`.

### Outbound adapters

Implement application ports:

- encrypted file keystore and atomic persistence;
- YAML configuration document;
- secure backup writer;
- TRON gateway and TronGrid history reader;
- Ledger device transport;
- token repository and price provider.

### Bootstrap

- `argv.ts`: pre-yargs global scan required to select output and secret channels.
- `composition.ts`: constructs process-scoped adapters and shared services.
- `runner.ts`: executes one invocation and owns the terminal error boundary.
- `family-registry.ts`: enabled plugin list and family-keyed projections.
- `families/*.ts`: family-specific gateway/signing/use-case/command composition.

## 4. Command flow

```mermaid
flowchart LR
ARGV[argv] --> SHELL[yargs shell]
SHELL --> DEF[CommandDefinition]
DEF --> TARGET[target + capability gates]
TARGET --> USECASE[application use case]
USECASE --> PORT[application port]
PORT --> ADAPTER[outbound adapter]
USECASE --> RESULT[typed result]
RESULT --> FORMAT[text or JSON formatter]
FORMAT --> STREAM[one terminal frame]
```

Zod remains the single source for command input shape, defaults, validation, help fields and JSON
Schema. Global flags remain table-driven. Business execution modes and confirmation behavior have
one implementation in application services, not duplicate command helpers.

## 5. Transaction flow

```mermaid
flowchart LR
RESOLVE[resolve signer] --> BUILD[build]
BUILD --> ESTIMATE[estimate]
ESTIMATE --> MODE{mode}
MODE -->|dry-run| PLAN[plan]
MODE -->|sign| SIGN[software or Ledger sign]
SIGN --> BROADCAST{broadcast?}
BROADCAST -->|no| SIGNED[signed transaction]
BROADCAST -->|yes| SUBMIT[submitted receipt]
SUBMIT --> CONFIRM{wait?}
CONFIRM --> FINAL[submitted / confirmed / failed]
```

Chain-specific builders and confirmation readers are provided by the family gateway. The shared
pipeline knows only signer and broadcaster ports.

## 6. Chain-family extension

A family plugin owns the concrete composition for one chain family:

The complete EVM implementation order, public discovery requirements, network contract and
acceptance checklist are defined in [evm-development-plan.zh-TW.md](./evm-development-plan.zh-TW.md).

```ts
interface FamilyPlugin<F extends ChainFamily> {
meta: FamilyMeta & { family: F }
signStrategy: SignStrategy
createGateway(network: NetworkDescriptor): ChainGatewayMap[F]
createModule(dependencies: FamilyApplicationDependencies): ChainModule
}
```

Adding EVM requires:

1. Add `evm` to `ChainFamily` and `FamilyMeta` facts.
2. Extend the discriminated `NetworkDescriptor` union.
3. Extend `ChainGatewayMap` with an `EvmGateway` port.
4. Implement EVM address codec, gateway and signing strategy.
5. Implement EVM use cases and CLI command module without changing TRON use cases.
6. Add `bootstrap/families/evm.ts` and one registry entry.
7. Add routing, signer, capability, output and contract tests.

Only genuinely shared capabilities receive shared ports. TRON staking/resource methods and EVM
gas/nonce methods stay in their family gateways; no universal gateway may accumulate unrelated
chain operations.

## 7. Behavioral invariants

- JSON success and error use `wallet-cli.result.v1`.
- Usage errors exit 2; execution errors exit 1; success/meta requests exit 0.
- JSON stdout contains exactly one terminal frame.
- Progress and diagnostics use stderr.
- Unknown exceptions are redacted.
- A run consumes stdin through at most one secret channel.
- Secrets never enter logs, envelopes, argv or environment variables.
- Dry-run does not decrypt, sign or broadcast.
- Watch-only accounts never sign.
- Every persistent mutation is locked and atomically replaced.
- Already-broadcast transactions do not become command failures solely because confirmation times
out.

## 8. Verification gates

```bash
npm run typecheck
npm run depcruise
npm test
npm run build
npm run test:live:nile
```

`test:live:nile` exercises the live surface in an isolated wallet home and emits a raw log without
exposing the test secret.
Loading