@ftptech/canton-agent-wallet 2.1.0 → 2.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.
Files changed (54) hide show
  1. package/README.md +53 -15
  2. package/dist/cli-args.d.ts +27 -0
  3. package/dist/cli-args.d.ts.map +1 -1
  4. package/dist/cli-args.js +74 -0
  5. package/dist/cli-args.js.map +1 -1
  6. package/dist/cli.js +139 -34
  7. package/dist/cli.js.map +1 -1
  8. package/dist/merge.d.ts +21 -2
  9. package/dist/merge.d.ts.map +1 -1
  10. package/dist/merge.js +194 -31
  11. package/dist/merge.js.map +1 -1
  12. package/dist/pay-lock.d.ts.map +1 -1
  13. package/dist/pay-lock.js +8 -14
  14. package/dist/pay-lock.js.map +1 -1
  15. package/dist/pay.d.ts +5 -1
  16. package/dist/pay.d.ts.map +1 -1
  17. package/dist/pay.js +43 -8
  18. package/dist/pay.js.map +1 -1
  19. package/dist/registry-parties.d.ts +6 -0
  20. package/dist/registry-parties.d.ts.map +1 -1
  21. package/dist/registry-parties.js +8 -0
  22. package/dist/registry-parties.js.map +1 -1
  23. package/dist/relay-client.d.ts +29 -0
  24. package/dist/relay-client.d.ts.map +1 -1
  25. package/dist/relay-client.js +83 -11
  26. package/dist/relay-client.js.map +1 -1
  27. package/dist/relay-signer.d.ts.map +1 -1
  28. package/dist/relay-signer.js +16 -1
  29. package/dist/relay-signer.js.map +1 -1
  30. package/dist/swap.d.ts +5 -0
  31. package/dist/swap.d.ts.map +1 -1
  32. package/dist/swap.js +29 -12
  33. package/dist/swap.js.map +1 -1
  34. package/dist/tx.d.ts +6 -11
  35. package/dist/tx.d.ts.map +1 -1
  36. package/dist/tx.js +11 -75
  37. package/dist/tx.js.map +1 -1
  38. package/dist/venue.d.ts +17 -11
  39. package/dist/venue.d.ts.map +1 -1
  40. package/dist/venue.js +20 -14
  41. package/dist/venue.js.map +1 -1
  42. package/dist/verify-prepared.d.ts +14 -26
  43. package/dist/verify-prepared.d.ts.map +1 -1
  44. package/dist/verify-prepared.js +12 -73
  45. package/dist/verify-prepared.js.map +1 -1
  46. package/dist/withdraw.d.ts +2 -1
  47. package/dist/withdraw.d.ts.map +1 -1
  48. package/dist/withdraw.js +70 -22
  49. package/dist/withdraw.js.map +1 -1
  50. package/package.json +4 -4
  51. package/dist/quest-fund.d.ts +0 -142
  52. package/dist/quest-fund.d.ts.map +0 -1
  53. package/dist/quest-fund.js +0 -355
  54. package/dist/quest-fund.js.map +0 -1
package/README.md CHANGED
@@ -24,22 +24,31 @@ Bin: `canton-agent-wallet`.
24
24
  ```
25
25
  canton-agent-wallet create --relay-url <url> # generate + onboard a self-custody wallet (idempotent)
26
26
  canton-agent-wallet address # print the party id (fund this)
27
- canton-agent-wallet balance # print the balance of every instrument the wallet holds (CC + any CIP-56 registry token, e.g. USDCx)
27
+ canton-agent-wallet balance # print the balance of every instrument the wallet holds (CC + any CIP-56 registry token, e.g. USDCx; "≥" when the wallet is too large to list in one read)
28
28
  canton-agent-wallet claim # accept incoming transfers — CC and registry tokens (expired offers are skipped; one bad offer never blocks the rest)
29
- canton-agent-wallet merge [--target <n>] [--batch <n>] [--max-rounds <n>] [--dry-run] [--admin <registrar> --id <instrument>] # consolidate dust (CC, or a registry token) so a busy wallet can be enumerated + spent again
30
- canton-agent-wallet preapproval [--status] [--admin <DSO>] [--days <n>] # MERCHANT: self-provision this wallet's own TransferPreapproval so transfer-factory payments settle in one round-trip (--status just checks)
31
- canton-agent-wallet pay --relay-url <url> <url> # fetch a URL, auto-paying any x402 402 challenge
32
- canton-agent-wallet swap --in <SYM> --out <SYM> --amount <n> [--venue cantex|tradecraft|auto] [--slippage <pct>] [--direct] [--no-wait] [--max-fee <cc>] [--swap-merchant <party>] [--no-preapprove] # swap CC <-> a CIP-56 registry token via Tradecraft; waits for the output to land (see below)
29
+ canton-agent-wallet merge [--target <n>] [--batch <n>] [--max-rounds <n>] [--dry-run] [--yes] [--admin <registrar> --id <instrument> | --all-tokens] # consolidate dust (CC, one registry token, or every trusted token) so a busy wallet can be enumerated + spent again
30
+ canton-agent-wallet preapproval [--status] [--admin <DSO|registrar>] [--id <instrument>] [--days <n>] # MERCHANT: self-provision this wallet's own TransferPreapproval so transfer-factory payments settle in one round-trip (--status just checks)
31
+ canton-agent-wallet pay [--relay-url <url>] [--asset <symbol>] [--max <amount>] [--pay-to <party>] <url> # fetch a URL, auto-paying any x402 402 challenge (--max caps one payment, --pay-to pins the payee)
32
+ canton-agent-wallet swap --in <SYM> --out <SYM> --amount <n> [--venue cantex|tradecraft|auto] [--slippage <pct>] [--direct] [--no-wait] [--max-fee <cc>] [--swap-merchant <party>] [--no-preapprove] # swap CC <-> a CIP-56 registry token on Cantex (default) or Tradecraft; waits for the output to land (see below)
33
33
  canton-agent-wallet withdraw --to <party> [--amount <n>] [--admin <registrar> --id <instrument>] # send CC (default: full balance) or a registry token back out
34
34
  canton-agent-wallet export # print the private key (backup; guard it)
35
+ canton-agent-wallet set-relay <url> # switch the relay saved in wallet.json (refuses one on another network)
35
36
  ```
36
37
 
37
38
  `create` and `pay` **require** a relay (facilitator) URL: pass `--relay-url
38
39
  <url>` or set `CANTON_AGENT_RELAY_URL`. There is no default: a stale built-in
39
40
  default would silently send payments to a dead host, so the CLI fails fast when
40
- none is given. `address`, `balance`, `claim`, `withdraw` and `export` reuse the
41
- relay stored in the wallet at `create` time. The current FTP facilitator is
42
- `https://facilitator.ftptech.xyz`.
41
+ none is given. Every other command uses the relay stored in the wallet at
42
+ `create` time, unless `--relay-url` / `CANTON_AGENT_RELAY_URL` names another for
43
+ that run (said on stderr; the saved one is not changed). `set-relay <url>`
44
+ changes the saved relay, after checking it settles on the wallet's network. The
45
+ current FTP facilitator is `https://facilitator.ftptech.xyz`.
46
+
47
+ `pay --max <amount>` caps what one payment may cost, in the unit of what is
48
+ paid: with `--asset <token>` that token's own amount, otherwise Canton Coin (and
49
+ a 402 asking for a token is then refused — add `--asset`). `--pay-to <party>`
50
+ pins the only payee the wallet will sign for. Both refuse before anything is
51
+ signed; without them `pay` pays whatever the 402 asks, as before.
43
52
 
44
53
  ### Merchant onboarding: self-provision a `TransferPreapproval`
45
54
 
@@ -77,7 +86,7 @@ batch collapses ~`--batch` amulets into ~2, restoring a spendable wallet:
77
86
 
78
87
  ```bash
79
88
  canton-agent-wallet merge --dry-run # show the plan (counts + batches), change nothing
80
- canton-agent-wallet merge # consolidate (defaults: --batch 90, --target 2, --max-rounds 30)
89
+ canton-agent-wallet merge # consolidate (defaults: --batch 70, --target 2, --max-rounds 30)
81
90
  canton-agent-wallet merge --yes # required for a LARGE whale pass (>20 batches; see cost note)
82
91
  ```
83
92
 
@@ -94,7 +103,33 @@ holdings), so each batch is self-transferred for its exact total, holdings are
94
103
  read through the token-standard `HoldingV1` view, locked holdings are never used
95
104
  as inputs, and the default target is 1 holding. The registrar must be a known or
96
105
  trusted registry admin (`CANTON_AGENT_REGISTRY_TRUSTED_PARTIES` for one the
97
- wallet does not ship with).
106
+ wallet does not ship with). The default batch is 90 for a registry token and 70
107
+ for Canton Coin (a transaction is capped at 256 nodes: a registry input costs
108
+ ~1, an Amulet input ~3).
109
+
110
+ **A wallet with more holdings than the participant lists in one read** (a
111
+ merchant paid thousands of times in a token: the participant refuses an
112
+ unbounded list past its element cap, 200 by default). The relay then answers a
113
+ bounded read of the wallet's **oldest** holdings instead of an error, and
114
+ `merge` works through it: merge a batch, read again — the merged holdings drop
115
+ out and the ones behind them come into view. Because the size is unknown up
116
+ front, such a run needs `--yes`, and `--max-rounds` (default 30) bounds it.
117
+ A per-token merge can only work on its own token; when the oldest holdings are
118
+ mostly another token's dust it says so and points at `--all-tokens`, which
119
+ merges every token of a trusted registrar, the largest group in view first,
120
+ until each is down to one holding:
121
+
122
+ ```bash
123
+ canton-agent-wallet merge --all-tokens --dry-run
124
+ canton-agent-wallet merge --all-tokens --yes
125
+ ```
126
+
127
+ On such a wallet `balance` shows token totals as lower bounds ("≥"), `withdraw`
128
+ spends the fewest holdings that cover the amount (and refuses a full-balance
129
+ withdraw, whose amount it cannot know), and a swap into that token says it
130
+ cannot confirm delivery instead of waiting out a false timeout. Paying from it
131
+ works while the amount is coverable from the visible holdings; otherwise the
132
+ relay answers `holdings_merge_required` with the merge to run.
98
133
 
99
134
  For a wallet already too large to enumerate on-ledger, `merge` reads the holdings
100
135
  from the public SV Scan ACS snapshot (paginated, no node cap), sweeps over them
@@ -266,6 +301,9 @@ const r = await executeSwap({ amount: "60", ticket }); // sends, waits for
266
301
  settable via `--network <net>`.
267
302
  - `CANTON_AGENT_API_KEY`: only if the relay requires one (sent as the
268
303
  `X-Agent-Key` header).
304
+ - `CANTON_AGENT_RELAY_TIMEOUT_MS`: per-request relay timeout (default 120000). A
305
+ timed-out read fails the command; a timed-out SUBMISSION is reported as
306
+ "outcome unknown" — check `balance` before retrying, it may have committed.
269
307
  - `HTTPS_PROXY` / `HTTP_PROXY` (or `ALL_PROXY`): if set, the CLI routes all
270
308
  relay calls through that proxy. Node's `fetch` does NOT honor these on its
271
309
  own, so behind a corporate/regional proxy you must set one of them or every
@@ -273,13 +311,13 @@ const r = await executeSwap({ amount: "60", ticket }); // sends, waits for
273
311
  (`http://user:pass@host:port`) and never logged.
274
312
  - `CANTON_AGENT_HOME`: override the wallet directory (default `~/.canton-agent`;
275
313
  used by tests and power users).
276
- - `CANTON_AGENT_VENUE_KEY` + `CANTON_AGENT_VENUE_TAG`: venue attribution, **off by
277
- default** (both must be set). When set — e.g. `CANTON_AGENT_VENUE_KEY=ftp/venue`
278
- and `CANTON_AGENT_VENUE_TAG=ftp/agentic-wallet` — the wallet stamps that key/value
314
+ - `CANTON_AGENT_VENUE_KEY` + `CANTON_AGENT_VENUE_TAG`: venue attribution, **on by
315
+ default** as `ftp/venue` = `ftp/agentic-wallet`. The wallet stamps that key/value
279
316
  into the metadata of every outbound **CIP-56 registry token** transfer it authors
280
317
  (swap-sell, withdraw, and x402 pay), so a token issuer's incentive program can
281
- attribute that activity to this venue. Generic across tokens (CBTC, USDXLR, …);
282
- set your own key/value, but the key **must end in `/venue`** and fit 64 chars, and
318
+ attribute that activity to this venue. Generic across tokens (CBTC, USDXLR, …).
319
+ `CANTON_AGENT_VENUE_KEY=off` disables it. To attribute to your own venue, set
320
+ your own key/value: the key **must end in `/venue`** and fit 64 chars, and
283
321
  the tag 128 chars — the facilitator's pay/prepare enforces exactly these bounds,
284
322
  so a non-conforming config stamps nothing (one stderr warning) instead of breaking
285
323
  payments. Advisory metadata only (rides in `transfer.meta.values`, not
@@ -100,4 +100,31 @@ export declare function preapprovalMode(args: readonly string[], env: Record<str
100
100
  mode: "legacy";
101
101
  operatorToken: string;
102
102
  };
103
+ /**
104
+ * `pay --max <amount> [--pay-to <party>]` → the signer's spend limits.
105
+ *
106
+ * `--max` caps the payment in the unit of what is paid. With `--asset` naming a
107
+ * registry token that is that token's own ceiling; otherwise it is the Canton
108
+ * Coin ceiling, and a 402 that asks for a token is then refused by the signer
109
+ * (a CC number cannot cap a token whose unit is worth something else) with the
110
+ * hint to add `--asset`. An asset we cannot resolve cannot be capped, so it is
111
+ * refused rather than paid uncapped. Present-but-invalid values fail fast.
112
+ */
113
+ export declare function payLimits(args: readonly string[], env?: NodeJS.ProcessEnv): {
114
+ kind: "ok";
115
+ maxPaymentValue?: string;
116
+ maxPaymentValueByInstrument?: Record<string, string>;
117
+ expectedPayTo?: string;
118
+ } | {
119
+ kind: "error";
120
+ message: string;
121
+ };
122
+ /**
123
+ * The registry instrument id `preapproval` provisions for registrar `admin`:
124
+ * `--id` when given, else the one instrument this wallet knows for that
125
+ * registrar. Undefined when it cannot be decided — the caller must then
126
+ * require `--id` rather than guess (it used to default to USDCx for EVERY
127
+ * registrar, provisioning the wrong instrument for CBTC, USDXLR, …).
128
+ */
129
+ export declare function preapprovalInstrumentId(args: readonly string[], admin: string): string | undefined;
103
130
  //# sourceMappingURL=cli-args.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,8EAA8E;AAC9E,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAyB1C,CAAC;AAEH,gEAAgE;AAChE,wBAAgB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAU9E;AAED;;0EAE0E;AAC1E,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAUvE;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAQpB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAW7D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,SAAS,CAEpB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAER;AAED,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,QAEkD,CAAC;AAElF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAY5F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CASvF"}
1
+ {"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAEhD,8EAA8E;AAC9E,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CA8B1C,CAAC;AAEH,gEAAgE;AAChE,wBAAgB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAU9E;AAED;;0EAE0E;AAC1E,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAUvE;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAQpB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAW7D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,SAAS,CAEpB;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAER;AAED,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,QAEkD,CAAC;AAElF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAY5F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CASvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GAElC;IACE,IAAI,EAAE,IAAI,CAAC;IACX,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2BAA2B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CA+CrC;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,SAAS,CAKpB"}
package/dist/cli-args.js CHANGED
@@ -5,6 +5,7 @@
5
5
  * `cli.ts` (which auto-runs `main()` on import and would call
6
6
  * `process.exit`). `cli.ts` composes these with its env + `fail()`.
7
7
  */
8
+ import { envTrustedRegistryAdmins, instrumentKey, KNOWN_INSTRUMENTS, resolveSwapSymbol, } from "./registry-parties.js";
8
9
  export const DEFAULT_NETWORK = "canton:testnet";
9
10
  /** Known `--flag <value>` options; used to strip flags out of positionals. */
10
11
  export const VALUE_FLAGS = new Set([
@@ -32,6 +33,11 @@ export const VALUE_FLAGS = new Set([
32
33
  "--expires-at",
33
34
  "--days",
34
35
  "--operator-token",
36
+ "--id",
37
+ "--venue",
38
+ // pay: spend ceiling and payee pin.
39
+ "--max",
40
+ "--pay-to",
35
41
  ]);
36
42
  /** First value following `--<name>` in `args`, or undefined. */
37
43
  export function flag(args, name) {
@@ -178,4 +184,72 @@ export function preapprovalMode(args, env) {
178
184
  ambientIgnored: Boolean(env["CANTON_X402_OPERATOR_TOKEN"]),
179
185
  };
180
186
  }
187
+ /**
188
+ * `pay --max <amount> [--pay-to <party>]` → the signer's spend limits.
189
+ *
190
+ * `--max` caps the payment in the unit of what is paid. With `--asset` naming a
191
+ * registry token that is that token's own ceiling; otherwise it is the Canton
192
+ * Coin ceiling, and a 402 that asks for a token is then refused by the signer
193
+ * (a CC number cannot cap a token whose unit is worth something else) with the
194
+ * hint to add `--asset`. An asset we cannot resolve cannot be capped, so it is
195
+ * refused rather than paid uncapped. Present-but-invalid values fail fast.
196
+ */
197
+ export function payLimits(args, env = process.env) {
198
+ const out = { kind: "ok" };
199
+ if (flagPresent(args, "--pay-to")) {
200
+ const to = flag(args, "--pay-to")?.trim();
201
+ if (!to)
202
+ return { kind: "error", message: "pay --pay-to was given with no party — refusing." };
203
+ out.expectedPayTo = to;
204
+ }
205
+ if (!flagPresent(args, "--max"))
206
+ return out;
207
+ const raw = flag(args, "--max")?.trim() ?? "";
208
+ if (!/^\d+(\.\d+)?$/.test(raw) || Number(raw) <= 0) {
209
+ return {
210
+ kind: "error",
211
+ message: `pay --max must be a positive amount (got ${JSON.stringify(raw)}) — refusing to pay uncapped.`,
212
+ };
213
+ }
214
+ const asset = flag(args, "--asset");
215
+ if (asset === undefined) {
216
+ out.maxPaymentValue = raw;
217
+ return out;
218
+ }
219
+ const sym = resolveSwapSymbol(asset);
220
+ if (sym.kind === "cc") {
221
+ out.maxPaymentValue = raw;
222
+ }
223
+ else if (sym.kind === "registry") {
224
+ // Keyed on the MainNet registrar AND every registrar trusted through
225
+ // CANTON_AGENT_REGISTRY_TRUSTED_PARTIES: on testnet/devnet the same symbol
226
+ // comes from such a registrar, and a cap keyed only on the MainNet one would
227
+ // never apply there. The signer looks the cap up by the instrument actually
228
+ // paid, so a key for a registrar that does not issue this id is inert.
229
+ const admins = new Set([sym.instrument.admin, ...envTrustedRegistryAdmins(env)]);
230
+ out.maxPaymentValueByInstrument = Object.fromEntries([...admins].map((admin) => [instrumentKey(admin, sym.instrument.id), raw]));
231
+ }
232
+ else {
233
+ return {
234
+ kind: "error",
235
+ message: `pay --max with --asset ${asset}: that asset is not one this wallet knows, so the cap ` +
236
+ `cannot be applied to it — refusing to pay uncapped.`,
237
+ };
238
+ }
239
+ return out;
240
+ }
241
+ /**
242
+ * The registry instrument id `preapproval` provisions for registrar `admin`:
243
+ * `--id` when given, else the one instrument this wallet knows for that
244
+ * registrar. Undefined when it cannot be decided — the caller must then
245
+ * require `--id` rather than guess (it used to default to USDCx for EVERY
246
+ * registrar, provisioning the wrong instrument for CBTC, USDXLR, …).
247
+ */
248
+ export function preapprovalInstrumentId(args, admin) {
249
+ const explicit = flag(args, "--id")?.trim();
250
+ if (explicit)
251
+ return explicit;
252
+ const known = Object.values(KNOWN_INSTRUMENTS).filter((i) => i.admin === admin);
253
+ return known.length === 1 ? known[0].id : undefined;
254
+ }
181
255
  //# sourceMappingURL=cli-args.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * address print the party id (fund this)
7
7
  * balance print every instrument the wallet holds (CC + registry tokens)
8
8
  * claim accept incoming transfers (e.g. the initial funding)
9
- * merge [--target <n>] [--batch <n>] [--max-rounds <n>] [--dry-run] [--yes] [--admin <registrar> --id <instrument>]
9
+ * merge [--target <n>] [--batch <n>] [--max-rounds <n>] [--dry-run] [--yes] [--admin <registrar> --id <instrument> | --all-tokens]
10
10
  * consolidate dust amulets (self-transfer in batches)
11
11
  * so a wallet with thousands of holdings can be
12
12
  * enumerated + spent again. --dry-run prints the plan;
@@ -18,13 +18,13 @@
18
18
  * transfer-factory path — SELF-provisions the
19
19
  * merchant's own preapproval so x402 CC payments
20
20
  * settle in one round-trip. --status just checks.
21
- * pay [--relay-url <url>] [--asset <symbol>] <url> fetch a URL, auto-paying any
21
+ * pay [--relay-url <url>] [--asset <symbol>] [--max <amount>] [--pay-to <party>] <url> fetch a URL, auto-paying any
22
22
  * x402 402 challenge; --asset prefers that
23
23
  * instrument (e.g. USDCx) when the 402 offers
24
24
  * several, else the first compatible entry
25
25
  * withdraw --to <p> send CC back out (--amount <cc> for a partial amount)
26
- * swap --in <SYM> --out <SYM> --amount <n> [--slippage <pct>] [--direct] [--no-wait]
27
- * swap on Tradecraft: pre-approve the output, send
26
+ * swap --in <SYM> --out <SYM> --amount <n> [--venue cantex|tradecraft|auto] [--slippage <pct>] [--direct] [--no-wait]
27
+ * swap on Cantex (default) or Tradecraft: pre-approve the output, send
28
28
  * the input to the pool, and WAIT until the output
29
29
  * lands (no manual claim). By DEFAULT the ticket (pool,
30
30
  * minimum, amm_cid) comes from our x402 swap endpoint (a
@@ -36,14 +36,17 @@
36
36
  * export print the private key (backup; guard it)
37
37
  * import [--relay-url <url>] [--key-file <p>] restore a wallet from a backed-up
38
38
  * key (stdin or --key-file); inverse of export
39
+ * set-relay <url> switch the relay saved in wallet.json (checks it
40
+ * settles on this wallet's network first)
39
41
  *
40
42
  * The relay (facilitator) URL is REQUIRED for the commands that talk to a
41
43
  * relay (`create`, `pay`, `import`). There is intentionally no built-in default — a
42
44
  * wrong/stale default would silently send an agent's payments to a dead host.
43
45
  * Supply it via `--relay-url <url>` or the `CANTON_AGENT_RELAY_URL` env var.
44
- * The current FTP facilitator is `https://facilitator.ftptech.xyz`. `address`,
45
- * `balance`, `claim`, `withdraw` and `export` reuse the relay stored in the
46
- * wallet at `create` time and do not need the flag.
46
+ * The current FTP facilitator is `https://facilitator.ftptech.xyz`. Every other
47
+ * command uses the relay saved in the wallet at `create` time, unless
48
+ * `--relay-url` / CANTON_AGENT_RELAY_URL names another one for that run;
49
+ * `set-relay` changes the saved one.
47
50
  *
48
51
  * Behind a proxy? Node's fetch ignores HTTP(S)_PROXY by default; this CLI wires
49
52
  * an undici ProxyAgent when HTTPS_PROXY/HTTP_PROXY/ALL_PROXY is set (see proxy.ts).
@@ -54,18 +57,18 @@
54
57
  /* eslint-disable no-console -- CLI entrypoint: stdout is its result channel */
55
58
  import { readFileSync } from "node:fs";
56
59
  import { ensureWallet } from "./onboard.js";
57
- import { loadWallet } from "./store.js";
60
+ import { loadWallet, saveWallet, walletPath } from "./store.js";
58
61
  import { agentKeyFromPrivatePem } from "./keys.js";
59
62
  import { makePayingFetch } from "./pay.js";
60
63
  import { RelayClient, isHoldingsExceedNodeLimitError } from "./relay-client.js";
61
64
  import { claimAll, selfProvisionPreapproval, selfProvisionRegistryPreapproval, } from "./tx.js";
62
65
  import { isTrustedRegistryAdmin } from "./registry-parties.js";
63
- import { mergeHoldings } from "./merge.js";
66
+ import { mergeHoldings, mergeAllRegistryHoldings } from "./merge.js";
64
67
  import { withdraw } from "./withdraw.js";
65
68
  import { executeSwap } from "./swap.js";
66
69
  import { resolveSwapIntent, resolveSwapTicket, swapFloorNotes, parseSwapVenue, parseSlippagePct, SWAP_MAX_FEE_CC, } from "./swap-route.js";
67
70
  import { installProxyFromEnv } from "./proxy.js";
68
- import { flag, boolFlag, intFlag, positionals, resolveNetwork, resolveRelayUrl, MISSING_RELAY_HELP, withdrawAmount, preapprovalMode, } from "./cli-args.js";
71
+ import { flag, boolFlag, intFlag, positionals, resolveNetwork, resolveRelayUrl, MISSING_RELAY_HELP, withdrawAmount, preapprovalMode, payLimits, preapprovalInstrumentId, } from "./cli-args.js";
69
72
  import { resolveTrustedDsoParty } from "./trusted-dso.js";
70
73
  const API_KEY = process.env.CANTON_AGENT_API_KEY;
71
74
  function fail(msg) {
@@ -94,11 +97,34 @@ async function main() {
94
97
  if (proxy)
95
98
  console.error(`(using proxy ${proxy})`);
96
99
  const [cmd, ...args] = process.argv.slice(2);
100
+ /** The relay this run talks to: `--relay-url` / CANTON_AGENT_RELAY_URL when
101
+ * given, else the one saved at `create`. A flag that was silently ignored
102
+ * pointed commands at the old relay after a cut-over; now it wins, and is
103
+ * never written back (`set-relay` does that on purpose). */
104
+ const relayFor = (w) => {
105
+ const url = resolveRelayUrl(args);
106
+ if (!url || url === w.relayUrl)
107
+ return w;
108
+ console.error(`(using relay ${url}; this wallet's saved relay is ${w.relayUrl} — ` +
109
+ `\`canton-agent-wallet set-relay <url>\` changes it)`);
110
+ return { ...w, relayUrl: url };
111
+ };
112
+ const requireWallet = () => {
113
+ const w = loadWallet();
114
+ if (!w)
115
+ return fail("no wallet yet — run: canton-agent-wallet create");
116
+ return relayFor(w);
117
+ };
97
118
  switch (cmd) {
98
119
  case "create": {
99
120
  const relayUrl = requireRelay(args);
100
- if (!loadWallet())
121
+ const existing = loadWallet();
122
+ if (!existing)
101
123
  console.error("creating the wallet (takes about half a minute)…");
124
+ else if (existing.relayUrl !== relayUrl) {
125
+ console.error(`a wallet already exists; it keeps its saved relay ${existing.relayUrl}. ` +
126
+ `To switch it to ${relayUrl}: canton-agent-wallet set-relay ${relayUrl}`);
127
+ }
102
128
  // A relay that funds new wallets sends a starter grant; ensureWallet
103
129
  // waits for it and accepts it before returning.
104
130
  const grant = {};
@@ -122,7 +148,7 @@ async function main() {
122
148
  ` party: ${w.party}\n` +
123
149
  starterLine +
124
150
  ` fund me: send CC to that party id, then run: canton-agent-wallet claim\n` +
125
- ` keyfile: ~/.canton-agent/wallet.json <- this IS your wallet; back it up\n` +
151
+ ` keyfile: ${walletPath()} <- this IS your wallet; back it up\n` +
126
152
  ` By creating a wallet you accept the Terms of Service: https://ftp-tech.mintlify.site/terms`);
127
153
  break;
128
154
  }
@@ -134,9 +160,7 @@ async function main() {
134
160
  break;
135
161
  }
136
162
  case "balance": {
137
- const w = loadWallet();
138
- if (!w)
139
- return fail("no wallet yet — run: canton-agent-wallet create");
163
+ const w = requireWallet();
140
164
  try {
141
165
  const relay = new RelayClient({ relayUrl: w.relayUrl, apiKey: API_KEY });
142
166
  const b = await relay.balance(w.party);
@@ -145,19 +169,37 @@ async function main() {
145
169
  // and printed after CC on purpose: a relay that predates /holdings makes
146
170
  // this line throw, and a wallet must keep showing its Canton Coin on an
147
171
  // older relay rather than fail the whole command over a token listing.
172
+ //
173
+ // Read BOUNDED: a wallet with more holdings than the participant lists in
174
+ // one read gets its oldest ones (complete:false). The totals are then
175
+ // lower bounds, shown as "≥", with the command that makes them exact —
176
+ // this line used to swallow that 413 and print nothing at all, so a
177
+ // wallet full of USDCx looked like it held only Canton Coin.
148
178
  try {
149
- const h = await relay.holdings(w.party);
179
+ const h = await relay.holdings(w.party, undefined, { slice: true });
180
+ const ge = h.complete ? "" : "≥ ";
150
181
  for (const ins of h.instruments) {
151
182
  if (ins.id === "Amulet")
152
183
  continue; // already printed as CC above
153
184
  const locked = ins.holdings.filter((x) => x.locked).length;
154
- console.log(`${ins.total} ${ins.id} (${ins.holdings.length} holding${ins.holdings.length === 1 ? "" : "s"}` +
185
+ console.log(`${ge}${ins.total} ${ins.id} (${ge}${ins.holdings.length} holding${ins.holdings.length === 1 ? "" : "s"}` +
155
186
  (locked ? `, ${locked} locked` : "") +
156
187
  `) @ ${ins.admin}`);
157
188
  }
189
+ if (!h.complete) {
190
+ console.log(" (this wallet holds more than the participant lists in one read — token totals above " +
191
+ "are lower bounds; consolidate with: canton-agent-wallet merge --all-tokens, " +
192
+ "and plain `canton-agent-wallet merge` for Canton Coin dust)");
193
+ }
158
194
  }
159
- catch {
160
- /* older relay without /holdings — CC line above already shown */
195
+ catch (err) {
196
+ // An older relay without /holdings answers Fastify's route-not-found;
197
+ // that stays quiet (the CC line above is the whole answer there). Any
198
+ // other failure is said, not swallowed.
199
+ const notServed = err instanceof Error && /-> 404 .*Route GET:/.test(err.message);
200
+ if (!notServed) {
201
+ console.error(`tokens: could not list (${err instanceof Error ? err.message.split("\n")[0] : String(err)})`);
202
+ }
161
203
  }
162
204
  }
163
205
  catch (err) {
@@ -177,9 +219,7 @@ async function main() {
177
219
  // Consolidate dust amulets by self-transferring them in batches (receiver ==
178
220
  // this wallet, enforced by verify-before-sign). Restores `balance`/`pay` for
179
221
  // a wallet whose amulet count outgrew the participant's ACS element cap.
180
- const w = loadWallet();
181
- if (!w)
182
- return fail("no wallet yet — run: canton-agent-wallet create");
222
+ const w = requireWallet();
183
223
  const target = intFlag(args, "--target");
184
224
  const batch = intFlag(args, "--batch");
185
225
  const maxRounds = intFlag(args, "--max-rounds");
@@ -195,6 +235,24 @@ async function main() {
195
235
  if ((mAdmin && !mId) || (!mAdmin && mId)) {
196
236
  return fail("merge: --admin <registrar> and --id <instrument> must be given together");
197
237
  }
238
+ // --all-tokens: every token of a trusted registrar, whatever the mix — the
239
+ // way through a wallet whose oldest holdings are another token's dust.
240
+ if (boolFlag(args, "--all-tokens")) {
241
+ if (mAdmin)
242
+ return fail("merge: --all-tokens merges every trusted token — drop --admin/--id");
243
+ const res = await mergeAllRegistryHoldings(new RelayClient({ relayUrl: w.relayUrl, apiKey: API_KEY }), w, {
244
+ ...(batch !== undefined ? { batch } : {}),
245
+ ...(maxRounds !== undefined ? { maxRounds } : {}),
246
+ dryRun,
247
+ yes,
248
+ onProgress: (line) => console.log(line),
249
+ });
250
+ if (!dryRun) {
251
+ console.log(`merge complete: ${res.rounds} batch transfer(s), ${res.merged.length} token holding(s) consumed` +
252
+ (res.skippedStale > 0 ? ` (${res.skippedStale} stale batch(es) skipped)` : ""));
253
+ }
254
+ break;
255
+ }
198
256
  if (mAdmin && !isTrustedRegistryAdmin(mAdmin, process.env)) {
199
257
  return fail(`merge: ${mAdmin} is not a known registry admin — add it via ` +
200
258
  `CANTON_AGENT_REGISTRY_TRUSTED_PARTIES before merging its token`);
@@ -218,16 +276,16 @@ async function main() {
218
276
  `${res.merged.length} ${mId ? `${mId} holding(s)` : "amulet(s)"} consumed` +
219
277
  (extras.length ? ` (${extras.join(", ")})` : "") +
220
278
  (res.finalHoldings === null
221
- ? " — remaining holdings predate this run; re-run `merge` after the next " +
222
- "Scan snapshot (~12:00 UTC daily) to continue"
279
+ ? mId
280
+ ? " — more remain than one read lists; see above"
281
+ : " — remaining holdings predate this run; re-run `merge` after the next " +
282
+ "Scan snapshot (~12:00 UTC daily) to continue"
223
283
  : ` → ${res.finalHoldings} holding(s) remain`));
224
284
  }
225
285
  break;
226
286
  }
227
287
  case "claim": {
228
- const w = loadWallet();
229
- if (!w)
230
- return fail("no wallet yet — run: canton-agent-wallet create");
288
+ const w = requireWallet();
231
289
  const r = await claimAll(new RelayClient({ relayUrl: w.relayUrl, apiKey: API_KEY }), w);
232
290
  // Say what happened to EVERY pending row, not just the claimed ones. An
233
291
  // expired offer is skipped (the ledger would refuse its accept); a failed
@@ -269,9 +327,7 @@ async function main() {
269
327
  // pays the fee) — ONLY works if the merchant delegated
270
328
  // CanActAs to the facilitator user; NOT for
271
329
  // self-custody merchants. Prefer the self default.
272
- const w = loadWallet();
273
- if (!w)
274
- return fail("no wallet yet — run: canton-agent-wallet create");
330
+ const w = requireWallet();
275
331
  const admin = flag(args, "--admin") ||
276
332
  resolveTrustedDsoParty(process.env, w.network) ||
277
333
  "";
@@ -355,7 +411,11 @@ async function main() {
355
411
  // client's trusted-registry table/env, so an arbitrary --admin cannot be
356
412
  // blind-trusted.
357
413
  if (isTrustedRegistryAdmin(admin, process.env)) {
358
- const id = flag(args, "--id") || "USDCx";
414
+ const id = preapprovalInstrumentId(args, admin);
415
+ if (id === undefined) {
416
+ return fail(`preapproval: pass --id <instrument> — this wallet does not know which instrument ` +
417
+ `${admin} issues, and provisioning the wrong one is not harmless`);
418
+ }
359
419
  // ABSENCE MUST BE PROVEN BEFORE CREATING, because this create is not
360
420
  // idempotent: run it twice and the merchant ends up with two live
361
421
  // Utility.Registry TransferPreapproval contracts for the same
@@ -393,7 +453,7 @@ async function main() {
393
453
  // DEFAULT: SELF-provision with the merchant's OWN key — no token, no
394
454
  // delegation. The merchant prepays the fee from its own CC.
395
455
  try {
396
- const res = await selfProvisionPreapproval(relay, w, { expiresAt });
456
+ const res = await selfProvisionPreapproval(relay, w, { expiresAt, dso: admin });
397
457
  console.log(`TransferPreapproval self-provisioned for ${w.party} (ready for transfer-factory payments)\n` +
398
458
  ` provider: ${w.party} (self)\n` +
399
459
  ` expiresAt: ${expiresAt}\n` +
@@ -414,7 +474,7 @@ async function main() {
414
474
  case "pay": {
415
475
  const url = positionals(args)[0];
416
476
  if (!url)
417
- return fail("usage: canton-agent-wallet pay [--relay-url <url>] [--asset <symbol>] <url>");
477
+ return fail("usage: canton-agent-wallet pay [--relay-url <url>] [--asset <symbol>] [--max <amount>] [--pay-to <party>] <url>");
418
478
  // Reuse the wallet's stored relay (like claim/balance) when neither
419
479
  // --relay-url nor CANTON_AGENT_RELAY_URL is given, so an already-created
420
480
  // agent can pay without re-specifying the relay each time.
@@ -426,11 +486,22 @@ async function main() {
426
486
  // Selection only; spending a registry token still needs
427
487
  // CANTON_AGENT_PAYABLE_INSTRUMENTS.
428
488
  const preferAsset = flag(args, "--asset");
489
+ // --max caps what one payment may cost (in the paid asset's own unit);
490
+ // --pay-to pins the only payee the wallet will sign for. Both refuse
491
+ // BEFORE anything is signed; see payLimits.
492
+ const limits = payLimits(args);
493
+ if (limits.kind === "error")
494
+ return fail(limits.message);
429
495
  const f = await makePayingFetch({
430
496
  relayUrl,
431
497
  network: existing?.network ?? resolveNetwork(args),
432
498
  apiKey: API_KEY,
433
499
  ...(preferAsset ? { preferAsset } : {}),
500
+ ...(limits.maxPaymentValue !== undefined ? { maxPaymentValue: limits.maxPaymentValue } : {}),
501
+ ...(limits.maxPaymentValueByInstrument !== undefined
502
+ ? { maxPaymentValueByInstrument: limits.maxPaymentValueByInstrument }
503
+ : {}),
504
+ ...(limits.expectedPayTo !== undefined ? { expectedPayTo: limits.expectedPayTo } : {}),
434
505
  });
435
506
  const res = await f(url);
436
507
  console.log(`${res.status} ${res.statusText}`);
@@ -470,6 +541,7 @@ async function main() {
470
541
  }
471
542
  const r = await withdraw({
472
543
  to,
544
+ wallet: requireWallet(),
473
545
  ...(amt.kind === "amount" ? { amount: amt.amount } : {}),
474
546
  ...(wAdmin && wId ? { instrumentAdmin: wAdmin, instrumentId: wId } : {}),
475
547
  apiKey: API_KEY,
@@ -550,6 +622,7 @@ async function main() {
550
622
  const sr = await executeSwap({
551
623
  amount,
552
624
  ticket,
625
+ ...(existing ? { wallet: relayFor(existing) } : {}),
553
626
  apiKey: API_KEY,
554
627
  preapproveOutput: !boolFlag(args, "--no-preapprove"),
555
628
  waitForOutput: wait,
@@ -568,6 +641,11 @@ async function main() {
568
641
  console.log(`${head}\n ! input RETURNED — nothing was swapped: the CC balance is back at its ` +
569
642
  `pre-send level (the venue hands back a trade it will not fill, e.g. below its minimum)`);
570
643
  }
644
+ else if (sr.deliveryUnobservable) {
645
+ console.log(`${head}\n ! cannot confirm delivery: this wallet holds more ${outCanon} holdings than the ` +
646
+ `participant lists in one read, so the new one is out of view. Do NOT resend — run ` +
647
+ `\`canton-agent-wallet merge --all-tokens\`, then check \`balance\`.`);
648
+ }
571
649
  else {
572
650
  // Waited, no arrival: still settling, or a below-minimum trade whose input
573
651
  // was returned (the per-tick claim picks that return up — `claimed` shows
@@ -626,8 +704,35 @@ async function main() {
626
704
  ` run: canton-agent-wallet balance`);
627
705
  break;
628
706
  }
707
+ case "set-relay": {
708
+ // Change the relay SAVED in wallet.json — the one every command uses when
709
+ // no --relay-url is given. Refuses a relay that settles on a different
710
+ // network: the wallet's DSO pin and parties are network-specific.
711
+ const url = positionals(args)[0];
712
+ if (!url)
713
+ return fail("usage: canton-agent-wallet set-relay <url>");
714
+ const w = loadWallet();
715
+ if (!w)
716
+ return fail("no wallet yet — run: canton-agent-wallet create");
717
+ let net;
718
+ try {
719
+ const sup = await new RelayClient({ relayUrl: url, apiKey: API_KEY }).supported();
720
+ net = sup?.kinds?.find((k) => typeof k?.network === "string" && k.network.length > 0)?.network;
721
+ }
722
+ catch (err) {
723
+ return fail(`set-relay: could not reach ${url} (${err instanceof Error ? err.message : String(err)})`);
724
+ }
725
+ if (!net)
726
+ return fail(`set-relay: ${url} does not say which network it settles on — not switching`);
727
+ if (net !== w.network) {
728
+ return fail(`set-relay: ${url} settles on ${net}, but this wallet is on ${w.network} — not switching`);
729
+ }
730
+ saveWallet({ ...w, relayUrl: url });
731
+ console.log(`relay set: ${w.relayUrl} → ${url} (${net})`);
732
+ break;
733
+ }
629
734
  default:
630
- console.error("canton-agent-wallet: create | address | balance | claim | merge [--target <n>] [--batch <n>] [--max-rounds <n>] [--dry-run] [--yes] [--admin <registrar> --id <instrument>] | preapproval [--status] [--admin <DSO>] [--operator-token <t>] [--days <n>] [--expires-at <iso>] | pay [--asset <symbol>] <url> | swap --in <SYM> --out <SYM> --amount <n> [--venue cantex|tradecraft|auto] [--slippage <pct>] [--direct] [--no-wait] [--max-fee <cc>] [--swap-merchant <party>] [--no-preapprove] | withdraw --to <party> [--amount <n>] [--admin <registrar> --id <instrument>] | export | import\n" +
735
+ console.error("canton-agent-wallet: create | address | balance | claim | merge [--target <n>] [--batch <n>] [--max-rounds <n>] [--dry-run] [--yes] [--admin <registrar> --id <instrument> | --all-tokens] | preapproval [--status] [--admin <DSO|registrar>] [--id <instrument>] [--operator-token <t>] [--days <n>] [--expires-at <iso>] | pay [--asset <symbol>] [--max <amount>] [--pay-to <party>] <url> | swap --in <SYM> --out <SYM> --amount <n> [--venue cantex|tradecraft|auto] [--slippage <pct>] [--direct] [--no-wait] [--max-fee <cc>] [--swap-merchant <party>] [--no-preapprove] | withdraw --to <party> [--amount <n>] [--admin <registrar> --id <instrument>] | export | import | set-relay <url>\n" +
631
736
  " relay required for create/pay/import: --relay-url <url> or CANTON_AGENT_RELAY_URL\n" +
632
737
  " preapproval = merchant TransferPreapproval setup for the transfer-factory (V3, 1-tx) path (--status to just check).");
633
738
  process.exit(cmd ? 1 : 0);