@ftptech/canton-agent-wallet 2.2.0 → 2.3.1
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 +55 -10
- package/dist/cli-args.d.ts +27 -0
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +74 -0
- package/dist/cli-args.js.map +1 -1
- package/dist/cli.js +159 -34
- package/dist/cli.js.map +1 -1
- package/dist/merge.d.ts +26 -2
- package/dist/merge.d.ts.map +1 -1
- package/dist/merge.js +212 -31
- package/dist/merge.js.map +1 -1
- package/dist/pay-lock.d.ts.map +1 -1
- package/dist/pay-lock.js +8 -14
- package/dist/pay-lock.js.map +1 -1
- package/dist/pay.d.ts +5 -1
- package/dist/pay.d.ts.map +1 -1
- package/dist/pay.js +43 -8
- package/dist/pay.js.map +1 -1
- package/dist/registry-parties.d.ts +6 -0
- package/dist/registry-parties.d.ts.map +1 -1
- package/dist/registry-parties.js +8 -0
- package/dist/registry-parties.js.map +1 -1
- package/dist/relay-client.d.ts +39 -0
- package/dist/relay-client.d.ts.map +1 -1
- package/dist/relay-client.js +91 -11
- package/dist/relay-client.js.map +1 -1
- package/dist/relay-signer.d.ts.map +1 -1
- package/dist/relay-signer.js +15 -0
- package/dist/relay-signer.js.map +1 -1
- package/dist/swap.d.ts +14 -1
- package/dist/swap.d.ts.map +1 -1
- package/dist/swap.js +55 -12
- package/dist/swap.js.map +1 -1
- package/dist/tx.d.ts +4 -9
- package/dist/tx.d.ts.map +1 -1
- package/dist/tx.js +11 -75
- package/dist/tx.js.map +1 -1
- package/dist/verify-prepared.d.ts +14 -26
- package/dist/verify-prepared.d.ts.map +1 -1
- package/dist/verify-prepared.js +12 -73
- package/dist/verify-prepared.js.map +1 -1
- package/dist/withdraw.d.ts.map +1 -1
- package/dist/withdraw.js +66 -20
- package/dist/withdraw.js.map +1 -1
- package/package.json +4 -4
- package/dist/quest-fund.d.ts +0 -142
- package/dist/quest-fund.d.ts.map +0 -1
- package/dist/quest-fund.js +0 -355
- 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
|
|
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>
|
|
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
|
|
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.
|
|
41
|
-
|
|
42
|
-
`
|
|
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
|
|
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,40 @@ 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
|
+
A current relay narrows each read to what was asked for: Canton Coin by its own
|
|
128
|
+
templates, a token by its registry's holding template. So thousands of Canton
|
|
129
|
+
Coin dust holdings no longer hide a token, or make its total a lower bound —
|
|
130
|
+
only more holdings of that token's own registry than one read lists do (tokens
|
|
131
|
+
of one registry share a template), and on an older relay any mix does.
|
|
132
|
+
|
|
133
|
+
On such a wallet `balance` shows token totals as lower bounds ("≥"), `withdraw`
|
|
134
|
+
spends the fewest holdings that cover the amount (and refuses a full-balance
|
|
135
|
+
withdraw, whose amount it cannot know), and a swap into that token says it
|
|
136
|
+
cannot confirm delivery instead of reporting a false timeout (it still claims a
|
|
137
|
+
returned input during its window; `claim` picks up one that comes back later).
|
|
138
|
+
Paying from it works while the amount is coverable from the visible holdings;
|
|
139
|
+
otherwise the relay answers `holdings_merge_required` with the merge to run.
|
|
98
140
|
|
|
99
141
|
For a wallet already too large to enumerate on-ledger, `merge` reads the holdings
|
|
100
142
|
from the public SV Scan ACS snapshot (paginated, no node cap), sweeps over them
|
|
@@ -266,6 +308,9 @@ const r = await executeSwap({ amount: "60", ticket }); // sends, waits for
|
|
|
266
308
|
settable via `--network <net>`.
|
|
267
309
|
- `CANTON_AGENT_API_KEY`: only if the relay requires one (sent as the
|
|
268
310
|
`X-Agent-Key` header).
|
|
311
|
+
- `CANTON_AGENT_RELAY_TIMEOUT_MS`: per-request relay timeout (default 120000). A
|
|
312
|
+
timed-out read fails the command; a timed-out SUBMISSION is reported as
|
|
313
|
+
"outcome unknown" — check `balance` before retrying, it may have committed.
|
|
269
314
|
- `HTTPS_PROXY` / `HTTP_PROXY` (or `ALL_PROXY`): if set, the CLI routes all
|
|
270
315
|
relay calls through that proxy. Node's `fetch` does NOT honor these on its
|
|
271
316
|
own, so behind a corporate/regional proxy you must set one of them or every
|
package/dist/cli-args.d.ts
CHANGED
|
@@ -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
|
package/dist/cli-args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-args.d.ts","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
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
|
package/dist/cli-args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-args.js","sourceRoot":"","sources":["../src/cli-args.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,gBAAgB,CAAC;AAEhD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,WAAW,GAAwB,IAAI,GAAG,CAAC;IACtD,aAAa;IACb,SAAS;IACT,MAAM;IACN,OAAO;IACP,YAAY;IACZ,WAAW;IACX,iBAAiB;IACjB,WAAW;IACX,MAAM;IACN,UAAU;IACV,YAAY;IACZ,iFAAiF;IACjF,kFAAkF;IAClF,mFAAmF;IACnF,UAAU;IACV,SAAS;IACT,cAAc;IACd,2EAA2E;IAC3E,4EAA4E;IAC5E,+EAA+E;IAC/E,SAAS;IACT,cAAc;IACd,QAAQ;IACR,kBAAkB;
|
|
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`.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
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,39 @@ 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 part of them (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. Only the
|
|
178
|
+
// tokens are asked for (`registryOnly`): on a current relay Canton Coin
|
|
179
|
+
// dust then no longer turns every token total into a lower bound.
|
|
148
180
|
try {
|
|
149
|
-
const h = await relay.holdings(w.party);
|
|
181
|
+
const h = await relay.holdings(w.party, undefined, { slice: true, registryOnly: true });
|
|
182
|
+
const ge = h.complete ? "" : "≥ ";
|
|
150
183
|
for (const ins of h.instruments) {
|
|
151
184
|
if (ins.id === "Amulet")
|
|
152
185
|
continue; // already printed as CC above
|
|
153
186
|
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"}` +
|
|
187
|
+
console.log(`${ge}${ins.total} ${ins.id} (${ge}${ins.holdings.length} holding${ins.holdings.length === 1 ? "" : "s"}` +
|
|
155
188
|
(locked ? `, ${locked} locked` : "") +
|
|
156
189
|
`) @ ${ins.admin}`);
|
|
157
190
|
}
|
|
191
|
+
if (!h.complete) {
|
|
192
|
+
console.log(" (this wallet holds more than the participant lists in one read — token totals above " +
|
|
193
|
+
"are lower bounds; consolidate with: canton-agent-wallet merge --all-tokens, " +
|
|
194
|
+
"and plain `canton-agent-wallet merge` for Canton Coin dust)");
|
|
195
|
+
}
|
|
158
196
|
}
|
|
159
|
-
catch {
|
|
160
|
-
|
|
197
|
+
catch (err) {
|
|
198
|
+
// An older relay without /holdings answers Fastify's route-not-found;
|
|
199
|
+
// that stays quiet (the CC line above is the whole answer there). Any
|
|
200
|
+
// other failure is said, not swallowed.
|
|
201
|
+
const notServed = err instanceof Error && /-> 404 .*Route GET:/.test(err.message);
|
|
202
|
+
if (!notServed) {
|
|
203
|
+
console.error(`tokens: could not list (${err instanceof Error ? err.message.split("\n")[0] : String(err)})`);
|
|
204
|
+
}
|
|
161
205
|
}
|
|
162
206
|
}
|
|
163
207
|
catch (err) {
|
|
@@ -177,9 +221,7 @@ async function main() {
|
|
|
177
221
|
// Consolidate dust amulets by self-transferring them in batches (receiver ==
|
|
178
222
|
// this wallet, enforced by verify-before-sign). Restores `balance`/`pay` for
|
|
179
223
|
// a wallet whose amulet count outgrew the participant's ACS element cap.
|
|
180
|
-
const w =
|
|
181
|
-
if (!w)
|
|
182
|
-
return fail("no wallet yet — run: canton-agent-wallet create");
|
|
224
|
+
const w = requireWallet();
|
|
183
225
|
const target = intFlag(args, "--target");
|
|
184
226
|
const batch = intFlag(args, "--batch");
|
|
185
227
|
const maxRounds = intFlag(args, "--max-rounds");
|
|
@@ -195,6 +237,24 @@ async function main() {
|
|
|
195
237
|
if ((mAdmin && !mId) || (!mAdmin && mId)) {
|
|
196
238
|
return fail("merge: --admin <registrar> and --id <instrument> must be given together");
|
|
197
239
|
}
|
|
240
|
+
// --all-tokens: every token of a trusted registrar, whatever the mix — the
|
|
241
|
+
// way through a wallet whose oldest holdings are another token's dust.
|
|
242
|
+
if (boolFlag(args, "--all-tokens")) {
|
|
243
|
+
if (mAdmin)
|
|
244
|
+
return fail("merge: --all-tokens merges every trusted token — drop --admin/--id");
|
|
245
|
+
const res = await mergeAllRegistryHoldings(new RelayClient({ relayUrl: w.relayUrl, apiKey: API_KEY }), w, {
|
|
246
|
+
...(batch !== undefined ? { batch } : {}),
|
|
247
|
+
...(maxRounds !== undefined ? { maxRounds } : {}),
|
|
248
|
+
dryRun,
|
|
249
|
+
yes,
|
|
250
|
+
onProgress: (line) => console.log(line),
|
|
251
|
+
});
|
|
252
|
+
if (!dryRun) {
|
|
253
|
+
console.log(`merge complete: ${res.rounds} batch transfer(s), ${res.merged.length} token holding(s) consumed` +
|
|
254
|
+
(res.skippedStale > 0 ? ` (${res.skippedStale} stale batch(es) skipped)` : ""));
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
198
258
|
if (mAdmin && !isTrustedRegistryAdmin(mAdmin, process.env)) {
|
|
199
259
|
return fail(`merge: ${mAdmin} is not a known registry admin — add it via ` +
|
|
200
260
|
`CANTON_AGENT_REGISTRY_TRUSTED_PARTIES before merging its token`);
|
|
@@ -218,16 +278,16 @@ async function main() {
|
|
|
218
278
|
`${res.merged.length} ${mId ? `${mId} holding(s)` : "amulet(s)"} consumed` +
|
|
219
279
|
(extras.length ? ` (${extras.join(", ")})` : "") +
|
|
220
280
|
(res.finalHoldings === null
|
|
221
|
-
?
|
|
222
|
-
"
|
|
281
|
+
? mId
|
|
282
|
+
? " — more remain than one read lists; see above"
|
|
283
|
+
: " — remaining holdings predate this run; re-run `merge` after the next " +
|
|
284
|
+
"Scan snapshot (~12:00 UTC daily) to continue"
|
|
223
285
|
: ` → ${res.finalHoldings} holding(s) remain`));
|
|
224
286
|
}
|
|
225
287
|
break;
|
|
226
288
|
}
|
|
227
289
|
case "claim": {
|
|
228
|
-
const w =
|
|
229
|
-
if (!w)
|
|
230
|
-
return fail("no wallet yet — run: canton-agent-wallet create");
|
|
290
|
+
const w = requireWallet();
|
|
231
291
|
const r = await claimAll(new RelayClient({ relayUrl: w.relayUrl, apiKey: API_KEY }), w);
|
|
232
292
|
// Say what happened to EVERY pending row, not just the claimed ones. An
|
|
233
293
|
// expired offer is skipped (the ledger would refuse its accept); a failed
|
|
@@ -269,9 +329,7 @@ async function main() {
|
|
|
269
329
|
// pays the fee) — ONLY works if the merchant delegated
|
|
270
330
|
// CanActAs to the facilitator user; NOT for
|
|
271
331
|
// self-custody merchants. Prefer the self default.
|
|
272
|
-
const w =
|
|
273
|
-
if (!w)
|
|
274
|
-
return fail("no wallet yet — run: canton-agent-wallet create");
|
|
332
|
+
const w = requireWallet();
|
|
275
333
|
const admin = flag(args, "--admin") ||
|
|
276
334
|
resolveTrustedDsoParty(process.env, w.network) ||
|
|
277
335
|
"";
|
|
@@ -355,7 +413,11 @@ async function main() {
|
|
|
355
413
|
// client's trusted-registry table/env, so an arbitrary --admin cannot be
|
|
356
414
|
// blind-trusted.
|
|
357
415
|
if (isTrustedRegistryAdmin(admin, process.env)) {
|
|
358
|
-
const id =
|
|
416
|
+
const id = preapprovalInstrumentId(args, admin);
|
|
417
|
+
if (id === undefined) {
|
|
418
|
+
return fail(`preapproval: pass --id <instrument> — this wallet does not know which instrument ` +
|
|
419
|
+
`${admin} issues, and provisioning the wrong one is not harmless`);
|
|
420
|
+
}
|
|
359
421
|
// ABSENCE MUST BE PROVEN BEFORE CREATING, because this create is not
|
|
360
422
|
// idempotent: run it twice and the merchant ends up with two live
|
|
361
423
|
// Utility.Registry TransferPreapproval contracts for the same
|
|
@@ -393,7 +455,7 @@ async function main() {
|
|
|
393
455
|
// DEFAULT: SELF-provision with the merchant's OWN key — no token, no
|
|
394
456
|
// delegation. The merchant prepays the fee from its own CC.
|
|
395
457
|
try {
|
|
396
|
-
const res = await selfProvisionPreapproval(relay, w, { expiresAt });
|
|
458
|
+
const res = await selfProvisionPreapproval(relay, w, { expiresAt, dso: admin });
|
|
397
459
|
console.log(`TransferPreapproval self-provisioned for ${w.party} (ready for transfer-factory payments)\n` +
|
|
398
460
|
` provider: ${w.party} (self)\n` +
|
|
399
461
|
` expiresAt: ${expiresAt}\n` +
|
|
@@ -414,7 +476,7 @@ async function main() {
|
|
|
414
476
|
case "pay": {
|
|
415
477
|
const url = positionals(args)[0];
|
|
416
478
|
if (!url)
|
|
417
|
-
return fail("usage: canton-agent-wallet pay [--relay-url <url>] [--asset <symbol>] <url>");
|
|
479
|
+
return fail("usage: canton-agent-wallet pay [--relay-url <url>] [--asset <symbol>] [--max <amount>] [--pay-to <party>] <url>");
|
|
418
480
|
// Reuse the wallet's stored relay (like claim/balance) when neither
|
|
419
481
|
// --relay-url nor CANTON_AGENT_RELAY_URL is given, so an already-created
|
|
420
482
|
// agent can pay without re-specifying the relay each time.
|
|
@@ -426,11 +488,22 @@ async function main() {
|
|
|
426
488
|
// Selection only; spending a registry token still needs
|
|
427
489
|
// CANTON_AGENT_PAYABLE_INSTRUMENTS.
|
|
428
490
|
const preferAsset = flag(args, "--asset");
|
|
491
|
+
// --max caps what one payment may cost (in the paid asset's own unit);
|
|
492
|
+
// --pay-to pins the only payee the wallet will sign for. Both refuse
|
|
493
|
+
// BEFORE anything is signed; see payLimits.
|
|
494
|
+
const limits = payLimits(args);
|
|
495
|
+
if (limits.kind === "error")
|
|
496
|
+
return fail(limits.message);
|
|
429
497
|
const f = await makePayingFetch({
|
|
430
498
|
relayUrl,
|
|
431
499
|
network: existing?.network ?? resolveNetwork(args),
|
|
432
500
|
apiKey: API_KEY,
|
|
433
501
|
...(preferAsset ? { preferAsset } : {}),
|
|
502
|
+
...(limits.maxPaymentValue !== undefined ? { maxPaymentValue: limits.maxPaymentValue } : {}),
|
|
503
|
+
...(limits.maxPaymentValueByInstrument !== undefined
|
|
504
|
+
? { maxPaymentValueByInstrument: limits.maxPaymentValueByInstrument }
|
|
505
|
+
: {}),
|
|
506
|
+
...(limits.expectedPayTo !== undefined ? { expectedPayTo: limits.expectedPayTo } : {}),
|
|
434
507
|
});
|
|
435
508
|
const res = await f(url);
|
|
436
509
|
console.log(`${res.status} ${res.statusText}`);
|
|
@@ -470,6 +543,7 @@ async function main() {
|
|
|
470
543
|
}
|
|
471
544
|
const r = await withdraw({
|
|
472
545
|
to,
|
|
546
|
+
wallet: requireWallet(),
|
|
473
547
|
...(amt.kind === "amount" ? { amount: amt.amount } : {}),
|
|
474
548
|
...(wAdmin && wId ? { instrumentAdmin: wAdmin, instrumentId: wId } : {}),
|
|
475
549
|
apiKey: API_KEY,
|
|
@@ -550,6 +624,7 @@ async function main() {
|
|
|
550
624
|
const sr = await executeSwap({
|
|
551
625
|
amount,
|
|
552
626
|
ticket,
|
|
627
|
+
...(existing ? { wallet: relayFor(existing) } : {}),
|
|
553
628
|
apiKey: API_KEY,
|
|
554
629
|
preapproveOutput: !boolFlag(args, "--no-preapprove"),
|
|
555
630
|
waitForOutput: wait,
|
|
@@ -568,6 +643,29 @@ async function main() {
|
|
|
568
643
|
console.log(`${head}\n ! input RETURNED — nothing was swapped: the CC balance is back at its ` +
|
|
569
644
|
`pre-send level (the venue hands back a trade it will not fill, e.g. below its minimum)`);
|
|
570
645
|
}
|
|
646
|
+
else if (sr.deliveryUnobservable) {
|
|
647
|
+
// Reached only after the input was sent (sentUpdateId above), so "do not
|
|
648
|
+
// resend" is true here. The wait claimed offers and watched for a
|
|
649
|
+
// returned input; only the delivery itself could not be seen.
|
|
650
|
+
console.log(`${head}\n ! cannot confirm delivery: the relay cannot list every ${outCanon} holding of ` +
|
|
651
|
+
`this wallet (more of that registry's holdings than the participant lists in one ` +
|
|
652
|
+
`read), so the new one is out of view. The input WAS sent: do NOT resend. A trade ` +
|
|
653
|
+
`the venue will not fill comes back as an offer — run \`canton-agent-wallet claim\` ` +
|
|
654
|
+
`in a minute to pick it up. Run \`canton-agent-wallet merge --all-tokens\` (plain ` +
|
|
655
|
+
`\`canton-agent-wallet merge\` first if Canton Coin dust fills the wallet), then ` +
|
|
656
|
+
`check \`balance\`.` +
|
|
657
|
+
(sr.partialRise !== undefined
|
|
658
|
+
? `\n note: before that, the ${outCanon} balance rose ${sr.partialRise}, below the ` +
|
|
659
|
+
`ticket minimum ${ticket.minOutput} — possibly an unrelated inbound, not this swap's fill`
|
|
660
|
+
: "") +
|
|
661
|
+
(sr.claimed > 0
|
|
662
|
+
? `\n claimed ${sr.claimed} incoming transfer(s): ${sr.claimedUpdateIds.join(", ")}`
|
|
663
|
+
: "") +
|
|
664
|
+
(sr.pollErrors > 0
|
|
665
|
+
? `\n ! ${sr.pollErrors} poll error(s) while waiting — the relay was flaky, but ` +
|
|
666
|
+
"the input WAS sent (updateId above); do NOT resend, check `balance` first"
|
|
667
|
+
: ""));
|
|
668
|
+
}
|
|
571
669
|
else {
|
|
572
670
|
// Waited, no arrival: still settling, or a below-minimum trade whose input
|
|
573
671
|
// was returned (the per-tick claim picks that return up — `claimed` shows
|
|
@@ -626,8 +724,35 @@ async function main() {
|
|
|
626
724
|
` run: canton-agent-wallet balance`);
|
|
627
725
|
break;
|
|
628
726
|
}
|
|
727
|
+
case "set-relay": {
|
|
728
|
+
// Change the relay SAVED in wallet.json — the one every command uses when
|
|
729
|
+
// no --relay-url is given. Refuses a relay that settles on a different
|
|
730
|
+
// network: the wallet's DSO pin and parties are network-specific.
|
|
731
|
+
const url = positionals(args)[0];
|
|
732
|
+
if (!url)
|
|
733
|
+
return fail("usage: canton-agent-wallet set-relay <url>");
|
|
734
|
+
const w = loadWallet();
|
|
735
|
+
if (!w)
|
|
736
|
+
return fail("no wallet yet — run: canton-agent-wallet create");
|
|
737
|
+
let net;
|
|
738
|
+
try {
|
|
739
|
+
const sup = await new RelayClient({ relayUrl: url, apiKey: API_KEY }).supported();
|
|
740
|
+
net = sup?.kinds?.find((k) => typeof k?.network === "string" && k.network.length > 0)?.network;
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
return fail(`set-relay: could not reach ${url} (${err instanceof Error ? err.message : String(err)})`);
|
|
744
|
+
}
|
|
745
|
+
if (!net)
|
|
746
|
+
return fail(`set-relay: ${url} does not say which network it settles on — not switching`);
|
|
747
|
+
if (net !== w.network) {
|
|
748
|
+
return fail(`set-relay: ${url} settles on ${net}, but this wallet is on ${w.network} — not switching`);
|
|
749
|
+
}
|
|
750
|
+
saveWallet({ ...w, relayUrl: url });
|
|
751
|
+
console.log(`relay set: ${w.relayUrl} → ${url} (${net})`);
|
|
752
|
+
break;
|
|
753
|
+
}
|
|
629
754
|
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
|
|
755
|
+
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
756
|
" relay required for create/pay/import: --relay-url <url> or CANTON_AGENT_RELAY_URL\n" +
|
|
632
757
|
" preapproval = merchant TransferPreapproval setup for the transfer-factory (V3, 1-tx) path (--status to just check).");
|
|
633
758
|
process.exit(cmd ? 1 : 0);
|