clanker-wallet 0.4.2 → 0.6.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/README.md +84 -9
- package/SKILL.md +54 -4
- package/bin/cli.js +208 -47
- package/dist/bech32.d.ts +27 -0
- package/dist/bech32.js +111 -0
- package/dist/client.d.ts +14 -1
- package/dist/client.js +90 -20
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/mcp.d.ts +156 -0
- package/dist/mcp.js +370 -0
- package/dist/types.d.ts +52 -0
- package/dist/validate.d.ts +13 -2
- package/dist/validate.js +63 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# clanker-wallet
|
|
2
2
|
|
|
3
|
-
Human-approved blockchain transactions for AI agents. Agent proposes a tx, human reviews and signs it from the [web app](https://clanker-wallet.xyz). All communication is end-to-end encrypted.
|
|
3
|
+
Human-approved blockchain transactions for AI agents. Agent proposes a tx, human reviews and signs it from the [web app](https://clanker-wallet.xyz). All communication is end-to-end encrypted. EVM chains take raw transactions; bitcoin and chia take transfers (network, recipient, amount) that the human's wallet builds.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -36,7 +36,7 @@ clanker-wallet status
|
|
|
36
36
|
|
|
37
37
|
### `pair`
|
|
38
38
|
|
|
39
|
-
Pair with a human's web app. Run once — `pair` writes identity and pairing to `~/.clanker-wallet/`.
|
|
39
|
+
Pair with a human's web app. Run once per chain family — `pair` writes identity and pairing to `~/.clanker-wallet/`. The web app's bitcoin and chia tabs give pairing JSON with `"chain_family":"bitcoin"` / `"chain_family":"chia"`; each family is kept in its own file, so pairing one never replaces another.
|
|
40
40
|
|
|
41
41
|
If the pairing JSON contains `"mode":"managed"`, the relay simulates each transaction for the human's review screen, so `tx` also sends the plain transaction fields to it. Otherwise the relay only sees ciphertext. Pair with the JSON as given.
|
|
42
42
|
|
|
@@ -78,13 +78,33 @@ clanker-wallet tx \
|
|
|
78
78
|
| `--expected-outcome <text>` | no | — | What will happen |
|
|
79
79
|
| `--timeout <ms>` | no | `3600000` (1 h) | How long `tx` blocks waiting for the human |
|
|
80
80
|
|
|
81
|
+
### `send`
|
|
82
|
+
|
|
83
|
+
Send bitcoin or chia. The human's wallet builds, signs and broadcasts the transaction; you give the network, recipient and amount. Needs a pairing from the web app's bitcoin or chia tab. Same blocking, `--reason`/`--urgency`/`--expected-outcome`/`--timeout`, output and exit codes as `tx`; exit code 4 means that chain family isn't paired.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
clanker-wallet send --btc testnet --to tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx --sats 1000 --reason "send 1000 sats on bitcoin testnet"
|
|
87
|
+
clanker-wallet send --xch testnet11 --to txch1qqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sw0amhg --mojos 1000 --fee-mojos 0 --reason "send 1000 mojos on chia testnet11"
|
|
88
|
+
# approved → {"tx_hash":"<bitcoin txid or chia transaction id>","status":"approved"}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| Flag | Required | Default | Description |
|
|
92
|
+
|------|----------|---------|-------------|
|
|
93
|
+
| `--btc <network>` | one of `--btc`/`--xch` | — | `mainnet`, `testnet` or `signet` |
|
|
94
|
+
| `--xch <network>` | one of `--btc`/`--xch` | — | `mainnet` or `testnet11` |
|
|
95
|
+
| `--to <address>` | yes | — | On that network: segwit `bc1...`/`tb1...` (no legacy `1...`/`3...`), or `xch1...`/`txch1...` |
|
|
96
|
+
| `--sats <n>` | with `--btc` | — | Whole sats (1 BTC = `100000000`); the wallet sets the fee |
|
|
97
|
+
| `--mojos <n>` | with `--xch` | — | Whole mojos (1 XCH = `1000000000000`) |
|
|
98
|
+
| `--fee-mojos <n>` | no (chia only) | `0` | Chia fee, shown to the human |
|
|
99
|
+
|
|
81
100
|
### `status`
|
|
82
101
|
|
|
83
|
-
Check current pairing status.
|
|
102
|
+
Check current pairing status. `wallet` and `relay` are the evm pairing's; `pairings` lists every paired chain family.
|
|
84
103
|
|
|
85
104
|
```bash
|
|
86
105
|
clanker-wallet status
|
|
87
|
-
# → {"agent_id":"agent_abc123","paired":true,"wallet":"0x...","relay":"wss://..."
|
|
106
|
+
# → {"agent_id":"agent_abc123","paired":true,"wallet":"0x...","relay":"wss://...",
|
|
107
|
+
# "pairings":{"evm":{"wallet":"0x...","relay":"wss://...","mode":null,"chain_network":null}}}
|
|
88
108
|
```
|
|
89
109
|
|
|
90
110
|
### `whoami`
|
|
@@ -104,7 +124,7 @@ clanker-wallet whoami
|
|
|
104
124
|
| 1 | Rejected by human |
|
|
105
125
|
| 2 | Timeout |
|
|
106
126
|
| 3 | Connection/relay error |
|
|
107
|
-
| 4 | Not paired |
|
|
127
|
+
| 4 | Not paired (`send`: not paired for that chain family) |
|
|
108
128
|
| 5 | Invalid arguments (bad or missing flag; nothing sent) |
|
|
109
129
|
|
|
110
130
|
## State
|
|
@@ -113,10 +133,15 @@ Identity and pairing are persisted in `~/.clanker-wallet/`:
|
|
|
113
133
|
|
|
114
134
|
```
|
|
115
135
|
~/.clanker-wallet/
|
|
116
|
-
identity.json
|
|
117
|
-
|
|
136
|
+
identity.json # agent keypair (auto-generated on first use)
|
|
137
|
+
pairings/evm.json # per chain family: human's public key + relay URL
|
|
138
|
+
pairings/bitcoin.json
|
|
139
|
+
pairings/chia.json
|
|
140
|
+
pairing.json # written by 0.4.x; still read as the evm pairing, never changed
|
|
118
141
|
```
|
|
119
142
|
|
|
143
|
+
`~/.clanker-wallet/pairings/<family>.json` takes precedence over a 0.4.x `pairing.json`, which is never moved, rewritten or deleted.
|
|
144
|
+
|
|
120
145
|
## Common Chain IDs
|
|
121
146
|
|
|
122
147
|
| Chain | ID |
|
|
@@ -129,6 +154,35 @@ Identity and pairing are persisted in `~/.clanker-wallet/`:
|
|
|
129
154
|
| Sepolia (testnet) | 11155111 |
|
|
130
155
|
| Base Sepolia (testnet) | 84532 |
|
|
131
156
|
|
|
157
|
+
## MCP (assistants)
|
|
158
|
+
|
|
159
|
+
`clanker-wallet mcp` runs an MCP server over stdio, so an assistant (Claude Desktop, Claude Code, Cursor, or another MCP host that runs local commands) can ask for transactions as tool calls. The human approves every request on their own device; the server holds no wallet keys. It uses the CLI's identity and pairings in `~/.clanker-wallet/`, so pair first (`clanker-wallet pair '<JSON>'`, once per chain family). In a sandbox the server needs the same relay access and `~/.clanker-wallet/` writes as the CLI.
|
|
160
|
+
|
|
161
|
+
Claude Code:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
claude mcp add clanker-wallet -- npx -y clanker-wallet mcp
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor (`~/.cursor/mcp.json`), and any other MCP host that runs local stdio servers (command `npx`, args `-y clanker-wallet mcp`):
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"mcpServers": {
|
|
172
|
+
"clanker-wallet": { "command": "npx", "args": ["-y", "clanker-wallet", "mcp"] }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
| Tool | Arguments | Returns |
|
|
178
|
+
|------|-----------|---------|
|
|
179
|
+
| `request_evm_tx` | `chain_id`, `to`, `value` (wei, integer string), `data` (default `0x`), `reason`, optional `urgency` / `expected_outcome` | `{"request_id":"req_...","status":"pending","expires_at":"..."}` |
|
|
180
|
+
| `request_transfer` | `family` (`bitcoin`), `network` (`mainnet`, `testnet`, `signet`), `to`, `amount` (sats, integer string), `reason`, optional `urgency` / `expected_outcome` | as above |
|
|
181
|
+
| `get_request` | `request_id`, optional `wait_seconds` (at most 50) | `status`: `pending`, `approved` (with `tx_hash`), `rejected`, `failed` (with `error`), `unknown` (with `error`) or `expired` |
|
|
182
|
+
| `list_pairings` | none | `agent_id`, `fingerprint`, and the paired families (`evm`, `bitcoin`) with network, wallet and mode |
|
|
183
|
+
|
|
184
|
+
Requests are asynchronous: a request tool returns once the relay has the request, then the assistant calls `get_request` (with `wait_seconds` to wait for an answer) until the status is no longer `pending`. Invalid input or a missing pairing is a tool error, and nothing is sent. Pending requests live in the server process: after 1 h the server stops waiting and reports `expired`, and a restarted server no longer knows them (`get_request` then reports an unknown `request_id`). In both cases the human may still approve the request in their wallet, so ask them before sending it again. `unknown` means the request was delivered but the server lost its relay connection: the human may still approve it, so ask them before sending it again. Chia is not offered over MCP yet.
|
|
185
|
+
|
|
132
186
|
---
|
|
133
187
|
|
|
134
188
|
## Library Usage (Advanced)
|
|
@@ -155,6 +209,19 @@ const txHash = await wallet.requestTx({
|
|
|
155
209
|
|
|
156
210
|
`requestTx` throws `InvalidTxError` (with `field` and `reason`) before anything is sent if `chain_id`, `to`, `value` or `data` is malformed.
|
|
157
211
|
|
|
212
|
+
With a bitcoin or chia pairing, use `requestTransfer`; the family comes from the pairing:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// 1000 mojos on chia testnet11; amount and fee are integer strings or bigints
|
|
216
|
+
const txId = await wallet.requestTransfer({
|
|
217
|
+
chain_network: 'testnet11',
|
|
218
|
+
transfer: { asset: 'xch', to: 'txch1qqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sw0amhg', amount: '1000', fee: '0' },
|
|
219
|
+
context: { reason: 'send 1000 mojos on chia testnet11' },
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
It throws `InvalidTxError` before sending for an evm pairing, an unknown network, a recipient from another network, a non-integer amount, or a bitcoin `fee` (the wallet sets bitcoin fees). Chia `fee` defaults to `"0"`.
|
|
224
|
+
|
|
158
225
|
See full API: [`src/`](./src/)
|
|
159
226
|
|
|
160
227
|
## Python SDK
|
|
@@ -176,14 +243,22 @@ tx_hash = await agent.request_tx(RequestTxOptions(
|
|
|
176
243
|
transaction=Transaction(to="0x000000000000000000000000000000000000dEaD", value="1000000000000000", data="0x"),
|
|
177
244
|
context=TxContext(reason="send 0.001 ETH on base sepolia"),
|
|
178
245
|
))
|
|
246
|
+
|
|
247
|
+
# with a bitcoin pairing: 1000 sats on bitcoin testnet
|
|
248
|
+
from clanker_wallet import RequestTransferOptions, Transfer
|
|
249
|
+
|
|
250
|
+
txid = await agent.request_transfer(RequestTransferOptions(
|
|
251
|
+
chain_network="testnet",
|
|
252
|
+
transfer=Transfer(asset="btc", to="tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", amount=1000),
|
|
253
|
+
))
|
|
179
254
|
```
|
|
180
255
|
|
|
181
256
|
## How It Works
|
|
182
257
|
|
|
183
258
|
1. Human opens [clanker-wallet.xyz](https://clanker-wallet.xyz), connects wallet, clicks "copy prompt for agent" — gets a paste-ready prompt that bundles intent, the setup guide URL, and the pairing JSON
|
|
184
259
|
2. Human pastes the prompt to their AI agent. Agent extracts the JSON and runs `clanker-wallet pair` — establishes E2E encrypted channel
|
|
185
|
-
3. Agent runs `clanker-wallet tx` — encrypts
|
|
186
|
-
4. Human reviews tx details, simulation, risk flags in the web app. A new agent shows up as unverified with its fingerprint; auto-approve rules only apply after the human trusts it. Contract rules (address + methods) don't limit token amounts or recipients
|
|
260
|
+
3. Agent runs `clanker-wallet tx` (or `send` for bitcoin/chia) — encrypts the request with nacl.box, sends via WebSocket relay
|
|
261
|
+
4. Human reviews tx details, simulation, risk flags in the web app. A new agent shows up as unverified with its fingerprint; auto-approve rules only apply after the human trusts it. Trust applies on every chain family; each chain family has its own policy (rules and remote URL), edited while that chain is selected. Value and daily limits are wei for evm and sats for bitcoin (the sent amount; the wallet's fee isn't counted); a send on another family never matches a rule that sets one, and chia has none. On bitcoin the wallet still confirms each auto-approved send. Contract rules (address + methods) don't limit token amounts or recipients
|
|
187
262
|
5. Human approves → wallet signs + broadcasts → agent gets the tx hash
|
|
188
263
|
|
|
189
264
|
Request and response payloads are encrypted end-to-end; the relay only forwards them. Exception: when paired in managed mode, the agent also sends the transaction fields (from, to, value, data, chainId) to the relay for simulation.
|
package/SKILL.md
CHANGED
|
@@ -51,13 +51,15 @@ clanker-wallet pair '{"version":1,"pubkey":"ab12...","relay":"wss://...","wallet
|
|
|
51
51
|
|
|
52
52
|
If the human only pastes raw JSON (no prompt prose), pair with it directly. If they only paste the URL, ask them to click "copy prompt for agent" on the pairing screen.
|
|
53
53
|
|
|
54
|
-
`pair` writes your identity and the pairing to `~/.clanker-wallet/`. You only need to do this once.
|
|
54
|
+
`pair` writes your identity and the pairing to `~/.clanker-wallet/`. You only need to do this once per chain family.
|
|
55
|
+
|
|
56
|
+
The web app has an evm, a bitcoin and a chia tab, and each tab gives its own pairing JSON (bitcoin and chia add `"chain_family":"bitcoin"` / `"chain_family":"chia"`). Pair each one you need: every family is kept in its own file, `~/.clanker-wallet/pairings/<family>.json` (`evm`, `bitcoin`, `chia`), so pairing one never replaces another. A `~/.clanker-wallet/pairing.json` written by clanker-wallet 0.4.x is still read as the evm pairing and is left untouched.
|
|
55
57
|
|
|
56
58
|
If the JSON contains `"mode":"managed"`, the human chose managed mode: the relay simulates each transaction for the review screen, so `tx` also sends the plain transaction fields (from, to, value, data, chainId) to the relay, which shares them with its simulation provider. Without it (or with `"mode":"encrypted"`) the relay only sees ciphertext. Pair with the JSON exactly as given either way.
|
|
57
59
|
|
|
58
60
|
### Trust (first request)
|
|
59
61
|
|
|
60
|
-
The web app lists a new agent as unverified and shows its fingerprint (e.g. `ab12 cd34 ef56 7890`). Until the human compares it with the `fingerprint` from `clanker-wallet whoami` and clicks "trust this agent", none of their auto-approve rules apply: every tx waits for manual approval. Contract rules (contract address + allowed methods) do not limit token amounts or recipients: a rule allowing `transfer` on a token auto-approves any amount to any recipient. If asked, run `clanker-wallet whoami` and share the fingerprint.
|
|
62
|
+
The web app lists a new agent as unverified and shows its fingerprint (e.g. `ab12 cd34 ef56 7890`). Until the human compares it with the `fingerprint` from `clanker-wallet whoami` and clicks "trust this agent", none of their auto-approve rules apply: every tx waits for manual approval. Trust applies on every chain family; each chain family has its own policy (rules and remote URL), edited while that chain is selected. Value and daily limits come in two units: wei limits apply to evm only, sats limits to bitcoin only (they count the sent amount, not the fee the wallet adds); a `send` on any other family never matches a rule that sets one and waits for the human. Chia has no value limits. On bitcoin the wallet still asks the human to confirm each auto-approved `send`. Contract rules (contract address + allowed methods) do not limit token amounts or recipients: a rule allowing `transfer` on a token auto-approves any amount to any recipient. If asked, run `clanker-wallet whoami` and share the fingerprint.
|
|
61
63
|
|
|
62
64
|
### Transaction notifications
|
|
63
65
|
|
|
@@ -117,6 +119,21 @@ The command **blocks until the human approves or rejects**, for up to 1 hour by
|
|
|
117
119
|
| `--expected-outcome <text>` | no | — | What will happen |
|
|
118
120
|
| `--timeout <ms>` | no | `3600000` (1 h) | How long `tx` blocks waiting for the human |
|
|
119
121
|
|
|
122
|
+
## Sending Bitcoin or Chia
|
|
123
|
+
|
|
124
|
+
Bitcoin and chia sends are transfers: you name the network, recipient and amount, and the human's wallet builds, signs and broadcasts the transaction. Pair the web app's bitcoin or chia tab first.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
clanker-wallet send --btc testnet --to tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx --sats 1000 --reason "Send 1000 sats on bitcoin testnet"
|
|
128
|
+
clanker-wallet send --xch testnet11 --to txch1qqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sw0amhg --mojos 1000 --fee-mojos 0 --reason "Send 1000 mojos on chia testnet11"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- **network:** `--btc mainnet|testnet|signet` or `--xch mainnet|testnet11`. There is no default network; the recipient must belong to it (`bc1...` on bitcoin mainnet, `tb1...` on testnet/signet, `xch1...` / `txch1...` on chia).
|
|
132
|
+
- **bitcoin recipients are segwit addresses only** (`bc1q...`, `bc1p...`, `tb1...`); legacy `1...`/`3...` addresses are refused.
|
|
133
|
+
- **amounts are whole base units**: `--sats` (1 BTC = `100000000`), `--mojos` (1 XCH = `1000000000000`). No decimals.
|
|
134
|
+
- **fees:** bitcoin fees are set in the human's wallet. Chia takes `--fee-mojos` (default `0`), which the human sees before approving.
|
|
135
|
+
- `--reason`, `--urgency`, `--expected-outcome` and `--timeout` work as for `tx`, and so do the output and exit codes. On approval `tx_hash` is the bitcoin txid or the chia transaction id. Exit code 4 means that chain family isn't paired.
|
|
136
|
+
|
|
120
137
|
## Common Chain IDs
|
|
121
138
|
|
|
122
139
|
| Chain | ID |
|
|
@@ -165,17 +182,50 @@ clanker-wallet tx \
|
|
|
165
182
|
```bash
|
|
166
183
|
# Check pairing status
|
|
167
184
|
clanker-wallet status
|
|
168
|
-
# → {"agent_id":"agent_abc123","paired":true,"wallet":"0x...","relay":"wss://..."
|
|
185
|
+
# → {"agent_id":"agent_abc123","paired":true,"wallet":"0x...","relay":"wss://...",
|
|
186
|
+
# "pairings":{"evm":{"wallet":"0x...","relay":"wss://...","mode":null,"chain_network":null},"chia":{"wallet":"xch1...",...}}}
|
|
187
|
+
# wallet and relay are the evm pairing's; pairings lists every paired chain family
|
|
169
188
|
|
|
170
189
|
# Show agent identity
|
|
171
190
|
clanker-wallet whoami
|
|
172
191
|
# → {"agent_id":"agent_abc123","public_key":"ab12cd34ef567890...","fingerprint":"ab12 cd34 ef56 7890"}
|
|
173
192
|
```
|
|
174
193
|
|
|
194
|
+
## MCP (assistants)
|
|
195
|
+
|
|
196
|
+
`clanker-wallet mcp` runs an MCP server over stdio, so an assistant (Claude Desktop, Claude Code, Cursor, or another MCP host that runs local commands) can ask for transactions as tool calls. The human approves every request on their own device; the server holds no wallet keys. It uses the CLI's identity and pairings in `~/.clanker-wallet/`, so pair first (`clanker-wallet pair '<JSON>'`, once per chain family). In a sandbox the server needs the same relay access and `~/.clanker-wallet/` writes as the CLI.
|
|
197
|
+
|
|
198
|
+
If your host supports MCP, the human can add the server instead of you running `tx` / `send`:
|
|
199
|
+
|
|
200
|
+
Claude Code:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
claude mcp add clanker-wallet -- npx -y clanker-wallet mcp
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor (`~/.cursor/mcp.json`), and any other MCP host that runs local stdio servers (command `npx`, args `-y clanker-wallet mcp`):
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"mcpServers": {
|
|
211
|
+
"clanker-wallet": { "command": "npx", "args": ["-y", "clanker-wallet", "mcp"] }
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
| Tool | Arguments | Returns |
|
|
217
|
+
|------|-----------|---------|
|
|
218
|
+
| `request_evm_tx` | `chain_id`, `to`, `value` (wei, integer string), `data` (default `0x`), `reason`, optional `urgency` / `expected_outcome` | `{"request_id":"req_...","status":"pending","expires_at":"..."}` |
|
|
219
|
+
| `request_transfer` | `family` (`bitcoin`), `network` (`mainnet`, `testnet`, `signet`), `to`, `amount` (sats, integer string), `reason`, optional `urgency` / `expected_outcome` | as above |
|
|
220
|
+
| `get_request` | `request_id`, optional `wait_seconds` (at most 50) | `status`: `pending`, `approved` (with `tx_hash`), `rejected`, `failed` (with `error`), `unknown` (with `error`) or `expired` |
|
|
221
|
+
| `list_pairings` | none | `agent_id`, `fingerprint`, and the paired families (`evm`, `bitcoin`) with network, wallet and mode |
|
|
222
|
+
|
|
223
|
+
Flow: call `list_pairings` to see which chains you can use, call `request_evm_tx` or `request_transfer` (same rules as `tx` / `send`: wei and sats as integer strings, real addresses), tell the human a request is waiting (see "Transaction notifications"), then call `get_request` with `wait_seconds` until the status is no longer `pending`. Invalid input or a missing pairing comes back as a tool error and nothing is sent. After 1 h the server stops waiting and reports `expired`, and a restarted server no longer knows its earlier requests (`get_request` then reports an unknown `request_id`). In both cases the human may still approve the request in their wallet, so ask them before sending it again. `unknown` means the request was delivered but the server lost its relay connection: the human may still approve it, so ask them before sending it again. Chia is not offered over MCP yet.
|
|
224
|
+
|
|
175
225
|
## Troubleshooting
|
|
176
226
|
|
|
177
227
|
- **exit code 5 / "invalid --flag: ..."** — fix the named flag; nothing was sent. Usually a placeholder `--to`, a `--value` in ETH instead of wei, or `--data` that isn't even-length hex
|
|
178
|
-
- **exit code 4 / "not paired"** — run `clanker-wallet pair '<JSON>'` first
|
|
228
|
+
- **exit code 4 / "not paired"** — run `clanker-wallet pair '<JSON>'` first (for `send`, with the JSON from the web app's bitcoin or chia tab)
|
|
179
229
|
- **exit code 2 / timeout** — human didn't respond in time, increase `--timeout` or ask them to check the web app
|
|
180
230
|
- **exit code 1 / "rejected"** — human clicked reject. Explain why the tx is needed and try again if appropriate.
|
|
181
231
|
- **exit code 3 / "relay connection error: could not reach wss://..."** — if you run in a sandbox or behind a firewall, allow that host (`relay.clanker-wallet.xyz` by default) and, if the error mentions it, rerun with `NODE_USE_ENV_PROXY=1` (see "Running in a sandbox"); otherwise the relay may be down or rate limiting, try again in a few seconds
|
package/bin/cli.js
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { ClankerWallet, InvalidTxError, validateTx } from '../dist/index.js'
|
|
2
|
+
import { ClankerWallet, InvalidTxError, validateTransfer, validateTx } from '../dist/index.js'
|
|
3
|
+
import { createMcpServer, MAX_WAIT_SECONDS, REQUEST_STATUSES } from '../dist/mcp.js'
|
|
3
4
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from 'fs'
|
|
4
5
|
import { join } from 'path'
|
|
5
6
|
import { homedir } from 'os'
|
|
7
|
+
import { createInterface } from 'readline'
|
|
6
8
|
|
|
7
9
|
// ── constants ────────────────────────────────────────────────────────
|
|
8
10
|
const STATE_DIR = join(homedir(), '.clanker-wallet')
|
|
9
11
|
const IDENTITY_FILE = join(STATE_DIR, 'identity.json')
|
|
10
|
-
|
|
12
|
+
// one pairing per chain family: pairings/<family>.json. 0.4.x wrote pairing.json (evm only); it is still read as the
|
|
13
|
+
// evm pairing and never moved, rewritten or deleted
|
|
14
|
+
const PAIRINGS_DIR = join(STATE_DIR, 'pairings')
|
|
15
|
+
const LEGACY_PAIRING_FILE = join(STATE_DIR, 'pairing.json')
|
|
16
|
+
const FAMILIES = ['evm', 'bitcoin', 'chia']
|
|
11
17
|
|
|
12
18
|
const EXIT = { OK: 0, REJECTED: 1, TIMEOUT: 2, CONNECTION: 3, NOT_PAIRED: 4, BAD_ARGS: 5 }
|
|
13
19
|
const DEFAULT_TIMEOUT_MS = 3_600_000
|
|
14
20
|
/** validateTx field -> the tx flag that sets it */
|
|
15
21
|
const FIELD_FLAGS = { chain_id: '--chain', to: '--to', value: '--value', data: '--data' }
|
|
22
|
+
/** per send network flag (named after the asset it sends): the pairing family it uses and its amount/fee flags */
|
|
23
|
+
const SEND_FAMILIES = {
|
|
24
|
+
btc: { family: 'bitcoin', amountFlag: 'sats', networks: 'mainnet, testnet or signet' },
|
|
25
|
+
xch: { family: 'chia', amountFlag: 'mojos', feeFlag: 'fee-mojos', networks: 'mainnet or testnet11' },
|
|
26
|
+
}
|
|
16
27
|
|
|
17
28
|
// ── helpers ──────────────────────────────────────────────────────────
|
|
18
29
|
const hex = (bytes) => Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')
|
|
@@ -50,15 +61,20 @@ function saveIdentity(data) {
|
|
|
50
61
|
chmodSync(IDENTITY_FILE, 0o600)
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
function
|
|
54
|
-
if (!existsSync(
|
|
55
|
-
try { return JSON.parse(readFileSync(
|
|
64
|
+
function readJson(path) {
|
|
65
|
+
if (!existsSync(path)) return null
|
|
66
|
+
try { return JSON.parse(readFileSync(path, 'utf8')) }
|
|
56
67
|
catch { return null }
|
|
57
68
|
}
|
|
58
69
|
|
|
59
|
-
function
|
|
70
|
+
function loadPairing(family) {
|
|
71
|
+
return readJson(join(PAIRINGS_DIR, `${family}.json`)) ?? (family === 'evm' ? readJson(LEGACY_PAIRING_FILE) : null)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function savePairing(family, data) {
|
|
60
75
|
ensureDir()
|
|
61
|
-
|
|
76
|
+
if (!existsSync(PAIRINGS_DIR)) mkdirSync(PAIRINGS_DIR, { mode: 0o700 })
|
|
77
|
+
writeFileSync(join(PAIRINGS_DIR, `${family}.json`), JSON.stringify(data, null, 2) + '\n')
|
|
62
78
|
}
|
|
63
79
|
|
|
64
80
|
/** restore or create a ClankerWallet with persisted identity */
|
|
@@ -114,11 +130,19 @@ function cmdPair(positional) {
|
|
|
114
130
|
die(`pair failed: ${err.message}`, EXIT.BAD_ARGS)
|
|
115
131
|
}
|
|
116
132
|
|
|
117
|
-
|
|
133
|
+
// the family names the pairing file, so only the known ones are accepted
|
|
134
|
+
const family = pairingData.chain_family || 'evm'
|
|
135
|
+
if (!FAMILIES.includes(family)) {
|
|
136
|
+
die(`pair failed: unsupported chain_family ${JSON.stringify(family)} (expected ${FAMILIES.join(', ')})`, EXIT.BAD_ARGS)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
savePairing(family, {
|
|
118
140
|
pubkey: pairingData.pubkey,
|
|
119
141
|
relay: pairingData.relay,
|
|
120
142
|
wallet: pairingData.wallet || null,
|
|
121
143
|
mode: pairingData.mode || null,
|
|
144
|
+
chain_family: pairingData.chain_family,
|
|
145
|
+
chain_network: pairingData.chain_network,
|
|
122
146
|
})
|
|
123
147
|
|
|
124
148
|
out({
|
|
@@ -126,55 +150,43 @@ function cmdPair(positional) {
|
|
|
126
150
|
status: 'paired',
|
|
127
151
|
wallet: pairingData.wallet || null,
|
|
128
152
|
relay: pairingData.relay,
|
|
153
|
+
...(family !== 'evm' && { chain_family: family }),
|
|
129
154
|
})
|
|
130
155
|
}
|
|
131
156
|
|
|
132
|
-
|
|
133
|
-
const pairing = loadPairing()
|
|
134
|
-
if (!pairing) die('not paired — run: clanker-wallet pair \'<JSON>\'', EXIT.NOT_PAIRED)
|
|
135
|
-
|
|
136
|
-
const chain = flags.chain
|
|
137
|
-
const to = flags.to
|
|
138
|
-
if (!chain) die('missing --chain', EXIT.BAD_ARGS)
|
|
139
|
-
if (!to) die('missing --to', EXIT.BAD_ARGS)
|
|
140
|
-
|
|
141
|
-
if (!/^[0-9]+$/.test(chain)) die(`invalid --chain: must be a positive integer chain id, got ${JSON.stringify(chain)}`, EXIT.BAD_ARGS)
|
|
157
|
+
function parseTimeout(flags) {
|
|
142
158
|
if (flags.timeout !== undefined && !/^[1-9][0-9]*$/.test(flags.timeout)) {
|
|
143
159
|
die(`invalid --timeout: must be a positive number of milliseconds, got ${JSON.stringify(flags.timeout)}`, EXIT.BAD_ARGS)
|
|
144
160
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// same checks requestTx runs, done here so a bad flag exits 5 instead of surfacing as a connection error
|
|
148
|
-
let request
|
|
149
|
-
try {
|
|
150
|
-
request = validateTx(Number(chain), { to, value: flags.value ?? '0', data: flags.data ?? '0x' })
|
|
151
|
-
} catch (err) {
|
|
152
|
-
if (!(err instanceof InvalidTxError)) throw err
|
|
153
|
-
die(`invalid ${FIELD_FLAGS[err.field]}: ${err.reason}`, EXIT.BAD_ARGS)
|
|
154
|
-
}
|
|
161
|
+
return flags.timeout ? Number(flags.timeout) : DEFAULT_TIMEOUT_MS
|
|
162
|
+
}
|
|
155
163
|
|
|
164
|
+
function contextFrom(flags) {
|
|
156
165
|
const context = {}
|
|
157
166
|
if (flags.reason) context.reason = flags.reason
|
|
158
167
|
if (flags.urgency) context.urgency = flags.urgency
|
|
159
168
|
if (flags['expected-outcome']) context.expected_outcome = flags['expected-outcome']
|
|
169
|
+
return Object.keys(context).length > 0 ? context : undefined
|
|
170
|
+
}
|
|
160
171
|
|
|
161
|
-
|
|
162
|
-
|
|
172
|
+
/** a ClankerWallet (the saved identity by default) paired from a saved pairing */
|
|
173
|
+
function pairedWallet(pairing, wallet = getWallet()) {
|
|
174
|
+
wallet.pair(JSON.stringify({
|
|
163
175
|
version: 1,
|
|
164
176
|
pubkey: pairing.pubkey,
|
|
165
177
|
relay: pairing.relay,
|
|
166
178
|
wallet: pairing.wallet,
|
|
167
179
|
mode: pairing.mode || undefined,
|
|
168
|
-
|
|
169
|
-
|
|
180
|
+
chain_family: pairing.chain_family,
|
|
181
|
+
chain_network: pairing.chain_network,
|
|
182
|
+
}))
|
|
183
|
+
return wallet
|
|
184
|
+
}
|
|
170
185
|
|
|
186
|
+
/** wait for the human's answer and exit with its code */
|
|
187
|
+
async function exitWithResult(request) {
|
|
171
188
|
try {
|
|
172
|
-
const txHash = await
|
|
173
|
-
chain_id: request.chain_id,
|
|
174
|
-
transaction: request.transaction,
|
|
175
|
-
context: Object.keys(context).length > 0 ? context : undefined,
|
|
176
|
-
timeout_ms: timeout,
|
|
177
|
-
})
|
|
189
|
+
const txHash = await request
|
|
178
190
|
out({ tx_hash: txHash, status: 'approved' })
|
|
179
191
|
process.exit(EXIT.OK)
|
|
180
192
|
} catch (err) {
|
|
@@ -192,15 +204,99 @@ async function cmdTx(flags) {
|
|
|
192
204
|
}
|
|
193
205
|
}
|
|
194
206
|
|
|
207
|
+
async function cmdTx(flags) {
|
|
208
|
+
const pairing = loadPairing('evm')
|
|
209
|
+
if (!pairing) die('not paired — run: clanker-wallet pair \'<JSON>\'', EXIT.NOT_PAIRED)
|
|
210
|
+
|
|
211
|
+
const chain = flags.chain
|
|
212
|
+
const to = flags.to
|
|
213
|
+
if (!chain) die('missing --chain', EXIT.BAD_ARGS)
|
|
214
|
+
if (!to) die('missing --to', EXIT.BAD_ARGS)
|
|
215
|
+
|
|
216
|
+
if (!/^[0-9]+$/.test(chain)) die(`invalid --chain: must be a positive integer chain id, got ${JSON.stringify(chain)}`, EXIT.BAD_ARGS)
|
|
217
|
+
const timeout = parseTimeout(flags)
|
|
218
|
+
|
|
219
|
+
// same checks requestTx runs, done here so a bad flag exits 5 instead of surfacing as a connection error
|
|
220
|
+
let request
|
|
221
|
+
try {
|
|
222
|
+
request = validateTx(Number(chain), { to, value: flags.value ?? '0', data: flags.data ?? '0x' })
|
|
223
|
+
} catch (err) {
|
|
224
|
+
if (!(err instanceof InvalidTxError)) throw err
|
|
225
|
+
die(`invalid ${FIELD_FLAGS[err.field]}: ${err.reason}`, EXIT.BAD_ARGS)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const wallet = pairedWallet(pairing)
|
|
229
|
+
await exitWithResult(wallet.requestTx({
|
|
230
|
+
chain_id: request.chain_id,
|
|
231
|
+
transaction: request.transaction,
|
|
232
|
+
context: contextFrom(flags),
|
|
233
|
+
timeout_ms: timeout,
|
|
234
|
+
}))
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function cmdSend(flags) {
|
|
238
|
+
const picked = Object.keys(SEND_FAMILIES).filter((flag) => flags[flag] !== undefined)
|
|
239
|
+
if (picked.length !== 1) die('invalid --btc/--xch: give exactly one of --btc <network> or --xch <network>', EXIT.BAD_ARGS)
|
|
240
|
+
const networkFlag = picked[0]
|
|
241
|
+
const { family, amountFlag, feeFlag, networks } = SEND_FAMILIES[networkFlag]
|
|
242
|
+
const network = flags[networkFlag]
|
|
243
|
+
if (network === true) die(`invalid --${networkFlag}: give the network, ${networks}`, EXIT.BAD_ARGS)
|
|
244
|
+
|
|
245
|
+
// another family's amount/fee flag is a mistake, not something to ignore
|
|
246
|
+
for (const flag of ['sats', 'mojos', 'fee-mojos']) {
|
|
247
|
+
if (flag !== amountFlag && flag !== feeFlag && flags[flag] !== undefined) {
|
|
248
|
+
die(`invalid --${flag}: not a --${networkFlag} flag (${family} amounts are --${amountFlag}${feeFlag ? `, fee --${feeFlag}` : ', and the wallet sets the fee'})`, EXIT.BAD_ARGS)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
for (const flag of ['to', amountFlag, feeFlag]) {
|
|
252
|
+
if (flag && flags[flag] === true) die(`invalid --${flag}: missing value`, EXIT.BAD_ARGS)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const pairing = loadPairing(family)
|
|
256
|
+
if (!pairing) die(`not paired with a ${family} wallet — open the web app's ${family} tab and run: clanker-wallet pair '<JSON>'`, EXIT.NOT_PAIRED)
|
|
257
|
+
|
|
258
|
+
if (!flags.to) die('missing --to', EXIT.BAD_ARGS)
|
|
259
|
+
if (flags[amountFlag] === undefined) die(`missing --${amountFlag}`, EXIT.BAD_ARGS)
|
|
260
|
+
const timeout = parseTimeout(flags)
|
|
261
|
+
|
|
262
|
+
const transfer = { asset: networkFlag, to: flags.to, amount: flags[amountFlag] }
|
|
263
|
+
if (feeFlag && flags[feeFlag] !== undefined) transfer.fee = flags[feeFlag]
|
|
264
|
+
const flagFor = { chain_family: networkFlag, chain_network: networkFlag, asset: networkFlag, to: 'to', amount: amountFlag, fee: feeFlag }
|
|
265
|
+
const badArg = (err) => die(`invalid --${flagFor[err.field]}: ${err.reason}`, EXIT.BAD_ARGS)
|
|
266
|
+
|
|
267
|
+
// same checks requestTransfer runs, done here so a bad flag exits 5 instead of surfacing as a connection error
|
|
268
|
+
try {
|
|
269
|
+
validateTransfer(family, network, transfer)
|
|
270
|
+
} catch (err) {
|
|
271
|
+
if (!(err instanceof InvalidTxError)) throw err
|
|
272
|
+
badArg(err)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const wallet = pairedWallet(pairing)
|
|
276
|
+
// requestTransfer also refuses a network the pairing isn't for; that is a bad flag too
|
|
277
|
+
const request = wallet.requestTransfer({ chain_network: network, transfer, context: contextFrom(flags), timeout_ms: timeout })
|
|
278
|
+
await request.catch((err) => { if (err instanceof InvalidTxError) badArg(err) })
|
|
279
|
+
await exitWithResult(request)
|
|
280
|
+
}
|
|
281
|
+
|
|
195
282
|
function cmdStatus() {
|
|
196
283
|
const identity = loadIdentity()
|
|
197
|
-
const
|
|
284
|
+
const pairings = {}
|
|
285
|
+
for (const family of FAMILIES) {
|
|
286
|
+
const pairing = loadPairing(family)
|
|
287
|
+
if (pairing) {
|
|
288
|
+
pairings[family] = { wallet: pairing.wallet || null, relay: pairing.relay || null, mode: pairing.mode || null, chain_network: pairing.chain_network || null }
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const evm = pairings.evm
|
|
198
292
|
|
|
293
|
+
// wallet/relay stay the evm pairing's, as in 0.4.x
|
|
199
294
|
out({
|
|
200
295
|
agent_id: identity?.agent_id || null,
|
|
201
|
-
paired:
|
|
202
|
-
wallet:
|
|
203
|
-
relay:
|
|
296
|
+
paired: Object.keys(pairings).length > 0,
|
|
297
|
+
wallet: evm?.wallet || null,
|
|
298
|
+
relay: evm?.relay || null,
|
|
299
|
+
pairings,
|
|
204
300
|
})
|
|
205
301
|
}
|
|
206
302
|
|
|
@@ -213,9 +309,55 @@ function cmdWhoami() {
|
|
|
213
309
|
})
|
|
214
310
|
}
|
|
215
311
|
|
|
312
|
+
function packageVersion() {
|
|
313
|
+
return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version
|
|
314
|
+
}
|
|
315
|
+
|
|
216
316
|
function cmdVersion() {
|
|
217
|
-
|
|
218
|
-
|
|
317
|
+
process.stdout.write(packageVersion() + '\n')
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function showMcpHelp() {
|
|
321
|
+
process.stderr.write(`clanker-wallet mcp — MCP server over stdio for assistants (Claude Desktop, Claude Code, Cursor, ...)
|
|
322
|
+
|
|
323
|
+
uses this agent's identity and pairings (~/.clanker-wallet/); pair first with: clanker-wallet pair '<JSON>'
|
|
324
|
+
every request waits for the human to approve it on their own device.
|
|
325
|
+
|
|
326
|
+
tools:
|
|
327
|
+
request_evm_tx ask for an evm transaction (value in wei, integer string) -> { request_id }
|
|
328
|
+
request_transfer ask for a bitcoin send (amount in sats, integer string) -> { request_id }
|
|
329
|
+
get_request ${REQUEST_STATUSES.join(' | ')}; wait_seconds up to ${MAX_WAIT_SECONDS}
|
|
330
|
+
list_pairings paired chain families, networks, wallets, and the agent fingerprint
|
|
331
|
+
|
|
332
|
+
claude code: claude mcp add clanker-wallet -- npx -y clanker-wallet mcp
|
|
333
|
+
other hosts: {"mcpServers":{"clanker-wallet":{"command":"npx","args":["-y","clanker-wallet","mcp"]}}}
|
|
334
|
+
|
|
335
|
+
requests are kept in memory: after 1 h the server stops waiting (expired), and a restart forgets pending ones.
|
|
336
|
+
expired, unknown (relay lost after delivery) or forgotten: the human may still approve it, so ask before sending it again.
|
|
337
|
+
docs: https://clanker-wallet.xyz/SKILL.md
|
|
338
|
+
`)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** serve MCP on stdin/stdout until stdin ends; stdout carries only protocol messages */
|
|
342
|
+
async function cmdMcp(flags) {
|
|
343
|
+
if (flags.help) return showMcpHelp()
|
|
344
|
+
|
|
345
|
+
const identity = getWallet()
|
|
346
|
+
const server = createMcpServer({
|
|
347
|
+
version: packageVersion(),
|
|
348
|
+
identity: { agent_id: identity.agentId, fingerprint: fingerprint(hex(identity.publicKey)) },
|
|
349
|
+
loadPairing,
|
|
350
|
+
// the identity loaded at startup, which list_pairings shows: re-reading identity.json per request could switch agents mid-run
|
|
351
|
+
wallet: (pairing) => pairedWallet(pairing, new ClankerWallet({ secretKey: identity.secretKey })),
|
|
352
|
+
})
|
|
353
|
+
const lines = createInterface({ input: process.stdin, crlfDelay: Infinity })
|
|
354
|
+
// each line is answered when it settles, so a get_request that waits never holds up the others
|
|
355
|
+
lines.on('line', (line) => {
|
|
356
|
+
server.handleLine(line).then((reply) => { if (reply) process.stdout.write(reply + '\n') })
|
|
357
|
+
})
|
|
358
|
+
await new Promise((resolve) => lines.on('close', resolve))
|
|
359
|
+
// the host is gone: drop pending requests (their relay sockets would keep the process alive)
|
|
360
|
+
process.exit(EXIT.OK)
|
|
219
361
|
}
|
|
220
362
|
|
|
221
363
|
function showHelp() {
|
|
@@ -223,9 +365,11 @@ function showHelp() {
|
|
|
223
365
|
|
|
224
366
|
commands:
|
|
225
367
|
pair '<JSON>' pair with human's web app (one-time setup)
|
|
226
|
-
tx send
|
|
227
|
-
|
|
368
|
+
tx send an evm transaction (blocks until approved/rejected)
|
|
369
|
+
send send btc or xch; the human's wallet builds it (blocks until approved/rejected)
|
|
370
|
+
status check pairing status (one pairing per chain family: evm, bitcoin, chia)
|
|
228
371
|
whoami show agent identity
|
|
372
|
+
mcp run an MCP server over stdio for assistants (Claude Desktop/Code, Cursor, ...); mcp --help
|
|
229
373
|
|
|
230
374
|
tx flags:
|
|
231
375
|
--chain <id> chain ID (required) — 1, 8453, 84532 (base sepolia), etc.
|
|
@@ -237,18 +381,29 @@ tx flags:
|
|
|
237
381
|
--expected-outcome <text> what will happen
|
|
238
382
|
--timeout <ms> how long tx blocks waiting for the human (default: 3600000 = 1 h)
|
|
239
383
|
|
|
384
|
+
send flags (pair the web app's bitcoin or chia tab first):
|
|
385
|
+
send --btc <mainnet|testnet|signet> --to <address> --sats <n>
|
|
386
|
+
send --xch <mainnet|testnet11> --to <address> --mojos <n> [--fee-mojos <n>]
|
|
387
|
+
--to <address> recipient: segwit bc1/tb1 for bitcoin, xch1/txch1 for chia (required)
|
|
388
|
+
--sats <n> bitcoin amount in sats (1 BTC = 100000000); the wallet sets the fee
|
|
389
|
+
--mojos <n> chia amount in mojos (1 XCH = 1000000000000)
|
|
390
|
+
--fee-mojos <n> chia fee in mojos (default: 0), shown to the human
|
|
391
|
+
--reason, --urgency, --expected-outcome, --timeout as for tx
|
|
392
|
+
|
|
240
393
|
exit codes:
|
|
241
394
|
0 success
|
|
242
395
|
1 rejected by human
|
|
243
396
|
2 timeout
|
|
244
397
|
3 connection error (in a sandbox or behind a firewall, allow the relay host, wss://relay.clanker-wallet.xyz/ws by default;
|
|
245
398
|
behind HTTPS_PROXY run with NODE_USE_ENV_PROXY=1)
|
|
246
|
-
4 not paired
|
|
399
|
+
4 not paired (send: not paired for that chain family)
|
|
247
400
|
5 invalid arguments
|
|
248
401
|
|
|
249
402
|
examples:
|
|
250
403
|
clanker-wallet pair '{"version":1,"pubkey":"ab12...","relay":"wss://...","wallet":"0x..."}'
|
|
251
404
|
clanker-wallet tx --chain 84532 --to 0x000000000000000000000000000000000000dEaD --value 1000000000000000 --reason "send 0.001 ETH on base sepolia"
|
|
405
|
+
clanker-wallet send --btc testnet --to tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx --sats 1000 --reason "test payment"
|
|
406
|
+
clanker-wallet send --xch testnet11 --to txch1qqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sw0amhg --mojos 1000 --fee-mojos 0
|
|
252
407
|
clanker-wallet status
|
|
253
408
|
clanker-wallet whoami
|
|
254
409
|
|
|
@@ -267,12 +422,18 @@ switch (cmd) {
|
|
|
267
422
|
case 'tx':
|
|
268
423
|
await cmdTx(flags)
|
|
269
424
|
break
|
|
425
|
+
case 'send':
|
|
426
|
+
await cmdSend(flags)
|
|
427
|
+
break
|
|
270
428
|
case 'status':
|
|
271
429
|
cmdStatus()
|
|
272
430
|
break
|
|
273
431
|
case 'whoami':
|
|
274
432
|
cmdWhoami()
|
|
275
433
|
break
|
|
434
|
+
case 'mcp':
|
|
435
|
+
await cmdMcp(flags)
|
|
436
|
+
break
|
|
276
437
|
case '--version':
|
|
277
438
|
case '-v':
|
|
278
439
|
cmdVersion()
|