@vultisig/cli 4.0.0 → 4.0.2

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 +27 -0
  2. package/dist/index.js +403 -97
  3. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 4.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1580](https://github.com/vultisig/vultisig-sdk/pull/1580) [`1a87e08`](https://github.com/vultisig/vultisig-sdk/commit/1a87e080449b37475434f38c99212cc877fd5a4e) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Publish `@vultisig/sdk/tools/bridge` as a real package subpath with dedicated JS and declaration bundles.
8
+
9
+ - [#1641](https://github.com/vultisig/vultisig-sdk/pull/1641) [`199507d`](https://github.com/vultisig/vultisig-sdk/commit/199507da2be585e8b8414d7d12077174c80afb7b) Thanks [@neavra](https://github.com/neavra)! - Replace unavailable Ethereum and Polygon gas-refresh RPC endpoints, warn when pre-sign gas or nonce refreshes fail, and preserve nonce-gap safeguards when a pending-nonce lookup fails. Ethereum and Polygon pending-nonce checks now share the vault address and request timing with PublicNode.
10
+
11
+ - [#1578](https://github.com/vultisig/vultisig-sdk/pull/1578) [`9ff1eb2`](https://github.com/vultisig/vultisig-sdk/commit/9ff1eb22030cf2551269b4195155e395d624223b) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Export the full River helper family from `@vultisig/sdk/react-native`, including market/hint helpers and trove-status canonicals.
12
+
13
+ - [#1576](https://github.com/vultisig/vultisig-sdk/pull/1576) [`7ca8c40`](https://github.com/vultisig/vultisig-sdk/commit/7ca8c406a718624a701b8616a3cee1bbab6c5554) Thanks [@gomesalexandre](https://github.com/gomesalexandre)! - Fix the Astroport swap builder to use a React Native / Hermes-safe fetch timeout instead of `AbortSignal.timeout()`.
14
+
15
+ - [#1720](https://github.com/vultisig/vultisig-sdk/pull/1720) [`413423b`](https://github.com/vultisig/vultisig-sdk/commit/413423b70655e6e4d7faf9cb9f10b63f601e42dc) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Carry caller-supplied THORChain and MayaChain swap destinations through agent MsgDeposit execution, preserve the existing self-swap default, and reject quote memos that substitute another destination.
16
+
17
+ - Updated dependencies [[`1a87e08`](https://github.com/vultisig/vultisig-sdk/commit/1a87e080449b37475434f38c99212cc877fd5a4e), [`9ff1eb2`](https://github.com/vultisig/vultisig-sdk/commit/9ff1eb22030cf2551269b4195155e395d624223b), [`7ca8c40`](https://github.com/vultisig/vultisig-sdk/commit/7ca8c406a718624a701b8616a3cee1bbab6c5554), [`9dfd76c`](https://github.com/vultisig/vultisig-sdk/commit/9dfd76c617812418521c820e65a07a02003d5e5e), [`413423b`](https://github.com/vultisig/vultisig-sdk/commit/413423b70655e6e4d7faf9cb9f10b63f601e42dc)]:
18
+ - @vultisig/sdk@4.0.2
19
+ - @vultisig/core-chain@2.31.1
20
+
21
+ ## 4.0.1
22
+
23
+ ### Patch Changes
24
+
25
+ - [#1561](https://github.com/vultisig/vultisig-sdk/pull/1561) [`38be8ef`](https://github.com/vultisig/vultisig-sdk/commit/38be8eff97b061d241c7ac1c1616b48115fbb974) Thanks [@NeOMakinG](https://github.com/NeOMakinG)! - Render `yield_opportunities` and `polymarket_markets` agent surfaces as prose instead of leaking raw `{"surface":...}` JSON into the terminal. The CLI only advertised `balance_summary`/`turn_outcome` in `supported_surfaces`, so any other surface fell back to the backend's legacy verbatim-echo path. Adds parsers, prose renderers, and a legacy-echo fallback for both surfaces, mirroring the existing `balance_summary` handling, and wires them through the TUI, pipe mode, and `agent ask`.
26
+
27
+ - Updated dependencies [[`3767033`](https://github.com/vultisig/vultisig-sdk/commit/3767033f51804f3fff5088098c767280577bd4a4), [`68df301`](https://github.com/vultisig/vultisig-sdk/commit/68df301134b8000187a11cf92b9427e8200d4623)]:
28
+ - @vultisig/sdk@4.0.1
29
+
3
30
  ## 4.0.0
4
31
 
5
32
  ### Minor Changes
package/dist/index.js CHANGED
@@ -24,11 +24,11 @@ var __export = (target, all) => {
24
24
  for (var name in all)
25
25
  __defProp(target, name, { get: all[name], enumerable: true });
26
26
  };
27
- var __copyProps = (to, from, except, desc) => {
28
- if (from && typeof from === "object" || typeof from === "function") {
29
- for (let key of __getOwnPropNames(from))
27
+ var __copyProps = (to, from2, except, desc) => {
28
+ if (from2 && typeof from2 === "object" || typeof from2 === "function") {
29
+ for (let key of __getOwnPropNames(from2))
30
30
  if (!__hasOwnProp.call(to, key) && key !== except)
31
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
31
+ __defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
32
32
  }
33
33
  return to;
34
34
  };
@@ -1140,7 +1140,7 @@ var init_size = __esm({
1140
1140
  var version;
1141
1141
  var init_version = __esm({
1142
1142
  "../../node_modules/viem/_esm/errors/version.js"() {
1143
- version = "2.53.1";
1143
+ version = "2.55.10";
1144
1144
  }
1145
1145
  });
1146
1146
 
@@ -2122,10 +2122,7 @@ function encodeAbiParameters(params, values) {
2122
2122
  params,
2123
2123
  values
2124
2124
  });
2125
- const data = encodeParams(preparedParams);
2126
- if (data.length === 0)
2127
- return "0x";
2128
- return data;
2125
+ return encodeParams(preparedParams);
2129
2126
  }
2130
2127
  function prepareParams({ params, values }) {
2131
2128
  const preparedParams = [];
@@ -2191,7 +2188,7 @@ function encodeParams(preparedParams) {
2191
2188
  staticParams.push(encoded);
2192
2189
  }
2193
2190
  }
2194
- return concat([...staticParams, ...dynamicParams]);
2191
+ return concatHex([...staticParams, ...dynamicParams]);
2195
2192
  }
2196
2193
  function encodeAddress(value) {
2197
2194
  if (!isAddress(value))
@@ -2208,7 +2205,7 @@ function encodeArray(value, { length, param }) {
2208
2205
  givenLength: value.length,
2209
2206
  type: `${param.type}[${length}]`
2210
2207
  });
2211
- let dynamicChild = false;
2208
+ let dynamicChild = value.length === 0 && isDynamicType(param);
2212
2209
  const preparedParams = [];
2213
2210
  for (let i = 0; i < value.length; i++) {
2214
2211
  const preparedParam = prepareParam({ param, value: value[i] });
@@ -2222,7 +2219,7 @@ function encodeArray(value, { length, param }) {
2222
2219
  const length2 = numberToHex(preparedParams.length, { size: 32 });
2223
2220
  return {
2224
2221
  dynamic: true,
2225
- encoded: preparedParams.length > 0 ? concat([length2, data]) : length2
2222
+ encoded: concatHex([length2, data])
2226
2223
  };
2227
2224
  }
2228
2225
  if (dynamicChild)
@@ -2230,7 +2227,7 @@ function encodeArray(value, { length, param }) {
2230
2227
  }
2231
2228
  return {
2232
2229
  dynamic: false,
2233
- encoded: concat(preparedParams.map(({ encoded }) => encoded))
2230
+ encoded: concatHex(preparedParams.map(({ encoded }) => encoded))
2234
2231
  };
2235
2232
  }
2236
2233
  function encodeBytes(value, { param }) {
@@ -2245,7 +2242,10 @@ function encodeBytes(value, { param }) {
2245
2242
  });
2246
2243
  return {
2247
2244
  dynamic: true,
2248
- encoded: concat([padHex(numberToHex(bytesSize, { size: 32 })), value_])
2245
+ encoded: concatHex([
2246
+ padHex(numberToHex(bytesSize, { size: 32 })),
2247
+ value_
2248
+ ])
2249
2249
  };
2250
2250
  }
2251
2251
  if (bytesSize !== Number.parseInt(paramSize, 10))
@@ -2292,7 +2292,7 @@ function encodeString(value) {
2292
2292
  }
2293
2293
  return {
2294
2294
  dynamic: true,
2295
- encoded: concat([
2295
+ encoded: concatHex([
2296
2296
  padHex(numberToHex(size(hexValue), { size: 32 })),
2297
2297
  ...parts
2298
2298
  ])
@@ -2314,7 +2314,7 @@ function encodeTuple(value, { param }) {
2314
2314
  }
2315
2315
  return {
2316
2316
  dynamic,
2317
- encoded: dynamic ? encodeParams(preparedParams) : concat(preparedParams.map(({ encoded }) => encoded))
2317
+ encoded: dynamic ? encodeParams(preparedParams) : concatHex(preparedParams.map(({ encoded }) => encoded))
2318
2318
  };
2319
2319
  }
2320
2320
  function getArrayComponents(type) {
@@ -2324,6 +2324,21 @@ function getArrayComponents(type) {
2324
2324
  [matches[2] ? Number(matches[2]) : null, matches[1]]
2325
2325
  ) : void 0;
2326
2326
  }
2327
+ function isDynamicType(param) {
2328
+ const { type } = param;
2329
+ if (type === "string")
2330
+ return true;
2331
+ if (type === "bytes")
2332
+ return true;
2333
+ if (type.endsWith("[]"))
2334
+ return true;
2335
+ if (type === "tuple")
2336
+ return param.components.some(isDynamicType);
2337
+ const arrayComponents = getArrayComponents(type);
2338
+ if (arrayComponents)
2339
+ return isDynamicType({ ...param, type: arrayComponents[1] });
2340
+ return false;
2341
+ }
2327
2342
  var init_encodeAbiParameters = __esm({
2328
2343
  "../../node_modules/viem/_esm/utils/abi/encodeAbiParameters.js"() {
2329
2344
  init_abi();
@@ -2351,8 +2366,10 @@ var init_stringify = __esm({
2351
2366
  }
2352
2367
  });
2353
2368
 
2354
- // ../../node_modules/viem/_esm/utils/unit/formatUnits.js
2355
- function formatUnits(value, decimals) {
2369
+ // ../../node_modules/viem/_esm/utils/unit/Value.js
2370
+ function format(value, decimals = 0) {
2371
+ if (!Number.isInteger(decimals) || decimals < 0)
2372
+ throw new InvalidDecimalsError({ decimals });
2356
2373
  let display = value.toString();
2357
2374
  const negative = display.startsWith("-");
2358
2375
  if (negative)
@@ -2365,8 +2382,80 @@ function formatUnits(value, decimals) {
2365
2382
  fraction = fraction.replace(/(0+)$/, "");
2366
2383
  return `${negative ? "-" : ""}${integer || "0"}${fraction ? `.${fraction}` : ""}`;
2367
2384
  }
2368
- var init_formatUnits = __esm({
2369
- "../../node_modules/viem/_esm/utils/unit/formatUnits.js"() {
2385
+ function from(value, decimals = 0) {
2386
+ if (!Number.isInteger(decimals) || decimals < 0)
2387
+ throw new InvalidDecimalsError({ decimals });
2388
+ if (!/^-?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)$/.test(value))
2389
+ throw new InvalidDecimalNumberError({ value });
2390
+ let [integer = "", fraction = "0"] = value.split(".");
2391
+ const negative = integer.startsWith("-");
2392
+ if (negative)
2393
+ integer = integer.slice(1);
2394
+ if (integer === "")
2395
+ integer = "0";
2396
+ fraction = fraction.replace(/(0+)$/, "");
2397
+ if (decimals === 0) {
2398
+ if (fraction.length > 0 && Number.parseInt(fraction[0], 10) >= 5)
2399
+ integer = `${BigInt(integer) + 1n}`;
2400
+ fraction = "";
2401
+ } else if (fraction.length > decimals) {
2402
+ const left = fraction.slice(0, decimals);
2403
+ const roundDigit = Number.parseInt(fraction.slice(decimals, decimals + 1), 10);
2404
+ if (roundDigit >= 5) {
2405
+ const carried = carry(left);
2406
+ if (carried.length > decimals) {
2407
+ fraction = carried.slice(1);
2408
+ integer = `${BigInt(integer) + 1n}`;
2409
+ } else {
2410
+ fraction = carried;
2411
+ }
2412
+ } else {
2413
+ fraction = left;
2414
+ }
2415
+ } else {
2416
+ fraction = fraction.padEnd(decimals, "0");
2417
+ }
2418
+ return BigInt(`${negative ? "-" : ""}${integer}${fraction}`);
2419
+ }
2420
+ function carry(digits) {
2421
+ const out = digits.split("");
2422
+ let i = out.length - 1;
2423
+ while (i >= 0) {
2424
+ const d = Number.parseInt(out[i], 10) + 1;
2425
+ if (d < 10) {
2426
+ out[i] = String(d);
2427
+ return out.join("");
2428
+ }
2429
+ out[i] = "0";
2430
+ i--;
2431
+ }
2432
+ return `1${out.join("")}`;
2433
+ }
2434
+ var InvalidDecimalNumberError, InvalidDecimalsError;
2435
+ var init_Value = __esm({
2436
+ "../../node_modules/viem/_esm/utils/unit/Value.js"() {
2437
+ InvalidDecimalNumberError = class extends Error {
2438
+ constructor({ value }) {
2439
+ super(`Value \`${value}\` is not a valid decimal number.`);
2440
+ Object.defineProperty(this, "name", {
2441
+ enumerable: true,
2442
+ configurable: true,
2443
+ writable: true,
2444
+ value: "Value.InvalidDecimalNumberError"
2445
+ });
2446
+ }
2447
+ };
2448
+ InvalidDecimalsError = class extends Error {
2449
+ constructor({ decimals }) {
2450
+ super(`\`decimals\` must be a non-negative integer. Got \`${decimals}\`.`);
2451
+ Object.defineProperty(this, "name", {
2452
+ enumerable: true,
2453
+ configurable: true,
2454
+ writable: true,
2455
+ value: "Value.InvalidDecimalsError"
2456
+ });
2457
+ }
2458
+ };
2370
2459
  }
2371
2460
  });
2372
2461
 
@@ -3982,7 +4071,7 @@ function weierstrass(curveDef) {
3982
4071
  function normalizeS(s) {
3983
4072
  return isBiggerThanHalfOrder(s) ? modN2(-s) : s;
3984
4073
  }
3985
- const slcNum = (b, from, to) => bytesToNumberBE(b.slice(from, to));
4074
+ const slcNum = (b, from2, to) => bytesToNumberBE(b.slice(from2, to));
3986
4075
  class Signature {
3987
4076
  constructor(r, s, recovery) {
3988
4077
  aInRange("r", r, _1n5, CURVE_ORDER);
@@ -4185,14 +4274,14 @@ function weierstrass(curveDef) {
4185
4274
  const sg = signature;
4186
4275
  msgHash = ensureBytes("msgHash", msgHash);
4187
4276
  publicKey = ensureBytes("publicKey", publicKey);
4188
- const { lowS, prehash, format } = opts;
4277
+ const { lowS, prehash, format: format2 } = opts;
4189
4278
  validateSigVerOpts(opts);
4190
4279
  if ("strict" in opts)
4191
4280
  throw new Error("options.strict was renamed to lowS");
4192
- if (format !== void 0 && format !== "compact" && format !== "der")
4281
+ if (format2 !== void 0 && format2 !== "compact" && format2 !== "der")
4193
4282
  throw new Error("format must be compact or der");
4194
4283
  const isHex2 = typeof sg === "string" || isBytes2(sg);
4195
- const isObj = !isHex2 && !format && typeof sg === "object" && sg !== null && typeof sg.r === "bigint" && typeof sg.s === "bigint";
4284
+ const isObj = !isHex2 && !format2 && typeof sg === "object" && sg !== null && typeof sg.r === "bigint" && typeof sg.s === "bigint";
4196
4285
  if (!isHex2 && !isObj)
4197
4286
  throw new Error("invalid signature, expected Uint8Array, hex string or Signature instance");
4198
4287
  let _sig = void 0;
@@ -4202,13 +4291,13 @@ function weierstrass(curveDef) {
4202
4291
  _sig = new Signature(sg.r, sg.s);
4203
4292
  if (isHex2) {
4204
4293
  try {
4205
- if (format !== "compact")
4294
+ if (format2 !== "compact")
4206
4295
  _sig = Signature.fromDER(sg);
4207
4296
  } catch (derError) {
4208
4297
  if (!(derError instanceof DER.Err))
4209
4298
  throw derError;
4210
4299
  }
4211
- if (!_sig && format !== "der")
4300
+ if (!_sig && format2 !== "der")
4212
4301
  _sig = Signature.fromCompact(sg);
4213
4302
  }
4214
4303
  P = Point2.fromHex(publicKey);
@@ -9282,54 +9371,18 @@ function encodeField({ types, name, type, value }) {
9282
9371
  return [{ type }, value];
9283
9372
  }
9284
9373
 
9285
- // ../../node_modules/viem/_esm/errors/unit.js
9286
- init_base();
9287
- var InvalidDecimalNumberError = class extends BaseError {
9288
- constructor({ value }) {
9289
- super(`Number \`${value}\` is not a valid decimal number.`, {
9290
- name: "InvalidDecimalNumberError"
9291
- });
9292
- }
9293
- };
9374
+ // ../../node_modules/viem/_esm/utils/unit/formatUnits.js
9375
+ init_Value();
9376
+ function formatUnits(value, decimals) {
9377
+ return format(value, decimals);
9378
+ }
9294
9379
 
9295
9380
  // ../../node_modules/viem/_esm/utils/unit/parseUnits.js
9381
+ init_Value();
9296
9382
  function parseUnits(value, decimals) {
9297
- if (!/^(-?)([0-9]*)\.?([0-9]*)$/.test(value))
9298
- throw new InvalidDecimalNumberError({ value });
9299
- let [integer, fraction = "0"] = value.split(".");
9300
- const negative = integer.startsWith("-");
9301
- if (negative)
9302
- integer = integer.slice(1);
9303
- fraction = fraction.replace(/(0+)$/, "");
9304
- if (decimals === 0) {
9305
- if (Math.round(Number(`.${fraction}`)) === 1)
9306
- integer = `${BigInt(integer) + 1n}`;
9307
- fraction = "";
9308
- } else if (fraction.length > decimals) {
9309
- const [left, unit, right] = [
9310
- fraction.slice(0, decimals - 1),
9311
- fraction.slice(decimals - 1, decimals),
9312
- fraction.slice(decimals)
9313
- ];
9314
- const rounded = Math.round(Number(`${unit}.${right}`));
9315
- if (rounded > 9)
9316
- fraction = `${BigInt(left) + BigInt(1)}0`.padStart(left.length + 1, "0");
9317
- else
9318
- fraction = `${left}${rounded}`;
9319
- if (fraction.length > decimals) {
9320
- fraction = fraction.slice(1);
9321
- integer = `${BigInt(integer) + 1n}`;
9322
- }
9323
- fraction = fraction.slice(0, decimals);
9324
- } else {
9325
- fraction = fraction.padEnd(decimals, "0");
9326
- }
9327
- return BigInt(`${negative ? "-" : ""}${integer}${fraction}`);
9383
+ return from(value, decimals);
9328
9384
  }
9329
9385
 
9330
- // ../../node_modules/viem/_esm/index.js
9331
- init_formatUnits();
9332
-
9333
9386
  // ../../packages/core/chain/dist/amount/toChainAmount.js
9334
9387
  var ChainAmountParseError = class extends Error {
9335
9388
  name = "ChainAmountParseError";
@@ -10129,6 +10182,8 @@ var AskInterface = class {
10129
10182
  toolCalls = [];
10130
10183
  transactions = [];
10131
10184
  cards = [];
10185
+ yieldCards = [];
10186
+ polymarketCards = [];
10132
10187
  warnings = [];
10133
10188
  outcome;
10134
10189
  error;
@@ -10181,6 +10236,12 @@ var AskInterface = class {
10181
10236
  onBalanceSummary: (card) => {
10182
10237
  this.cards.push(card);
10183
10238
  },
10239
+ onYieldOpportunities: (card) => {
10240
+ this.yieldCards.push(card);
10241
+ },
10242
+ onPolymarketMarkets: (card) => {
10243
+ this.polymarketCards.push(card);
10244
+ },
10184
10245
  onTurnOutcome: (outcome) => {
10185
10246
  this.outcome = outcome;
10186
10247
  },
@@ -10239,6 +10300,8 @@ var AskInterface = class {
10239
10300
  this.toolCalls = [];
10240
10301
  this.transactions = [];
10241
10302
  this.cards = [];
10303
+ this.yieldCards = [];
10304
+ this.polymarketCards = [];
10242
10305
  this.warnings = [];
10243
10306
  this.outcome = void 0;
10244
10307
  this.error = void 0;
@@ -10262,6 +10325,8 @@ var AskInterface = class {
10262
10325
  toolCalls: this.toolCalls,
10263
10326
  transactions: this.transactions,
10264
10327
  cards: this.cards,
10328
+ yieldCards: this.yieldCards,
10329
+ polymarketCards: this.polymarketCards,
10265
10330
  warnings: this.warnings,
10266
10331
  error: this.error,
10267
10332
  ...this.outcome ? { outcome: this.outcome } : {}
@@ -10326,7 +10391,12 @@ async function authenticateVault(client, vault, password, maxAttempts = 3) {
10326
10391
 
10327
10392
  // src/agent/cards.ts
10328
10393
  import chalk8 from "chalk";
10329
- var CLI_SUPPORTED_SURFACES = ["balance_summary", "turn_outcome"];
10394
+ var CLI_SUPPORTED_SURFACES = [
10395
+ "balance_summary",
10396
+ "turn_outcome",
10397
+ "yield_opportunities",
10398
+ "polymarket_markets"
10399
+ ];
10330
10400
  function parseTurnOutcome(raw) {
10331
10401
  if (!raw || typeof raw !== "object") return null;
10332
10402
  const kind = raw.kind;
@@ -10474,6 +10544,142 @@ function renderBalanceSummaryCard(card) {
10474
10544
  }
10475
10545
  return lines.join("\n");
10476
10546
  }
10547
+ function parseYieldOpportunity(v) {
10548
+ if (!v || typeof v !== "object") return null;
10549
+ const o = v;
10550
+ const id = asString(o.id);
10551
+ const symbol = asString(o.token) || asString(o.symbol);
10552
+ const chain = asString(o.network) || asString(o.chain);
10553
+ if (!symbol) return null;
10554
+ const opportunity = { id, chain, symbol };
10555
+ const apy = o.apy;
10556
+ if (typeof apy === "number" && Number.isFinite(apy)) {
10557
+ opportunity.apy = `${apy.toFixed(2)}%`;
10558
+ } else if (typeof apy === "string" && apy) {
10559
+ opportunity.apy = stripControlChars(apy);
10560
+ }
10561
+ const provider = asString(o.provider);
10562
+ if (provider) opportunity.provider = provider;
10563
+ const type = asString(o.type);
10564
+ if (type) opportunity.type = type;
10565
+ return opportunity;
10566
+ }
10567
+ function parseYieldOpportunitiesEnvelope(value) {
10568
+ if (!value || typeof value !== "object") return null;
10569
+ const o = value;
10570
+ if (o.surface !== "yield_opportunities") return null;
10571
+ const raw = Array.isArray(o.opportunities) ? o.opportunities : Array.isArray(o.data) ? o.data : [];
10572
+ const opportunities = raw.map(parseYieldOpportunity).filter((y) => y !== null);
10573
+ if (opportunities.length === 0) return null;
10574
+ const card = { surface: "yield_opportunities", opportunities };
10575
+ const title = asString(o.title);
10576
+ if (title) card.title = title;
10577
+ return card;
10578
+ }
10579
+ function renderYieldOpportunitiesCard(card) {
10580
+ const lines = [chalk8.bold(` ${card.title || "Yield opportunities"}`)];
10581
+ for (const opp of card.opportunities) {
10582
+ const label = opp.symbol || opp.id || "opportunity";
10583
+ const apyCol = opp.apy ? chalk8.green(` ${opp.apy} APY`) : "";
10584
+ const chainCol = opp.chain ? chalk8.gray(` (${opp.chain})`) : "";
10585
+ const providerCol = opp.provider ? chalk8.gray(` via ${opp.provider}`) : "";
10586
+ lines.push(` ${chalk8.bold(label)}${apyCol}${chainCol}${providerCol}`);
10587
+ }
10588
+ return lines.join("\n");
10589
+ }
10590
+ function extractYieldOpportunitiesFromText(content) {
10591
+ return extractSurfaceFromText(content, "yield_opportunities", parseYieldOpportunitiesEnvelope);
10592
+ }
10593
+ function formatProbability(v) {
10594
+ if (typeof v !== "number" || !Number.isFinite(v)) return void 0;
10595
+ if (v <= 0 || v >= 1) return void 0;
10596
+ return `${Math.round(v * 100)}%`;
10597
+ }
10598
+ function buildPolymarketOutcomes(o) {
10599
+ const yesPrice = formatProbability(o.yesPrice);
10600
+ const noPrice = formatProbability(o.noPrice);
10601
+ const topOutcome = asString(o.topOutcome);
10602
+ if (topOutcome) {
10603
+ const outcome = { name: topOutcome };
10604
+ if (yesPrice) outcome.price = yesPrice;
10605
+ return [outcome];
10606
+ }
10607
+ const outcomes = [];
10608
+ if (yesPrice) outcomes.push({ name: "YES", price: yesPrice });
10609
+ if (noPrice) outcomes.push({ name: "NO", price: noPrice });
10610
+ return outcomes;
10611
+ }
10612
+ function parsePolymarketMarket(v) {
10613
+ if (!v || typeof v !== "object") return null;
10614
+ const o = v;
10615
+ const question = asString(o.question) || asString(o.title);
10616
+ if (!question) return null;
10617
+ const id = asString(o.id) || asString(o.slug);
10618
+ const outcomes = buildPolymarketOutcomes(o);
10619
+ const market = { id, question, outcomes };
10620
+ const volume24h = o.volume24h;
10621
+ if (typeof volume24h === "number" && Number.isFinite(volume24h)) {
10622
+ market.volume = formatUsd(volume24h);
10623
+ } else if (typeof volume24h === "string" && volume24h) {
10624
+ market.volume = stripControlChars(volume24h);
10625
+ }
10626
+ const endDate = asString(o.endDate) || asString(o.end_date);
10627
+ if (endDate) market.endDate = endDate;
10628
+ return market;
10629
+ }
10630
+ function parsePolymarketMarketsEnvelope(value) {
10631
+ if (!value || typeof value !== "object") return null;
10632
+ const o = value;
10633
+ if (o.surface !== "polymarket_markets") return null;
10634
+ const raw = Array.isArray(o.markets) ? o.markets : Array.isArray(o.data) ? o.data : [];
10635
+ const markets = raw.map(parsePolymarketMarket).filter((m) => m !== null);
10636
+ if (markets.length === 0) return null;
10637
+ const card = { surface: "polymarket_markets", markets };
10638
+ const title = asString(o.title);
10639
+ if (title) card.title = title;
10640
+ const subtitle = asString(o.subtitle);
10641
+ if (subtitle) card.subtitle = subtitle;
10642
+ return card;
10643
+ }
10644
+ function renderPolymarketMarketsCard(card) {
10645
+ const lines = [chalk8.bold(` ${card.title || "Polymarket markets"}`)];
10646
+ if (card.subtitle) lines.push(chalk8.gray(` ${card.subtitle}`));
10647
+ for (const market of card.markets) {
10648
+ const metaBits = [market.volume ? `${market.volume} vol` : "", market.endDate ? `ends ${market.endDate}` : ""].filter(Boolean).join(", ");
10649
+ lines.push(` ${chalk8.bold(market.question)}${metaBits ? chalk8.gray(` (${metaBits})`) : ""}`);
10650
+ for (const outcome of market.outcomes) {
10651
+ const priceCol = outcome.price ? chalk8.gray(` ${outcome.price}`) : "";
10652
+ lines.push(` ${outcome.name}${priceCol}`);
10653
+ }
10654
+ }
10655
+ return lines.join("\n");
10656
+ }
10657
+ function extractPolymarketMarketsFromText(content) {
10658
+ return extractSurfaceFromText(content, "polymarket_markets", parsePolymarketMarketsEnvelope);
10659
+ }
10660
+ function extractSurfaceFromText(content, surfaceKey, parser) {
10661
+ if (!content || !content.includes(surfaceKey)) return null;
10662
+ if (content.length > 2e5) return null;
10663
+ for (let i = content.indexOf("{"); i !== -1; i = content.indexOf("{", i + 1)) {
10664
+ const end = matchBrace(content, i);
10665
+ if (end === -1) break;
10666
+ const blob = content.slice(i, end + 1);
10667
+ if (!blob.includes(surfaceKey)) continue;
10668
+ let parsed;
10669
+ try {
10670
+ parsed = JSON.parse(blob);
10671
+ } catch {
10672
+ continue;
10673
+ }
10674
+ const card = parser(parsed);
10675
+ if (!card) continue;
10676
+ const before = content.slice(0, i).replace(/```(?:json)?\s*$/i, "");
10677
+ const after = content.slice(end + 1).replace(/^\s*```/, "");
10678
+ const remainingText = (before + after).trim();
10679
+ return { card, remainingText };
10680
+ }
10681
+ return null;
10682
+ }
10477
10683
 
10478
10684
  // src/agent/client.ts
10479
10685
  import { randomUUID } from "node:crypto";
@@ -10665,9 +10871,9 @@ var EVM_CHAINS = /* @__PURE__ */ new Set([
10665
10871
  "Sei"
10666
10872
  ]);
10667
10873
  var EVM_GAS_RPC = {
10668
- Ethereum: "https://eth.llamarpc.com",
10874
+ Ethereum: "https://ethereum-rpc.publicnode.com",
10669
10875
  BSC: "https://bsc-dataseed.binance.org",
10670
- Polygon: "https://polygon-rpc.com",
10876
+ Polygon: "https://polygon-bor-rpc.publicnode.com",
10671
10877
  Avalanche: "https://api.avax.network/ext/bc/C/rpc",
10672
10878
  Arbitrum: "https://arb1.arbitrum.io/rpc",
10673
10879
  Optimism: "https://mainnet.optimism.io",
@@ -11295,29 +11501,23 @@ var AgentExecutor = class {
11295
11501
  * destination chain + asset, look up the corresponding `Chain` enum,
11296
11502
  * then call `vault.swap` which builds the MsgDeposit internally.
11297
11503
  *
11298
- * IMPORTANT destination address handling: `vault.swap` re-derives the
11299
- * destination address from `vault.address(toChain)` when fetching the
11300
- * native swap quote (see `findSwapQuote` `getNativeSwapQuote` —
11301
- * `destination: to.address`). It does NOT honor the destination address
11302
- * encoded in the envelope's memo. As a fund-safety guard we therefore
11303
- * require the memo's destination address to match the vault's own
11304
- * destination address (self-swap) and throw otherwise — see Phase D
11305
- * review F1. Cross-account routing must wait on a Phase E SDK extension
11306
- * that lets `vault.swap` accept a user-supplied destination.
11504
+ * The destination encoded in the server-issued memo is forwarded through
11505
+ * `vault.swap({ recipient })`. The SDK uses the same recipient both for the
11506
+ * destination coin and the THORChain/MayaChain quote request, so an explicit
11507
+ * cross-account route cannot be silently replaced with the vault's address.
11508
+ * An omitted destination keeps the existing self-swap default.
11307
11509
  */
11308
11510
  async signThorMsgDepositSwap(serverTxData, chain) {
11309
11511
  const txArgs = serverTxData?.txArgs ?? {};
11310
11512
  const memo = typeof txArgs.memo === "string" ? txArgs.memo : "";
11311
11513
  const parsed = parseThorSwapMemo(memo);
11312
- const toChain = parsed.toChain;
11313
- const vaultDestAddress = await this.vault.address(toChain);
11314
- const normalizeForCompare = (addr) => EVM_CHAINS.has(toChain) ? addr.toLowerCase() : addr;
11315
- if (parsed.destAddress && normalizeForCompare(parsed.destAddress) !== normalizeForCompare(vaultDestAddress)) {
11514
+ if (/\s/.test(parsed.destAddress)) {
11316
11515
  throw new VaultError3(
11317
- VaultErrorCode3.NotImplemented,
11318
- `signThorMsgDepositSwap: memo destination '${parsed.destAddress}' does not match vault address '${vaultDestAddress}' on ${toChain}. Phase D only supports self-swaps; cross-account routing requires a Phase E SDK extension that passes the user-supplied destination through to vault.swap.`
11516
+ VaultErrorCode3.InvalidConfig,
11517
+ `signThorMsgDepositSwap: destination address in memo '${memo}' must not contain whitespace.`
11319
11518
  );
11320
11519
  }
11520
+ const toChain = parsed.toChain;
11321
11521
  const fromSymbol = chain === Chain11.THORChain ? "RUNE" : "CACAO";
11322
11522
  const amountRaw = typeof txArgs.amount === "string" ? txArgs.amount : void 0;
11323
11523
  if (!amountRaw) {
@@ -11337,7 +11537,8 @@ var AgentExecutor = class {
11337
11537
  fromSymbol,
11338
11538
  toChain,
11339
11539
  toSymbol: parsed.destAsset,
11340
- amount: amountDecimal
11540
+ amount: amountDecimal,
11541
+ ...parsed.destAddress && { recipient: parsed.destAddress }
11341
11542
  });
11342
11543
  if (result.dryRun) {
11343
11544
  throw new VaultError3(
@@ -11756,15 +11957,20 @@ var AgentExecutor = class {
11756
11957
  }
11757
11958
  const nonceGap = nextNonce - rpcNonce;
11758
11959
  if (pendingNonce === null && nonceGap > 3n) {
11759
- if (this.verbose)
11760
- process.stderr.write(
11761
- `[nonce] Large nonce gap for ${chain} (${nonceGap}) and couldn't verify pending txs \u2014 using on-chain nonce ${rpcNonce}
11960
+ process.stderr.write(
11961
+ `[nonce] Warning: pending nonce was not verified for ${chain}; signing will continue with on-chain nonce ${rpcNonce} because the local gap is ${nonceGap}
11762
11962
  `
11763
- );
11963
+ );
11764
11964
  this.stateStore.clearEvmState(chain);
11765
11965
  return;
11766
11966
  }
11767
11967
  bs.value.nonce = nextNonce;
11968
+ if (pendingNonce === null) {
11969
+ process.stderr.write(
11970
+ `[nonce] Warning: pending nonce was not verified for ${chain}; signing will continue with local nonce ${nextNonce}
11971
+ `
11972
+ );
11973
+ }
11768
11974
  if (this.verbose) process.stderr.write(`[nonce] Patched ${chain} nonce: ${rpcNonce} \u2192 ${nextNonce}
11769
11975
  `);
11770
11976
  }
@@ -11793,7 +11999,10 @@ var AgentExecutor = class {
11793
11999
  signal: AbortSignal.timeout(5e3)
11794
12000
  });
11795
12001
  const data = await res.json();
11796
- const baseFee = BigInt(data.result?.baseFeePerGas || "0");
12002
+ if (!res.ok || data?.error || data?.result?.baseFeePerGas === void 0 || data?.result?.baseFeePerGas === null) {
12003
+ throw new Error(`Failed to fetch current base fee for ${chain}`);
12004
+ }
12005
+ const baseFee = BigInt(data.result.baseFeePerGas);
11797
12006
  if (baseFee === 0n) return;
11798
12007
  const currentPriorityFee = BigInt(bs.value.priorityFee || "0");
11799
12008
  const currentMaxFee = BigInt(bs.value.maxFeePerGasWei || "0");
@@ -11807,8 +12016,10 @@ var AgentExecutor = class {
11807
12016
  );
11808
12017
  }
11809
12018
  } catch {
11810
- if (this.verbose) process.stderr.write(`[gas] Failed to refresh base fee for ${chain}, keeping original
11811
- `);
12019
+ process.stderr.write(
12020
+ `[gas] Warning: gas estimate was not refreshed for ${chain}; signing will continue with the original estimate
12021
+ `
12022
+ );
11812
12023
  }
11813
12024
  }
11814
12025
  /**
@@ -11832,7 +12043,10 @@ var AgentExecutor = class {
11832
12043
  signal: AbortSignal.timeout(5e3)
11833
12044
  });
11834
12045
  const data = await res.json();
11835
- return BigInt(data.result || "0");
12046
+ if (!res.ok || data?.error || data?.result === void 0 || data?.result === null) {
12047
+ return null;
12048
+ }
12049
+ return BigInt(data.result);
11836
12050
  } catch {
11837
12051
  return null;
11838
12052
  }
@@ -12833,6 +13047,16 @@ var AgentClient = class {
12833
13047
  callbacks.onBalanceSummary?.(card);
12834
13048
  break;
12835
13049
  }
13050
+ case "yield_opportunities": {
13051
+ const card = v1Data ?? parsed.data ?? parsed;
13052
+ callbacks.onYieldOpportunities?.(card);
13053
+ break;
13054
+ }
13055
+ case "polymarket_markets": {
13056
+ const card = v1Data ?? parsed.data ?? parsed;
13057
+ callbacks.onPolymarketMarkets?.(card);
13058
+ break;
13059
+ }
12836
13060
  case "turn_outcome": {
12837
13061
  const outcome = parseTurnOutcome(v1Data ?? parsed.data ?? parsed);
12838
13062
  if (outcome) callbacks.onTurnOutcome?.(outcome);
@@ -13029,6 +13253,10 @@ var AgentClient = class {
13029
13253
  return "suggestions";
13030
13254
  case "data-balance_summary":
13031
13255
  return "balance_summary";
13256
+ case "data-yield_opportunities":
13257
+ return "yield_opportunities";
13258
+ case "data-polymarket_markets":
13259
+ return "polymarket_markets";
13032
13260
  case "data-turn_outcome":
13033
13261
  return "turn_outcome";
13034
13262
  case "data-message":
@@ -13451,6 +13679,12 @@ var PipeInterface = class {
13451
13679
  onBalanceSummary: (card) => {
13452
13680
  this.emit({ type: "balance_summary", card });
13453
13681
  },
13682
+ onYieldOpportunities: (card) => {
13683
+ this.emit({ type: "yield_opportunities", card });
13684
+ },
13685
+ onPolymarketMarkets: (card) => {
13686
+ this.emit({ type: "polymarket_markets", card });
13687
+ },
13454
13688
  onSuggestions: (suggestions) => {
13455
13689
  this.emit({ type: "suggestions", suggestions });
13456
13690
  },
@@ -14067,6 +14301,8 @@ var AgentSession = class {
14067
14301
  }
14068
14302
  let toolOutputCandidate = null;
14069
14303
  let balanceCardRendered = false;
14304
+ let yieldCardRendered = false;
14305
+ let polymarketCardRendered = false;
14070
14306
  const pendingDispatches = [];
14071
14307
  let dispatchChain = Promise.resolve();
14072
14308
  const callbacks = {
@@ -14104,6 +14340,20 @@ var AgentSession = class {
14104
14340
  ui.onBalanceSummary?.(card);
14105
14341
  }
14106
14342
  },
14343
+ onYieldOpportunities: (raw) => {
14344
+ const card = parseYieldOpportunitiesEnvelope(raw);
14345
+ if (card) {
14346
+ yieldCardRendered = true;
14347
+ ui.onYieldOpportunities?.(card);
14348
+ }
14349
+ },
14350
+ onPolymarketMarkets: (raw) => {
14351
+ const card = parsePolymarketMarketsEnvelope(raw);
14352
+ if (card) {
14353
+ polymarketCardRendered = true;
14354
+ ui.onPolymarketMarkets?.(card);
14355
+ }
14356
+ },
14107
14357
  onTurnOutcome: (outcome) => {
14108
14358
  ui.onTurnOutcome?.(outcome);
14109
14359
  },
@@ -14145,6 +14395,8 @@ var AgentSession = class {
14145
14395
  let displayText = stripLeakedToolCallTags(responseText);
14146
14396
  if (displayText) {
14147
14397
  displayText = this.renderEchoedBalanceCard(displayText, balanceCardRendered, ui);
14398
+ displayText = this.renderEchoedYieldOpportunitiesCard(displayText, yieldCardRendered, ui);
14399
+ displayText = this.renderEchoedPolymarketMarketsCard(displayText, polymarketCardRendered, ui);
14148
14400
  }
14149
14401
  if (displayText) {
14150
14402
  ui.onAssistantMessage(displayText);
@@ -14410,6 +14662,32 @@ var AgentSession = class {
14410
14662
  }
14411
14663
  return extracted.remainingText;
14412
14664
  }
14665
+ /**
14666
+ * Legacy-path fallback for echoed yield_opportunities cards (rj3p). Mirrors
14667
+ * {@link renderEchoedBalanceCard} — strips a verbatim-echoed envelope from
14668
+ * the displayed text and renders it, unless the typed SSE part already fired.
14669
+ */
14670
+ renderEchoedYieldOpportunitiesCard(displayText, alreadyRendered, ui) {
14671
+ const extracted = extractYieldOpportunitiesFromText(displayText);
14672
+ if (!extracted) return displayText;
14673
+ if (!alreadyRendered) {
14674
+ ui.onYieldOpportunities?.(extracted.card);
14675
+ }
14676
+ return extracted.remainingText;
14677
+ }
14678
+ /**
14679
+ * Legacy-path fallback for echoed polymarket_markets cards (rj3p). Mirrors
14680
+ * {@link renderEchoedBalanceCard} — strips a verbatim-echoed envelope from
14681
+ * the displayed text and renders it, unless the typed SSE part already fired.
14682
+ */
14683
+ renderEchoedPolymarketMarketsCard(displayText, alreadyRendered, ui) {
14684
+ const extracted = extractPolymarketMarketsFromText(displayText);
14685
+ if (!extracted) return displayText;
14686
+ if (!alreadyRendered) {
14687
+ ui.onPolymarketMarkets?.(extracted.card);
14688
+ }
14689
+ return extracted.remainingText;
14690
+ }
14413
14691
  /**
14414
14692
  * Wrap a per-tool dispatch with the password-prompt gate (for tools in
14415
14693
  * {@link PASSWORD_REQUIRED_TOOLS}) and `ui.onToolCall` /
@@ -14740,6 +15018,22 @@ var ChatTUI = class {
14740
15018
  this.currentStreamText = "";
14741
15019
  console.log(renderBalanceSummaryCard(card));
14742
15020
  },
15021
+ onYieldOpportunities: (card) => {
15022
+ if (this.isStreaming) {
15023
+ process.stdout.write("\n");
15024
+ this.isStreaming = false;
15025
+ }
15026
+ this.currentStreamText = "";
15027
+ console.log(renderYieldOpportunitiesCard(card));
15028
+ },
15029
+ onPolymarketMarkets: (card) => {
15030
+ if (this.isStreaming) {
15031
+ process.stdout.write("\n");
15032
+ this.isStreaming = false;
15033
+ }
15034
+ this.currentStreamText = "";
15035
+ console.log(renderPolymarketMarketsCard(card));
15036
+ },
14743
15037
  onSuggestions: (suggestions) => {
14744
15038
  if (suggestions.length > 0) {
14745
15039
  console.log(chalk9.gray(" Suggestions:"));
@@ -15111,6 +15405,16 @@ function outputAskHuman(result, confirmationRequired, proposed) {
15111
15405
  for (const card of result.cards) {
15112
15406
  process.stdout.write(`
15113
15407
  ${renderBalanceSummaryCard(card)}
15408
+ `);
15409
+ }
15410
+ for (const card of result.yieldCards) {
15411
+ process.stdout.write(`
15412
+ ${renderYieldOpportunitiesCard(card)}
15413
+ `);
15414
+ }
15415
+ for (const card of result.polymarketCards) {
15416
+ process.stdout.write(`
15417
+ ${renderPolymarketMarketsCard(card)}
15114
15418
  `);
15115
15419
  }
15116
15420
  if (result.response) {
@@ -15142,6 +15446,8 @@ function outputAskSuccess(wantsJson, result, conversationId) {
15142
15446
  tool_calls: result.toolCalls,
15143
15447
  transactions: result.transactions,
15144
15448
  ...result.cards.length > 0 ? { cards: result.cards } : {},
15449
+ ...result.yieldCards.length > 0 ? { yield_cards: result.yieldCards } : {},
15450
+ ...result.polymarketCards.length > 0 ? { polymarket_cards: result.polymarketCards } : {},
15145
15451
  ...result.warnings.length > 0 ? { warnings: result.warnings } : {},
15146
15452
  // a2a-02: the typed turn ending (success | blocked | refusal | error) at
15147
15453
  // `data.outcome` — the same relative slot as on the error envelope. Present
@@ -15345,7 +15651,7 @@ var cachedVersion = null;
15345
15651
  function getVersion() {
15346
15652
  if (cachedVersion) return cachedVersion;
15347
15653
  if (true) {
15348
- cachedVersion = "4.0.0";
15654
+ cachedVersion = "4.0.2";
15349
15655
  return cachedVersion;
15350
15656
  }
15351
15657
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
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": {
@@ -74,9 +74,9 @@
74
74
  "@napi-rs/keyring": "^1.3.0",
75
75
  "@noble/hashes": "^2.2.0",
76
76
  "@vultisig/client-shared": "^0.3.2",
77
- "@vultisig/core-chain": "^2.31.0",
77
+ "@vultisig/core-chain": "^2.31.1",
78
78
  "@vultisig/rujira": "^59.0.0",
79
- "@vultisig/sdk": "^4.0.0",
79
+ "@vultisig/sdk": "^4.0.2",
80
80
  "chalk": "^5.6.2",
81
81
  "cli-table3": "^0.6.5",
82
82
  "commander": "^15.0.0",
@@ -86,7 +86,7 @@
86
86
  "qrcode-terminal": "^0.12.0",
87
87
  "ripple-address-codec": "^5.0.1",
88
88
  "tabtab": "^3.0.2",
89
- "viem": "^2.53.1",
89
+ "viem": "^2.55.10",
90
90
  "ws": "^8.21.1"
91
91
  },
92
92
  "devDependencies": {
@@ -94,7 +94,7 @@
94
94
  "@types/node": "^26.1.1",
95
95
  "@types/tabtab": "^3.0.4",
96
96
  "@types/ws": "^8.18.1",
97
- "@vultisig/core-mpc": "^1.16.0",
97
+ "@vultisig/core-mpc": "^1.16.1",
98
98
  "esbuild": "^0.28.1",
99
99
  "ethers": "^6.17.0",
100
100
  "node-pty": "^1.1.0",