@vultisig/cli 2.23.0 → 3.0.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 (3) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/index.js +128 -133
  3. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,66 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1606](https://github.com/vultisig/vultisig-sdk/pull/1606) [`c593ba0`](https://github.com/vultisig/vultisig-sdk/commit/c593ba0693f6a02402aac0912a97d9e36e8efad0) Thanks [@neavra](https://github.com/neavra)! - Make token references resolve consistently across the vault and CLI surfaces.
8
+
9
+ **`@vultisig/sdk` — consumer-visible behaviour change on `send`, `swap` and `balance`:**
10
+
11
+ - A token reference (the `symbol` on `send`/`swap`, the `tokenId` on `balance`)
12
+ now resolves by contract address / stored vault token id as well as by symbol
13
+ or well-known ticker, through one shared resolver. Previously `send` matched
14
+ by symbol only while `balance` treated the same value as a raw contract
15
+ address, so no single value worked on both paths and an ERC-20 send could not
16
+ be built at all.
17
+ - The change is additive. Symbol/ticker is still matched first and the vault's
18
+ own tokens still shadow the well-known registry, so every reference that
19
+ resolved before resolves to the same token and produces the same signed
20
+ payload. Only references that previously threw now resolve. A reference
21
+ matching nothing is still passed through to the balance layer untouched.
22
+ - `send({ dryRun: true })` gains `feeSymbol` and reports the network fee in the
23
+ chain's native asset. For a token send, `fee` was previously formatted with
24
+ the token's decimals and `total` was amount + fee — both meaningless when the
25
+ fee is paid in a different asset. `total` is now denominated in the asset
26
+ being sent. Native sends are unaffected.
27
+ - A `Balance` for a token is labelled by the same resolution. Previously the
28
+ token was found by an exact match against the vault's stored token id, so a
29
+ well-known token the vault does not track — or one added with an id that is
30
+ not its bare contract address — fell through to a default of 18 decimals with
31
+ the raw id as `symbol`. `formatBalance` now resolves the same way everything
32
+ else does; a token in no registry still falls back as before.
33
+
34
+ **`@vultisig/cli`:**
35
+
36
+ - `send --token` works with either a contract address or a symbol, and its
37
+ dry-run preview quotes the fee in the native asset. The preview also warns
38
+ separately when the native balance cannot cover the fee — a token send draws
39
+ its fee from a different balance than the one `total` is checked against.
40
+ - `portfolio`'s total now equals the sum of the breakdown printed under it, with
41
+ each held token itemized as its own row (`chainBalances[].tokens`).
42
+ - `balance <chain> --tokens` returns token balances instead of silently
43
+ ignoring the flag; without the flag the output is unchanged.
44
+ - `tokens --discover` documents that it saves discovered tokens to the vault.
45
+
46
+ ### Patch Changes
47
+
48
+ - [#1174](https://github.com/vultisig/vultisig-sdk/pull/1174) [`0d73d9f`](https://github.com/vultisig/vultisig-sdk/commit/0d73d9f5d08b10e52b9461678d7d700afba52e01) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Fix CLI shell completion so vault suggestions read the SDK's real storage layout and honor `VULTISIG_CONFIG_DIR`.
49
+
50
+ - [#1163](https://github.com/vultisig/vultisig-sdk/pull/1163) [`286e342`](https://github.com/vultisig/vultisig-sdk/commit/286e342962e8fa63525c8de25bed84295fd468cd) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Promote THOR/Maya swap-memo parsing into the public SDK API and update the CLI executor to consume the shared helper.
51
+
52
+ - [#1165](https://github.com/vultisig/vultisig-sdk/pull/1165) [`f3fd265`](https://github.com/vultisig/vultisig-sdk/commit/f3fd2654b8d90f5d5aaa3c578fcd5fad752203d3) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Re-export the RN-safe `parseChain`, `parseTicker`, and `knownContracts` helpers from `@vultisig/sdk/platforms/react-native` so mobile consumers can use the SDK's canonical public helpers without maintaining local copies or reaching into non-RN entrypoints.
53
+
54
+ - [#1171](https://github.com/vultisig/vultisig-sdk/pull/1171) [`a24771a`](https://github.com/vultisig/vultisig-sdk/commit/a24771afba7c210423c586f9492d3531cdcd47f5) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Make the SDK's default Node/Electron `FileStorage` honor `VULTISIG_CONFIG_DIR` so default vault storage stays co-located with the CLI config, credentials, cache, and broadcast journal paths.
55
+
56
+ - [#1168](https://github.com/vultisig/vultisig-sdk/pull/1168) [`e40aab2`](https://github.com/vultisig/vultisig-sdk/commit/e40aab24e02c46fd2c3fe95873b14aad4fd2fe6d) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Consolidate `VULTISIG_CONFIG_DIR` resolution into a shared helper so empty or whitespace-only overrides consistently fall back to the default config directory across credentials, config storage, token cache, and broadcast journal paths.
57
+
58
+ - Updated dependencies [[`8061f30`](https://github.com/vultisig/vultisig-sdk/commit/8061f3072cc299196894b3c118bf7046eff8a004), [`4c76c50`](https://github.com/vultisig/vultisig-sdk/commit/4c76c507df970c6bbb1542c72a7ab0e06d039e65), [`bb752e2`](https://github.com/vultisig/vultisig-sdk/commit/bb752e25dc318488a72d9b6b7aa8d484c568dbaa), [`0d73d9f`](https://github.com/vultisig/vultisig-sdk/commit/0d73d9f5d08b10e52b9461678d7d700afba52e01), [`ec4aac7`](https://github.com/vultisig/vultisig-sdk/commit/ec4aac78e96db00cae283fe8a506983a30c42412), [`286e342`](https://github.com/vultisig/vultisig-sdk/commit/286e342962e8fa63525c8de25bed84295fd468cd), [`c593ba0`](https://github.com/vultisig/vultisig-sdk/commit/c593ba0693f6a02402aac0912a97d9e36e8efad0), [`d1ed4bb`](https://github.com/vultisig/vultisig-sdk/commit/d1ed4bbd459bfed006cf9f319d9e39356ffe25b9), [`648d932`](https://github.com/vultisig/vultisig-sdk/commit/648d932b7e3c6f3c30ff7007f3c4e4387879ba38), [`f3fd265`](https://github.com/vultisig/vultisig-sdk/commit/f3fd2654b8d90f5d5aaa3c578fcd5fad752203d3), [`a24771a`](https://github.com/vultisig/vultisig-sdk/commit/a24771afba7c210423c586f9492d3531cdcd47f5), [`6763ddb`](https://github.com/vultisig/vultisig-sdk/commit/6763ddbd382bd71cdf9f24bbd3bde0116a694906), [`e40aab2`](https://github.com/vultisig/vultisig-sdk/commit/e40aab24e02c46fd2c3fe95873b14aad4fd2fe6d), [`259837f`](https://github.com/vultisig/vultisig-sdk/commit/259837f482717624d6422797e088a9341d4f1a23)]:
59
+ - @vultisig/core-chain@2.29.2
60
+ - @vultisig/sdk@3.0.0
61
+ - @vultisig/client-shared@0.3.1
62
+ - @vultisig/rujira@57.0.0
63
+
3
64
  ## 2.23.0
4
65
 
5
66
  ### Patch Changes
package/dist/index.js CHANGED
@@ -5855,12 +5855,20 @@ function displayPortfolio(portfolio, currency, _raw = false) {
5855
5855
  printResult(chalk2.cyan("| ") + chalk2.bold.green(totalDisplay) + chalk2.cyan(" |"));
5856
5856
  printResult(chalk2.cyan("+----------------------------------------+\n"));
5857
5857
  printResult(chalk2.bold("Chain Breakdown:\n"));
5858
- const table = portfolio.chainBalances.map(({ chain, balance, value }) => ({
5859
- Chain: chain,
5860
- Amount: balance.formattedAmount,
5861
- Symbol: balance.symbol,
5862
- Value: value ? `${value.amount} ${value.currency.toUpperCase()}` : "N/A"
5863
- }));
5858
+ const table = portfolio.chainBalances.flatMap(({ chain, balance, value, tokens }) => [
5859
+ {
5860
+ Chain: chain,
5861
+ Amount: balance.formattedAmount,
5862
+ Symbol: balance.symbol,
5863
+ Value: value ? `${value.amount} ${value.currency.toUpperCase()}` : "N/A"
5864
+ },
5865
+ ...(tokens ?? []).map((token) => ({
5866
+ Chain: chain,
5867
+ Amount: token.balance?.formattedAmount ?? "-",
5868
+ Symbol: token.balance?.symbol ?? token.tokenId,
5869
+ Value: `${token.value.amount} ${token.value.currency.toUpperCase()}`
5870
+ }))
5871
+ ]);
5864
5872
  printTable(table);
5865
5873
  }
5866
5874
  function displayAddresses(addresses) {
@@ -6085,6 +6093,16 @@ async function executeBalance(ctx2, options = {}) {
6085
6093
  const spinner = createSpinner("Loading balances...");
6086
6094
  const raw = options.raw ?? false;
6087
6095
  if (options.chain) {
6096
+ if (options.includeTokens) {
6097
+ const balances = await vault.balances([options.chain], true);
6098
+ spinner.succeed("Balances loaded");
6099
+ if (isJsonOutput()) {
6100
+ outputJson({ chain: options.chain, balances });
6101
+ return;
6102
+ }
6103
+ displayBalancesTable(balances, raw);
6104
+ return;
6105
+ }
6088
6106
  const balance = await vault.balance(options.chain);
6089
6107
  spinner.succeed("Balance loaded");
6090
6108
  if (isJsonOutput()) {
@@ -6115,7 +6133,6 @@ async function executePortfolio(ctx2, options = {}) {
6115
6133
  }
6116
6134
  const currencyName = fiatCurrencyNameRecord2[currency];
6117
6135
  const spinner = createSpinner(`Loading portfolio in ${currencyName}...`);
6118
- const totalValue = await vault.getTotalValue(currency);
6119
6136
  const chains = vault.chains;
6120
6137
  const results = await Promise.all(
6121
6138
  chains.map(async (chain) => {
@@ -6125,12 +6142,34 @@ async function executePortfolio(ctx2, options = {}) {
6125
6142
  } catch (err) {
6126
6143
  return { failure: { chain, stage: "balance", error: conciseError(err) } };
6127
6144
  }
6145
+ let values;
6128
6146
  try {
6129
- const value = await vault.getValue(chain, void 0, currency);
6130
- return { entry: { chain, balance, value } };
6147
+ values = await vault.getValues(chain, currency);
6131
6148
  } catch (err) {
6132
6149
  return { entry: { chain, balance }, failure: { chain, stage: "value", error: conciseError(err) } };
6133
6150
  }
6151
+ const { native, ...tokenValues } = values;
6152
+ if (!native) {
6153
+ try {
6154
+ const retried = await vault.getValue(chain, void 0, currency);
6155
+ return { entry: { chain, balance, value: retried, tokens: await withAmounts(tokenValues) } };
6156
+ } catch (err) {
6157
+ return {
6158
+ entry: { chain, balance, tokens: await withAmounts(tokenValues) },
6159
+ failure: { chain, stage: "value", error: conciseError(err) }
6160
+ };
6161
+ }
6162
+ }
6163
+ return { entry: { chain, balance, value: native, tokens: await withAmounts(tokenValues) } };
6164
+ async function withAmounts(byTokenId) {
6165
+ return Promise.all(
6166
+ Object.entries(byTokenId).map(async ([tokenId, value]) => ({
6167
+ tokenId,
6168
+ value,
6169
+ balance: await vault.balance(chain, tokenId).catch(() => void 0)
6170
+ }))
6171
+ );
6172
+ }
6134
6173
  })
6135
6174
  );
6136
6175
  const chainBalances = [];
@@ -6147,6 +6186,11 @@ async function executePortfolio(ctx2, options = {}) {
6147
6186
  ["Check your internet connection", "Retry in a few moments"]
6148
6187
  );
6149
6188
  }
6189
+ const total = chainBalances.reduce(
6190
+ (sum, entry) => sum + parseFloat(entry.value?.amount ?? "0") + (entry.tokens ?? []).reduce((tokenSum, token) => tokenSum + parseFloat(token.value.amount), 0),
6191
+ 0
6192
+ );
6193
+ const totalValue = { amount: total.toFixed(2), currency, lastUpdated: Date.now() };
6150
6194
  const portfolio = { totalValue, chainBalances };
6151
6195
  spinner.succeed("Portfolio loaded");
6152
6196
  if (isJsonOutput()) {
@@ -6324,8 +6368,8 @@ import {
6324
6368
  writeFileSync,
6325
6369
  writeSync
6326
6370
  } from "node:fs";
6327
- import { homedir } from "node:os";
6328
6371
  import { dirname, join } from "node:path";
6372
+ import { getVultisigConfigDir } from "@vultisig/client-shared";
6329
6373
 
6330
6374
  // src/agent/agentErrors.ts
6331
6375
  import { VaultError as VaultError2, VaultErrorCode as VaultErrorCode2, VaultImportError as VaultImportError2, VaultImportErrorCode as VaultImportErrorCode2 } from "@vultisig/sdk";
@@ -6560,8 +6604,7 @@ function nowMs() {
6560
6604
  function journalPath() {
6561
6605
  const explicit = process.env.VULTISIG_BROADCAST_JOURNAL_PATH;
6562
6606
  if (explicit && explicit.trim()) return explicit;
6563
- const dir = process.env.VULTISIG_CONFIG_DIR && process.env.VULTISIG_CONFIG_DIR.trim() ? process.env.VULTISIG_CONFIG_DIR : join(homedir(), ".vultisig");
6564
- return join(dir, "broadcasts.jsonl");
6607
+ return join(getVultisigConfigDir(), "broadcasts.jsonl");
6565
6608
  }
6566
6609
  function normalize(v, canonicalizeEmptyCalldata = false) {
6567
6610
  const normalized = (v ?? "").trim().toLowerCase();
@@ -7289,7 +7332,7 @@ async function discoverTokens(ctx2, chain) {
7289
7332
  });
7290
7333
  }
7291
7334
  const allTokens = vault.getTokens(chain);
7292
- spinner.succeed(`Discovered ${newTokens.length} new token(s) on ${chain}`);
7335
+ spinner.succeed(`Now tracking ${newTokens.length} new token(s) on ${chain}`);
7293
7336
  if (isJsonOutput()) {
7294
7337
  outputJson({
7295
7338
  chain,
@@ -7306,6 +7349,9 @@ async function discoverTokens(ctx2, chain) {
7306
7349
  printResult(` ${d.ticker} (${d.contractAddress})`);
7307
7350
  }
7308
7351
  info(chalk4.gray(`
7352
+ Saved to this vault \u2014 tracked tokens count toward portfolio and balance --tokens.`));
7353
+ info(chalk4.gray(`Use --remove <tokenId> to stop tracking one.`));
7354
+ info(chalk4.gray(`
7309
7355
  ${allTokens.length} total token(s) tracked on ${chain}`));
7310
7356
  }
7311
7357
  async function listTokens(ctx2, chain) {
@@ -7378,6 +7424,51 @@ async function executeSend(ctx2, params) {
7378
7424
  }
7379
7425
  return sendTransaction(vault, params);
7380
7426
  }
7427
+ async function previewDryRun(vault, params, dryResult, to, destinationTag) {
7428
+ const balance = await vault.balance(params.chain, params.tokenId);
7429
+ const hasInsufficientBalance = parseFloat(dryResult.total) > parseFloat(balance.formattedAmount);
7430
+ const isTokenSend = balance.tokenId !== void 0;
7431
+ const feeBalance = isTokenSend ? await vault.balance(params.chain).catch(() => void 0) : void 0;
7432
+ const warnings = [];
7433
+ if (hasInsufficientBalance) {
7434
+ warnings.push(`Insufficient balance: you have ${balance.formattedAmount} ${balance.symbol}`);
7435
+ }
7436
+ if (isTokenSend && feeBalance === void 0) {
7437
+ warnings.push(`Could not check your ${dryResult.feeSymbol} balance for the network fee`);
7438
+ } else if (feeBalance && parseFloat(dryResult.fee) > parseFloat(feeBalance.formattedAmount)) {
7439
+ warnings.push(
7440
+ `Insufficient ${dryResult.feeSymbol} for the network fee: you have ${feeBalance.formattedAmount} ${dryResult.feeSymbol}, the fee is ${dryResult.fee}`
7441
+ );
7442
+ }
7443
+ const result = {
7444
+ dryRun: true,
7445
+ chain: params.chain,
7446
+ to,
7447
+ amount: params.amount,
7448
+ symbol: balance.symbol,
7449
+ fee: dryResult.fee,
7450
+ feeSymbol: dryResult.feeSymbol,
7451
+ total: dryResult.total,
7452
+ balance: balance.formattedAmount,
7453
+ destinationTag,
7454
+ ...warnings.length > 0 ? { warning: warnings.join(". ") } : {}
7455
+ };
7456
+ if (isJsonOutput()) {
7457
+ outputJson(result);
7458
+ return result;
7459
+ }
7460
+ info(`
7461
+ Dry-run preview:`);
7462
+ info(` Chain: ${result.chain}`);
7463
+ info(` To: ${result.to}`);
7464
+ info(` Amount: ${result.amount} ${result.symbol}`);
7465
+ if (result.destinationTag !== void 0) info(` Destination tag: ${result.destinationTag}`);
7466
+ info(` Fee: ${result.fee} ${result.feeSymbol}`);
7467
+ info(` Total: ${result.total} ${result.symbol}`);
7468
+ info(` Balance: ${result.balance} ${result.symbol}`);
7469
+ if (result.warning) warn(` Warning: ${result.warning}`);
7470
+ return result;
7471
+ }
7381
7472
  async function sendTransaction(vault, params) {
7382
7473
  if (!params.dryRun && !params.yes && isNonInteractive()) {
7383
7474
  throw new ConfirmationRequiredError(
@@ -7406,37 +7497,7 @@ async function sendTransaction(vault, params) {
7406
7497
  prepareSpinner.succeed("Transaction prepared");
7407
7498
  if (!dryResult.dryRun) throw new Error("unreachable");
7408
7499
  if (params.dryRun) {
7409
- const balance2 = await vault.balance(params.chain, params.tokenId);
7410
- const hasInsufficientBalance = parseFloat(dryResult.total) > parseFloat(balance2.formattedAmount);
7411
- const result = {
7412
- dryRun: true,
7413
- chain: params.chain,
7414
- to,
7415
- amount: params.amount,
7416
- symbol: balance2.symbol,
7417
- fee: dryResult.fee,
7418
- total: dryResult.total,
7419
- balance: balance2.formattedAmount,
7420
- destinationTag
7421
- };
7422
- if (hasInsufficientBalance) {
7423
- result.warning = `Insufficient balance: you have ${balance2.formattedAmount} ${balance2.symbol}`;
7424
- }
7425
- if (isJsonOutput()) {
7426
- outputJson(result);
7427
- } else {
7428
- info(`
7429
- Dry-run preview:`);
7430
- info(` Chain: ${result.chain}`);
7431
- info(` To: ${result.to}`);
7432
- info(` Amount: ${result.amount} ${result.symbol}`);
7433
- if (result.destinationTag !== void 0) info(` Destination tag: ${result.destinationTag}`);
7434
- info(` Fee: ${result.fee} ${result.symbol}`);
7435
- info(` Total: ${result.total} ${result.symbol}`);
7436
- info(` Balance: ${result.balance} ${result.symbol}`);
7437
- if (result.warning) warn(` Warning: ${result.warning}`);
7438
- }
7439
- return result;
7500
+ return previewDryRun(vault, params, dryResult, to, destinationTag);
7440
7501
  }
7441
7502
  let gas;
7442
7503
  try {
@@ -10358,6 +10419,7 @@ import {
10358
10419
  Chain as Chain11,
10359
10420
  chainFeeCoin,
10360
10421
  getChainKind as getChainKind3,
10422
+ parseThorSwapMemo,
10361
10423
  resolveChainReference,
10362
10424
  VaultError as VaultError3,
10363
10425
  VaultErrorCode as VaultErrorCode3,
@@ -10367,7 +10429,7 @@ import {
10367
10429
  // src/core/VaultStateStore.ts
10368
10430
  import * as fs3 from "node:fs";
10369
10431
  import * as path3 from "node:path";
10370
- import { getVultisigConfigDir } from "@vultisig/client-shared";
10432
+ import { getVultisigConfigDir as getVultisigConfigDir2 } from "@vultisig/client-shared";
10371
10433
  var LOCK_STALE_MS = 6e4;
10372
10434
  var LOCK_RETRY_INIT_MS = 100;
10373
10435
  var LOCK_MAX_WAIT_MS = 3e4;
@@ -10379,7 +10441,7 @@ var VaultStateStore = class {
10379
10441
  if (!safeId) {
10380
10442
  throw new Error("Invalid vaultId: must contain alphanumeric characters");
10381
10443
  }
10382
- this.baseDir = path3.join(getVultisigConfigDir(), "vault-state", safeId);
10444
+ this.baseDir = path3.join(getVultisigConfigDir2(), "vault-state", safeId);
10383
10445
  fs3.mkdirSync(this.baseDir, { recursive: true });
10384
10446
  }
10385
10447
  // -------------------------------------------------------------------------
@@ -10520,45 +10582,6 @@ function sleep2(ms) {
10520
10582
  }
10521
10583
 
10522
10584
  // src/agent/executor.ts
10523
- var THOR_MEMO_CHAIN_TO_ENUM = {
10524
- BTC: Chain11.Bitcoin,
10525
- ETH: Chain11.Ethereum,
10526
- BSC: Chain11.BSC,
10527
- AVAX: Chain11.Avalanche,
10528
- BASE: Chain11.Base,
10529
- // L2 — THORChain routinely quotes Base destinations (PR #439 review finding 1)
10530
- ARB: Chain11.Arbitrum,
10531
- // L1-via-bridge path (PR #439 review finding 1)
10532
- BCH: Chain11.BitcoinCash,
10533
- LTC: Chain11.Litecoin,
10534
- DOGE: Chain11.Dogecoin,
10535
- GAIA: Chain11.Cosmos,
10536
- THOR: Chain11.THORChain,
10537
- RUNE: Chain11.THORChain,
10538
- XRP: Chain11.Ripple,
10539
- DASH: Chain11.Dash,
10540
- ZEC: Chain11.Zcash,
10541
- MAYA: Chain11.MayaChain,
10542
- CACAO: Chain11.MayaChain
10543
- };
10544
- var THOR_MEMO_ASSET_SHORTCUTS = {
10545
- b: "BTC.BTC",
10546
- e: "ETH.ETH",
10547
- s: "BSC.BNB",
10548
- a: "AVAX.AVAX",
10549
- c: "BCH.BCH",
10550
- l: "LTC.LTC",
10551
- d: "DOGE.DOGE",
10552
- g: "GAIA.ATOM",
10553
- r: "THOR.RUNE",
10554
- x: "XRP.XRP",
10555
- cacao: "MAYA.CACAO",
10556
- dash: "DASH.DASH",
10557
- zec: "ZEC.ZEC"
10558
- // BASE / ARB don't have documented single-letter shortcuts; THORChain
10559
- // emits these as the full CHAIN.ASSET form in memos. Listed in
10560
- // THOR_MEMO_CHAIN_TO_ENUM only.
10561
- };
10562
10585
  var EVM_CHAINS = /* @__PURE__ */ new Set([
10563
10586
  "Ethereum",
10564
10587
  "BSC",
@@ -11219,13 +11242,7 @@ var AgentExecutor = class {
11219
11242
  const txArgs = serverTxData?.txArgs ?? {};
11220
11243
  const memo = typeof txArgs.memo === "string" ? txArgs.memo : "";
11221
11244
  const parsed = parseThorSwapMemo(memo);
11222
- const toChain = THOR_MEMO_CHAIN_TO_ENUM[parsed.destChainCode];
11223
- if (!toChain) {
11224
- throw new VaultError3(
11225
- VaultErrorCode3.UnsupportedChain,
11226
- `signThorMsgDepositSwap: unsupported destination chain code '${parsed.destChainCode}' in memo '${memo}'.`
11227
- );
11228
- }
11245
+ const toChain = parsed.toChain;
11229
11246
  const vaultDestAddress = await this.vault.address(toChain);
11230
11247
  const normalizeForCompare = (addr) => EVM_CHAINS.has(toChain) ? addr.toLowerCase() : addr;
11231
11248
  if (parsed.destAddress && normalizeForCompare(parsed.destAddress) !== normalizeForCompare(vaultDestAddress)) {
@@ -12034,31 +12051,6 @@ function parseNonEvmEnvelope(serverTxData, chain) {
12034
12051
  const memo = typeof txArgs.memo === "string" && txArgs.memo.length > 0 ? txArgs.memo : void 0;
12035
12052
  return { chain, to, amount: amountDecimal, symbol, memo };
12036
12053
  }
12037
- function parseThorSwapMemo(memo) {
12038
- if (!memo.startsWith("=:")) {
12039
- throw new VaultError3(
12040
- VaultErrorCode3.NotImplemented,
12041
- `parseThorSwapMemo: only swap memos (=:CHAIN.ASSET:DEST...) supported on this path; got memo='${memo}'. LP memos (+:/-:) route through signThorMsgDepositLp; loan / validator ops out of scope.`
12042
- );
12043
- }
12044
- const memoBody = memo.slice(2);
12045
- const parts = memoBody.split(":");
12046
- let chainAsset = parts[0];
12047
- if (chainAsset && !chainAsset.includes(".")) {
12048
- const expanded = THOR_MEMO_ASSET_SHORTCUTS[chainAsset.toLowerCase()];
12049
- if (expanded) chainAsset = expanded;
12050
- }
12051
- if (!chainAsset || !chainAsset.includes(".")) {
12052
- throw new VaultError3(
12053
- VaultErrorCode3.InvalidConfig,
12054
- `parseThorSwapMemo: malformed swap memo '${memo}': missing CHAIN.ASSET in first segment.`
12055
- );
12056
- }
12057
- const [destChainCode, destAssetRaw] = chainAsset.split(".");
12058
- const destAsset = destAssetRaw?.split("-")[0] ?? "";
12059
- const destAddress = typeof parts[1] === "string" ? parts[1] : "";
12060
- return { destChainCode, destAsset, destAddress };
12061
- }
12062
12054
  function resolveChainId(chainId) {
12063
12055
  return resolveChainReference(chainId) ?? null;
12064
12056
  }
@@ -13495,13 +13487,13 @@ import {
13495
13487
  statSync as statSync3,
13496
13488
  writeFileSync as writeFileSync3
13497
13489
  } from "node:fs";
13498
- import { homedir as homedir2 } from "node:os";
13490
+ import { homedir } from "node:os";
13499
13491
  import { dirname as dirname2, join as join3 } from "node:path";
13500
13492
  var LOCK_RETRY_MS = 25;
13501
13493
  var LOCK_MAX_WAIT_MS2 = 5e3;
13502
13494
  var LOCK_STALE_MS2 = 3e4;
13503
13495
  function getTokenCachePath() {
13504
- const dir = process.env.VULTISIG_CONFIG_DIR ?? join3(homedir2(), ".vultisig");
13496
+ const dir = process.env.VULTISIG_CONFIG_DIR ?? join3(homedir(), ".vultisig");
13505
13497
  return join3(dir, "agent-tokens.json");
13506
13498
  }
13507
13499
  function tokenCacheKey(scope) {
@@ -15271,7 +15263,7 @@ function formatDate(iso) {
15271
15263
  }
15272
15264
 
15273
15265
  // src/lib/version.ts
15274
- import { getVultisigConfigDir as getVultisigConfigDir2 } from "@vultisig/client-shared";
15266
+ import { getVultisigConfigDir as getVultisigConfigDir3 } from "@vultisig/client-shared";
15275
15267
  import chalk11 from "chalk";
15276
15268
  import { existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
15277
15269
  import { join as join4 } from "path";
@@ -15279,7 +15271,7 @@ var cachedVersion = null;
15279
15271
  function getVersion() {
15280
15272
  if (cachedVersion) return cachedVersion;
15281
15273
  if (true) {
15282
- cachedVersion = "2.23.0";
15274
+ cachedVersion = "3.0.0";
15283
15275
  return cachedVersion;
15284
15276
  }
15285
15277
  try {
@@ -15292,7 +15284,7 @@ function getVersion() {
15292
15284
  return cachedVersion;
15293
15285
  }
15294
15286
  }
15295
- var CACHE_DIR = join4(getVultisigConfigDir2(), "cache");
15287
+ var CACHE_DIR = join4(getVultisigConfigDir3(), "cache");
15296
15288
  var VERSION_CACHE_FILE = join4(CACHE_DIR, "version-check.json");
15297
15289
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
15298
15290
  function readVersionCache() {
@@ -15376,7 +15368,7 @@ function formatVersionShort() {
15376
15368
  }
15377
15369
  function formatVersionDetailed() {
15378
15370
  const lines = [];
15379
- const configDir = getVultisigConfigDir2();
15371
+ const configDir = getVultisigConfigDir3();
15380
15372
  lines.push(chalk11.bold(`Vultisig CLI v${getVersion()}`));
15381
15373
  lines.push("");
15382
15374
  lines.push(` Node.js: ${process.version}`);
@@ -16816,10 +16808,10 @@ Error: ${error2.message}`));
16816
16808
  };
16817
16809
 
16818
16810
  // src/lib/completion.ts
16811
+ import { getVultisigConfigDir as getVultisigConfigDir4 } from "@vultisig/client-shared";
16819
16812
  import { SUPPORTED_CHAINS as SUPPORTED_CHAINS2 } from "@vultisig/sdk";
16820
16813
  import { program } from "commander";
16821
16814
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync5 } from "fs";
16822
- import { homedir as homedir3 } from "os";
16823
16815
  import { join as join5 } from "path";
16824
16816
  var tabtab = null;
16825
16817
  async function getTabtab() {
@@ -16843,7 +16835,7 @@ function getChains() {
16843
16835
  }
16844
16836
  function getVaultNames() {
16845
16837
  try {
16846
- const vaultDir = join5(homedir3(), ".vultisig", "vaults");
16838
+ const vaultDir = getVultisigConfigDir4();
16847
16839
  if (!existsSync3(vaultDir)) return [];
16848
16840
  const files = readdirSync(vaultDir);
16849
16841
  const names = [];
@@ -16851,9 +16843,10 @@ function getVaultNames() {
16851
16843
  if (file.startsWith("vault:") && file.endsWith(".json")) {
16852
16844
  try {
16853
16845
  const content = readFileSync5(join5(vaultDir, file), "utf-8");
16854
- const vault = JSON.parse(content);
16855
- if (vault.name) names.push(vault.name);
16856
- if (vault.id) names.push(vault.id);
16846
+ const parsed = JSON.parse(content);
16847
+ const vault = parsed?.value ?? parsed;
16848
+ if (vault?.name) names.push(vault.name);
16849
+ if (vault?.id) names.push(vault.id);
16857
16850
  } catch {
16858
16851
  }
16859
16852
  }
@@ -17095,12 +17088,10 @@ complete -c vsig -n "__fish_seen_subcommand_from import export" -a "(__fish_comp
17095
17088
  }
17096
17089
 
17097
17090
  // src/lib/config.ts
17091
+ import { getVultisigConfigDir as getVultisigConfigDir5 } from "@vultisig/client-shared";
17098
17092
  import { FileStorage } from "@vultisig/sdk/node";
17099
- import { homedir as homedir4 } from "os";
17100
- import { join as join6 } from "path";
17101
17093
  function getConfigDir() {
17102
- const override = process.env.VULTISIG_CONFIG_DIR?.trim();
17103
- return override ? override : join6(homedir4(), ".vultisig");
17094
+ return getVultisigConfigDir5();
17104
17095
  }
17105
17096
  function createVaultStorage() {
17106
17097
  return new FileStorage({ basePath: getConfigDir() });
@@ -17658,9 +17649,13 @@ program2.command("delete [vault]").description("Delete a vault from local storag
17658
17649
  });
17659
17650
  })
17660
17651
  );
17661
- program2.command("tokens <chain>").description("List and manage tokens for a chain").option("--add <contractAddress>", "Add a token by contract address").option("--remove <tokenId>", "Remove a token by ID").option("--discover", "Auto-discover tokens with balances on the chain").addHelpText(
17652
+ program2.command("tokens <chain>").description("List and manage tokens for a chain").option("--add <contractAddress>", "Add a token by contract address").option("--remove <tokenId>", "Remove a token by ID").option("--discover", "Find tokens with balances on the chain and save them to this vault").addHelpText(
17662
17653
  "after",
17663
17654
  `
17655
+ --discover writes to the vault: every token it finds is saved to the tracked
17656
+ list, so it also changes what portfolio and balance --tokens report. Use
17657
+ --remove <tokenId> to stop tracking one.
17658
+
17664
17659
  Examples:
17665
17660
  vultisig tokens Ethereum
17666
17661
  vultisig tokens Ethereum --discover --output json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "2.23.0",
3
+ "version": "3.0.0",
4
4
  "description": "The self-custody MPC wallet CLI for AI coding agents (Claude Code, Cursor, OpenCode). Natural-language agent mode, 36+ chains, DKLS23 threshold signatures. Seedless.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -73,10 +73,10 @@
73
73
  "@cosmjs/stargate": "^0.39.0",
74
74
  "@napi-rs/keyring": "^1.3.0",
75
75
  "@noble/hashes": "^2.2.0",
76
- "@vultisig/client-shared": "^0.3.0",
77
- "@vultisig/core-chain": "^2.29.1",
78
- "@vultisig/rujira": "^56.0.0",
79
- "@vultisig/sdk": "^2.23.0",
76
+ "@vultisig/client-shared": "^0.3.1",
77
+ "@vultisig/core-chain": "^2.29.2",
78
+ "@vultisig/rujira": "^57.0.0",
79
+ "@vultisig/sdk": "^3.0.0",
80
80
  "chalk": "^5.6.2",
81
81
  "cli-table3": "^0.6.5",
82
82
  "commander": "^15.0.0",