midnight-wallet-cli 0.2.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +95 -20
- package/dist/mcp-server.js +1130 -237
- package/dist/wallet.js +1528 -377
- package/docs/SKILL.md +136 -0
- package/package.json +37 -10
package/docs/SKILL.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# midnight-wallet-cli — Agent Skill
|
|
2
|
+
|
|
3
|
+
You have access to the `midnight-wallet-cli` MCP server. This document teaches you how to use it well. Read it once at the start of a session; use it as reference throughout.
|
|
4
|
+
|
|
5
|
+
## What you can do with this CLI
|
|
6
|
+
|
|
7
|
+
Manage wallets, check balances, transfer NIGHT tokens, register dust (fee token), run a local Midnight network, and deploy/call/inspect Compact smart contracts — all on behalf of the user. Every CLI command is also an MCP tool.
|
|
8
|
+
|
|
9
|
+
## Core concepts (teach these accurately)
|
|
10
|
+
|
|
11
|
+
- **Midnight** — a privacy-first blockchain. Supports both **unshielded** (transparent, like Bitcoin) and **shielded** (ZK-private, like Zcash) transactions in one chain.
|
|
12
|
+
- **NIGHT** — the native token. 1 NIGHT = 1,000,000 micro-NIGHT (6 decimals).
|
|
13
|
+
- **DUST** — a second token used to pay transaction fees. Not transferable. **Generated passively** by registered NIGHT UTXOs. Every wallet holding NIGHT must register those UTXOs for dust generation before it can send anything. Don't explain dust as "gas" — it's time-phase regenerative, not a fee market.
|
|
14
|
+
- **Shielded vs unshielded** — shielded addresses start `mn_shield-addr_*`; unshielded start `mn_addr_*`. A wallet has one of each per network. Sender controls the privacy: `mn transfer --shielded` sends privately.
|
|
15
|
+
- **No self-shielding** — a wallet cannot move its own funds from unshielded to shielded. To get shielded NIGHT, receive a shielded transfer from a wallet that already has some (genesis wallet has 250M shielded NIGHT on localnet).
|
|
16
|
+
- **Networks**
|
|
17
|
+
- `undeployed` — local Docker network via `mn localnet up`. Use this for dev iteration.
|
|
18
|
+
- `preprod` — public testnet. First-time wallet sync processes the full event history and can take significantly longer than subsequent syncs (which restore from cached wallet state). Exact time depends on wallet age, event density, network, and hardware.
|
|
19
|
+
- `preview` — public preview testnet. Same first-sync trade-off as preprod.
|
|
20
|
+
- **Proof server** — local HTTP service that generates ZK proofs for transactions. Default port 6300. Version must match the network's ledger version or writes fail with "Custom error 170".
|
|
21
|
+
|
|
22
|
+
## Intent routing (natural language → tool)
|
|
23
|
+
|
|
24
|
+
| User says | Call this tool |
|
|
25
|
+
|---|---|
|
|
26
|
+
| "Create a new wallet called alice" | `midnight_wallet_generate({ name: "alice" })` |
|
|
27
|
+
| "Show my wallets" / "list wallets" | `midnight_wallet_list()` |
|
|
28
|
+
| "Switch to bob" / "use wallet bob" | `midnight_wallet_use({ name: "bob" })` |
|
|
29
|
+
| "What's my balance?" | `midnight_balance()` |
|
|
30
|
+
| "Balance for this address 0x…" | `midnight_balance({ address: "mn_addr_..." })` |
|
|
31
|
+
| "Send 100 NIGHT to alice" | `midnight_transfer({ to: "alice", amount: "100" })` — ask consent first |
|
|
32
|
+
| "Send shielded" | add `--shielded` (the CLI command) or use shielded address directly |
|
|
33
|
+
| "Fund my wallet" (localnet only) | `midnight_airdrop({ amount: "1000" })` |
|
|
34
|
+
| "Register dust" / "I need fees" | `midnight_dust_register()` |
|
|
35
|
+
| "Am I registered for dust?" | `midnight_dust_status()` |
|
|
36
|
+
| "Start localnet" / "start a local network" | `midnight_localnet_up()` |
|
|
37
|
+
| "Stop localnet" | `midnight_localnet_stop()` |
|
|
38
|
+
|
|
39
|
+
## Canonical flows
|
|
40
|
+
|
|
41
|
+
### New user, first session (localnet)
|
|
42
|
+
1. `midnight_localnet_up()` — spin up Docker network.
|
|
43
|
+
2. `midnight_wallet_generate({ name: "alice" })` — create wallet, set active.
|
|
44
|
+
3. `midnight_airdrop({ amount: "1000" })` — fund from genesis.
|
|
45
|
+
4. `midnight_dust_register()` — make wallet able to pay fees.
|
|
46
|
+
5. `midnight_balance()` — confirm funds visible.
|
|
47
|
+
|
|
48
|
+
At step 5 the user is ready to transact.
|
|
49
|
+
|
|
50
|
+
### User wants to send tokens (safely — two-step confirmation flow)
|
|
51
|
+
|
|
52
|
+
`midnight_transfer` does **not** execute on the first call. It returns a pending token that you must redeem via `midnight_confirm_operation` after the user confirms.
|
|
53
|
+
|
|
54
|
+
1. Call `midnight_transfer({ to, amount })` — returns `{ pending: true, token, description, expiresAt }`.
|
|
55
|
+
2. Show `description` (e.g. "Send 100 NIGHT from alice to mn_addr_preprod1… on preprod") to the user verbatim. Do not paraphrase amounts or recipients.
|
|
56
|
+
3. Wait for the user's explicit consent.
|
|
57
|
+
4. If yes: call `midnight_confirm_operation({ token })` — this actually executes the transfer and returns the result (tx hash, etc).
|
|
58
|
+
5. If no: do nothing; the token expires in 5 minutes.
|
|
59
|
+
6. On success: surface the tx hash. On failure: read the error and suggest recovery (see below).
|
|
60
|
+
|
|
61
|
+
**Never skip step 2–3.** The whole point of the token flow is that the user sees the exact operation before funds move.
|
|
62
|
+
|
|
63
|
+
### User wants to deploy a contract
|
|
64
|
+
Use `midnight_contract_*` MCP tools (or `mn contract` CLI). Flow: `compact compile` in the project → `midnight_contract_inspect({ path })` to verify artifacts → `midnight_contract_deploy({ path, wallet, network })` → returns address → `midnight_contract_call({ address, circuit, args, ... })` to exercise it. State reads via `midnight_contract_state({ address, ... })`.
|
|
65
|
+
|
|
66
|
+
**Project layouts handled.** The scan looks for `managed/<name>/compiler/contract-info.json` under: project root, `contract/`, `contract/src/`, `contracts/`, `contracts/src/`, `src/`. If none match, pass `managed: "<full path to managed/<name>/>"` directly.
|
|
67
|
+
|
|
68
|
+
**Multi-contract projects.** Inspect returns a `siblings: string[]` field listing other contracts under the same `managed/`. Pick one with `name: "<contract>"` on inspect/deploy/call/state.
|
|
69
|
+
|
|
70
|
+
**Arg encoding for circuits.** The MCP `args` param is a JSON string. The bridge auto-coerces, recursing into objects and arrays:
|
|
71
|
+
- `number` (integer, within `Number.MAX_SAFE_INTEGER`) → `BigInt`
|
|
72
|
+
- `"123n"` (BigInt-literal syntax) → `BigInt(123)` — required for values beyond safe-integer range, e.g. 256-bit field elements in elliptic-curve points like `{x: "3577…n", y: "5455…n"}`
|
|
73
|
+
- array of `[0–255]` ints → `Uint8Array` (any Compact `Bytes<N>` arg, e.g. `[1,2,3,…,32]`)
|
|
74
|
+
- objects: each value coerced under the same rules → `Struct` args
|
|
75
|
+
- arrays (non-byte): each element coerced
|
|
76
|
+
|
|
77
|
+
Plain strings, booleans, fractional numbers, and `null` pass through unchanged. No hex-string detection — `"1234"` stays a string.
|
|
78
|
+
|
|
79
|
+
**Compatibility caveat — runtime version skew.** Contracts compiled with an older `compactc` may fail at deploy/call with a message like `Version mismatch: compiled code expects <X>, runtime is <Y>`. The CLI bundles one specific `@midnight-ntwrk/compact-runtime` version; if the user's contract was compiled against a different one, recompile the contract with a matching `compactc` rather than asking the user to downgrade `mn`. `midnight_contract_inspect` shows the compiled `runtimeVersion` so you can flag the mismatch before attempting deploy.
|
|
80
|
+
|
|
81
|
+
**Stale MCP server.** Every MCP response carries `_serverVersion`. If the user upgraded `midnight-wallet-cli` but `_serverVersion` lags `mn --version`, the MCP client is talking to a long-lived stale process. Tell them to disconnect and re-add the MCP server (a /mcp reconnect alone will not respawn it).
|
|
82
|
+
|
|
83
|
+
### User wants to scaffold tests for a contract
|
|
84
|
+
Use `midnight_test_create({ path, strategy })`. Strategies:
|
|
85
|
+
- `"cli"` (default) — emits `dapp.test.json` + `tests/suites/cli-default/{suite,actions,assertions}.json` with deploy → state → one call per impure circuit → state. Placeholder args; user reviews and edits before `mn test run`.
|
|
86
|
+
- `"browser"` — additionally needs `port` and `build-cmd`; emits `prompt.md` instead of `actions.json`. Claude drives the UI per `prompt.md`.
|
|
87
|
+
|
|
88
|
+
Run via `midnight_test_run` (CLI: `mn test run --suite <name>`). Browser strategy supports `browserMode: "vision"` (screenshots — default), `"dom"` (accessibility tree, faster, requires `chrome-devtools-mcp`), `"script"` (direct JS, fastest, requires `chrome-devtools-mcp`). Pre-flight detects missing `chrome-devtools-mcp` and orphan automation Chromes from prior crashes.
|
|
89
|
+
|
|
90
|
+
## Safety rules (non-negotiable)
|
|
91
|
+
|
|
92
|
+
- **Read tools** (`readOnlyHint: true`) — safe to call without user consent. Examples: balance, info, list, status, address derivation.
|
|
93
|
+
- **Destructive tools** (`destructiveHint: true`) — require explicit user consent before calling. Examples: transfer, airdrop, dust register, wallet generate, wallet remove, cache clear, localnet down.
|
|
94
|
+
- **Open-world tools** (`openWorldHint: true`) — touch the network/chain/Docker. Can fail non-deterministically; retry is usually safe for reads, risky for writes.
|
|
95
|
+
- **Never** auto-execute a fund-moving operation. Always describe amount + recipient + network first.
|
|
96
|
+
- **Never** invent a mnemonic, seed, or private key. If the user needs one, call `midnight_wallet_generate`.
|
|
97
|
+
|
|
98
|
+
## Error recovery recipes
|
|
99
|
+
|
|
100
|
+
Every MCP tool error returns `{ error: true, code: <ERROR_CODE>, message: <human prose> }`. **Index on the `code`** — the message is human-targeted and may change. Stable codes:
|
|
101
|
+
|
|
102
|
+
| Code | Meaning | Recovery |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `INSUFFICIENT_BALANCE` | Wallet doesn't have enough NIGHT to cover the requested amount. | Show the user. On localnet: `midnight_airdrop({ amount: "..." })`. On hosted networks: tell user to fund the address. |
|
|
105
|
+
| `DUST_REQUIRED` | Wallet has NIGHT but no DUST to pay fees (or insufficient DUST). | Run `midnight_dust_register()` then wait for dust to regenerate (passive, on-chain — check `midnight_dust_status()` until `dustAvailable: true`). |
|
|
106
|
+
| `INVALID_DUST_PROOF` | Chain rejected the dust spend proof as malformed (substrate "Custom error 170"). Stale commitment tree. | `midnight_cache_clear({ wallet: "<name>" })` then retry. |
|
|
107
|
+
| `STALE_CACHE` | Local wallet cache disagrees with chain (e.g. localnet reset, remote testnet re-indexed). | `midnight_cache_clear({ wallet: "<name>" })` then retry. |
|
|
108
|
+
| `STALE_UTXO` | UTXO was already spent on-chain (substrate "error code 115"). | Re-sync (the next call will refresh state) and retry. |
|
|
109
|
+
| `PROOF_FAILURE` | Proof server rejected or failed to generate the ZK proof. Often a stale commitment tree or unreachable proof server. | Check `midnight_status()`; if proof server healthy, `midnight_cache_clear({ wallet: "<name>" })` then retry. |
|
|
110
|
+
| `PROOF_TIMEOUT` | ZK proof generation didn't finish within the deadline. | Retry — proofs can be slow under load. |
|
|
111
|
+
| `SYNC_TIMEOUT` | Long-running wallet sync hit its deadline. Common on a hosted-network first-cold-sync. | Retry; the cache resumes from the last applied event so progress is preserved. |
|
|
112
|
+
| `NETWORK_ERROR` | Indexer/node/proof-server connection refused or DNS failure. | Check `midnight_status()`; if localnet, `midnight_localnet_up()`. |
|
|
113
|
+
| `WALLET_NOT_FOUND` | Named wallet doesn't exist on disk. | `midnight_wallet_list()` — user may have removed it. |
|
|
114
|
+
| `INVALID_ARGS` | Missing/invalid argument. | Show the message to the user verbatim — it names the missing field. |
|
|
115
|
+
| `TX_REJECTED` | Chain rejected the submitted transaction (catch-all when no more specific code applies). | Read the message; usually a state mismatch resolved by re-sync + retry. |
|
|
116
|
+
| `CANCELLED` | Operation was aborted (SIGINT). | Don't retry without confirming with the user. |
|
|
117
|
+
| `UNKNOWN` | Error didn't match any known classifier. | Surface the message to the user. If reproducible, a new code may be warranted. |
|
|
118
|
+
|
|
119
|
+
## When to use which network
|
|
120
|
+
|
|
121
|
+
- **Developing a contract?** → `undeployed` (localnet). No first-sync cost; everything is local.
|
|
122
|
+
- **Integration testing against a hosted chain?** → `preprod`. First sync for a new wallet on this network processes full event history; subsequent syncs restore from cache and are much faster.
|
|
123
|
+
- **Demoing to users?** → `preview` (if the dApp is deployed there) or `undeployed` (if self-contained).
|
|
124
|
+
- **Mainnet?** → not on this CLI yet. Don't claim it is.
|
|
125
|
+
|
|
126
|
+
## What this CLI is NOT
|
|
127
|
+
|
|
128
|
+
- Not a custody service. Keys are on the user's disk (`~/.midnight/wallets/<name>.json`).
|
|
129
|
+
- Not a fast-sync solution for a first-time wallet on a hosted network. The wallet SDK must process the chain's event history on first run; the CLI caches state after that so repeat runs are fast.
|
|
130
|
+
- Not a contract compiler. The project's `package.json` should expose a `compact` script (Midnight convention, e.g. `"compact": "compact compile src/foo.compact src/managed/foo"`) or a `compile` script as a generic fallback. `mn dev` detects whichever is present; create-mn-app and midnight-starship templates ship with the `compact` script already wired.
|
|
131
|
+
|
|
132
|
+
## Authoritative references
|
|
133
|
+
|
|
134
|
+
- `mn help <command>` — per-command usage and flags.
|
|
135
|
+
- `mn help --agent` — structured reference manual for AI clients.
|
|
136
|
+
- `mn status` — live network health (indexer, node, proof server).
|
package/package.json
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "midnight-wallet-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Git-style CLI wallet for the Midnight blockchain",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
+
"homepage": "https://github.com/nel349/midnight-wallet-cli#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/nel349/midnight-wallet-cli.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/nel349/midnight-wallet-cli/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"midnight",
|
|
17
|
+
"blockchain",
|
|
18
|
+
"wallet",
|
|
19
|
+
"cli",
|
|
20
|
+
"zk",
|
|
21
|
+
"zero-knowledge",
|
|
22
|
+
"compact",
|
|
23
|
+
"mcp",
|
|
24
|
+
"ai-agent"
|
|
25
|
+
],
|
|
7
26
|
"workspaces": [
|
|
8
27
|
"packages/*"
|
|
9
28
|
],
|
|
@@ -14,7 +33,8 @@
|
|
|
14
33
|
"midnight-wallet-mcp": "dist/mcp-server.js"
|
|
15
34
|
},
|
|
16
35
|
"files": [
|
|
17
|
-
"dist"
|
|
36
|
+
"dist",
|
|
37
|
+
"docs/SKILL.md"
|
|
18
38
|
],
|
|
19
39
|
"scripts": {
|
|
20
40
|
"wallet": "tsx src/wallet.ts",
|
|
@@ -27,16 +47,23 @@
|
|
|
27
47
|
},
|
|
28
48
|
"dependencies": {
|
|
29
49
|
"@midnight-ntwrk/dapp-connector-api": "^4.0.1",
|
|
30
|
-
"@midnight-ntwrk/ledger-
|
|
31
|
-
"@midnight-ntwrk/midnight-js-
|
|
32
|
-
"@midnight-ntwrk/midnight-js-
|
|
50
|
+
"@midnight-ntwrk/ledger-v8": "^8.0.3",
|
|
51
|
+
"@midnight-ntwrk/midnight-js-contracts": "^4.0.4",
|
|
52
|
+
"@midnight-ntwrk/midnight-js-http-client-proof-provider": "^4.0.4",
|
|
53
|
+
"@midnight-ntwrk/midnight-js-indexer-public-data-provider": "^4.0.4",
|
|
54
|
+
"@midnight-ntwrk/midnight-js-level-private-state-provider": "^4.0.4",
|
|
55
|
+
"@midnight-ntwrk/midnight-js-network-id": "^4.0.4",
|
|
56
|
+
"@midnight-ntwrk/midnight-js-node-zk-config-provider": "^4.0.4",
|
|
57
|
+
"@midnight-ntwrk/midnight-js-types": "^4.0.4",
|
|
58
|
+
"@midnight-ntwrk/midnight-js-utils": "^4.0.4",
|
|
33
59
|
"@midnight-ntwrk/wallet-sdk-abstractions": "^2.0.0",
|
|
34
|
-
"@midnight-ntwrk/wallet-sdk-address-format": "^3.0
|
|
35
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
36
|
-
"@midnight-ntwrk/wallet-sdk-
|
|
60
|
+
"@midnight-ntwrk/wallet-sdk-address-format": "^3.1.0",
|
|
61
|
+
"@midnight-ntwrk/wallet-sdk-capabilities": "^3.2.0",
|
|
62
|
+
"@midnight-ntwrk/wallet-sdk-dust-wallet": "^4.0.0",
|
|
63
|
+
"@midnight-ntwrk/wallet-sdk-facade": "^4.0.0",
|
|
37
64
|
"@midnight-ntwrk/wallet-sdk-hd": "^3.0.1",
|
|
38
|
-
"@midnight-ntwrk/wallet-sdk-shielded": "^
|
|
39
|
-
"@midnight-ntwrk/wallet-sdk-unshielded-wallet": "^
|
|
65
|
+
"@midnight-ntwrk/wallet-sdk-shielded": "^3.0.0",
|
|
66
|
+
"@midnight-ntwrk/wallet-sdk-unshielded-wallet": "^3.0.0",
|
|
40
67
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
41
68
|
"@scure/bip39": "^2.0.1",
|
|
42
69
|
"rxjs": "^7.8.1",
|