@towns-labs/wallet 7.2.0 → 7.3.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 +256 -308
- package/dist/cli.js +260 -246
- package/dist/session-daemon.js +16 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,74 +1,64 @@
|
|
|
1
|
-
#
|
|
1
|
+
# tw — Towns Wallet CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Your keys. Your account. No signup. No login. Just run it.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Run with bunx
|
|
5
|
+
`tw` is a local-first CLI for managing smart accounts, session keys, and on-chain permissions on Towns Protocol. It works for humans at the terminal and for agents over `--json` or `--mcp`.
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
bunx @towns-labs/wallet --help
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
---
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
## Quick Start
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Create an account and send USDC in under a minute:
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
```bash
|
|
18
|
+
# 1. Create a local account (interactive password prompt)
|
|
19
|
+
tw account create --profile main
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- Default: human-readable text
|
|
21
|
+
# 2. Fund it
|
|
22
|
+
tw address --qr --amount 100
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
# 3. Send USDC
|
|
25
|
+
tw account send 10 vitalik.eth
|
|
26
|
+
```
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
# Show all commands
|
|
29
|
-
tw --help
|
|
28
|
+
No custody service. No API key. Everything runs locally with encrypted keystores.
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
tw account create --help
|
|
30
|
+
---
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
tw --llms
|
|
32
|
+
## How It Works
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
tw --mcp
|
|
34
|
+
`tw` manages a local keystore that holds your root key and session keys. Your root key creates and controls your on-chain smart account. Session keys are scoped signers — they can send transactions through the Towns relayer without exposing your root key.
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
tw
|
|
36
|
+
```text
|
|
37
|
+
~/.config/towns/tw/profiles/<env>/<profile>/default.keystore.json
|
|
38
|
+
~/.config/towns/tw/profiles/<env>/<profile>/sessions/<session-name>.json
|
|
42
39
|
```
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
For agents, the session daemon keeps decrypted keys in memory for a bounded duration so automated workflows can sign without prompting for a password on every call.
|
|
45
42
|
|
|
46
|
-
|
|
43
|
+
---
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
tw account create
|
|
50
|
-
```
|
|
45
|
+
## Commands
|
|
51
46
|
|
|
52
|
-
|
|
47
|
+
### Account
|
|
48
|
+
|
|
49
|
+
#### `account create` — Create a new account
|
|
53
50
|
|
|
54
51
|
```bash
|
|
55
52
|
tw account create --profile agent
|
|
56
53
|
```
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```text
|
|
61
|
-
~/.config/towns/tw/profiles/<env>/<profile>/default.keystore.json
|
|
62
|
-
~/.config/towns/tw/profiles/<env>/<profile>/sessions/<session-name>.json
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Resume a previously created profile:
|
|
55
|
+
Resume a previously interrupted flow:
|
|
66
56
|
|
|
67
57
|
```bash
|
|
68
58
|
tw account create --resume --profile agent
|
|
69
59
|
```
|
|
70
60
|
|
|
71
|
-
Non-interactive
|
|
61
|
+
Non-interactive (stdin password + JSON):
|
|
72
62
|
|
|
73
63
|
```bash
|
|
74
64
|
echo "my-password" | tw account create --password-stdin --json
|
|
@@ -80,461 +70,434 @@ Use an explicit keystore path:
|
|
|
80
70
|
tw account create --keystore-path ~/.config/towns/tw/profiles/prod/team/default.keystore.json
|
|
81
71
|
```
|
|
82
72
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Export metadata only (safe default):
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
tw account export --profile agent
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Export metadata in JSON:
|
|
73
|
+
#### `account status` — Check readiness
|
|
92
74
|
|
|
93
75
|
```bash
|
|
94
|
-
tw account
|
|
76
|
+
tw account status --profile agent --json
|
|
95
77
|
```
|
|
96
78
|
|
|
97
|
-
|
|
79
|
+
#### `account balance` — Check USDC balance
|
|
98
80
|
|
|
99
81
|
```bash
|
|
100
|
-
tw account
|
|
82
|
+
tw account balance --profile agent
|
|
83
|
+
tw account balance --profile agent --chain polygon
|
|
101
84
|
```
|
|
102
85
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
Print the main/root account address:
|
|
86
|
+
#### `account address` — Print root address
|
|
106
87
|
|
|
107
88
|
```bash
|
|
108
89
|
tw account address --profile agent
|
|
109
90
|
```
|
|
110
91
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
`tw address` is a root alias for `tw account address` with optional funding display helpers.
|
|
114
|
-
|
|
115
|
-
Print address + default funding context (USDC on Base):
|
|
92
|
+
#### `account send` — Send USDC
|
|
116
93
|
|
|
117
94
|
```bash
|
|
118
|
-
tw
|
|
95
|
+
tw account send 1 0x1111111111111111111111111111111111111111
|
|
96
|
+
tw account send 2.5 vitalik.eth --chain base
|
|
97
|
+
tw account send 10 treasury --chain polygon --json
|
|
119
98
|
```
|
|
120
99
|
|
|
121
|
-
|
|
100
|
+
Use a specific local session (without changing active session):
|
|
122
101
|
|
|
123
102
|
```bash
|
|
124
|
-
tw
|
|
103
|
+
tw account send 1 0x... --profile agent --session worker-2
|
|
125
104
|
```
|
|
126
105
|
|
|
127
|
-
|
|
106
|
+
Use a portable session file (no root keystore required):
|
|
128
107
|
|
|
129
108
|
```bash
|
|
130
|
-
tw
|
|
109
|
+
tw account send 1 0x... --chain base --session-file ./worker-1.session.json
|
|
131
110
|
```
|
|
132
111
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
tw address --token 0x1111111111111111111111111111111111111111 --amount 1 --decimals 18 --link
|
|
137
|
-
```
|
|
112
|
+
`--session` and `--session-file` are mutually exclusive.
|
|
138
113
|
|
|
139
|
-
|
|
114
|
+
#### `account swap` — Swap tokens on the same chain
|
|
140
115
|
|
|
141
|
-
|
|
116
|
+
Powered by [relay.link](https://relay.link). Interactive confirmation by default.
|
|
142
117
|
|
|
143
118
|
```bash
|
|
144
|
-
tw account
|
|
119
|
+
tw account swap --from ETH --to USDC --amount 0.1 --chain base
|
|
120
|
+
tw account swap --from USDC --to ETH --amount 50 --chain base --yes --json
|
|
145
121
|
```
|
|
146
122
|
|
|
147
|
-
|
|
123
|
+
#### `account bridge` — Bridge tokens cross-chain
|
|
148
124
|
|
|
149
125
|
```bash
|
|
150
|
-
tw account
|
|
126
|
+
tw account bridge --token USDC --amount 100 --to-chain polygon
|
|
127
|
+
tw account bridge --token ETH --amount 0.5 --to-chain base --recipient 0x... --yes --json
|
|
151
128
|
```
|
|
152
129
|
|
|
153
|
-
|
|
130
|
+
#### `account delegate` — Delegate on additional chains
|
|
154
131
|
|
|
155
|
-
|
|
132
|
+
If your account was created on Base and you need it on Polygon:
|
|
156
133
|
|
|
157
134
|
```bash
|
|
158
|
-
tw account
|
|
135
|
+
echo "my-password" | tw account delegate --chain polygon --profile agent --password-stdin
|
|
159
136
|
```
|
|
160
137
|
|
|
161
|
-
|
|
138
|
+
#### `account history` — Relayer call history
|
|
162
139
|
|
|
163
140
|
```bash
|
|
164
|
-
tw account
|
|
141
|
+
tw account history --profile agent
|
|
142
|
+
tw account history --address 0x... --limit 10 --offset 20
|
|
143
|
+
tw account history --chain base,polygon --json
|
|
165
144
|
```
|
|
166
145
|
|
|
167
|
-
|
|
146
|
+
`--limit` defaults to 20 (max 100). `offset + limit` must be <= 1000.
|
|
147
|
+
|
|
148
|
+
#### `account export` — Export metadata or private keys
|
|
168
149
|
|
|
169
150
|
```bash
|
|
170
|
-
tw account
|
|
151
|
+
tw account export --profile agent --json
|
|
152
|
+
tw account export --profile agent --show-private
|
|
171
153
|
```
|
|
172
154
|
|
|
173
|
-
|
|
155
|
+
#### `account nonce` — Read account nonce
|
|
174
156
|
|
|
175
157
|
```bash
|
|
176
|
-
tw account
|
|
158
|
+
tw account nonce --profile agent
|
|
177
159
|
```
|
|
178
160
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Get paginated relayer call history for an EOA. By default this uses your local profile root EOA from keystore:
|
|
161
|
+
#### `account update password` — Rotate keystore password
|
|
182
162
|
|
|
183
163
|
```bash
|
|
184
|
-
tw account
|
|
164
|
+
tw account update password --profile agent
|
|
165
|
+
printf "old\nnew\n" | tw account update password --current-password-stdin --new-password-stdin --json
|
|
185
166
|
```
|
|
186
167
|
|
|
187
|
-
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Address
|
|
171
|
+
|
|
172
|
+
`tw address` is a top-level shortcut for showing your funding address with optional payment helpers.
|
|
188
173
|
|
|
189
174
|
```bash
|
|
190
|
-
tw
|
|
175
|
+
tw address
|
|
176
|
+
tw address --link --amount 100
|
|
177
|
+
tw address --qr --amount 100
|
|
191
178
|
```
|
|
192
179
|
|
|
193
|
-
|
|
180
|
+
Custom token:
|
|
194
181
|
|
|
195
182
|
```bash
|
|
196
|
-
tw
|
|
183
|
+
tw address --token 0x... --amount 1 --decimals 18 --link
|
|
197
184
|
```
|
|
198
185
|
|
|
199
|
-
|
|
186
|
+
---
|
|
200
187
|
|
|
201
|
-
|
|
202
|
-
- `--offset` defaults to `0`
|
|
203
|
-
- `offset + limit` must be `<= 1000`
|
|
204
|
-
- History queries are EOA-based (root EOA), not delegated account address
|
|
188
|
+
### Session Keys
|
|
205
189
|
|
|
206
|
-
|
|
190
|
+
Session keys are scoped signers that can act on behalf of your account without exposing the root key.
|
|
207
191
|
|
|
208
|
-
|
|
192
|
+
#### `session create` — Create and authorize a session key
|
|
209
193
|
|
|
210
|
-
```
|
|
211
|
-
|
|
194
|
+
```bash
|
|
195
|
+
echo "my-password" | tw session create worker-1 --profile agent --password-stdin --json
|
|
212
196
|
```
|
|
213
197
|
|
|
214
|
-
|
|
198
|
+
Create and activate immediately:
|
|
215
199
|
|
|
216
200
|
```bash
|
|
217
|
-
tw
|
|
218
|
-
tw contacts add treasury 0x1111111111111111111111111111111111111111
|
|
201
|
+
echo "my-password" | tw session create worker-2 --profile agent --activate --password-stdin
|
|
219
202
|
```
|
|
220
203
|
|
|
221
|
-
|
|
204
|
+
Grant full wildcard access:
|
|
222
205
|
|
|
223
206
|
```bash
|
|
224
|
-
tw
|
|
225
|
-
tw contacts show vitalik --json
|
|
207
|
+
echo "my-password" | tw session create worker-admin --profile agent --full-access --password-stdin
|
|
226
208
|
```
|
|
227
209
|
|
|
228
|
-
|
|
210
|
+
`--full-access` is mutually exclusive with `--target`, `--selector`, `--spend-limit`, `--spend-limit-raw`, and `--spend-period`. Omitting both `--target` and `--selector` uses wildcard call permissions by default.
|
|
211
|
+
|
|
212
|
+
Resume an interrupted flow:
|
|
229
213
|
|
|
230
214
|
```bash
|
|
231
|
-
tw
|
|
232
|
-
tw contacts rename vitalik vitalik-main
|
|
233
|
-
tw contacts remove vitalik-main
|
|
215
|
+
echo "my-password" | tw session create worker-1 --profile agent --resume --password-stdin --json
|
|
234
216
|
```
|
|
235
217
|
|
|
236
|
-
|
|
218
|
+
#### `session list` — List local sessions
|
|
237
219
|
|
|
238
220
|
```bash
|
|
239
|
-
tw
|
|
240
|
-
tw
|
|
241
|
-
tw contacts import ./contacts.json --force --json
|
|
221
|
+
tw session list --profile agent --json
|
|
222
|
+
tw session list --profile agent --on-chain --json
|
|
242
223
|
```
|
|
243
224
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
- `importedCount`
|
|
247
|
-
- `skippedInvalidCount`
|
|
248
|
-
- `skippedConflictCount`
|
|
249
|
-
- `overwrittenCount`
|
|
250
|
-
|
|
251
|
-
Send via alias:
|
|
225
|
+
#### `session rotate` — Rotate the active session
|
|
252
226
|
|
|
253
227
|
```bash
|
|
254
|
-
tw
|
|
228
|
+
echo "my-password" | tw session rotate --profile agent --password-stdin --json
|
|
229
|
+
echo "my-password" | tw session rotate --profile agent --new-name worker-3 --password-stdin
|
|
230
|
+
echo "my-password" | tw session rotate --profile agent --resume --password-stdin --json
|
|
255
231
|
```
|
|
256
232
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
- If ENS re-resolution fails, `tw account send` falls back to the stored address.
|
|
260
|
-
- If ENS re-resolution succeeds but points to a different address than stored, send aborts and requires explicit `tw contacts update`.
|
|
261
|
-
|
|
262
|
-
Send with a portable session file (no root keystore required):
|
|
233
|
+
#### `session revoke` — Revoke a session on-chain
|
|
263
234
|
|
|
264
235
|
```bash
|
|
265
|
-
tw
|
|
266
|
-
|
|
236
|
+
echo "my-password" | tw session revoke worker-1 --profile agent --password-stdin --json
|
|
237
|
+
echo "my-password" | tw session revoke worker-2 --profile agent --force --password-stdin
|
|
238
|
+
echo "my-password" | tw session revoke worker-2 --profile agent --resume --password-stdin --json
|
|
267
239
|
```
|
|
268
240
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
## Account Status
|
|
272
|
-
|
|
273
|
-
Show compact readiness status:
|
|
241
|
+
#### `session export` — Export as portable file
|
|
274
242
|
|
|
275
243
|
```bash
|
|
276
|
-
|
|
244
|
+
TW_PASSWORD="my-password" TW_EXPORT_PASSWORD="export-password" \
|
|
245
|
+
tw session export worker-1 --profile agent --output ./worker-1.session.json
|
|
277
246
|
```
|
|
278
247
|
|
|
279
|
-
|
|
248
|
+
Or via stdin:
|
|
280
249
|
|
|
281
250
|
```bash
|
|
282
|
-
tw
|
|
251
|
+
echo "export-password" | tw session export worker-1 --profile agent \
|
|
252
|
+
--output ./worker-1.session.json --export-password-stdin
|
|
283
253
|
```
|
|
284
254
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
Rotate local keystore encryption password (interactive):
|
|
255
|
+
#### `session import` — Import a portable session
|
|
288
256
|
|
|
289
257
|
```bash
|
|
290
|
-
tw
|
|
258
|
+
tw session import ./worker-1.session.json --profile worker-1
|
|
291
259
|
```
|
|
292
260
|
|
|
293
|
-
|
|
261
|
+
Creates a session-only profile that can execute `account send` but cannot run manager commands requiring a root keystore.
|
|
294
262
|
|
|
295
|
-
|
|
296
|
-
printf "old-password\nnew-password\n" | tw account update password --current-password-stdin --new-password-stdin --json
|
|
297
|
-
```
|
|
263
|
+
---
|
|
298
264
|
|
|
299
|
-
|
|
265
|
+
### Session Daemon
|
|
300
266
|
|
|
301
|
-
|
|
267
|
+
The daemon holds decrypted session keys in memory so automated workflows can sign without re-entering passwords.
|
|
268
|
+
|
|
269
|
+
#### `session start` — Start the daemon
|
|
302
270
|
|
|
303
271
|
```bash
|
|
304
|
-
|
|
272
|
+
tw session start --json
|
|
273
|
+
tw session start --foreground --json
|
|
305
274
|
```
|
|
306
275
|
|
|
307
|
-
|
|
276
|
+
Idempotent — if already running, returns the active pid/socket.
|
|
277
|
+
|
|
278
|
+
#### `session unlock` — Load a key into daemon memory
|
|
308
279
|
|
|
309
280
|
```bash
|
|
310
|
-
echo "my-password" | tw session
|
|
281
|
+
echo "my-password" | tw session unlock worker-1 --profile agent --password-stdin --duration 15m --json
|
|
311
282
|
```
|
|
312
283
|
|
|
313
|
-
|
|
284
|
+
Key material stays in daemon memory only; encrypted files remain unchanged.
|
|
285
|
+
|
|
286
|
+
#### `session lock` — Remove a key from daemon memory
|
|
314
287
|
|
|
315
288
|
```bash
|
|
316
|
-
|
|
289
|
+
tw session lock worker-1 --json
|
|
317
290
|
```
|
|
318
291
|
|
|
319
|
-
|
|
292
|
+
#### `session status` — Check daemon health
|
|
320
293
|
|
|
321
294
|
```bash
|
|
322
|
-
|
|
295
|
+
tw session status --json
|
|
323
296
|
```
|
|
324
297
|
|
|
325
|
-
|
|
326
|
-
Omitting both `--target` and `--selector` uses wildcard call permissions by default; you do not need to pass magic wildcard values.
|
|
327
|
-
`--spend-limit` accepts human USDC amounts (for example `10` = `10 USDC`). Use `--spend-limit-raw` for base units.
|
|
328
|
-
|
|
329
|
-
## Session List
|
|
330
|
-
|
|
331
|
-
List local sessions:
|
|
298
|
+
#### `session stop` — Shut down the daemon
|
|
332
299
|
|
|
333
300
|
```bash
|
|
334
|
-
tw session
|
|
301
|
+
tw session stop --json
|
|
335
302
|
```
|
|
336
303
|
|
|
337
|
-
|
|
304
|
+
---
|
|
338
305
|
|
|
339
|
-
|
|
340
|
-
tw session list --profile agent --on-chain --json
|
|
341
|
-
```
|
|
306
|
+
### Permissions
|
|
342
307
|
|
|
343
|
-
|
|
308
|
+
Fine-grained on-chain permission rules for session keys.
|
|
344
309
|
|
|
345
|
-
|
|
310
|
+
#### `permissions list` — List keys and their permissions
|
|
346
311
|
|
|
347
312
|
```bash
|
|
348
|
-
|
|
349
|
-
tw session export worker-1 --profile agent --output ./worker-1.session.json
|
|
313
|
+
tw permissions list --profile agent --json
|
|
350
314
|
```
|
|
351
315
|
|
|
352
|
-
|
|
316
|
+
#### `permissions show` — Show rules for a specific key
|
|
353
317
|
|
|
354
318
|
```bash
|
|
355
|
-
|
|
356
|
-
|
|
319
|
+
tw permissions show agent-key --profile agent --json
|
|
320
|
+
tw permissions show --key-hash 0xaaa... --profile agent --json
|
|
357
321
|
```
|
|
358
322
|
|
|
359
|
-
|
|
323
|
+
#### `permissions grant` — Grant a permission rule
|
|
360
324
|
|
|
361
|
-
|
|
325
|
+
Wildcard call permission:
|
|
362
326
|
|
|
363
327
|
```bash
|
|
364
|
-
tw
|
|
328
|
+
echo "my-password" | tw permissions grant agent-key --type call --target any --selector any --profile agent --password-stdin --json
|
|
365
329
|
```
|
|
366
330
|
|
|
367
|
-
|
|
331
|
+
Daily USDC spend limit:
|
|
368
332
|
|
|
369
333
|
```bash
|
|
370
|
-
|
|
334
|
+
echo "my-password" | tw permissions grant agent-key --type spend --token USDC --spend-limit 100 --period day --profile agent --password-stdin --json
|
|
371
335
|
```
|
|
372
336
|
|
|
373
|
-
|
|
337
|
+
Use `--spend-limit-raw` for base units instead of human amounts.
|
|
374
338
|
|
|
375
|
-
|
|
339
|
+
#### `permissions revoke` — Revoke permission rules
|
|
376
340
|
|
|
377
|
-
|
|
341
|
+
Revoke a single rule:
|
|
378
342
|
|
|
379
343
|
```bash
|
|
380
|
-
echo "my-password" | tw
|
|
344
|
+
echo "my-password" | tw permissions revoke agent-key --rule call:0x...:0xa9059cbb --profile agent --password-stdin --json
|
|
381
345
|
```
|
|
382
346
|
|
|
383
|
-
|
|
347
|
+
Revoke all rules for a key:
|
|
384
348
|
|
|
385
349
|
```bash
|
|
386
|
-
echo "my-password" | tw
|
|
350
|
+
echo "my-password" | tw permissions revoke agent-key --all --profile agent --password-stdin --json
|
|
387
351
|
```
|
|
388
352
|
|
|
389
|
-
|
|
390
|
-
Omitting both `--target` and `--selector` uses wildcard call permissions by default; you do not need to pass magic wildcard values.
|
|
391
|
-
`--spend-limit` accepts human USDC amounts (for example `10` = `10 USDC`). Use `--spend-limit-raw` for base units.
|
|
353
|
+
---
|
|
392
354
|
|
|
393
|
-
|
|
355
|
+
### Escrow
|
|
394
356
|
|
|
395
|
-
|
|
396
|
-
echo "my-password" | tw session rotate --profile agent --resume --password-stdin --json
|
|
397
|
-
```
|
|
357
|
+
USDC escrow: lock funds as buyer with a seller and oracle; settle (oracle signs) or refund after deadline.
|
|
398
358
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
Revoke a non-active session on-chain, verify, then delete local file:
|
|
359
|
+
#### `escrow create` — Create a new USDC escrow
|
|
402
360
|
|
|
403
361
|
```bash
|
|
404
|
-
|
|
362
|
+
tw escrow create 50 0x1111111111111111111111111111111111111111 \
|
|
363
|
+
--oracle 0x2222222222222222222222222222222222222222 --deadline 24h
|
|
364
|
+
tw escrow create 100 0x... --oracle 0x... --deadline 2d --chain base --env prod --json
|
|
405
365
|
```
|
|
406
366
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
```
|
|
367
|
+
- **Amount** and **seller** are positional; **oracle** and **deadline** are required options.
|
|
368
|
+
- **Deadline**: relative (`1h`, `2d`, `30m`, `1w`) or unix timestamp. After deadline, anyone can call `escrow refund`.
|
|
369
|
+
- Optional **salt** (hex, up to 12 bytes) for unique escrow IDs on repeated orders.
|
|
370
|
+
- Uses session key (daemon or direct) to sign; supports `--session-file` like `account send`.
|
|
412
371
|
|
|
413
|
-
|
|
372
|
+
#### `escrow status` — Check on-chain status
|
|
414
373
|
|
|
415
374
|
```bash
|
|
416
|
-
|
|
375
|
+
tw escrow status 0x<64 hex chars> --env prod
|
|
376
|
+
tw escrow status 0x... --chain base --json
|
|
417
377
|
```
|
|
418
378
|
|
|
419
|
-
|
|
379
|
+
Escrow ID must be the full 32-byte hex (0x + 64 hex chars), e.g. from `escrow create` output.
|
|
420
380
|
|
|
421
|
-
|
|
381
|
+
#### `escrow settle` — Settle (release USDC to seller)
|
|
422
382
|
|
|
423
|
-
|
|
383
|
+
Oracle signs a settlement; any account can submit the signed settlement via relayer.
|
|
424
384
|
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
|
|
385
|
+
```bash
|
|
386
|
+
TW_ORACLE_PRIVATE_KEY=0x... tw escrow settle 0x<escrowId> \
|
|
387
|
+
--settlement-id 0x<orderId> --oracle 0x2222... --profile agent
|
|
428
388
|
```
|
|
429
389
|
|
|
430
|
-
|
|
390
|
+
Or with pre-signed signature (oracle signs offline):
|
|
431
391
|
|
|
432
392
|
```bash
|
|
433
|
-
|
|
434
|
-
TW_PASSWORD="my-password" tw agent create bob --profile agent
|
|
393
|
+
tw escrow settle 0x<escrowId> --settlement-id 0x... --oracle 0x... --signature 0x... --profile agent
|
|
435
394
|
```
|
|
436
395
|
|
|
437
|
-
|
|
396
|
+
Settlement ID is the bytes32 order ID used at creation (see create output or `escrow status`).
|
|
438
397
|
|
|
439
|
-
|
|
440
|
-
TW_PASSWORD="my-password" tw agent list --profile agent --json
|
|
441
|
-
```
|
|
398
|
+
#### `escrow refund` — Refund to buyer after deadline
|
|
442
399
|
|
|
443
|
-
|
|
400
|
+
Permissionless: after the escrow deadline, anyone can trigger refund to the buyer.
|
|
444
401
|
|
|
445
402
|
```bash
|
|
446
|
-
|
|
403
|
+
tw escrow refund 0x<escrowId> --profile agent
|
|
404
|
+
tw escrow refund 0x<escrowId> --env prod --json
|
|
447
405
|
```
|
|
448
406
|
|
|
449
|
-
|
|
407
|
+
---
|
|
450
408
|
|
|
451
|
-
|
|
452
|
-
- `secret` — the shared rendezvous secret, returned only when the binding is first created
|
|
409
|
+
### Contacts
|
|
453
410
|
|
|
454
|
-
|
|
411
|
+
Store recipient aliases so you never have to copy-paste addresses.
|
|
455
412
|
|
|
456
|
-
```
|
|
457
|
-
|
|
413
|
+
```text
|
|
414
|
+
~/.config/towns/tw/contacts.json
|
|
458
415
|
```
|
|
459
416
|
|
|
460
|
-
Send to a named channel:
|
|
461
|
-
|
|
462
417
|
```bash
|
|
463
|
-
|
|
418
|
+
tw contacts add vitalik vitalik.eth
|
|
419
|
+
tw contacts add treasury 0x1111111111111111111111111111111111111111
|
|
420
|
+
tw contacts list --json
|
|
421
|
+
tw contacts show vitalik --json
|
|
422
|
+
tw contacts update vitalik 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
|
|
423
|
+
tw contacts rename vitalik vitalik-main
|
|
424
|
+
tw contacts remove vitalik-main
|
|
464
425
|
```
|
|
465
426
|
|
|
466
|
-
|
|
427
|
+
Export and import:
|
|
467
428
|
|
|
468
429
|
```bash
|
|
469
|
-
|
|
430
|
+
tw contacts export --json
|
|
431
|
+
tw contacts import ./contacts.json --json
|
|
432
|
+
tw contacts import ./contacts.json --force --json
|
|
470
433
|
```
|
|
471
434
|
|
|
472
|
-
|
|
435
|
+
Import returns `importedCount`, `skippedInvalidCount`, `skippedConflictCount`, and `overwrittenCount`.
|
|
436
|
+
|
|
437
|
+
Send via alias:
|
|
473
438
|
|
|
474
439
|
```bash
|
|
475
|
-
|
|
440
|
+
tw account send 10 vitalik
|
|
476
441
|
```
|
|
477
442
|
|
|
478
|
-
|
|
443
|
+
ENS safety: if ENS re-resolves to a different address than stored, send aborts and requires `tw contacts update`.
|
|
479
444
|
|
|
480
|
-
|
|
481
|
-
TW_PASSWORD="my-password" tw agent listen --from bob --stream 77aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --profile agent
|
|
482
|
-
```
|
|
445
|
+
---
|
|
483
446
|
|
|
484
|
-
|
|
447
|
+
### Agents
|
|
485
448
|
|
|
486
|
-
|
|
487
|
-
|
|
449
|
+
Agents are session-key-backed Towns identities with their own encryption device and named channel bindings — built for autonomous agent-to-agent communication.
|
|
450
|
+
|
|
451
|
+
```text
|
|
452
|
+
~/.config/towns/tw/profiles/<env>/<profile>/sessions/agent-<name>.json
|
|
453
|
+
~/.config/towns/tw/profiles/<env>/<profile>/agent-channels.json
|
|
488
454
|
```
|
|
489
455
|
|
|
490
|
-
|
|
456
|
+
#### `agent create` / `agent list` / `agent remove`
|
|
491
457
|
|
|
492
458
|
```bash
|
|
459
|
+
TW_PASSWORD="my-password" tw agent create alice --profile agent
|
|
460
|
+
TW_PASSWORD="my-password" tw agent create bob --profile agent
|
|
461
|
+
TW_PASSWORD="my-password" tw agent list --profile agent --json
|
|
493
462
|
TW_PASSWORD="my-password" tw agent remove bob --profile agent
|
|
494
463
|
```
|
|
495
464
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
This is the shortest end-to-end flow to start a conversation between two local agents.
|
|
465
|
+
#### `agent connect` — Create or bind a named channel
|
|
499
466
|
|
|
500
|
-
|
|
467
|
+
First side creates the channel and returns the shared secret:
|
|
501
468
|
|
|
502
469
|
```bash
|
|
503
|
-
tw
|
|
470
|
+
TW_PASSWORD="my-password" tw agent connect --from alice --channel art --to bob --profile agent
|
|
504
471
|
```
|
|
505
472
|
|
|
506
|
-
|
|
473
|
+
Second side binds using that secret:
|
|
507
474
|
|
|
508
475
|
```bash
|
|
509
|
-
TW_PASSWORD="my-password" tw agent
|
|
510
|
-
TW_PASSWORD="my-password" tw agent create bob --profile agent
|
|
476
|
+
TW_PASSWORD="my-password" tw agent connect --from bob --channel art --secret "<shared-secret>" --to alice --profile agent
|
|
511
477
|
```
|
|
512
478
|
|
|
513
|
-
|
|
479
|
+
#### `agent send` — Send a message
|
|
514
480
|
|
|
515
|
-
|
|
516
|
-
TW_PASSWORD="my-password" tw agent connect --from alice --channel art --to bob --profile agent
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
4. Bind the same channel from Bob using that secret:
|
|
481
|
+
To a named channel:
|
|
520
482
|
|
|
521
483
|
```bash
|
|
522
|
-
TW_PASSWORD="my-password" tw agent
|
|
484
|
+
TW_PASSWORD="my-password" tw agent send --from alice --channel art "hello from alice" --profile agent
|
|
523
485
|
```
|
|
524
486
|
|
|
525
|
-
|
|
487
|
+
To a raw stream ID:
|
|
526
488
|
|
|
527
489
|
```bash
|
|
528
|
-
TW_PASSWORD="my-password" tw agent
|
|
490
|
+
TW_PASSWORD="my-password" tw agent send --from alice 77aaa... "debug message" --profile agent
|
|
529
491
|
```
|
|
530
492
|
|
|
531
|
-
|
|
493
|
+
#### `agent listen` — Listen for messages
|
|
532
494
|
|
|
533
495
|
```bash
|
|
534
|
-
TW_PASSWORD="my-password" tw agent
|
|
496
|
+
TW_PASSWORD="my-password" tw agent listen --from bob --channel art --profile agent
|
|
497
|
+
TW_PASSWORD="my-password" tw agent listen --from bob --stream 77aaa... --profile agent
|
|
535
498
|
```
|
|
536
499
|
|
|
537
|
-
|
|
500
|
+
Messages arrive as NDJSON:
|
|
538
501
|
|
|
539
502
|
```json
|
|
540
503
|
{
|
|
@@ -547,85 +510,70 @@ TW_PASSWORD="my-password" tw agent send --from alice --channel art "hello from a
|
|
|
547
510
|
}
|
|
548
511
|
```
|
|
549
512
|
|
|
550
|
-
|
|
513
|
+
#### `agent channels` — Inspect bindings
|
|
551
514
|
|
|
552
515
|
```bash
|
|
553
|
-
TW_PASSWORD="my-password" tw agent channels --from alice --profile agent
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
For a single-command live verification, run the built-in smoke script:
|
|
557
|
-
|
|
558
|
-
```bash
|
|
559
|
-
cd packages/wallet
|
|
560
|
-
TW_PASSWORD="my-password" bun run smoke:agent
|
|
516
|
+
TW_PASSWORD="my-password" tw agent channels --from alice --profile agent --json
|
|
561
517
|
```
|
|
562
518
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
List all on-chain keys with summarized call/spend permissions:
|
|
519
|
+
#### Agent Quickstart
|
|
566
520
|
|
|
567
521
|
```bash
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
522
|
+
# Create profile + two agents
|
|
523
|
+
tw account create --profile agent
|
|
524
|
+
TW_PASSWORD="pw" tw agent create alice --profile agent
|
|
525
|
+
TW_PASSWORD="pw" tw agent create bob --profile agent
|
|
572
526
|
|
|
573
|
-
|
|
527
|
+
# Alice creates a channel → copy the returned secret
|
|
528
|
+
TW_PASSWORD="pw" tw agent connect --from alice --channel art --to bob --profile agent
|
|
574
529
|
|
|
575
|
-
|
|
576
|
-
tw
|
|
577
|
-
```
|
|
530
|
+
# Bob binds with that secret
|
|
531
|
+
TW_PASSWORD="pw" tw agent connect --from bob --channel art --secret "<secret>" --to alice --profile agent
|
|
578
532
|
|
|
579
|
-
|
|
533
|
+
# Terminal 1: listen
|
|
534
|
+
TW_PASSWORD="pw" tw agent listen --from bob --channel art --profile agent
|
|
580
535
|
|
|
581
|
-
|
|
582
|
-
tw
|
|
536
|
+
# Terminal 2: send
|
|
537
|
+
TW_PASSWORD="pw" tw agent send --from alice --channel art "hello" --profile agent
|
|
583
538
|
```
|
|
584
539
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
Grant wildcard call permission:
|
|
540
|
+
Smoke test:
|
|
588
541
|
|
|
589
542
|
```bash
|
|
590
|
-
|
|
543
|
+
cd packages/wallet
|
|
544
|
+
TW_PASSWORD="my-password" bun run smoke:agent
|
|
591
545
|
```
|
|
592
546
|
|
|
593
|
-
|
|
547
|
+
---
|
|
594
548
|
|
|
595
|
-
|
|
596
|
-
echo "my-password" | tw permissions grant agent-key --type spend --token USDC --spend-limit 100 --period day --profile agent --password-stdin --json
|
|
597
|
-
```
|
|
549
|
+
## Agent & Automation Integration
|
|
598
550
|
|
|
599
|
-
|
|
551
|
+
Every command supports `--json` for machine-readable output. Treat JSON output schemas as the stable contract.
|
|
600
552
|
|
|
601
553
|
```bash
|
|
602
|
-
|
|
554
|
+
tw <command> --json
|
|
603
555
|
```
|
|
604
556
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
Revoke one rule by stable rule id:
|
|
608
|
-
|
|
609
|
-
```bash
|
|
610
|
-
echo "my-password" | tw permissions revoke agent-key --rule call:0x2222222222222222222222222222222222222222:0xa9059cbb --profile agent --password-stdin --json
|
|
611
|
-
```
|
|
557
|
+
### Password Automation
|
|
612
558
|
|
|
613
|
-
|
|
559
|
+
Use `TW_PASSWORD` to skip interactive prompts:
|
|
614
560
|
|
|
615
561
|
```bash
|
|
616
|
-
|
|
562
|
+
TW_PASSWORD="my-password" tw session list --profile agent --json
|
|
617
563
|
```
|
|
618
564
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
Use `TW_PASSWORD` for non-interactive flows:
|
|
565
|
+
Or pipe via stdin for commands that accept `--password-stdin`:
|
|
622
566
|
|
|
623
567
|
```bash
|
|
624
|
-
|
|
568
|
+
echo "my-password" | tw session rotate --profile agent --password-stdin --json
|
|
625
569
|
```
|
|
626
570
|
|
|
627
|
-
|
|
571
|
+
### Discovery
|
|
628
572
|
|
|
629
573
|
```bash
|
|
630
|
-
|
|
574
|
+
tw --help # All commands
|
|
575
|
+
tw account send --help # Command-specific help
|
|
576
|
+
tw --llms # Machine-readable command manifest
|
|
577
|
+
tw --mcp # Run as MCP server
|
|
578
|
+
tw --version # Version
|
|
631
579
|
```
|