@vultisig/cli 5.1.0 → 6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 6.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#2218](https://github.com/vultisig/vultisig-sdk/pull/2218) [`d043dc4`](https://github.com/vultisig/vultisig-sdk/commit/d043dc43d9fe33a3160c2739150fd6294f0e7eff) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Unify the workspace on the coordinated Noble/Scure v2 stack and align related workspace peer dependency ranges.
8
+
9
+ ### Minor Changes
10
+
11
+ - [#2024](https://github.com/vultisig/vultisig-sdk/pull/2024) [`821c8a7`](https://github.com/vultisig/vultisig-sdk/commit/821c8a73dd7d3aee55eaca59c22adc1ce8dae2f2) Thanks [@neavra](https://github.com/neavra)! - Label XRP spendable-vs-total balance. `getRippleNativeBalanceDetail` exposes the `{ total, spendable, reserve }` breakdown of a native XRP balance (the existing resolvers keep returning the spendable number, so no consumer changes meaning). SDK `Balance` gains optional `totalAmount`/`reserveAmount` fields, populated for native XRP. The CLI labels the spendable headline and prints the locked reserve for XRP balances; all other chains render unchanged.
12
+
13
+ ### Patch Changes
14
+
15
+ - [#2176](https://github.com/vultisig/vultisig-sdk/pull/2176) [`d2edffd`](https://github.com/vultisig/vultisig-sdk/commit/d2edffd98730daa474d0c2f35a305d5277e3eb15) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Export a canonical typed parser for backend `tx_ready` and execute-prep envelopes, and migrate the CLI's non-EVM and THOR/Maya execution routing to that shared contract.
16
+
17
+ - [#1736](https://github.com/vultisig/vultisig-sdk/pull/1736) [`b58da97`](https://github.com/vultisig/vultisig-sdk/commit/b58da972a3830eb11449401d97ae6b5e62189501) Thanks [@neavra](https://github.com/neavra)! - Fix agent send consent summaries so token tickers, chains, and contract details render exactly once; strip negated routed-chain phrases without leaving a dangling "not" and strip ASCII-ellipsis truncated contract copies like the Unicode form.
18
+
19
+ - [#1760](https://github.com/vultisig/vultisig-sdk/pull/1760) [`9759c64`](https://github.com/vultisig/vultisig-sdk/commit/9759c6429225afe0ee96a8b51aff6b79026e1b8b) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Preserve optional `priceProviderId` on `discoverTokens()` results so downstream consumers can price newly discovered tokens without app-local metadata backfills.
20
+
21
+ - [#1870](https://github.com/vultisig/vultisig-sdk/pull/1870) [`3e37e69`](https://github.com/vultisig/vultisig-sdk/commit/3e37e69fdc60d63cb53a2ef7c74b01aa930b27e1) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Add a public `pollTxStatusUntilFinal` helper to `@vultisig/sdk` and reuse it across SDK/CLI transaction finality polling paths.
22
+
23
+ - [#2018](https://github.com/vultisig/vultisig-sdk/pull/2018) [`3163f30`](https://github.com/vultisig/vultisig-sdk/commit/3163f30ed91dad7c9e8f860c6d4dbb6e61899be8) Thanks [@neavra](https://github.com/neavra)! - The aggregate `balance` and `portfolio` views now say they only cover the vault's enabled chains. When the enabled set is a strict subset of the SDK-supported chains, the human output appends a hint pointing at `vultisig balance <chain>` and `vultisig chains --add <chain>`, and JSON mode carries the same text in a dedicated `scopeHint` field (omitted when every supported chain is enabled). The balances/portfolio payloads and the scoped `balance <chain>` path are unchanged.
24
+
25
+ - Updated dependencies [[`d043dc4`](https://github.com/vultisig/vultisig-sdk/commit/d043dc43d9fe33a3160c2739150fd6294f0e7eff), [`d2edffd`](https://github.com/vultisig/vultisig-sdk/commit/d2edffd98730daa474d0c2f35a305d5277e3eb15), [`9759c64`](https://github.com/vultisig/vultisig-sdk/commit/9759c6429225afe0ee96a8b51aff6b79026e1b8b), [`eccbc25`](https://github.com/vultisig/vultisig-sdk/commit/eccbc25a1fd61fb9eb03494afb0d5ceb6977be8c), [`bd44426`](https://github.com/vultisig/vultisig-sdk/commit/bd44426fe80b160d8c9a0a866e3cbb759bf781ba), [`dd82325`](https://github.com/vultisig/vultisig-sdk/commit/dd82325ee2f5700429178cb513b338dac27aa281), [`e6375ee`](https://github.com/vultisig/vultisig-sdk/commit/e6375ee8a4d77035b4ae8dae01184b2100ac8ca7), [`e2be3d4`](https://github.com/vultisig/vultisig-sdk/commit/e2be3d41214b988c3b917538e26dbe1e123725de), [`f170bdd`](https://github.com/vultisig/vultisig-sdk/commit/f170bdddafd80d892cd35a050cf8f5393776a6a0), [`6c535ae`](https://github.com/vultisig/vultisig-sdk/commit/6c535ae3aed1c376d2e1bbbeb100e8b5c21c8929), [`5bf33f2`](https://github.com/vultisig/vultisig-sdk/commit/5bf33f264c90ec4055d2c12e90222bec9aea501b), [`3203589`](https://github.com/vultisig/vultisig-sdk/commit/3203589af5da361dfc51695aad4fef77afa2d78e), [`3e37e69`](https://github.com/vultisig/vultisig-sdk/commit/3e37e69fdc60d63cb53a2ef7c74b01aa930b27e1), [`4c30666`](https://github.com/vultisig/vultisig-sdk/commit/4c306668690f798ff4fb9094cf16dd26eaafa8e7), [`1672265`](https://github.com/vultisig/vultisig-sdk/commit/16722657dcefafff32744a7673c277e403e62a77), [`2faf1a2`](https://github.com/vultisig/vultisig-sdk/commit/2faf1a2b833b1fc0f10a02ad2c8bec50ef4b1f57), [`beaca3c`](https://github.com/vultisig/vultisig-sdk/commit/beaca3c086679a8b87592fb69ca30a0a9d1877db), [`5500717`](https://github.com/vultisig/vultisig-sdk/commit/5500717036e760ed81d4880c66bda559d37331cd), [`d04f246`](https://github.com/vultisig/vultisig-sdk/commit/d04f246a992f3256e4fe7151e5319003d8ae267a), [`da2ea4e`](https://github.com/vultisig/vultisig-sdk/commit/da2ea4e1ae00f17cfec3d27da4fff2e6cb5b1190), [`821c8a7`](https://github.com/vultisig/vultisig-sdk/commit/821c8a73dd7d3aee55eaca59c22adc1ce8dae2f2)]:
26
+ - @vultisig/core-chain@4.0.0
27
+ - @vultisig/sdk@6.0.0
28
+ - @vultisig/rujira@70.0.0
29
+ - @vultisig/client-shared@0.3.5
30
+
31
+ ## 5.2.0
32
+
33
+ ### Minor Changes
34
+
35
+ - [#1719](https://github.com/vultisig/vultisig-sdk/pull/1719) [`ac213ac`](https://github.com/vultisig/vultisig-sdk/commit/ac213ac23f49e0d3ea3ad1cb10a055f28c921469) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Add direct unsigned CLI preparation commands for contract calls, IBC, SPL, TRC-20, Jetton, Sui tokens, Polkadot assets, Cosmos staking, and CW-20 transfers.
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies [[`397c2b6`](https://github.com/vultisig/vultisig-sdk/commit/397c2b6629fdc7002d55bd95bb8585dce78de416), [`df144e9`](https://github.com/vultisig/vultisig-sdk/commit/df144e9e21c2fb64e73efcf12ac35c958dbf1c7b), [`7d6a014`](https://github.com/vultisig/vultisig-sdk/commit/7d6a01440fd0b855356cce81469b5b5a91665862)]:
40
+ - @vultisig/sdk@5.2.0
41
+ - @vultisig/core-chain@3.0.0
42
+ - @vultisig/rujira@69.0.0
43
+
3
44
  ## 5.1.0
4
45
 
5
46
  ### Patch Changes
package/README.md CHANGED
@@ -367,6 +367,28 @@ Swap quotes and previews show your VULT discount tier when affiliate fees are ap
367
367
  | `sign` | Sign pre-hashed bytes for custom transactions |
368
368
  | `broadcast` | Broadcast a pre-signed raw transaction |
369
369
  | `tx-status <chain> <txHash>` | Check transaction confirmation status |
370
+ | `prep <helper>` | Build an unsigned SDK transaction payload |
371
+
372
+ #### Unsigned transaction preparation
373
+
374
+ `vultisig prep` exposes the SDK's specialized transaction builders directly. These commands only build unsigned
375
+ payloads: they never sign or broadcast. Identity-dependent helpers use the active vault's public identity by default;
376
+ isolated tooling can pass the same non-secret fields with `--identity <json>`.
377
+
378
+ ```bash
379
+ vultisig prep contract-call Ethereum <contractAddress> approve --sender <senderAddress> --abi '[{"type":"function","name":"approve","inputs":[{"type":"address","name":"spender"},{"type":"uint256","name":"amount"}]}]' --args '["<spenderAddress>","1000000"]'
380
+ vultisig prep ibc-transfer Osmosis <sourceAddress> <receiverAddress> uosmo 1000000 --to-chain Cosmos
381
+ vultisig prep spl-transfer <mint> <from> <to> 1000000 6
382
+ vultisig prep trc20-transfer <contract> <from> <to> 1000000
383
+ vultisig prep jetton-transfer <recipient> <senderJettonWallet> 1000000 5
384
+ vultisig prep sui-token-transfer <coinType> <from> <to> 1000000 --decimals 6 --ticker USDC
385
+ vultisig prep polkadot-asset-send 1984 <from> <to> 1000000
386
+ vultisig prep cosmos-staking delegate <delegator> <validator> 5000000 uosmo
387
+ vultisig prep cw20-transfer osmo <contract> <recipient> 1000000 <sender>
388
+ ```
389
+
390
+ Run `vultisig prep <helper> --help` for the complete optional parameter set. JSON mode returns
391
+ `{ helper, unsigned: true, result }` inside the standard versioned success envelope.
370
392
 
371
393
  #### Transaction Status
372
394
 
package/dist/index.js CHANGED
@@ -2506,7 +2506,7 @@ var init_toBytes = __esm({
2506
2506
  }
2507
2507
  });
2508
2508
 
2509
- // ../../node_modules/@noble/hashes/esm/_u64.js
2509
+ // ../../node_modules/viem/node_modules/@noble/hashes/esm/_u64.js
2510
2510
  function fromBig(n, le = false) {
2511
2511
  if (le)
2512
2512
  return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
@@ -2524,7 +2524,7 @@ function split(lst, le = false) {
2524
2524
  }
2525
2525
  var U32_MASK64, _32n, rotlSH, rotlSL, rotlBH, rotlBL;
2526
2526
  var init_u64 = __esm({
2527
- "../../node_modules/@noble/hashes/esm/_u64.js"() {
2527
+ "../../node_modules/viem/node_modules/@noble/hashes/esm/_u64.js"() {
2528
2528
  U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
2529
2529
  _32n = /* @__PURE__ */ BigInt(32);
2530
2530
  rotlSH = (h, l, s) => h << s | l >>> 32 - s;
@@ -2534,16 +2534,16 @@ var init_u64 = __esm({
2534
2534
  }
2535
2535
  });
2536
2536
 
2537
- // ../../node_modules/@noble/hashes/esm/cryptoNode.js
2537
+ // ../../node_modules/viem/node_modules/@noble/hashes/esm/cryptoNode.js
2538
2538
  import * as nc from "node:crypto";
2539
2539
  var crypto2;
2540
2540
  var init_cryptoNode = __esm({
2541
- "../../node_modules/@noble/hashes/esm/cryptoNode.js"() {
2541
+ "../../node_modules/viem/node_modules/@noble/hashes/esm/cryptoNode.js"() {
2542
2542
  crypto2 = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
2543
2543
  }
2544
2544
  });
2545
2545
 
2546
- // ../../node_modules/@noble/hashes/esm/utils.js
2546
+ // ../../node_modules/viem/node_modules/@noble/hashes/esm/utils.js
2547
2547
  function isBytes(a) {
2548
2548
  return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
2549
2549
  }
@@ -2644,7 +2644,7 @@ function randomBytes2(bytesLength = 32) {
2644
2644
  }
2645
2645
  var isLE, swap32IfBE, Hash;
2646
2646
  var init_utils2 = __esm({
2647
- "../../node_modules/@noble/hashes/esm/utils.js"() {
2647
+ "../../node_modules/viem/node_modules/@noble/hashes/esm/utils.js"() {
2648
2648
  init_cryptoNode();
2649
2649
  isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
2650
2650
  swap32IfBE = isLE ? (u) => u : byteSwap32;
@@ -2653,7 +2653,7 @@ var init_utils2 = __esm({
2653
2653
  }
2654
2654
  });
2655
2655
 
2656
- // ../../node_modules/@noble/hashes/esm/sha3.js
2656
+ // ../../node_modules/viem/node_modules/@noble/hashes/esm/sha3.js
2657
2657
  function keccakP(s, rounds = 24) {
2658
2658
  const B = new Uint32Array(5 * 2);
2659
2659
  for (let round = 24 - rounds; round < 24; round++) {
@@ -2696,7 +2696,7 @@ function keccakP(s, rounds = 24) {
2696
2696
  }
2697
2697
  var _0n, _1n, _2n, _7n, _256n, _0x71n, SHA3_PI, SHA3_ROTL, _SHA3_IOTA, IOTAS, SHA3_IOTA_H, SHA3_IOTA_L, rotlH, rotlL, Keccak, gen, keccak_256;
2698
2698
  var init_sha3 = __esm({
2699
- "../../node_modules/@noble/hashes/esm/sha3.js"() {
2699
+ "../../node_modules/viem/node_modules/@noble/hashes/esm/sha3.js"() {
2700
2700
  init_u64();
2701
2701
  init_utils2();
2702
2702
  _0n = BigInt(0);
@@ -3852,10 +3852,10 @@ var init_decodeAbiParameters = __esm({
3852
3852
  });
3853
3853
 
3854
3854
  // ../../node_modules/viem/_esm/utils/stringify.js
3855
- var stringify;
3855
+ var stringify2;
3856
3856
  var init_stringify = __esm({
3857
3857
  "../../node_modules/viem/_esm/utils/stringify.js"() {
3858
- stringify = (value, replacer, space) => JSON.stringify(value, (key, value_) => {
3858
+ stringify2 = (value, replacer, space) => JSON.stringify(value, (key, value_) => {
3859
3859
  const value2 = typeof value_ === "bigint" ? value_.toString() : value_;
3860
3860
  return typeof replacer === "function" ? replacer(key, value2) : value2;
3861
3861
  }, space);
@@ -3895,7 +3895,7 @@ var init_Value = __esm({
3895
3895
  }
3896
3896
  });
3897
3897
 
3898
- // ../../node_modules/@noble/hashes/esm/_md.js
3898
+ // ../../node_modules/viem/node_modules/@noble/hashes/esm/_md.js
3899
3899
  function setBigUint64(view, byteOffset, value, isLE2) {
3900
3900
  if (typeof view.setBigUint64 === "function")
3901
3901
  return view.setBigUint64(byteOffset, value, isLE2);
@@ -3916,7 +3916,7 @@ function Maj(a, b, c) {
3916
3916
  }
3917
3917
  var HashMD, SHA256_IV;
3918
3918
  var init_md = __esm({
3919
- "../../node_modules/@noble/hashes/esm/_md.js"() {
3919
+ "../../node_modules/viem/node_modules/@noble/hashes/esm/_md.js"() {
3920
3920
  init_utils2();
3921
3921
  HashMD = class extends Hash {
3922
3922
  constructor(blockLen, outputLen, padOffset, isLE2) {
@@ -4021,10 +4021,10 @@ var init_md = __esm({
4021
4021
  }
4022
4022
  });
4023
4023
 
4024
- // ../../node_modules/@noble/hashes/esm/sha2.js
4024
+ // ../../node_modules/viem/node_modules/@noble/hashes/esm/sha2.js
4025
4025
  var SHA256_K, SHA256_W, SHA256, sha256;
4026
4026
  var init_sha2 = __esm({
4027
- "../../node_modules/@noble/hashes/esm/sha2.js"() {
4027
+ "../../node_modules/viem/node_modules/@noble/hashes/esm/sha2.js"() {
4028
4028
  init_md();
4029
4029
  init_utils2();
4030
4030
  SHA256_K = /* @__PURE__ */ Uint32Array.from([
@@ -4168,10 +4168,10 @@ var init_sha2 = __esm({
4168
4168
  }
4169
4169
  });
4170
4170
 
4171
- // ../../node_modules/@noble/hashes/esm/hmac.js
4171
+ // ../../node_modules/viem/node_modules/@noble/hashes/esm/hmac.js
4172
4172
  var HMAC, hmac;
4173
4173
  var init_hmac = __esm({
4174
- "../../node_modules/@noble/hashes/esm/hmac.js"() {
4174
+ "../../node_modules/viem/node_modules/@noble/hashes/esm/hmac.js"() {
4175
4175
  init_utils2();
4176
4176
  HMAC = class extends Hash {
4177
4177
  constructor(hash2, _key) {
@@ -9104,7 +9104,7 @@ x ${classified.message}`);
9104
9104
  }
9105
9105
 
9106
9106
  // src/commands/balance.ts
9107
- import { fiatCurrencies, fiatCurrencyNameRecord as fiatCurrencyNameRecord2 } from "@vultisig/sdk";
9107
+ import { fiatCurrencies, fiatCurrencyNameRecord as fiatCurrencyNameRecord2, SUPPORTED_CHAINS } from "@vultisig/sdk";
9108
9108
 
9109
9109
  // src/ui.ts
9110
9110
  import { fiatCurrencyNameRecord, fromChainAmountExact, Vultisig } from "@vultisig/sdk";
@@ -9115,10 +9115,19 @@ var escapeTerminalControls = (value) => Array.from(value, (character) => {
9115
9115
  const isTerminalControl = codePoint <= 31 || codePoint === 127 || codePoint >= 128 && codePoint <= 159;
9116
9116
  return isTerminalControl ? `\\x${codePoint.toString(16).padStart(2, "0").toUpperCase()}` : character;
9117
9117
  }).join("");
9118
+ var hasLockedReserve = (balance) => balance.reserveAmount !== void 0 && balance.reserveAmount !== "0" && balance.totalAmount !== void 0;
9119
+ var formatAmountCell = (balance) => hasLockedReserve(balance) ? `${balance.formattedAmount} (spendable)` : balance.formattedAmount;
9118
9120
  function displayBalance(chain, balance, _raw = false) {
9119
9121
  printResult(chalk2.cyan(`
9120
9122
  ${chain} Balance:`));
9121
- printResult(` Amount: ${balance.formattedAmount} ${balance.symbol}`);
9123
+ printResult(
9124
+ ` Amount: ${balance.formattedAmount} ${balance.symbol}${hasLockedReserve(balance) ? " (spendable)" : ""}`
9125
+ );
9126
+ if (hasLockedReserve(balance) && balance.totalAmount !== void 0) {
9127
+ const reserve = formatBalanceAmount(balance.reserveAmount, balance.decimals);
9128
+ const total = formatBalanceAmount(balance.totalAmount, balance.decimals);
9129
+ printResult(` Reserve: ${reserve} ${balance.symbol} locked (${total} ${balance.symbol} total on ledger)`);
9130
+ }
9122
9131
  if (balance.fiatValue && balance.fiatCurrency) {
9123
9132
  printResult(` Value: ${balance.fiatValue.toFixed(2)} ${balance.fiatCurrency}`);
9124
9133
  }
@@ -9127,7 +9136,7 @@ function displayBalancesTable(balances, _raw = false) {
9127
9136
  printResult(chalk2.cyan("\nPortfolio Balances:\n"));
9128
9137
  const tableData = Object.entries(balances).map(([chain, balance]) => ({
9129
9138
  Chain: chain,
9130
- Amount: balance.formattedAmount,
9139
+ Amount: formatAmountCell(balance),
9131
9140
  Symbol: balance.symbol,
9132
9141
  Value: balance.fiatValue && balance.fiatCurrency ? `${balance.fiatValue.toFixed(2)} ${balance.fiatCurrency}` : "N/A"
9133
9142
  }));
@@ -9145,7 +9154,7 @@ function displayPortfolio(portfolio, currency, _raw = false) {
9145
9154
  const table = portfolio.chainBalances.flatMap(({ chain, balance, value, tokens }) => [
9146
9155
  {
9147
9156
  Chain: chain,
9148
- Amount: balance.formattedAmount,
9157
+ Amount: formatAmountCell(balance),
9149
9158
  Symbol: balance.symbol,
9150
9159
  Value: value ? `${value.amount} ${value.currency.toUpperCase()}` : "N/A"
9151
9160
  },
@@ -9257,7 +9266,7 @@ async function confirmTransaction() {
9257
9266
  function setupVaultEvents(vault) {
9258
9267
  vault.on("balanceUpdated", ({ chain, balance, tokenId }) => {
9259
9268
  const asset = tokenId ? `${balance.symbol} token` : balance.symbol;
9260
- info(chalk2.blue(`i Balance updated for ${chain} (${asset}): ${balance.formattedAmount}`));
9269
+ info(chalk2.blue(`i Balance updated for ${chain} (${asset}): ${formatAmountCell(balance)}`));
9261
9270
  });
9262
9271
  vault.on("transactionBroadcast", ({ chain, txHash }) => {
9263
9272
  info(chalk2.green(`+ Transaction broadcast on ${chain}`));
@@ -9292,6 +9301,14 @@ function formatBigintAmount(amount, decimals) {
9292
9301
  const trimmed = fraction.replace(/0+$/, "");
9293
9302
  return trimmed ? `${whole}.${trimmed}` : whole;
9294
9303
  }
9304
+ function formatBalanceAmount(amount, decimals) {
9305
+ if (!amount || amount === "0") return "0";
9306
+ try {
9307
+ return formatBigintAmount(BigInt(amount), decimals);
9308
+ } catch {
9309
+ return amount;
9310
+ }
9311
+ }
9295
9312
  function displaySwapPreview(quote, fromAmount, fromSymbol, toSymbol, options) {
9296
9313
  const estimatedOutputFormatted = formatBigintAmount(quote.estimatedOutput, options.toDecimals);
9297
9314
  printResult(chalk2.cyan("\nSwap Preview:"));
@@ -9371,6 +9388,13 @@ async function confirmSwap() {
9371
9388
  }
9372
9389
 
9373
9390
  // src/commands/balance.ts
9391
+ function buildScopeHint(enabledChains) {
9392
+ const supportedCount = SUPPORTED_CHAINS.length;
9393
+ const enabledSupportedCount = new Set(enabledChains.filter((chain) => SUPPORTED_CHAINS.includes(chain))).size;
9394
+ if (enabledSupportedCount >= supportedCount) return void 0;
9395
+ const noun = enabledSupportedCount === 1 ? "chain" : "chains";
9396
+ return `Showing the vault's ${enabledSupportedCount} enabled ${noun} of ${supportedCount} supported. Funds on other chains won't appear here \u2014 run \`vultisig balance <chain>\` to check one, or \`vultisig chains --add <chain>\` to include it.`;
9397
+ }
9374
9398
  function conciseError(err) {
9375
9399
  const message = err instanceof Error ? err.message : String(err);
9376
9400
  return message.split("\n")[0].trim() || "Unknown error";
@@ -9400,11 +9424,14 @@ async function executeBalance(ctx2, options = {}) {
9400
9424
  } else {
9401
9425
  const balances = await vault.balances(void 0, options.includeTokens);
9402
9426
  spinner.succeed("Balances loaded");
9427
+ const scopeHint = buildScopeHint(vault.chains);
9403
9428
  if (isJsonOutput()) {
9404
- outputJson({ balances });
9429
+ outputJson({ balances, scopeHint });
9405
9430
  return;
9406
9431
  }
9407
9432
  displayBalancesTable(balances, raw);
9433
+ if (scopeHint) info(`
9434
+ ${scopeHint}`);
9408
9435
  }
9409
9436
  }
9410
9437
  async function executePortfolio(ctx2, options = {}) {
@@ -9480,11 +9507,14 @@ async function executePortfolio(ctx2, options = {}) {
9480
9507
  const totalValue = { amount: total.toFixed(2), currency, lastUpdated: Date.now() };
9481
9508
  const portfolio = { totalValue, chainBalances };
9482
9509
  spinner.succeed("Portfolio loaded");
9510
+ const scopeHint = buildScopeHint(chains);
9483
9511
  if (isJsonOutput()) {
9484
- outputJson({ portfolio, currency, failures });
9512
+ outputJson({ portfolio, currency, failures, scopeHint });
9485
9513
  return;
9486
9514
  }
9487
9515
  displayPortfolio(portfolio, currency, options.raw ?? false);
9516
+ if (scopeHint) info(`
9517
+ ${scopeHint}`);
9488
9518
  if (failures.length > 0) {
9489
9519
  warn(`
9490
9520
  Warning: ${failures.length} chain(s) failed to load fully:`);
@@ -9495,7 +9525,7 @@ Warning: ${failures.length} chain(s) failed to load fully:`);
9495
9525
  }
9496
9526
 
9497
9527
  // src/commands/chains.ts
9498
- import { SUPPORTED_CHAINS } from "@vultisig/sdk";
9528
+ import { SUPPORTED_CHAINS as SUPPORTED_CHAINS2 } from "@vultisig/sdk";
9499
9529
  import chalk3 from "chalk";
9500
9530
 
9501
9531
  // src/core/active-vault.ts
@@ -10332,19 +10362,19 @@ async function executeChains(ctx2, options = {}) {
10332
10362
  const vault = await ctx2.ensureActiveVault();
10333
10363
  if (options.addAll) {
10334
10364
  const currentCount = vault.chains.length;
10335
- const spinner = createSpinner(`Adding all ${SUPPORTED_CHAINS.length} supported chains...`);
10336
- await vault.setChains([...SUPPORTED_CHAINS]);
10337
- const addedCount = SUPPORTED_CHAINS.length - currentCount;
10338
- spinner.succeed(`Added ${addedCount} chains (${SUPPORTED_CHAINS.length} total)`);
10365
+ const spinner = createSpinner(`Adding all ${SUPPORTED_CHAINS2.length} supported chains...`);
10366
+ await vault.setChains([...SUPPORTED_CHAINS2]);
10367
+ const addedCount = SUPPORTED_CHAINS2.length - currentCount;
10368
+ spinner.succeed(`Added ${addedCount} chains (${SUPPORTED_CHAINS2.length} total)`);
10339
10369
  if (isJsonOutput()) {
10340
- outputJson({ chains: [...vault.chains], added: addedCount, total: SUPPORTED_CHAINS.length });
10370
+ outputJson({ chains: [...vault.chains], added: addedCount, total: SUPPORTED_CHAINS2.length });
10341
10371
  return;
10342
10372
  }
10343
10373
  info(chalk3.gray("\nAll supported chains are now enabled."));
10344
10374
  return;
10345
10375
  }
10346
10376
  if (options.add) {
10347
- if (!SUPPORTED_CHAINS.includes(options.add)) {
10377
+ if (!SUPPORTED_CHAINS2.includes(options.add)) {
10348
10378
  throw new InvalidChainError(
10349
10379
  `Unsupported chain: "${options.add}"`,
10350
10380
  'Run "vultisig chains" to see the supported chains, or check the spelling.',
@@ -10384,7 +10414,7 @@ Chain already active: ${options.add}` : `
10384
10414
  printResult(` - ${chain}`);
10385
10415
  });
10386
10416
  info(chalk3.gray(`
10387
- ${chains.length} of ${SUPPORTED_CHAINS.length} chains enabled`));
10417
+ ${chains.length} of ${SUPPORTED_CHAINS2.length} chains enabled`));
10388
10418
  info(chalk3.gray("Use --add <chain>, --add-all, or --remove <chain>"));
10389
10419
  }
10390
10420
  }
@@ -10836,6 +10866,307 @@ async function sendTransaction(vault, params) {
10836
10866
  }
10837
10867
  }
10838
10868
 
10869
+ // src/commands/prep.ts
10870
+ import {
10871
+ assertSafeDestination,
10872
+ buildCw20TransferMsg,
10873
+ buildDelegateMsg,
10874
+ buildRedelegateMsg,
10875
+ buildSplTransfer,
10876
+ buildUndelegateMsg,
10877
+ buildWithdrawRewardsMsg,
10878
+ prepareContractCallTxFromKeys,
10879
+ prepareIbcTransfer,
10880
+ prepareJettonTransferTxFromKeys,
10881
+ preparePolkadotAssetSend,
10882
+ prepareSuiTokenTransferFromKeys,
10883
+ prepareTrc20TransferFromKeys
10884
+ } from "@vultisig/sdk";
10885
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
10886
+ var parseJson = (raw, label) => {
10887
+ try {
10888
+ return JSON.parse(raw);
10889
+ } catch (error2) {
10890
+ const message = error2 instanceof Error ? error2.message : String(error2);
10891
+ throw new InvalidInputError(`Invalid ${label} JSON: ${message}`);
10892
+ }
10893
+ };
10894
+ var parseJsonArray = (raw, label) => {
10895
+ const value = parseJson(raw, label);
10896
+ if (!Array.isArray(value)) {
10897
+ throw new InvalidInputError(`${label} must be a JSON array`);
10898
+ }
10899
+ return value;
10900
+ };
10901
+ var MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
10902
+ var assertLosslessJsonNumberLexemes = (raw, label) => {
10903
+ let inString = false;
10904
+ let escaped = false;
10905
+ for (let index = 0; index < raw.length; index += 1) {
10906
+ const character = raw[index];
10907
+ if (inString) {
10908
+ if (escaped) {
10909
+ escaped = false;
10910
+ } else if (character === "\\") {
10911
+ escaped = true;
10912
+ } else if (character === '"') {
10913
+ inString = false;
10914
+ }
10915
+ continue;
10916
+ }
10917
+ if (character === '"') {
10918
+ inString = true;
10919
+ continue;
10920
+ }
10921
+ if (character !== "-" && (character < "0" || character > "9")) continue;
10922
+ const match = raw.slice(index).match(/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/);
10923
+ if (match === null) continue;
10924
+ const lexeme = match[0];
10925
+ if (/[.eE]/.test(lexeme) || BigInt(lexeme) > MAX_SAFE_INTEGER || BigInt(lexeme) < -MAX_SAFE_INTEGER) {
10926
+ throw new InvalidInputError(
10927
+ `${label} numeric values must be safe base-10 integer literals; pass ABI integers as decimal strings`
10928
+ );
10929
+ }
10930
+ index += lexeme.length - 1;
10931
+ }
10932
+ };
10933
+ var parseLosslessJsonArray = (raw, label) => {
10934
+ assertLosslessJsonNumberLexemes(raw, label);
10935
+ const value = parseJsonArray(raw, label);
10936
+ return value;
10937
+ };
10938
+ var parseDecimalBigInt = (raw, label) => {
10939
+ if (!/^-?\d+$/.test(raw)) {
10940
+ throw new InvalidInputError(`${label} must be a base-10 integer string`);
10941
+ }
10942
+ return BigInt(raw);
10943
+ };
10944
+ var parseInteger = (raw, label) => {
10945
+ if (!/^-?\d+$/.test(raw)) {
10946
+ throw new InvalidInputError(`${label} must be an integer`);
10947
+ }
10948
+ const value = Number(raw);
10949
+ if (!Number.isSafeInteger(value)) {
10950
+ throw new InvalidInputError(`${label} must be a safe integer`);
10951
+ }
10952
+ return value;
10953
+ };
10954
+ var parseOptionalInteger = (raw, label) => raw === void 0 ? void 0 : parseInteger(raw, label);
10955
+ var parseIdentity = (raw) => {
10956
+ const value = parseJson(raw, "identity");
10957
+ if (!isRecord(value)) {
10958
+ throw new InvalidInputError("identity must be a JSON object");
10959
+ }
10960
+ const requiredStrings = ["ecdsaPublicKey", "eddsaPublicKey", "hexChainCode", "localPartyId", "libType"];
10961
+ for (const field of requiredStrings) {
10962
+ if (typeof value[field] !== "string" || value[field].length === 0) {
10963
+ throw new InvalidInputError(`identity.${field} must be a non-empty string`);
10964
+ }
10965
+ }
10966
+ if (!["GG20", "DKLS", "KeyImport"].includes(value.libType)) {
10967
+ throw new InvalidInputError("identity.libType must be GG20, DKLS, or KeyImport");
10968
+ }
10969
+ if (value.chainPublicKeys !== void 0 && !isRecord(value.chainPublicKeys)) {
10970
+ throw new InvalidInputError("identity.chainPublicKeys must be a JSON object when provided");
10971
+ }
10972
+ return value;
10973
+ };
10974
+ var identityFromVault = (vault) => ({
10975
+ ecdsaPublicKey: vault.publicKeys.ecdsa,
10976
+ eddsaPublicKey: vault.publicKeys.eddsa,
10977
+ hexChainCode: vault.hexChainCode,
10978
+ localPartyId: vault.localPartyId,
10979
+ libType: vault.libType,
10980
+ publicKeyMldsa: vault.publicKeyMldsa,
10981
+ chainPublicKeys: vault.data.chainPublicKeys
10982
+ });
10983
+ var resolveIdentity = (vault, rawIdentity) => {
10984
+ if (rawIdentity !== void 0) return parseIdentity(rawIdentity);
10985
+ if (vault !== void 0) return identityFromVault(vault);
10986
+ throw new InvalidInputError("An active vault or --identity JSON is required");
10987
+ };
10988
+ var stringify = (value) => JSON.stringify(value, (_key, item) => typeof item === "bigint" ? item.toString() : item, 2);
10989
+ var emitPrepResult = (helper, result) => {
10990
+ const output = { helper, unsigned: true, result };
10991
+ if (isJsonOutput()) {
10992
+ outputJson(output);
10993
+ } else {
10994
+ printResult(stringify(output));
10995
+ }
10996
+ };
10997
+ async function executePrepContractCall(vault, chain, contractAddress, functionName, options) {
10998
+ const value = options.value === void 0 ? void 0 : parseDecimalBigInt(options.value, "value");
10999
+ if (value !== void 0 && value > 0n) {
11000
+ assertSafeDestination(chain, contractAddress);
11001
+ }
11002
+ const feeSettings = options.gasLimit !== void 0 || options.maxPriorityFeePerGas !== void 0 ? {
11003
+ gasLimit: parseDecimalBigInt(options.gasLimit ?? "", "gas-limit"),
11004
+ maxPriorityFeePerGas: parseDecimalBigInt(options.maxPriorityFeePerGas ?? "", "max-priority-fee-per-gas")
11005
+ } : void 0;
11006
+ const result = await prepareContractCallTxFromKeys(resolveIdentity(vault, options.identity), {
11007
+ chain,
11008
+ contractAddress,
11009
+ abi: parseJsonArray(options.abi, "abi"),
11010
+ functionName,
11011
+ args: options.args === void 0 ? void 0 : parseLosslessJsonArray(options.args, "args"),
11012
+ value,
11013
+ senderAddress: options.sender,
11014
+ feeSettings
11015
+ });
11016
+ emitPrepResult("contract-call", result);
11017
+ }
11018
+ function executePrepIbcTransfer(fromChain, fromAddress, toAddress, denom, amount, options) {
11019
+ const result = prepareIbcTransfer({
11020
+ fromChain,
11021
+ fromAddress,
11022
+ toAddress,
11023
+ denom,
11024
+ amount,
11025
+ toChainId: options.toChain,
11026
+ sourceChannel: options.sourceChannel,
11027
+ timeoutHeight: options.timeoutHeight,
11028
+ timeoutTimestamp: options.timeoutTimestamp,
11029
+ accountNumber: options.accountNumber,
11030
+ sequence: options.sequence,
11031
+ memo: options.memo,
11032
+ nowMs: parseOptionalInteger(options.nowMs, "now-ms")
11033
+ });
11034
+ emitPrepResult("ibc-transfer", result);
11035
+ }
11036
+ function executePrepSplTransfer(mint, from, to, amount, decimals, options) {
11037
+ assertSafeDestination("Solana", to);
11038
+ const result = buildSplTransfer({
11039
+ mint,
11040
+ from,
11041
+ to,
11042
+ amount: parseDecimalBigInt(amount, "amount"),
11043
+ decimals: parseInteger(decimals, "decimals"),
11044
+ isToken2022: options.token2022
11045
+ });
11046
+ emitPrepResult("spl-transfer", result);
11047
+ }
11048
+ function executePrepTrc20Transfer(contractAddress, from, to, amount, options) {
11049
+ const result = prepareTrc20TransferFromKeys({
11050
+ contractAddress,
11051
+ from,
11052
+ to,
11053
+ amount,
11054
+ memo: options.memo,
11055
+ feeLimitSun: options.feeLimitSun
11056
+ });
11057
+ emitPrepResult("trc20-transfer", result);
11058
+ }
11059
+ function executePrepJettonTransfer(vault, receiver, jettonWalletAddress, amount, seqno, options) {
11060
+ const built = prepareJettonTransferTxFromKeys(resolveIdentity(vault, options.identity), {
11061
+ receiver,
11062
+ jettonWalletAddress,
11063
+ amount: parseDecimalBigInt(amount, "amount"),
11064
+ seqno: parseInteger(seqno, "seqno"),
11065
+ memo: options.memo,
11066
+ validUntil: parseOptionalInteger(options.validUntil, "valid-until"),
11067
+ workchain: parseOptionalInteger(options.workchain, "workchain")
11068
+ });
11069
+ const result = {
11070
+ signingHashHex: built.signingHashHex,
11071
+ unsignedBocHex: built.unsignedBocHex,
11072
+ fromAddress: built.fromAddress
11073
+ };
11074
+ emitPrepResult("jetton-transfer", result);
11075
+ }
11076
+ async function executePrepSuiTokenTransfer(vault, coinType, from, to, amount, options) {
11077
+ const result = await prepareSuiTokenTransferFromKeys(resolveIdentity(vault, options.identity), {
11078
+ coinType,
11079
+ from,
11080
+ to,
11081
+ amount: parseDecimalBigInt(amount, "amount"),
11082
+ decimals: parseOptionalInteger(options.decimals, "decimals"),
11083
+ ticker: options.ticker
11084
+ });
11085
+ emitPrepResult("sui-token-transfer", result);
11086
+ }
11087
+ function executePrepPolkadotAssetSend(assetId, from, to, amount, options) {
11088
+ const result = preparePolkadotAssetSend({
11089
+ assetId: parseInteger(assetId, "asset-id"),
11090
+ from,
11091
+ to,
11092
+ amount: parseDecimalBigInt(amount, "amount"),
11093
+ decimals: parseOptionalInteger(options.decimals, "decimals"),
11094
+ ticker: options.ticker
11095
+ });
11096
+ emitPrepResult("polkadot-asset-send", result);
11097
+ }
11098
+ function executePrepCosmosStaking(action, delegatorAddress, validatorAddress, amount, denom, options) {
11099
+ const prefixes = {
11100
+ accountPrefix: options.accountPrefix,
11101
+ validatorPrefix: options.validatorPrefix
11102
+ };
11103
+ let result;
11104
+ switch (action) {
11105
+ case "delegate":
11106
+ if (amount === void 0 || denom === void 0) {
11107
+ throw new InvalidInputError("delegate requires amount and denom");
11108
+ }
11109
+ result = buildDelegateMsg({
11110
+ delegatorAddress,
11111
+ validatorAddress,
11112
+ amount,
11113
+ denom,
11114
+ ...prefixes
11115
+ });
11116
+ break;
11117
+ case "undelegate":
11118
+ if (amount === void 0 || denom === void 0) {
11119
+ throw new InvalidInputError("undelegate requires amount and denom");
11120
+ }
11121
+ result = buildUndelegateMsg({
11122
+ delegatorAddress,
11123
+ validatorAddress,
11124
+ amount,
11125
+ denom,
11126
+ ...prefixes
11127
+ });
11128
+ break;
11129
+ case "redelegate":
11130
+ if (amount === void 0 || denom === void 0 || options.validatorDst === void 0) {
11131
+ throw new InvalidInputError("redelegate requires amount, denom, and --validator-dst");
11132
+ }
11133
+ result = buildRedelegateMsg({
11134
+ delegatorAddress,
11135
+ validatorSrcAddress: validatorAddress,
11136
+ validatorDstAddress: options.validatorDst,
11137
+ amount,
11138
+ denom,
11139
+ ...prefixes
11140
+ });
11141
+ break;
11142
+ case "withdraw":
11143
+ if (amount !== void 0 || denom !== void 0) {
11144
+ throw new InvalidInputError("withdraw does not accept amount or denom");
11145
+ }
11146
+ result = buildWithdrawRewardsMsg({
11147
+ delegatorAddress,
11148
+ validatorAddress,
11149
+ ...prefixes
11150
+ });
11151
+ break;
11152
+ default:
11153
+ throw new InvalidInputError("staking action must be delegate, undelegate, redelegate, or withdraw");
11154
+ }
11155
+ emitPrepResult("cosmos-staking", result);
11156
+ }
11157
+ function executePrepCw20Transfer(bech32Prefix, contract, recipient, amount, sender, options) {
11158
+ const nativeDenoms = options.nativeDenoms?.split(",").map((value) => value.trim()).filter(Boolean);
11159
+ const result = buildCw20TransferMsg({
11160
+ bech32Prefix,
11161
+ contract,
11162
+ recipient,
11163
+ amount,
11164
+ sender,
11165
+ nativeDenoms
11166
+ });
11167
+ emitPrepResult("cw20-transfer", result);
11168
+ }
11169
+
10839
11170
  // src/commands/execute.ts
10840
11171
  var import_qrcode_terminal = __toESM(require_main(), 1);
10841
11172
  import {
@@ -11165,7 +11496,7 @@ async function executeBroadcast(ctx2, params) {
11165
11496
  }
11166
11497
 
11167
11498
  // src/commands/tx-status.ts
11168
- import { Chain as Chain7, isValidTxHash, Vultisig as Vultisig5 } from "@vultisig/sdk";
11499
+ import { Chain as Chain7, isValidTxHash, pollTxStatusUntilFinal, Vultisig as Vultisig5 } from "@vultisig/sdk";
11169
11500
  function resolveTxStatusParams(params) {
11170
11501
  if (!Object.values(Chain7).includes(params.chain)) {
11171
11502
  throw new InvalidInputError(`Invalid chain: ${params.chain}`);
@@ -11198,19 +11529,26 @@ async function executeTxStatus(ctx2, params, opts = {}) {
11198
11529
  try {
11199
11530
  let result = await vault.getTxStatus({ chain: params.chain, txHash: params.txHash });
11200
11531
  if (!params.noWait && !isTerminal(result.status)) {
11201
- const deadline = Date.now() + resolveTimeoutMs(params.timeoutSec);
11532
+ const timeoutMs = resolveTimeoutMs(params.timeoutSec);
11202
11533
  let waited = 0;
11203
- while (!isTerminal(result.status)) {
11204
- const remainingMs = deadline - Date.now();
11205
- if (remainingMs <= 0) {
11206
- spinner.fail(`Gave up waiting after ${Math.round(waited / 1e3)}s (status: ${result.status})`);
11207
- throw giveUpError(params, result, waited);
11534
+ const outcome = await pollTxStatusUntilFinal({
11535
+ chain: params.chain,
11536
+ txHash: params.txHash,
11537
+ initialResult: result,
11538
+ timeoutMs,
11539
+ intervalMs: pollIntervalMs,
11540
+ getTxStatus: (pollParams) => vault.getTxStatus(pollParams),
11541
+ isTerminal: (candidate) => isTerminal(candidate.status),
11542
+ sleep: async (ms) => {
11543
+ waited += ms;
11544
+ spinner.text = `Transaction ${result.status}... (${Math.round(waited / 1e3)}s)`;
11545
+ await sleep(ms);
11208
11546
  }
11209
- const sleepMs = Math.min(pollIntervalMs, remainingMs);
11210
- waited += sleepMs;
11211
- spinner.text = `Transaction ${result.status}... (${Math.round(waited / 1e3)}s)`;
11212
- await sleep(sleepMs);
11213
- result = await vault.getTxStatus({ chain: params.chain, txHash: params.txHash });
11547
+ });
11548
+ if (outcome.result) result = outcome.result;
11549
+ if (outcome.timedOut) {
11550
+ spinner.fail(`Gave up waiting after ${Math.round(outcome.elapsedMs / 1e3)}s (status: ${result.status})`);
11551
+ throw giveUpError(params, result, outcome.elapsedMs);
11214
11552
  }
11215
11553
  }
11216
11554
  if (result.status === "success") {
@@ -12293,7 +12631,7 @@ init_stringify();
12293
12631
  init_base();
12294
12632
  var InvalidDomainError = class extends BaseError2 {
12295
12633
  constructor({ domain }) {
12296
- super(`Invalid domain "${stringify(domain)}".`, {
12634
+ super(`Invalid domain "${stringify2(domain)}".`, {
12297
12635
  metaMessages: ["Must be a valid EIP-712 domain."]
12298
12636
  });
12299
12637
  }
@@ -13737,14 +14075,15 @@ import { getChainKind as getChainKind5 } from "@vultisig/sdk";
13737
14075
  // src/agent/executor.ts
13738
14076
  import {
13739
14077
  Chain as Chain11,
13740
- chainFeeCoin as chainFeeCoin2,
13741
14078
  computeEip712Hash,
13742
14079
  getChainKind as getChainKind4,
13743
14080
  getEvmRpcUrl,
13744
14081
  knownTokensIndex,
13745
- parseThorSwapMemo,
14082
+ parseTxReadyEnvelope,
14083
+ pollTxStatusUntilFinal as pollTxStatusUntilFinal2,
13746
14084
  resolveChainReference,
13747
14085
  toCanonicalEvmSignature,
14086
+ TxReadyParseError,
13748
14087
  VaultError as VaultError3,
13749
14088
  VaultErrorCode as VaultErrorCode3,
13750
14089
  Vultisig as VultisigSdk
@@ -14162,6 +14501,18 @@ function decodeErc20Transfer(calldata) {
14162
14501
  }
14163
14502
  }
14164
14503
  var isEvmChain = (chain) => EVM_CHAINS.has(chain);
14504
+ function stripEmbeddedPayloadContract(value, disclosedContract) {
14505
+ if (!disclosedContract) return value;
14506
+ const escapedContract = disclosedContract.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14507
+ const normalizedContract = disclosedContract.toLowerCase();
14508
+ return value.replace(new RegExp(escapedContract, "gi"), "").replace(/0x([0-9a-fA-F]{2,8})(?:…|\.{3})([0-9a-fA-F]{2,8})/g, (match, prefix, suffix) => {
14509
+ const normalizedPrefix = `0x${prefix}`.toLowerCase();
14510
+ return normalizedContract.startsWith(normalizedPrefix) && normalizedContract.endsWith(suffix.toLowerCase()) ? "" : match;
14511
+ }).replace(/\(\s*\)/g, "").replace(/\s{2,}/g, " ").trim();
14512
+ }
14513
+ function formatTokenContractDisclosure(disclosedContract) {
14514
+ return disclosedContract ? ` (token contract ${disclosedContract})` : "";
14515
+ }
14165
14516
  var AgentExecutor = class {
14166
14517
  vault;
14167
14518
  /** Owning SDK (optional); used for address book backed by app storage */
@@ -14551,11 +14902,35 @@ var AgentExecutor = class {
14551
14902
  if (transfer) {
14552
14903
  return this.renderErc20TransferSummary(transfer.amount, contractTo, stored.chain, to);
14553
14904
  }
14554
- const amount = labels.resolved_amount ?? p?.txArgs?.amount ?? "?";
14555
- const symbol = labels.token_resolved || labels.token_symbol || "";
14556
- const amountWithSymbol = symbol && !amount.endsWith(` ${symbol}`) ? `${amount} ${symbol}` : amount;
14557
- const contractPart = isContractSend && contractTo.toLowerCase() !== to.toLowerCase() ? ` (token contract ${contractTo})` : "";
14558
- return `send ${amountWithSymbol} on ${stored.chain} to ${to}${contractPart}`;
14905
+ const disclosedContract = isContractSend && contractTo.toLowerCase() !== to.toLowerCase() ? contractTo : "";
14906
+ return this.renderLabelSendSummary(p, labels, stored.chain, to, disclosedContract);
14907
+ }
14908
+ /**
14909
+ * Render the send summary from producer labels — the fallback for native,
14910
+ * non-EVM, and non-transfer envelopes only (decoded ERC-20 transfers are
14911
+ * rendered from calldata by renderErc20TransferSummary above). De-duplicates
14912
+ * token/chain/contract details the label may repeat, anchored to the routed
14913
+ * chain and the payload-derived contract disclosure.
14914
+ */
14915
+ renderLabelSendSummary(p, labels, chain, to, disclosedContract) {
14916
+ const amount = stripEmbeddedPayloadContract(labels.resolved_amount ?? p?.txArgs?.amount ?? "?", disclosedContract);
14917
+ const tokenLabel = stripEmbeddedPayloadContract(
14918
+ (labels.token_resolved || labels.token_symbol || "").trim(),
14919
+ disclosedContract
14920
+ );
14921
+ const symbol = tokenLabel.split(/\s+/, 1)[0];
14922
+ const escapedChain = String(chain).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14923
+ const routedChainPattern = new RegExp(`(?:^|\\s)(?:not\\s+)?on ${escapedChain}(?=\\s|$)`);
14924
+ const positiveRoutedChainPattern = new RegExp(`(?:^|\\s)(?<!\\bnot\\s+)on ${escapedChain}(?=\\s|$)`);
14925
+ const tokenContext = tokenLabel.slice(symbol.length);
14926
+ const labelCarriesRoutedChain = positiveRoutedChainPattern.test(tokenContext);
14927
+ const tokenDetail = tokenContext.replace(routedChainPattern, "").trim();
14928
+ const amountEmbedsTokenLabel = tokenLabel.length > 0 && amount.endsWith(` ${tokenLabel}`);
14929
+ const amountWithSymbol = symbol && !amount.endsWith(` ${symbol}`) && !amountEmbedsTokenLabel ? `${amount} ${symbol}` : amount;
14930
+ const tokenDetailSuffix = amountEmbedsTokenLabel || !tokenDetail ? "" : ` ${tokenDetail}`;
14931
+ const location = amountEmbedsTokenLabel && labelCarriesRoutedChain ? "" : `on ${chain}${tokenDetailSuffix}`;
14932
+ const contractPart = formatTokenContractDisclosure(disclosedContract);
14933
+ return `send ${amountWithSymbol}${location ? ` ${location}` : ""} to ${to}${contractPart}`;
14559
14934
  }
14560
14935
  /**
14561
14936
  * Render the consent amount for an ERC-20 transfer from the SIGNED calldata
@@ -14849,8 +15224,8 @@ var AgentExecutor = class {
14849
15224
  return this.signNonEvmServerTx(serverTxData, chain);
14850
15225
  }
14851
15226
  /**
14852
- * Non-EVM signing path: parse the agent's tx_ready envelope into a
14853
- * `vault.send`-shaped argument bag and call through. The SDK already
15227
+ * Non-EVM signing path: parse the agent's tx_ready envelope through the
15228
+ * SDK's canonical discriminated contract and call through. The SDK already
14854
15229
  * handles per-chain prepare/sign/broadcast internally via
14855
15230
  * `VaultBase.prepareSendTx` virtuals — sdk-cli only owns envelope
14856
15231
  * parsing here, not chain-specific signing logic.
@@ -14877,20 +15252,20 @@ var AgentExecutor = class {
14877
15252
  `signNonEvmServerTx: dispatcher chain '${chain}' disagrees with envelope chain '${txArgs.chain}'`
14878
15253
  );
14879
15254
  }
14880
- if (txArgs.msg_type === "deposit" && (chain === Chain11.THORChain || chain === Chain11.MayaChain)) {
14881
- const memo = typeof txArgs.memo === "string" ? txArgs.memo : "";
14882
- if (memo.startsWith("=:")) {
14883
- return this.signThorMsgDepositSwap(serverTxData, chain);
14884
- }
14885
- if (memo.startsWith("+:") || memo.startsWith("-:")) {
14886
- return this.signThorMsgDepositLp(serverTxData, chain);
14887
- }
15255
+ const parsed = parseTxReadyForCli(serverTxData, chain, this.vault.getTokens?.(chain) ?? []);
15256
+ if (parsed.kind === "thor-swap-deposit") {
15257
+ return this.signThorMsgDepositSwap(parsed);
15258
+ }
15259
+ if (parsed.kind === "thor-lp-deposit") {
15260
+ return this.signThorMsgDepositLp(parsed);
15261
+ }
15262
+ if (parsed.kind !== "send") {
14888
15263
  throw new VaultError3(
14889
- VaultErrorCode3.NotImplemented,
14890
- `signNonEvmServerTx: MsgDeposit memo prefix not supported on ${chain}: '${memo}'. Supported prefixes: '=:' (swap), '+:' (LP add), '-:' (LP remove). Loan / validator ops are out of scope.`
15264
+ VaultErrorCode3.InvalidConfig,
15265
+ `signNonEvmServerTx: expected a non-EVM send/deposit envelope, got ${parsed.kind}`
14891
15266
  );
14892
15267
  }
14893
- const args = parseNonEvmEnvelope(serverTxData, chain);
15268
+ const args = parsed;
14894
15269
  if (this.verbose)
14895
15270
  process.stderr.write(
14896
15271
  `[sign_non_evm_server_tx] chain=${chain}, to=${args.to}, amount=${args.amount}${args.symbol ? ` ${args.symbol}` : ""}, memo=${args.memo ? `"${args.memo}"` : "(none)"}
@@ -14939,38 +15314,20 @@ var AgentExecutor = class {
14939
15314
  * cross-account route cannot be silently replaced with the vault's address.
14940
15315
  * An omitted destination keeps the existing self-swap default.
14941
15316
  */
14942
- async signThorMsgDepositSwap(serverTxData, chain) {
14943
- const txArgs = serverTxData?.txArgs ?? {};
14944
- const memo = typeof txArgs.memo === "string" ? txArgs.memo : "";
14945
- const parsed = parseThorSwapMemo(memo);
14946
- if (/\s/.test(parsed.destAddress)) {
14947
- throw new VaultError3(
14948
- VaultErrorCode3.InvalidConfig,
14949
- `signThorMsgDepositSwap: destination address in memo '${memo}' must not contain whitespace.`
14950
- );
14951
- }
14952
- const toChain = parsed.toChain;
14953
- const fromSymbol = chain === Chain11.THORChain ? "RUNE" : "CACAO";
14954
- const amountRaw = typeof txArgs.amount === "string" ? txArgs.amount : void 0;
14955
- if (!amountRaw) {
14956
- throw new VaultError3(
14957
- VaultErrorCode3.InvalidConfig,
14958
- `signThorMsgDepositSwap: missing or non-string 'amount' field on ${chain} envelope`
14959
- );
14960
- }
14961
- const amountDecimal = convertBaseUnitsToDecimal(chain, amountRaw, "signThorMsgDepositSwap");
15317
+ async signThorMsgDepositSwap(parsed) {
15318
+ const { amount, chain, fromSymbol, memo, recipient, toChain, toSymbol } = parsed;
14962
15319
  if (this.verbose)
14963
15320
  process.stderr.write(
14964
- `[sign_thor_msg_deposit_swap] ${fromSymbol}@${chain} \u2192 ${parsed.destAsset}@${toChain}, amount=${amountDecimal}, memo='${memo}'
15321
+ `[sign_thor_msg_deposit_swap] ${fromSymbol}@${chain} \u2192 ${toSymbol}@${toChain}, amount=${amount}, memo='${memo}'
14965
15322
  `
14966
15323
  );
14967
15324
  const result = await this.vault.swap({
14968
15325
  fromChain: chain,
14969
15326
  fromSymbol,
14970
15327
  toChain,
14971
- toSymbol: parsed.destAsset,
14972
- amount: amountDecimal,
14973
- ...parsed.destAddress && { recipient: parsed.destAddress }
15328
+ toSymbol,
15329
+ amount,
15330
+ ...recipient && { recipient }
14974
15331
  });
14975
15332
  if (result.dryRun) {
14976
15333
  throw new VaultError3(
@@ -15002,28 +15359,16 @@ var AgentExecutor = class {
15002
15359
  * as base units directly (no decimal conversion) since the agent
15003
15360
  * already emits RUNE / CACAO in base units.
15004
15361
  */
15005
- async signThorMsgDepositLp(serverTxData, chain) {
15006
- const txArgs = serverTxData?.txArgs ?? {};
15007
- const memo = typeof txArgs.memo === "string" ? txArgs.memo : "";
15008
- const amountRaw = typeof txArgs.amount === "string" ? txArgs.amount : void 0;
15009
- if (!amountRaw) {
15010
- throw new VaultError3(
15011
- VaultErrorCode3.InvalidConfig,
15012
- `signThorMsgDepositLp: missing or non-string 'amount' field on ${chain} envelope`
15013
- );
15014
- }
15015
- if (amountRaw.length > MAX_AMOUNT_DIGITS) {
15016
- throw new VaultError3(
15017
- VaultErrorCode3.InvalidAmount,
15018
- `signThorMsgDepositLp: amount '${amountRaw}' for ${chain} exceeds ${MAX_AMOUNT_DIGITS}-digit safety bound. Likely a quote-side bug. Refusing to sign.`
15019
- );
15020
- }
15362
+ async signThorMsgDepositLp(parsed) {
15363
+ const { amountBaseUnits, chain, memo } = parsed;
15021
15364
  if (this.verbose)
15022
- process.stderr.write(`[sign_thor_msg_deposit_lp] chain=${chain}, memo='${memo}', amountBaseUnits=${amountRaw}
15023
- `);
15365
+ process.stderr.write(
15366
+ `[sign_thor_msg_deposit_lp] chain=${chain}, memo='${memo}', amountBaseUnits=${amountBaseUnits}
15367
+ `
15368
+ );
15024
15369
  const result = await this.vault.signMsgDeposit({
15025
15370
  chain,
15026
- amountBaseUnits: amountRaw,
15371
+ amountBaseUnits,
15027
15372
  memo
15028
15373
  });
15029
15374
  this.pendingPayloads.clear();
@@ -15211,20 +15556,20 @@ var AgentExecutor = class {
15211
15556
  * success.
15212
15557
  */
15213
15558
  async waitForEvmReceipt(chain, txHash, opts) {
15214
- const intervalMs = 3e3;
15215
- const deadline = Date.now() + opts.timeoutSec * 1e3;
15216
- while (Date.now() < deadline) {
15217
- try {
15218
- const result = await this.vault.getTxStatus({ chain, txHash });
15219
- if (result?.status === "success") return;
15220
- if (result?.status === "error") {
15221
- throw new VaultError3(VaultErrorCode3.BroadcastFailed, `approve tx reverted (${txHash})`);
15222
- }
15223
- } catch (e) {
15224
- if (e instanceof VaultError3 && e.code === VaultErrorCode3.BroadcastFailed) throw e;
15225
- if (e?.message?.includes("reverted")) throw e;
15559
+ const outcome = await pollTxStatusUntilFinal2({
15560
+ chain,
15561
+ txHash,
15562
+ timeoutMs: opts.timeoutSec * 1e3,
15563
+ intervalMs: 3e3,
15564
+ getTxStatus: (params) => this.vault.getTxStatus(params),
15565
+ shouldRetryError: (error2) => {
15566
+ if (error2 instanceof VaultError3 && error2.code === VaultErrorCode3.BroadcastFailed) return false;
15567
+ return !error2?.message?.includes("reverted");
15226
15568
  }
15227
- await new Promise((r) => setTimeout(r, intervalMs));
15569
+ });
15570
+ if (outcome.result?.status === "success") return;
15571
+ if (outcome.result?.status === "error") {
15572
+ throw new VaultError3(VaultErrorCode3.BroadcastFailed, `approve tx reverted (${txHash})`);
15228
15573
  }
15229
15574
  throw new VaultError3(VaultErrorCode3.Timeout, `approve tx ${txHash} not confirmed within ${opts.timeoutSec}s`);
15230
15575
  }
@@ -15719,49 +16064,14 @@ function resolveChainFromTxReady(txReadyData) {
15719
16064
  function extractNestedTx(txReadyData) {
15720
16065
  return txReadyData?.swap_tx || txReadyData?.send_tx || txReadyData?.tx || txReadyData?.txArgs?.tx;
15721
16066
  }
15722
- var MAX_AMOUNT_DIGITS = 26;
15723
- function convertBaseUnitsToDecimal(chain, amountRaw, context) {
15724
- if (amountRaw.length > MAX_AMOUNT_DIGITS) {
15725
- throw new VaultError3(
15726
- VaultErrorCode3.InvalidAmount,
15727
- `${context}: amount '${amountRaw}' for ${chain} exceeds ${MAX_AMOUNT_DIGITS}-digit safety bound. Likely a quote-side bug. Refusing to sign.`
15728
- );
15729
- }
15730
- const decimals = chainFeeCoin2[chain]?.decimals;
15731
- if (decimals === void 0) {
15732
- throw new VaultError3(VaultErrorCode3.UnsupportedChain, `${context}: no native decimals registered for ${chain}`);
15733
- }
16067
+ function parseTxReadyForCli(serverTxData, defaultChain, tokens = []) {
15734
16068
  try {
15735
- return formatUnits(BigInt(amountRaw), decimals);
15736
- } catch (err) {
15737
- throw new VaultError3(
15738
- VaultErrorCode3.InvalidAmount,
15739
- `${context}: failed to convert amount '${amountRaw}' for ${chain}: ${err?.message ?? err}`
15740
- );
15741
- }
15742
- }
15743
- function parseNonEvmEnvelope(serverTxData, chain) {
15744
- const txArgs = serverTxData?.txArgs ?? serverTxData;
15745
- if (!txArgs || typeof txArgs !== "object") {
15746
- throw new VaultError3(VaultErrorCode3.InvalidConfig, "parseNonEvmEnvelope: envelope missing txArgs");
15747
- }
15748
- const to = typeof txArgs.to === "string" ? txArgs.to : void 0;
15749
- if (!to) {
15750
- throw new VaultError3(VaultErrorCode3.InvalidConfig, `parseNonEvmEnvelope: missing 'to' field for ${chain}`);
15751
- }
15752
- const amountRaw = typeof txArgs.amount === "string" ? txArgs.amount : void 0;
15753
- if (!amountRaw) {
15754
- throw new VaultError3(VaultErrorCode3.InvalidConfig, `parseNonEvmEnvelope: missing 'amount' field for ${chain}`);
15755
- }
15756
- const amountDecimal = convertBaseUnitsToDecimal(chain, amountRaw, "parseNonEvmEnvelope");
15757
- let symbol;
15758
- const tokenResolved = serverTxData?.resolved?.labels?.token_resolved;
15759
- const nativeTicker = chainFeeCoin2[chain]?.ticker;
15760
- if (typeof tokenResolved === "string" && tokenResolved !== nativeTicker) {
15761
- symbol = tokenResolved;
16069
+ return parseTxReadyEnvelope(serverTxData, { defaultChain, tokens });
16070
+ } catch (error2) {
16071
+ if (!(error2 instanceof TxReadyParseError)) throw error2;
16072
+ const code = error2.code === "INVALID_AMOUNT" ? VaultErrorCode3.InvalidAmount : error2.code === "UNKNOWN_CHAIN" ? VaultErrorCode3.UnsupportedChain : error2.code === "UNSUPPORTED_DEPOSIT" || error2.code === "UNSUPPORTED_ENVELOPE" ? VaultErrorCode3.NotImplemented : VaultErrorCode3.InvalidConfig;
16073
+ throw new VaultError3(code, error2.message);
15762
16074
  }
15763
- const memo = typeof txArgs.memo === "string" && txArgs.memo.length > 0 ? txArgs.memo : void 0;
15764
- return { chain, to, amount: amountDecimal, symbol, memo };
15765
16075
  }
15766
16076
  function resolveChainId(chainId) {
15767
16077
  return resolveChainReference(chainId) ?? null;
@@ -16822,7 +17132,7 @@ var AgentClient = class {
16822
17132
  };
16823
17133
 
16824
17134
  // src/agent/context.ts
16825
- import { chainFeeCoin as chainFeeCoin3 } from "@vultisig/sdk";
17135
+ import { chainFeeCoin as chainFeeCoin2 } from "@vultisig/sdk";
16826
17136
  function applyChainPublicKeys(vault, context) {
16827
17137
  const raw = vault.data.chainPublicKeys;
16828
17138
  if (!raw) return;
@@ -16883,7 +17193,7 @@ async function buildMessageContext(vault) {
16883
17193
  const coins = [];
16884
17194
  const chains = vault.chains;
16885
17195
  for (const chain of chains) {
16886
- const nativeCoin = chainFeeCoin3[chain];
17196
+ const nativeCoin = chainFeeCoin2[chain];
16887
17197
  coins.push({
16888
17198
  chain: chain.toString(),
16889
17199
  ticker: nativeCoin?.ticker ?? chain.toString(),
@@ -19190,7 +19500,7 @@ var cachedVersion = null;
19190
19500
  function getVersion() {
19191
19501
  if (cachedVersion) return cachedVersion;
19192
19502
  if (true) {
19193
- cachedVersion = "5.1.0";
19503
+ cachedVersion = "6.0.0";
19194
19504
  return cachedVersion;
19195
19505
  }
19196
19506
  try {
@@ -19347,6 +19657,35 @@ var COMMAND_EXAMPLES = {
19347
19657
  execute: {
19348
19658
  examples: [`vultisig execute THORChain <contract> '{"swap":{}}'`]
19349
19659
  },
19660
+ "prep.contract-call": {
19661
+ examples: [
19662
+ `vultisig prep contract-call Ethereum <contractAddress> approve --sender <senderAddress> --abi '[{"type":"function","name":"approve","inputs":[{"type":"address","name":"spender"},{"type":"uint256","name":"amount"}]}]' --args '["<spenderAddress>","1000000"]'`
19663
+ ]
19664
+ },
19665
+ "prep.ibc-transfer": {
19666
+ examples: ["vultisig prep ibc-transfer Osmosis osmo1... cosmos1... uosmo 1000000 --to-chain Cosmos"]
19667
+ },
19668
+ "prep.spl-transfer": {
19669
+ examples: ["vultisig prep spl-transfer <mint> <from> <to> 1000000 6"]
19670
+ },
19671
+ "prep.trc20-transfer": {
19672
+ examples: ["vultisig prep trc20-transfer <contract> <from> <to> 1000000"]
19673
+ },
19674
+ "prep.jetton-transfer": {
19675
+ examples: ["vultisig prep jetton-transfer <recipient> <senderJettonWallet> 1000000 5"]
19676
+ },
19677
+ "prep.sui-token-transfer": {
19678
+ examples: ["vultisig prep sui-token-transfer <coinType> <from> <to> 1000000 --decimals 6"]
19679
+ },
19680
+ "prep.polkadot-asset-send": {
19681
+ examples: ["vultisig prep polkadot-asset-send 1984 <from> <to> 1000000"]
19682
+ },
19683
+ "prep.cosmos-staking": {
19684
+ examples: ["vultisig prep cosmos-staking delegate <delegator> <validator> 5000000 uosmo"]
19685
+ },
19686
+ "prep.cw20-transfer": {
19687
+ examples: ["vultisig prep cw20-transfer osmo <contract> <recipient> 1000000 <sender>"]
19688
+ },
19350
19689
  "swap-quote": {
19351
19690
  examples: ["vultisig swap-quote Ethereum Bitcoin 0.1 --output json"]
19352
19691
  },
@@ -19426,8 +19765,16 @@ function executeSchema(prog) {
19426
19765
  };
19427
19766
  })
19428
19767
  };
19429
- process.stdout.write(`${JSON.stringify(schema, null, 2)}
19430
- `);
19768
+ return new Promise((resolve, reject) => {
19769
+ process.stdout.write(`${JSON.stringify(schema, null, 2)}
19770
+ `, (error2) => {
19771
+ if (error2) {
19772
+ reject(error2);
19773
+ return;
19774
+ }
19775
+ resolve();
19776
+ });
19777
+ });
19431
19778
  }
19432
19779
 
19433
19780
  // src/core/server-endpoints.ts
@@ -20730,7 +21077,7 @@ Error: ${error2.message}`));
20730
21077
 
20731
21078
  // src/lib/completion.ts
20732
21079
  import { getVultisigConfigDir as getVultisigConfigDir5 } from "@vultisig/client-shared";
20733
- import { SUPPORTED_CHAINS as SUPPORTED_CHAINS2 } from "@vultisig/sdk";
21080
+ import { SUPPORTED_CHAINS as SUPPORTED_CHAINS3 } from "@vultisig/sdk";
20734
21081
  import { program } from "commander";
20735
21082
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync5 } from "fs";
20736
21083
  import { join as join5 } from "path";
@@ -20752,7 +21099,7 @@ function getCommands() {
20752
21099
  return program.commands.map((cmd) => cmd.name()).sort();
20753
21100
  }
20754
21101
  function getChains() {
20755
- return [...SUPPORTED_CHAINS2];
21102
+ return [...SUPPORTED_CHAINS3];
20756
21103
  }
20757
21104
  function getVaultNames() {
20758
21105
  try {
@@ -21372,6 +21719,66 @@ See also: balance, tx-status`
21372
21719
  }
21373
21720
  )
21374
21721
  );
21722
+ var prepCmd = program2.command("prep").description("Build unsigned transaction payloads with SDK preparation helpers (never signs or broadcasts)");
21723
+ var loadPrepVault = async (identity) => {
21724
+ if (identity !== void 0) return void 0;
21725
+ const context = await init(program2.opts().vault);
21726
+ return context.ensureActiveVault();
21727
+ };
21728
+ prepCmd.command("contract-call <chain> <contractAddress> <functionName>").description("Build an unsigned EVM contract-call KeysignPayload").requiredOption("--abi <json>", "Solidity ABI JSON array").requiredOption("--sender <address>", "Sender EVM address").option("--args <json>", "Function arguments as a JSON array").option("--value <baseUnits>", "Native value in base units", "0").option("--gas-limit <baseUnits>", "Custom EVM gas limit (requires --max-priority-fee-per-gas)").option("--max-priority-fee-per-gas <baseUnits>", "Custom EVM priority fee (requires --gas-limit)").option("--identity <json>", "Public VaultIdentity JSON; otherwise use the active vault").action(
21729
+ withExit(async (chain, contractAddress, functionName, options) => {
21730
+ const vault = await loadPrepVault(options.identity);
21731
+ await executePrepContractCall(vault, resolveChainOrThrow(chain), contractAddress, functionName, options);
21732
+ })
21733
+ );
21734
+ prepCmd.command("ibc-transfer <fromChain> <fromAddress> <toAddress> <denom> <amount>").description("Build an unsigned ICS-20 IBC MsgTransfer envelope").option("--to-chain <chainId>", "Destination chain ID or Vultisig chain name").option("--source-channel <channel>", "IBC source channel (channel-N)").option("--timeout-height <revision/height>", "IBC timeout height (default: 0/0)").option("--timeout-timestamp <nanoseconds>", "Future Unix timeout in nanoseconds").option("--account-number <number>", "Source-chain account number").option("--sequence <number>", "Source-chain account sequence").option("--memo <memo>", "IBC packet memo").option("--now-ms <milliseconds>", "Override current time for deterministic default timeout").action(
21735
+ withExit(
21736
+ async (fromChain, fromAddress, toAddress, denom, amount, options) => {
21737
+ executePrepIbcTransfer(fromChain, fromAddress, toAddress, denom, amount, options);
21738
+ }
21739
+ )
21740
+ );
21741
+ prepCmd.command("spl-transfer <mint> <from> <to> <amount> <decimals>").description("Build an unsigned SPL transferChecked instruction with derived ATAs").option("--token-2022", "Use the Token-2022 program instead of the legacy SPL Token program").action(
21742
+ withExit(async (mint, from, to, amount, decimals, options) => {
21743
+ executePrepSplTransfer(mint, from, to, amount, decimals, options);
21744
+ })
21745
+ );
21746
+ prepCmd.command("trc20-transfer <contractAddress> <from> <to> <amount>").description("Build an unsigned TRC-20 transfer descriptor").option("--memo <memo>", "Optional TRON transaction memo").option("--fee-limit-sun <amount>", "Energy/bandwidth ceiling in SUN").action(
21747
+ withExit(async (contractAddress, from, to, amount, options) => {
21748
+ executePrepTrc20Transfer(contractAddress, from, to, amount, options);
21749
+ })
21750
+ );
21751
+ prepCmd.command("jetton-transfer <receiver> <jettonWalletAddress> <amount> <seqno>").description("Build an unsigned TON Jetton transfer signing payload").option("--memo <memo>", "Forward comment (up to 123 UTF-8 bytes)").option("--valid-until <seconds>", "Unix-seconds expiry").option("--workchain <number>", "Sender wallet workchain (default: 0)").option("--identity <json>", "Public VaultIdentity JSON; otherwise use the active vault").action(
21752
+ withExit(async (receiver, jettonWalletAddress, amount, seqno, options) => {
21753
+ const vault = await loadPrepVault(options.identity);
21754
+ executePrepJettonTransfer(vault, receiver, jettonWalletAddress, amount, seqno, options);
21755
+ })
21756
+ );
21757
+ prepCmd.command("sui-token-transfer <coinType> <from> <to> <amount>").description("Build an unsigned Sui coin-object token-transfer KeysignPayload").option("--decimals <number>", "Token decimals metadata").option("--ticker <symbol>", "Token ticker metadata").option("--identity <json>", "Public VaultIdentity JSON; otherwise use the active vault").action(
21758
+ withExit(async (coinType, from, to, amount, options) => {
21759
+ const vault = await loadPrepVault(options.identity);
21760
+ await executePrepSuiTokenTransfer(vault, coinType, from, to, amount, options);
21761
+ })
21762
+ );
21763
+ prepCmd.command("polkadot-asset-send <assetId> <from> <to> <amount>").description("Build an unsigned Polkadot Asset Hub transferKeepAlive call").option("--decimals <number>", "Asset decimals override").option("--ticker <symbol>", "Asset ticker override").action(
21764
+ withExit(async (assetId, from, to, amount, options) => {
21765
+ executePrepPolkadotAssetSend(assetId, from, to, amount, options);
21766
+ })
21767
+ );
21768
+ prepCmd.command("cosmos-staking <action> <delegatorAddress> <validatorAddress> [amount] [denom]").description("Build an unsigned Cosmos delegate, undelegate, redelegate, or withdraw message").option("--validator-dst <address>", "Destination validator for redelegate").option("--account-prefix <prefix>", "Expected delegator bech32 prefix").option("--validator-prefix <prefix>", "Expected validator bech32 prefix").action(
21769
+ withExit(
21770
+ async (action, delegatorAddress, validatorAddress, amount, denom, options) => {
21771
+ executePrepCosmosStaking(action, delegatorAddress, validatorAddress, amount, denom, options);
21772
+ }
21773
+ )
21774
+ );
21775
+ prepCmd.command("cw20-transfer <bech32Prefix> <contract> <recipient> <amount> <sender>").description("Build an unsigned CosmWasm CW-20 transfer message").option("--native-denoms <csv>", "Comma-separated native denoms to reject on this route").action(
21776
+ withExit(
21777
+ async (bech32Prefix, contract, recipient, amount, sender, options) => {
21778
+ executePrepCw20Transfer(bech32Prefix, contract, recipient, amount, sender, options);
21779
+ }
21780
+ )
21781
+ );
21375
21782
  program2.command("execute <chain> <contract> <msg>").description("Execute a CosmWasm smart contract (THORChain, MayaChain)").option("--funds <funds>", 'Funds to send with execution (format: "denom:amount" or "denom:amount,denom2:amount2")').option("--memo <memo>", "Transaction memo").option("--dry-run", "Preview execution without signing or broadcasting").option("-y, --yes", "Skip confirmation prompt").option("--password <password>", "Vault password for signing").addHelpText(
21376
21783
  "after",
21377
21784
  `
@@ -21922,7 +22329,12 @@ authCmd.command("status").description("List configured vaults and their keyring
21922
22329
  const vaults = await executeAuthStatus();
21923
22330
  if (isJsonOutput()) {
21924
22331
  outputJson({
21925
- vaults: vaults.map((v) => ({ id: v.id, name: v.name, filePath: v.filePath, hasCredentials: v.hasCredentials }))
22332
+ vaults: vaults.map((v) => ({
22333
+ id: v.id,
22334
+ name: v.name,
22335
+ filePath: v.filePath,
22336
+ hasCredentials: v.hasCredentials
22337
+ }))
21926
22338
  });
21927
22339
  return;
21928
22340
  }
@@ -21941,7 +22353,11 @@ authCmd.command("logout").description("Clear keyring credentials for a vault").o
21941
22353
  withExit(async (options) => {
21942
22354
  await executeAuthLogout({ vaultId: options.vaultId, all: options.all });
21943
22355
  if (isJsonOutput()) {
21944
- outputJson({ cleared: true, vaultId: options.vaultId ?? null, all: !!options.all });
22356
+ outputJson({
22357
+ cleared: true,
22358
+ vaultId: options.vaultId ?? null,
22359
+ all: !!options.all
22360
+ });
21945
22361
  } else {
21946
22362
  printResult(chalk16.green("Credentials cleared."));
21947
22363
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "5.1.0",
3
+ "version": "6.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": {
@@ -67,17 +67,16 @@
67
67
  },
68
68
  "homepage": "https://vultisig.com",
69
69
  "dependencies": {
70
- "@cosmjs/cosmwasm-stargate": "^0.39.0",
71
70
  "@cosmjs/encoding": "^0.39.0",
72
71
  "@cosmjs/proto-signing": "^0.39.0",
73
72
  "@cosmjs/stargate": "^0.39.0",
74
73
  "@msgpack/msgpack": "^3.1.2",
75
74
  "@napi-rs/keyring": "^1.3.0",
76
75
  "@noble/hashes": "^2.2.0",
77
- "@vultisig/client-shared": "^0.3.4",
78
- "@vultisig/core-chain": "^2.39.0",
79
- "@vultisig/rujira": "^68.0.0",
80
- "@vultisig/sdk": "^5.1.0",
76
+ "@vultisig/client-shared": "^0.3.5",
77
+ "@vultisig/core-chain": "^4.0.0",
78
+ "@vultisig/rujira": "^70.0.0",
79
+ "@vultisig/sdk": "^6.0.0",
81
80
  "chalk": "^5.6.2",
82
81
  "cli-table3": "^0.6.5",
83
82
  "commander": "^15.0.0",
@@ -95,7 +94,7 @@
95
94
  "@types/node": "^26.1.1",
96
95
  "@types/tabtab": "^3.0.4",
97
96
  "@types/ws": "^8.18.1",
98
- "@vultisig/core-mpc": "^1.20.3",
97
+ "@vultisig/core-mpc": "^2.0.0",
99
98
  "esbuild": "^0.28.1",
100
99
  "ethers": "^6.17.0",
101
100
  "node-pty": "^1.1.0",
@@ -104,7 +103,7 @@
104
103
  "vitest": "^4.1.8"
105
104
  },
106
105
  "engines": {
107
- "node": ">=20.0.0"
106
+ "node": ">=20.19.0"
108
107
  },
109
108
  "publishConfig": {
110
109
  "access": "public",