@vultisig/cli 2.22.0 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/index.js +858 -711
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1687,7 +1687,7 @@ function createHasher(hashCons) {
1687
1687
  hashC.create = () => hashCons();
1688
1688
  return hashC;
1689
1689
  }
1690
- function randomBytes3(bytesLength = 32) {
1690
+ function randomBytes2(bytesLength = 32) {
1691
1691
  if (crypto2 && typeof crypto2.getRandomValues === "function") {
1692
1692
  return crypto2.getRandomValues(new Uint8Array(bytesLength));
1693
1693
  }
@@ -4467,7 +4467,7 @@ function getHash(hash) {
4467
4467
  return {
4468
4468
  hash,
4469
4469
  hmac: (key, ...msgs) => hmac(hash, key, concatBytes(...msgs)),
4470
- randomBytes: randomBytes3
4470
+ randomBytes: randomBytes2
4471
4471
  };
4472
4472
  }
4473
4473
  function createCurve(curveDef, defHash) {
@@ -4700,7 +4700,7 @@ function challenge(...args) {
4700
4700
  function schnorrGetPublicKey(privateKey) {
4701
4701
  return schnorrGetExtPubKey(privateKey).bytes;
4702
4702
  }
4703
- function schnorrSign(message, privateKey, auxRand = randomBytes3(32)) {
4703
+ function schnorrSign(message, privateKey, auxRand = randomBytes2(32)) {
4704
4704
  const m = ensureBytes("message", message);
4705
4705
  const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey);
4706
4706
  const a = ensureBytes("auxRand", auxRand, 32);
@@ -8890,210 +8890,624 @@ Please specify more characters of the vault ID.`
8890
8890
  throw new Error(`Vault not found: "${idOrName}"`);
8891
8891
  }
8892
8892
 
8893
- // src/commands/swap.ts
8894
- async function executeSwapChains(ctx2) {
8895
- const vault = await ctx2.ensureActiveVault();
8896
- const spinner = createSpinner("Loading supported swap chains...");
8897
- const chains = await vault.getSupportedSwapChains();
8898
- spinner.succeed("Swap chains loaded");
8899
- if (isJsonOutput()) {
8900
- outputJson({ swapChains: [...chains] });
8901
- return chains;
8902
- }
8903
- displaySwapChains(chains);
8904
- return chains;
8905
- }
8906
- async function executeSwapQuote(ctx2, options) {
8907
- const vault = await ctx2.ensureActiveVault();
8908
- const isMax = options.amount === "max";
8909
- if (!isMax && (isNaN(options.amount) || options.amount <= 0)) {
8910
- throw new Error("Invalid amount");
8911
- }
8912
- const spinner = createSpinner("Getting swap quote...");
8913
- const result = await vault.swap({
8914
- fromChain: options.fromChain,
8915
- fromSymbol: options.fromToken || "",
8916
- toChain: options.toChain,
8917
- toSymbol: options.toToken || "",
8918
- amount: isMax ? "max" : String(options.amount),
8919
- dryRun: true
8920
- });
8921
- if (!result.dryRun) throw new Error("unreachable");
8922
- spinner.succeed("Quote received");
8923
- const quote = result.quote;
8924
- const fromAmountDisplay = isMax ? `${formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals)} (max)` : String(options.amount);
8925
- if (isJsonOutput()) {
8926
- outputJson({
8927
- fromChain: options.fromChain,
8928
- toChain: options.toChain,
8929
- amount: isMax ? "max" : options.amount,
8930
- isMax,
8931
- quote
8932
- });
8933
- return quote;
8934
- }
8935
- const feeBalance = await vault.balance(options.fromChain);
8936
- const discountTier = await vault.getDiscountTier();
8937
- displaySwapPreview(quote, fromAmountDisplay, quote.fromCoin.ticker, quote.toCoin.ticker, {
8938
- fromDecimals: quote.fromCoin.decimals,
8939
- toDecimals: quote.toCoin.decimals,
8940
- feeDecimals: feeBalance.decimals,
8941
- feeSymbol: feeBalance.symbol,
8942
- discountTier
8943
- });
8944
- info('\nTo execute this swap, use the "swap" command');
8945
- return quote;
8946
- }
8947
- function validateSwapAmount(amount) {
8948
- if (amount === "max") return;
8949
- if (isNaN(amount) || amount <= 0) throw new Error("Invalid amount");
8950
- }
8951
- function getSwapAmountString(amount) {
8952
- return amount === "max" ? "max" : String(amount);
8953
- }
8954
- function toSwapRequest(options, amount, dryRun) {
8955
- return {
8956
- fromChain: options.fromChain,
8957
- fromSymbol: options.fromToken || "",
8958
- toChain: options.toChain,
8959
- toSymbol: options.toToken || "",
8960
- amount,
8961
- ...options.slippage !== void 0 && { slippageTolerance: options.slippage },
8962
- ...dryRun && { dryRun: true }
8963
- };
8964
- }
8965
- function toDryRunResult(options, quote, fromAmountRaw) {
8966
- const result = {
8967
- dryRun: true,
8968
- fromChain: String(options.fromChain),
8969
- fromToken: quote.fromCoin.ticker,
8970
- toChain: String(options.toChain),
8971
- toToken: quote.toCoin.ticker,
8972
- inputAmount: fromAmountRaw,
8973
- ...options.amount === "max" && { isMax: true },
8974
- estimatedOutput: formatBigintAmount(quote.estimatedOutput, quote.toCoin.decimals),
8975
- provider: quote.provider
8976
- };
8977
- if (quote.estimatedOutputFiat != null) result.estimatedOutputFiat = parseFloat(quote.estimatedOutputFiat.toFixed(2));
8978
- if (quote.requiresApproval) result.requiresApproval = true;
8979
- if (quote.warnings?.length) result.warnings = [...quote.warnings];
8980
- return result;
8893
+ // ../../node_modules/viem/_esm/accounts/utils/publicKeyToAddress.js
8894
+ init_getAddress();
8895
+ init_keccak256();
8896
+ function publicKeyToAddress(publicKey) {
8897
+ const address = keccak256(`0x${publicKey.substring(4)}`).substring(26);
8898
+ return checksumAddress(`0x${address}`);
8981
8899
  }
8982
- function displayDryRunResult(result) {
8983
- info(`
8984
- Dry-run preview:`);
8985
- info(` From: ${result.inputAmount} ${result.fromToken} (${result.fromChain})`);
8986
- info(` To: ${result.estimatedOutput} ${result.toToken} (${result.toChain})`);
8987
- info(` Provider: ${result.provider}`);
8988
- if (result.estimatedOutputFiat != null) info(` Est. value (USD): $${result.estimatedOutputFiat}`);
8989
- if (result.requiresApproval) info(` Requires approval: yes`);
8990
- if (result.warnings?.length) result.warnings.forEach((w) => warn(` Warning: ${w}`));
8900
+
8901
+ // ../../node_modules/viem/_esm/utils/signature/recoverPublicKey.js
8902
+ init_isHex();
8903
+ init_size();
8904
+ init_fromHex();
8905
+ init_toHex();
8906
+ async function recoverPublicKey({ hash, signature }) {
8907
+ const hashHex = isHex(hash) ? hash : toHex(hash);
8908
+ const { secp256k1: secp256k12 } = await Promise.resolve().then(() => (init_secp256k1(), secp256k1_exports));
8909
+ const signature_ = (() => {
8910
+ if (typeof signature === "object" && "r" in signature && "s" in signature) {
8911
+ const { r, s, v, yParity } = signature;
8912
+ const yParityOrV2 = Number(yParity ?? v);
8913
+ const recoveryBit2 = toRecoveryBit(yParityOrV2);
8914
+ return new secp256k12.Signature(hexToBigInt(r), hexToBigInt(s)).addRecoveryBit(recoveryBit2);
8915
+ }
8916
+ const signatureHex = isHex(signature) ? signature : toHex(signature);
8917
+ if (size(signatureHex) !== 65)
8918
+ throw new Error("invalid signature length");
8919
+ const yParityOrV = hexToNumber(`0x${signatureHex.slice(130)}`);
8920
+ const recoveryBit = toRecoveryBit(yParityOrV);
8921
+ return secp256k12.Signature.fromCompact(signatureHex.substring(2, 130)).addRecoveryBit(recoveryBit);
8922
+ })();
8923
+ const publicKey = signature_.recoverPublicKey(hashHex.substring(2)).toHex(false);
8924
+ return `0x${publicKey}`;
8991
8925
  }
8992
- function refuseSwapWhenNonInteractive() {
8993
- throw new ConfirmationRequiredError(
8994
- "Swap requires confirmation.",
8995
- "Pass --yes to confirm, or --dry-run to preview without signing."
8996
- );
8926
+ function toRecoveryBit(yParityOrV) {
8927
+ if (yParityOrV === 0 || yParityOrV === 1)
8928
+ return yParityOrV;
8929
+ if (yParityOrV === 27)
8930
+ return 0;
8931
+ if (yParityOrV === 28)
8932
+ return 1;
8933
+ throw new Error("Invalid yParityOrV value");
8997
8934
  }
8998
- async function confirmSwapIfNeeded(options) {
8999
- if (options.yes) return;
9000
- if (isNonInteractive()) {
9001
- refuseSwapWhenNonInteractive();
9002
- }
9003
- const confirmed = await confirmSwap();
9004
- if (!confirmed) {
9005
- throw new ConfirmationRequiredError("Swap declined at the confirmation prompt");
9006
- }
8935
+
8936
+ // ../../node_modules/viem/_esm/utils/signature/recoverAddress.js
8937
+ async function recoverAddress({ hash, signature }) {
8938
+ return publicKeyToAddress(await recoverPublicKey({ hash, signature }));
9007
8939
  }
9008
- async function executeSwap(ctx2, options) {
9009
- const vault = await ctx2.ensureActiveVault();
9010
- validateSwapAmount(options.amount);
9011
- const amountStr = getSwapAmountString(options.amount);
9012
- if (!options.dryRun && !options.yes && isNonInteractive()) {
9013
- refuseSwapWhenNonInteractive();
9014
- }
9015
- const quoteSpinner = createSpinner("Getting swap quote...");
9016
- const dryResult = await vault.swap(toSwapRequest(options, amountStr, true));
9017
- if (!dryResult.dryRun) throw new Error("unreachable");
9018
- quoteSpinner.succeed("Quote received");
9019
- const quote = dryResult.quote;
9020
- const fromAmountRaw = options.amount === "max" ? formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals) : String(options.amount);
9021
- const fromAmountDisplay = options.amount === "max" ? `${fromAmountRaw} (max)` : fromAmountRaw;
9022
- if (options.dryRun) {
9023
- const result = toDryRunResult(options, quote, fromAmountRaw);
9024
- if (isJsonOutput()) {
9025
- outputJson(result);
9026
- } else {
9027
- displayDryRunResult(result);
9028
- }
9029
- return result;
8940
+
8941
+ // ../../node_modules/viem/_esm/utils/signature/hashTypedData.js
8942
+ init_encodeAbiParameters();
8943
+ init_concat();
8944
+ init_toHex();
8945
+ init_keccak256();
8946
+
8947
+ // ../../node_modules/viem/_esm/utils/typedData.js
8948
+ init_abi();
8949
+ init_address();
8950
+
8951
+ // ../../node_modules/viem/_esm/errors/typedData.js
8952
+ init_stringify();
8953
+ init_base();
8954
+ var InvalidDomainError = class extends BaseError {
8955
+ constructor({ domain }) {
8956
+ super(`Invalid domain "${stringify(domain)}".`, {
8957
+ metaMessages: ["Must be a valid EIP-712 domain."]
8958
+ });
9030
8959
  }
9031
- const feeBalance = await vault.balance(options.fromChain);
9032
- const discountTier = await vault.getDiscountTier();
9033
- if (!isJsonOutput()) {
9034
- displaySwapPreview(quote, fromAmountDisplay, quote.fromCoin.ticker, quote.toCoin.ticker, {
9035
- fromDecimals: quote.fromCoin.decimals,
9036
- toDecimals: quote.toCoin.decimals,
9037
- feeDecimals: feeBalance.decimals,
9038
- feeSymbol: feeBalance.symbol,
9039
- discountTier
8960
+ };
8961
+ var InvalidPrimaryTypeError = class extends BaseError {
8962
+ constructor({ primaryType, types }) {
8963
+ super(`Invalid primary type \`${primaryType}\` must be one of \`${JSON.stringify(Object.keys(types))}\`.`, {
8964
+ docsPath: "/api/glossary/Errors#typeddatainvalidprimarytypeerror",
8965
+ metaMessages: ["Check that the primary type is a key in `types`."]
9040
8966
  });
9041
8967
  }
9042
- await confirmSwapIfNeeded(options);
9043
- await ensureVaultUnlocked(vault, options.password);
9044
- const intent = buildSwapBroadcastIntent(vault, {
9045
- fromChain: options.fromChain,
9046
- toChain: options.toChain,
9047
- fromToken: options.fromToken,
9048
- toToken: options.toToken,
9049
- amount: fromAmountRaw,
9050
- isMax: options.amount === "max"
9051
- });
9052
- let signSpinner;
9053
- try {
9054
- const broadcast = await guardedBroadcast(intent, options.force ?? false, async () => {
9055
- signSpinner = createSpinner("Signing swap transaction...");
9056
- vault.on("signingProgress", ({ step }) => {
9057
- if (signSpinner) signSpinner.text = `${step.message} (${step.progress}%)`;
9058
- });
9059
- const result = await vault.swap(toSwapRequest(options, amountStr));
9060
- if (result.dryRun) throw new Error("unreachable");
9061
- return result;
8968
+ };
8969
+ var InvalidStructTypeError = class extends BaseError {
8970
+ constructor({ type }) {
8971
+ super(`Struct type "${type}" is invalid.`, {
8972
+ metaMessages: ["Struct type must not be a Solidity type."],
8973
+ name: "InvalidStructTypeError"
9062
8974
  });
9063
- signSpinner?.succeed(`Swap broadcast: ${broadcast.txHash}`);
9064
- if (isJsonOutput()) {
9065
- outputJson({
9066
- txHash: broadcast.txHash,
9067
- fromChain: options.fromChain,
9068
- toChain: options.toChain,
9069
- quote
9070
- });
9071
- } else {
9072
- displaySwapResult(options.fromChain, options.toChain, broadcast.txHash, quote, quote.toCoin.decimals);
9073
- }
9074
- return { txHash: broadcast.txHash, quote };
9075
- } catch (err) {
9076
- signSpinner?.stop();
9077
- throw err;
9078
- } finally {
9079
- vault.removeAllListeners("signingProgress");
9080
8975
  }
9081
- }
8976
+ };
9082
8977
 
9083
- // src/commands/settings.ts
9084
- import { Chain as Chain9, fiatCurrencies as fiatCurrencies2, fiatCurrencyNameRecord as fiatCurrencyNameRecord3 } from "@vultisig/sdk";
9085
- import chalk6 from "chalk";
9086
- async function executeCurrency(ctx2, newCurrency) {
9087
- const vault = await ctx2.ensureActiveVault();
9088
- if (!newCurrency) {
9089
- const currentCurrency = vault.currency;
9090
- const currencyName2 = fiatCurrencyNameRecord3[currentCurrency];
9091
- printResult(chalk6.cyan("\nCurrent Currency Preference:"));
9092
- printResult(` ${chalk6.green(currentCurrency.toUpperCase())} - ${currencyName2}`);
9093
- info(chalk6.gray(`
9094
- Supported currencies: ${fiatCurrencies2.join(", ")}`));
9095
- info(chalk6.gray('Use "vultisig currency <code>" to change'));
9096
- return currentCurrency;
8978
+ // ../../node_modules/viem/_esm/utils/typedData.js
8979
+ init_isAddress();
8980
+ init_size();
8981
+ init_toHex();
8982
+ init_regex();
8983
+ function validateTypedData(parameters) {
8984
+ const { domain, message, primaryType, types } = parameters;
8985
+ const validateData = (struct, data) => {
8986
+ for (const param of struct) {
8987
+ const { name, type } = param;
8988
+ const value = data[name];
8989
+ const integerMatch = type.match(integerRegex);
8990
+ if (integerMatch && (typeof value === "number" || typeof value === "bigint")) {
8991
+ const [_type, base, size_] = integerMatch;
8992
+ numberToHex(value, {
8993
+ signed: base === "int",
8994
+ size: Number.parseInt(size_, 10) / 8
8995
+ });
8996
+ }
8997
+ if (type === "address" && typeof value === "string" && !isAddress(value))
8998
+ throw new InvalidAddressError2({ address: value });
8999
+ const bytesMatch = type.match(bytesRegex);
9000
+ if (bytesMatch) {
9001
+ const [_type, size_] = bytesMatch;
9002
+ if (size_ && size(value) !== Number.parseInt(size_, 10))
9003
+ throw new BytesSizeMismatchError({
9004
+ expectedSize: Number.parseInt(size_, 10),
9005
+ givenSize: size(value)
9006
+ });
9007
+ }
9008
+ const struct2 = types[type];
9009
+ if (struct2) {
9010
+ validateReference(type);
9011
+ validateData(struct2, value);
9012
+ }
9013
+ }
9014
+ };
9015
+ if (types.EIP712Domain && domain) {
9016
+ if (typeof domain !== "object")
9017
+ throw new InvalidDomainError({ domain });
9018
+ validateData(types.EIP712Domain, domain);
9019
+ }
9020
+ if (primaryType !== "EIP712Domain") {
9021
+ if (types[primaryType])
9022
+ validateData(types[primaryType], message);
9023
+ else
9024
+ throw new InvalidPrimaryTypeError({ primaryType, types });
9025
+ }
9026
+ }
9027
+ function getTypesForEIP712Domain({ domain }) {
9028
+ return [
9029
+ typeof domain?.name === "string" && { name: "name", type: "string" },
9030
+ domain?.version && { name: "version", type: "string" },
9031
+ (typeof domain?.chainId === "number" || typeof domain?.chainId === "bigint") && {
9032
+ name: "chainId",
9033
+ type: "uint256"
9034
+ },
9035
+ domain?.verifyingContract && {
9036
+ name: "verifyingContract",
9037
+ type: "address"
9038
+ },
9039
+ domain?.salt && { name: "salt", type: "bytes32" }
9040
+ ].filter(Boolean);
9041
+ }
9042
+ function validateReference(type) {
9043
+ if (type === "address" || type === "bool" || type === "string" || type.startsWith("bytes") || type.startsWith("uint") || type.startsWith("int"))
9044
+ throw new InvalidStructTypeError({ type });
9045
+ }
9046
+
9047
+ // ../../node_modules/viem/_esm/utils/signature/hashTypedData.js
9048
+ function hashTypedData(parameters) {
9049
+ const { domain = {}, message, primaryType } = parameters;
9050
+ const types = {
9051
+ EIP712Domain: getTypesForEIP712Domain({ domain }),
9052
+ ...parameters.types
9053
+ };
9054
+ validateTypedData({
9055
+ domain,
9056
+ message,
9057
+ primaryType,
9058
+ types
9059
+ });
9060
+ const parts = ["0x1901"];
9061
+ if (domain)
9062
+ parts.push(hashDomain({
9063
+ domain,
9064
+ types
9065
+ }));
9066
+ if (primaryType !== "EIP712Domain")
9067
+ parts.push(hashStruct({
9068
+ data: message,
9069
+ primaryType,
9070
+ types
9071
+ }));
9072
+ return keccak256(concat(parts));
9073
+ }
9074
+ function hashDomain({ domain, types }) {
9075
+ return hashStruct({
9076
+ data: domain,
9077
+ primaryType: "EIP712Domain",
9078
+ types
9079
+ });
9080
+ }
9081
+ function hashStruct({ data, primaryType, types }) {
9082
+ const encoded = encodeData({
9083
+ data,
9084
+ primaryType,
9085
+ types
9086
+ });
9087
+ return keccak256(encoded);
9088
+ }
9089
+ function encodeData({ data, primaryType, types }) {
9090
+ const encodedTypes = [{ type: "bytes32" }];
9091
+ const encodedValues = [hashType({ primaryType, types })];
9092
+ for (const field of types[primaryType]) {
9093
+ const [type, value] = encodeField({
9094
+ types,
9095
+ name: field.name,
9096
+ type: field.type,
9097
+ value: data[field.name]
9098
+ });
9099
+ encodedTypes.push(type);
9100
+ encodedValues.push(value);
9101
+ }
9102
+ return encodeAbiParameters(encodedTypes, encodedValues);
9103
+ }
9104
+ function hashType({ primaryType, types }) {
9105
+ const encodedHashType = toHex(encodeType({ primaryType, types }));
9106
+ return keccak256(encodedHashType);
9107
+ }
9108
+ function encodeType({ primaryType, types }) {
9109
+ let result = "";
9110
+ const unsortedDeps = findTypeDependencies({ primaryType, types });
9111
+ unsortedDeps.delete(primaryType);
9112
+ const deps = [primaryType, ...Array.from(unsortedDeps).sort()];
9113
+ for (const type of deps) {
9114
+ result += `${type}(${types[type].map(({ name, type: t }) => `${t} ${name}`).join(",")})`;
9115
+ }
9116
+ return result;
9117
+ }
9118
+ function findTypeDependencies({ primaryType: primaryType_, types }, results = /* @__PURE__ */ new Set()) {
9119
+ const match = primaryType_.match(/^\w*/u);
9120
+ const primaryType = match?.[0];
9121
+ if (results.has(primaryType) || types[primaryType] === void 0) {
9122
+ return results;
9123
+ }
9124
+ results.add(primaryType);
9125
+ for (const field of types[primaryType]) {
9126
+ findTypeDependencies({ primaryType: field.type, types }, results);
9127
+ }
9128
+ return results;
9129
+ }
9130
+ function encodeField({ types, name, type, value }) {
9131
+ if (types[type] !== void 0) {
9132
+ return [
9133
+ { type: "bytes32" },
9134
+ keccak256(encodeData({ data: value, primaryType: type, types }))
9135
+ ];
9136
+ }
9137
+ if (type === "bytes")
9138
+ return [{ type: "bytes32" }, keccak256(value)];
9139
+ if (type === "string")
9140
+ return [{ type: "bytes32" }, keccak256(toHex(value))];
9141
+ if (type.lastIndexOf("]") === type.length - 1) {
9142
+ const parsedType = type.slice(0, type.lastIndexOf("["));
9143
+ const typeValuePairs = value.map((item) => encodeField({
9144
+ name,
9145
+ type: parsedType,
9146
+ types,
9147
+ value: item
9148
+ }));
9149
+ return [
9150
+ { type: "bytes32" },
9151
+ keccak256(encodeAbiParameters(typeValuePairs.map(([t]) => t), typeValuePairs.map(([, v]) => v)))
9152
+ ];
9153
+ }
9154
+ return [{ type }, value];
9155
+ }
9156
+
9157
+ // ../../node_modules/viem/_esm/errors/unit.js
9158
+ init_base();
9159
+ var InvalidDecimalNumberError = class extends BaseError {
9160
+ constructor({ value }) {
9161
+ super(`Number \`${value}\` is not a valid decimal number.`, {
9162
+ name: "InvalidDecimalNumberError"
9163
+ });
9164
+ }
9165
+ };
9166
+
9167
+ // ../../node_modules/viem/_esm/utils/unit/parseUnits.js
9168
+ function parseUnits(value, decimals) {
9169
+ if (!/^(-?)([0-9]*)\.?([0-9]*)$/.test(value))
9170
+ throw new InvalidDecimalNumberError({ value });
9171
+ let [integer, fraction = "0"] = value.split(".");
9172
+ const negative = integer.startsWith("-");
9173
+ if (negative)
9174
+ integer = integer.slice(1);
9175
+ fraction = fraction.replace(/(0+)$/, "");
9176
+ if (decimals === 0) {
9177
+ if (Math.round(Number(`.${fraction}`)) === 1)
9178
+ integer = `${BigInt(integer) + 1n}`;
9179
+ fraction = "";
9180
+ } else if (fraction.length > decimals) {
9181
+ const [left, unit, right] = [
9182
+ fraction.slice(0, decimals - 1),
9183
+ fraction.slice(decimals - 1, decimals),
9184
+ fraction.slice(decimals)
9185
+ ];
9186
+ const rounded = Math.round(Number(`${unit}.${right}`));
9187
+ if (rounded > 9)
9188
+ fraction = `${BigInt(left) + BigInt(1)}0`.padStart(left.length + 1, "0");
9189
+ else
9190
+ fraction = `${left}${rounded}`;
9191
+ if (fraction.length > decimals) {
9192
+ fraction = fraction.slice(1);
9193
+ integer = `${BigInt(integer) + 1n}`;
9194
+ }
9195
+ fraction = fraction.slice(0, decimals);
9196
+ } else {
9197
+ fraction = fraction.padEnd(decimals, "0");
9198
+ }
9199
+ return BigInt(`${negative ? "-" : ""}${integer}${fraction}`);
9200
+ }
9201
+
9202
+ // ../../node_modules/viem/_esm/index.js
9203
+ init_formatUnits();
9204
+
9205
+ // ../../packages/core/chain/dist/amount/toChainAmount.js
9206
+ var ChainAmountParseError = class extends Error {
9207
+ name = "ChainAmountParseError";
9208
+ constructor(message) {
9209
+ super(message);
9210
+ }
9211
+ };
9212
+ var SCIENTIFIC_DECIMAL = /^([+-]?)(?:(\d+)\.?(\d*)|\.(\d+))[eE]([+-]?\d+)$/i;
9213
+ var MAX_SCALE_ABS = 10000n;
9214
+ var padFractionDigits = (frac, totalLen) => {
9215
+ const need = totalLen - BigInt(frac.length);
9216
+ if (need <= 0n) {
9217
+ return frac;
9218
+ }
9219
+ if (need <= BigInt(Number.MAX_SAFE_INTEGER)) {
9220
+ return `${"0".repeat(Number(need))}${frac}`;
9221
+ }
9222
+ let out = frac;
9223
+ while (BigInt(out.length) < totalLen) {
9224
+ out = `0${out}`;
9225
+ }
9226
+ return out;
9227
+ };
9228
+ var expandScientificNotationToDecimalString = (s) => {
9229
+ const m = SCIENTIFIC_DECIMAL.exec(s.trim());
9230
+ if (!m) {
9231
+ throw new ChainAmountParseError(`Invalid amount: "${s}"`);
9232
+ }
9233
+ const signNeg = m[1] === "-";
9234
+ let digitStr;
9235
+ let fracLen;
9236
+ if (m[4] !== void 0) {
9237
+ digitStr = m[4];
9238
+ fracLen = digitStr.length;
9239
+ } else {
9240
+ digitStr = `${m[2] ?? ""}${m[3] ?? ""}`;
9241
+ fracLen = (m[3] ?? "").length;
9242
+ }
9243
+ if (!/^\d+$/.test(digitStr)) {
9244
+ throw new ChainAmountParseError(`Invalid amount: "${s}"`);
9245
+ }
9246
+ const expStr = m[5] ?? "";
9247
+ if (expStr === "" || expStr === "+" || expStr === "-") {
9248
+ throw new ChainAmountParseError(`Invalid amount: "${s}"`);
9249
+ }
9250
+ const allDigits = BigInt(digitStr);
9251
+ const exp = BigInt(expStr);
9252
+ const scale = exp - BigInt(fracLen);
9253
+ const scaleAbs = scale < 0n ? -scale : scale;
9254
+ if (scaleAbs > MAX_SCALE_ABS) {
9255
+ throw new ChainAmountParseError(`Amount exponent out of supported range: "${s}"`);
9256
+ }
9257
+ let absResult;
9258
+ if (scale >= 0n) {
9259
+ const mult = 10n ** scale;
9260
+ absResult = (allDigits * mult).toString();
9261
+ } else {
9262
+ const k = -scale;
9263
+ const divisor = 10n ** k;
9264
+ const intPart = allDigits / divisor;
9265
+ const rem = allDigits % divisor;
9266
+ const frac = padFractionDigits(rem.toString(), k);
9267
+ absResult = intPart === 0n ? `0.${frac}` : `${intPart.toString()}.${frac}`;
9268
+ }
9269
+ if (signNeg && allDigits !== 0n) {
9270
+ return `-${absResult}`;
9271
+ }
9272
+ return absResult;
9273
+ };
9274
+ var formatNumberAmount = (amount) => {
9275
+ const str2 = amount.toString();
9276
+ return /[eE]/.test(str2) ? expandScientificNotationToDecimalString(str2) : str2;
9277
+ };
9278
+ var truncateToDecimals = (s, decimals) => {
9279
+ const dotIdx = s.indexOf(".");
9280
+ if (dotIdx === -1)
9281
+ return s;
9282
+ if (decimals === 0)
9283
+ return s.slice(0, dotIdx);
9284
+ const fracPart = s.slice(dotIdx + 1);
9285
+ if (fracPart.length <= decimals)
9286
+ return s;
9287
+ return `${s.slice(0, dotIdx + 1)}${fracPart.slice(0, decimals)}`;
9288
+ };
9289
+ var toChainAmount = (amount, decimals) => {
9290
+ if (typeof amount === "string") {
9291
+ const trimmed = amount.trim();
9292
+ if (!trimmed) {
9293
+ throw new ChainAmountParseError("Amount cannot be empty");
9294
+ }
9295
+ if (/[eE]/.test(trimmed)) {
9296
+ const expanded = expandScientificNotationToDecimalString(trimmed);
9297
+ return parseUnits(truncateToDecimals(expanded, decimals), decimals);
9298
+ }
9299
+ return parseUnits(truncateToDecimals(trimmed, decimals), decimals);
9300
+ }
9301
+ return parseUnits(truncateToDecimals(formatNumberAmount(amount), decimals), decimals);
9302
+ };
9303
+
9304
+ // src/commands/swap.ts
9305
+ async function executeSwapChains(ctx2) {
9306
+ const vault = await ctx2.ensureActiveVault();
9307
+ const spinner = createSpinner("Loading supported swap chains...");
9308
+ const chains = await vault.getSupportedSwapChains();
9309
+ spinner.succeed("Swap chains loaded");
9310
+ if (isJsonOutput()) {
9311
+ outputJson({ swapChains: [...chains] });
9312
+ return chains;
9313
+ }
9314
+ displaySwapChains(chains);
9315
+ return chains;
9316
+ }
9317
+ async function executeSwapQuote(ctx2, options) {
9318
+ const isMax = options.amount === "max";
9319
+ const amount = normalizeSwapAmount(options.amount);
9320
+ const vault = await ctx2.ensureActiveVault();
9321
+ const spinner = createSpinner("Getting swap quote...");
9322
+ const result = await vault.swap({
9323
+ fromChain: options.fromChain,
9324
+ fromSymbol: options.fromToken || "",
9325
+ toChain: options.toChain,
9326
+ toSymbol: options.toToken || "",
9327
+ amount,
9328
+ dryRun: true
9329
+ });
9330
+ if (!result.dryRun) throw new Error("unreachable");
9331
+ spinner.succeed("Quote received");
9332
+ const quote = result.quote;
9333
+ const semanticAmount = isMax ? amount : normalizeSwapAmount(amount, quote.fromCoin.decimals);
9334
+ const fromAmountDisplay = isMax ? `${formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals)} (max)` : semanticAmount;
9335
+ if (isJsonOutput()) {
9336
+ outputJson({
9337
+ fromChain: options.fromChain,
9338
+ toChain: options.toChain,
9339
+ amount: semanticAmount,
9340
+ isMax,
9341
+ quote
9342
+ });
9343
+ return quote;
9344
+ }
9345
+ const feeBalance = await vault.balance(options.fromChain);
9346
+ const discountTier = await vault.getDiscountTier();
9347
+ displaySwapPreview(quote, fromAmountDisplay, quote.fromCoin.ticker, quote.toCoin.ticker, {
9348
+ fromDecimals: quote.fromCoin.decimals,
9349
+ toDecimals: quote.toCoin.decimals,
9350
+ feeDecimals: feeBalance.decimals,
9351
+ feeSymbol: feeBalance.symbol,
9352
+ discountTier
9353
+ });
9354
+ info('\nTo execute this swap, use the "swap" command');
9355
+ return quote;
9356
+ }
9357
+ var SWAP_AMOUNT_CANONICAL_DECIMALS = 1e4;
9358
+ function normalizeSwapAmount(amount, decimals = SWAP_AMOUNT_CANONICAL_DECIMALS) {
9359
+ if (amount === "max") return amount;
9360
+ try {
9361
+ const chainAmount = toChainAmount(amount, decimals);
9362
+ if (chainAmount <= 0n) throw new Error("Invalid amount");
9363
+ return formatUnits(chainAmount, decimals);
9364
+ } catch {
9365
+ throw new Error("Invalid amount");
9366
+ }
9367
+ }
9368
+ function toSwapRequest(options, amount, dryRun) {
9369
+ return {
9370
+ fromChain: options.fromChain,
9371
+ fromSymbol: options.fromToken || "",
9372
+ toChain: options.toChain,
9373
+ toSymbol: options.toToken || "",
9374
+ amount,
9375
+ ...options.slippage !== void 0 && { slippageTolerance: options.slippage },
9376
+ ...dryRun && { dryRun: true }
9377
+ };
9378
+ }
9379
+ function toDryRunResult(options, quote, fromAmountRaw) {
9380
+ const result = {
9381
+ dryRun: true,
9382
+ fromChain: String(options.fromChain),
9383
+ fromToken: quote.fromCoin.ticker,
9384
+ toChain: String(options.toChain),
9385
+ toToken: quote.toCoin.ticker,
9386
+ inputAmount: fromAmountRaw,
9387
+ ...options.amount === "max" && { isMax: true },
9388
+ estimatedOutput: formatBigintAmount(quote.estimatedOutput, quote.toCoin.decimals),
9389
+ provider: quote.provider
9390
+ };
9391
+ if (quote.estimatedOutputFiat != null) result.estimatedOutputFiat = parseFloat(quote.estimatedOutputFiat.toFixed(2));
9392
+ if (quote.requiresApproval) result.requiresApproval = true;
9393
+ if (quote.warnings?.length) result.warnings = [...quote.warnings];
9394
+ return result;
9395
+ }
9396
+ function displayDryRunResult(result) {
9397
+ info(`
9398
+ Dry-run preview:`);
9399
+ info(` From: ${result.inputAmount} ${result.fromToken} (${result.fromChain})`);
9400
+ info(` To: ${result.estimatedOutput} ${result.toToken} (${result.toChain})`);
9401
+ info(` Provider: ${result.provider}`);
9402
+ if (result.estimatedOutputFiat != null) info(` Est. value (USD): $${result.estimatedOutputFiat}`);
9403
+ if (result.requiresApproval) info(` Requires approval: yes`);
9404
+ if (result.warnings?.length) result.warnings.forEach((w) => warn(` Warning: ${w}`));
9405
+ }
9406
+ function refuseSwapWhenNonInteractive() {
9407
+ throw new ConfirmationRequiredError(
9408
+ "Swap requires confirmation.",
9409
+ "Pass --yes to confirm, or --dry-run to preview without signing."
9410
+ );
9411
+ }
9412
+ async function confirmSwapIfNeeded(options) {
9413
+ if (options.yes) return;
9414
+ if (isNonInteractive()) {
9415
+ refuseSwapWhenNonInteractive();
9416
+ }
9417
+ const confirmed = await confirmSwap();
9418
+ if (!confirmed) {
9419
+ throw new ConfirmationRequiredError("Swap declined at the confirmation prompt");
9420
+ }
9421
+ }
9422
+ async function executeSwap(ctx2, options) {
9423
+ const amountStr = normalizeSwapAmount(options.amount);
9424
+ const vault = await ctx2.ensureActiveVault();
9425
+ if (!options.dryRun && !options.yes && isNonInteractive()) {
9426
+ refuseSwapWhenNonInteractive();
9427
+ }
9428
+ const quoteSpinner = createSpinner("Getting swap quote...");
9429
+ const dryResult = await vault.swap(toSwapRequest(options, amountStr, true));
9430
+ if (!dryResult.dryRun) throw new Error("unreachable");
9431
+ quoteSpinner.succeed("Quote received");
9432
+ const quote = dryResult.quote;
9433
+ const semanticAmount = options.amount === "max" ? amountStr : normalizeSwapAmount(amountStr, quote.fromCoin.decimals);
9434
+ const fromAmountRaw = options.amount === "max" ? formatBigintAmount(quote.maxSwapable, quote.fromCoin.decimals) : semanticAmount;
9435
+ const fromAmountDisplay = options.amount === "max" ? `${fromAmountRaw} (max)` : fromAmountRaw;
9436
+ if (options.dryRun) {
9437
+ const result = toDryRunResult(options, quote, fromAmountRaw);
9438
+ if (isJsonOutput()) {
9439
+ outputJson(result);
9440
+ } else {
9441
+ displayDryRunResult(result);
9442
+ }
9443
+ return result;
9444
+ }
9445
+ const feeBalance = await vault.balance(options.fromChain);
9446
+ const discountTier = await vault.getDiscountTier();
9447
+ if (!isJsonOutput()) {
9448
+ displaySwapPreview(quote, fromAmountDisplay, quote.fromCoin.ticker, quote.toCoin.ticker, {
9449
+ fromDecimals: quote.fromCoin.decimals,
9450
+ toDecimals: quote.toCoin.decimals,
9451
+ feeDecimals: feeBalance.decimals,
9452
+ feeSymbol: feeBalance.symbol,
9453
+ discountTier
9454
+ });
9455
+ }
9456
+ await confirmSwapIfNeeded(options);
9457
+ await ensureVaultUnlocked(vault, options.password);
9458
+ const intent = buildSwapBroadcastIntent(vault, {
9459
+ fromChain: options.fromChain,
9460
+ toChain: options.toChain,
9461
+ fromToken: options.fromToken,
9462
+ toToken: options.toToken,
9463
+ amount: fromAmountRaw,
9464
+ isMax: options.amount === "max"
9465
+ });
9466
+ let signSpinner;
9467
+ try {
9468
+ const broadcast = await guardedBroadcast(intent, options.force ?? false, async () => {
9469
+ signSpinner = createSpinner("Signing swap transaction...");
9470
+ vault.on("signingProgress", ({ step }) => {
9471
+ if (signSpinner) signSpinner.text = `${step.message} (${step.progress}%)`;
9472
+ });
9473
+ const result = await vault.swap(toSwapRequest(options, semanticAmount));
9474
+ if (result.dryRun) throw new Error("unreachable");
9475
+ return result;
9476
+ });
9477
+ signSpinner?.succeed(`Swap broadcast: ${broadcast.txHash}`);
9478
+ if (isJsonOutput()) {
9479
+ outputJson({
9480
+ txHash: broadcast.txHash,
9481
+ fromChain: options.fromChain,
9482
+ toChain: options.toChain,
9483
+ quote
9484
+ });
9485
+ } else {
9486
+ displaySwapResult(options.fromChain, options.toChain, broadcast.txHash, quote, quote.toCoin.decimals);
9487
+ }
9488
+ return { txHash: broadcast.txHash, quote };
9489
+ } catch (err) {
9490
+ signSpinner?.stop();
9491
+ throw err;
9492
+ } finally {
9493
+ vault.removeAllListeners("signingProgress");
9494
+ }
9495
+ }
9496
+
9497
+ // src/commands/settings.ts
9498
+ import { Chain as Chain9, fiatCurrencies as fiatCurrencies2, fiatCurrencyNameRecord as fiatCurrencyNameRecord3 } from "@vultisig/sdk";
9499
+ import chalk6 from "chalk";
9500
+ async function executeCurrency(ctx2, newCurrency) {
9501
+ const vault = await ctx2.ensureActiveVault();
9502
+ if (!newCurrency) {
9503
+ const currentCurrency = vault.currency;
9504
+ const currencyName2 = fiatCurrencyNameRecord3[currentCurrency];
9505
+ printResult(chalk6.cyan("\nCurrent Currency Preference:"));
9506
+ printResult(` ${chalk6.green(currentCurrency.toUpperCase())} - ${currencyName2}`);
9507
+ info(chalk6.gray(`
9508
+ Supported currencies: ${fiatCurrencies2.join(", ")}`));
9509
+ info(chalk6.gray('Use "vultisig currency <code>" to change'));
9510
+ return currentCurrency;
9097
9511
  }
9098
9512
  const currency = newCurrency.toLowerCase();
9099
9513
  if (!fiatCurrencies2.includes(currency)) {
@@ -9682,540 +10096,273 @@ var AskInterface = class {
9682
10096
  `);
9683
10097
  } else {
9684
10098
  process.stderr.write(`[confirm] auto-approved (--yes): ${message}
9685
- `);
9686
- }
9687
- return this.autoApprove;
9688
- }
9689
- };
9690
- }
9691
- /**
9692
- * Send a message and wait for the complete response.
9693
- * All tool calls and actions are executed automatically.
9694
- */
9695
- async ask(message) {
9696
- this.responseParts = [];
9697
- this.toolCalls = [];
9698
- this.transactions = [];
9699
- this.cards = [];
9700
- this.warnings = [];
9701
- this.outcome = void 0;
9702
- this.error = void 0;
9703
- this.errorIsTerminal = false;
9704
- const callbacks = this.getCallbacks();
9705
- await this.session.sendMessage(message, callbacks);
9706
- return this.partialResult();
9707
- }
9708
- /**
9709
- * Snapshot of everything collected so far this turn. Identical to a normal
9710
- * `ask()` return, but callable from a catch block when `ask()` THREW mid-turn
9711
- * — e.g. the follow-up request that reports recent_actions back to the backend
9712
- * fails (timeout/5xx/auth) AFTER a tx has already broadcast and `onTxStatus`
9713
- * fired. Lets the caller still surface the already-broadcast tx hash in the
9714
- * error envelope instead of stranding funds the turn just moved.
9715
- */
9716
- partialResult() {
9717
- return {
9718
- sessionId: this.session.getConversationId() || "",
9719
- response: this.responseParts[this.responseParts.length - 1] || "",
9720
- toolCalls: this.toolCalls,
9721
- transactions: this.transactions,
9722
- cards: this.cards,
9723
- warnings: this.warnings,
9724
- error: this.error,
9725
- ...this.outcome ? { outcome: this.outcome } : {}
9726
- };
9727
- }
9728
- };
9729
-
9730
- // src/agent/auth.ts
9731
- import { randomBytes as randomBytes2 } from "node:crypto";
9732
- import { Chain as Chain10, computePersonalSignHash, formatEcdsaSignature65 } from "@vultisig/sdk";
9733
- async function authenticateVault(client, vault, password, maxAttempts = 3) {
9734
- const publicKey = vault.publicKeys.ecdsa;
9735
- const chainCode = vault.hexChainCode;
9736
- const ethAddress = await vault.address(Chain10.Ethereum);
9737
- const nonce = "0x" + randomBytes2(16).toString("hex");
9738
- const expiresAt = new Date(Date.now() + 15 * 60 * 1e3).toISOString();
9739
- const authMessage = JSON.stringify({
9740
- message: "Sign into Vultisig Plugin Marketplace",
9741
- nonce,
9742
- expiresAt,
9743
- address: ethAddress
9744
- });
9745
- const messageHash = computePersonalSignHash(authMessage);
9746
- let lastError = null;
9747
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
9748
- try {
9749
- if (attempt > 1) {
9750
- process.stderr.write(` Retry ${attempt}/${maxAttempts}...
9751
- `);
9752
- }
9753
- const signature = await vault.signBytes({ data: Buffer.from(messageHash), chain: Chain10.Ethereum }, {});
9754
- if (signature.recovery === void 0) {
9755
- throw new Error("Agent authentication requires an ECDSA recovery id");
9756
- }
9757
- const sigHex = formatEcdsaSignature65(signature.signature, signature.recovery);
9758
- const authResponse = await client.authenticate({
9759
- public_key: publicKey,
9760
- chain_code_hex: chainCode,
9761
- message: authMessage,
9762
- signature: sigHex
9763
- });
9764
- return {
9765
- token: authResponse.token,
9766
- expiresAt: authResponse.expires_at,
9767
- // Captured + persisted by the session token cache. The backend exposes
9768
- // POST /auth/refresh to exchange it for a fresh access token without a
9769
- // new MPC round; wiring that exchange is a future enhancement — today
9770
- // the CLI re-auths via a full MPC re-sign (authenticateVault), which is
9771
- // always available and avoids depending on refresh-token rotation.
9772
- refreshToken: authResponse.refresh_token
9773
- };
9774
- } catch (err) {
9775
- lastError = err;
9776
- if (attempt < maxAttempts && err.message?.includes("timeout")) {
9777
- continue;
9778
- }
9779
- throw err;
9780
- }
9781
- }
9782
- throw lastError || new Error("Authentication failed after all attempts");
9783
- }
9784
-
9785
- // src/agent/cards.ts
9786
- import chalk8 from "chalk";
9787
- var CLI_SUPPORTED_SURFACES = ["balance_summary", "turn_outcome"];
9788
- function parseTurnOutcome(raw) {
9789
- if (!raw || typeof raw !== "object") return null;
9790
- const kind = raw.kind;
9791
- if (kind !== "success" && kind !== "blocked" && kind !== "refusal" && kind !== "error") return null;
9792
- const code = raw.code;
9793
- const detail = raw.detail;
9794
- return {
9795
- kind,
9796
- ...typeof code === "string" ? { code } : {},
9797
- ...typeof detail === "string" ? { detail } : {}
9798
- };
9799
- }
9800
- function stripControlChars(s) {
9801
- let out = "";
9802
- for (const ch of s) {
9803
- const code = ch.codePointAt(0) ?? 0;
9804
- if (code <= 31 || code >= 127 && code <= 159) continue;
9805
- out += ch;
9806
- }
9807
- return out;
9808
- }
9809
- function asString(v) {
9810
- return typeof v === "string" ? stripControlChars(v) : "";
9811
- }
9812
- function parseToken(v) {
9813
- if (!v || typeof v !== "object") return null;
9814
- const o = v;
9815
- const symbol = asString(o.symbol);
9816
- const amountDecimal = asString(o.amountDecimal);
9817
- if (!symbol && !amountDecimal) return null;
9818
- const token = { symbol, amountDecimal };
9819
- const amountUsd = asString(o.amountUsd);
9820
- if (amountUsd) token.amountUsd = amountUsd;
9821
- return token;
9822
- }
9823
- function parseAccount(v) {
9824
- if (!v || typeof v !== "object") return null;
9825
- const o = v;
9826
- const chainId = asString(o.chainId);
9827
- if (!chainId) return null;
9828
- const tokensRaw = Array.isArray(o.tokens) ? o.tokens : [];
9829
- const tokens = tokensRaw.map(parseToken).filter((t) => t !== null);
9830
- return { chainId, address: asString(o.address) || "\u2014", tokens };
9831
- }
9832
- function parseBalanceSummaryEnvelope(value) {
9833
- if (!value || typeof value !== "object") return null;
9834
- const o = value;
9835
- if (o.surface !== "balance_summary") return null;
9836
- if (!Array.isArray(o.accounts)) return null;
9837
- const accounts = o.accounts.map(parseAccount).filter((a) => a !== null);
9838
- if (accounts.length === 0) return null;
9839
- const card = { surface: "balance_summary", accounts };
9840
- if (o.stale === true) {
9841
- card.stale = true;
9842
- if (typeof o.stale_secs === "number") card.staleSecs = o.stale_secs;
9843
- }
9844
- return card;
9845
- }
9846
- function matchBrace(text, start) {
9847
- let depth = 0;
9848
- let inString = false;
9849
- let escaped = false;
9850
- for (let i = start; i < text.length; i++) {
9851
- const ch = text[i];
9852
- if (inString) {
9853
- if (escaped) escaped = false;
9854
- else if (ch === "\\") escaped = true;
9855
- else if (ch === '"') inString = false;
9856
- continue;
9857
- }
9858
- if (ch === '"') inString = true;
9859
- else if (ch === "{") depth++;
9860
- else if (ch === "}") {
9861
- depth--;
9862
- if (depth === 0) return i;
9863
- }
9864
- }
9865
- return -1;
9866
- }
9867
- function extractBalanceSummaryFromText(content) {
9868
- if (!content || !content.includes("balance_summary")) return null;
9869
- if (content.length > 2e5) return null;
9870
- for (let i = content.indexOf("{"); i !== -1; i = content.indexOf("{", i + 1)) {
9871
- const end = matchBrace(content, i);
9872
- if (end === -1) break;
9873
- const blob = content.slice(i, end + 1);
9874
- if (!blob.includes("balance_summary")) continue;
9875
- let parsed;
9876
- try {
9877
- parsed = JSON.parse(blob);
9878
- } catch {
9879
- continue;
9880
- }
9881
- const card = parseBalanceSummaryEnvelope(parsed);
9882
- if (!card) continue;
9883
- const before = content.slice(0, i).replace(/```(?:json)?\s*$/i, "");
9884
- const after = content.slice(end + 1).replace(/^\s*```/, "");
9885
- const remainingText = (before + after).trim();
9886
- return { card, remainingText };
9887
- }
9888
- return null;
9889
- }
9890
- function shortenAddress(address) {
9891
- if (!address || address === "\u2014") return address || "\u2014";
9892
- if (address.length <= 16) return address;
9893
- return `${address.slice(0, 8)}\u2026${address.slice(-6)}`;
9894
- }
9895
- function parseUsd(amountUsd) {
9896
- if (!amountUsd) return null;
9897
- const cleaned = amountUsd.replace(/[$,\s]/g, "");
9898
- if (!cleaned) return null;
9899
- const n = Number(cleaned);
9900
- return Number.isFinite(n) ? n : null;
9901
- }
9902
- function formatUsd(n) {
9903
- return `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
9904
- }
9905
- function renderBalanceSummaryCard(card) {
9906
- const lines = [];
9907
- const staleCue = card.stale ? chalk8.gray(` (stale${card.staleSecs ? ` ~${Math.round(card.staleSecs / 60)}m` : ""}, refreshing\u2026)`) : "";
9908
- lines.push(chalk8.bold(" Balances") + staleCue);
9909
- let total = 0;
9910
- let sawUsd = false;
9911
- for (const account of card.accounts) {
9912
- lines.push(` ${chalk8.cyan(account.chainId)} ${chalk8.gray(`(${shortenAddress(account.address)})`)}`);
9913
- if (account.tokens.length === 0) {
9914
- lines.push(chalk8.gray(" (no balances)"));
9915
- continue;
9916
- }
9917
- for (const token of account.tokens) {
9918
- const usd = parseUsd(token.amountUsd);
9919
- if (usd !== null) {
9920
- total += usd;
9921
- sawUsd = true;
10099
+ `);
10100
+ }
10101
+ return this.autoApprove;
9922
10102
  }
9923
- const symbol = token.symbol.padEnd(10);
9924
- const amount = token.amountDecimal.padStart(16);
9925
- const usdCol = token.amountUsd ? chalk8.gray(` ${token.amountUsd}`) : "";
9926
- lines.push(` ${chalk8.bold(symbol)}${amount}${usdCol}`);
9927
- }
9928
- }
9929
- if (sawUsd) {
9930
- lines.push(chalk8.gray(" " + "\u2500".repeat(36)));
9931
- lines.push(` ${chalk8.bold("Total")} ${chalk8.green(formatUsd(total))}`);
9932
- }
9933
- return lines.join("\n");
9934
- }
9935
-
9936
- // src/agent/client.ts
9937
- import { randomUUID } from "node:crypto";
9938
-
9939
- // src/agent/toolOutputSigning.ts
9940
- import { getChainKind as getChainKind4 } from "@vultisig/sdk";
9941
-
9942
- // src/agent/executor.ts
9943
- import {
9944
- Chain as Chain11,
9945
- chainFeeCoin,
9946
- getChainKind as getChainKind3,
9947
- resolveChainReference,
9948
- VaultError as VaultError3,
9949
- VaultErrorCode as VaultErrorCode3,
9950
- Vultisig as VultisigSdk
9951
- } from "@vultisig/sdk";
9952
-
9953
- // ../../node_modules/viem/_esm/accounts/utils/publicKeyToAddress.js
9954
- init_getAddress();
9955
- init_keccak256();
9956
- function publicKeyToAddress(publicKey) {
9957
- const address = keccak256(`0x${publicKey.substring(4)}`).substring(26);
9958
- return checksumAddress(`0x${address}`);
9959
- }
9960
-
9961
- // ../../node_modules/viem/_esm/utils/signature/recoverPublicKey.js
9962
- init_isHex();
9963
- init_size();
9964
- init_fromHex();
9965
- init_toHex();
9966
- async function recoverPublicKey({ hash, signature }) {
9967
- const hashHex = isHex(hash) ? hash : toHex(hash);
9968
- const { secp256k1: secp256k12 } = await Promise.resolve().then(() => (init_secp256k1(), secp256k1_exports));
9969
- const signature_ = (() => {
9970
- if (typeof signature === "object" && "r" in signature && "s" in signature) {
9971
- const { r, s, v, yParity } = signature;
9972
- const yParityOrV2 = Number(yParity ?? v);
9973
- const recoveryBit2 = toRecoveryBit(yParityOrV2);
9974
- return new secp256k12.Signature(hexToBigInt(r), hexToBigInt(s)).addRecoveryBit(recoveryBit2);
9975
- }
9976
- const signatureHex = isHex(signature) ? signature : toHex(signature);
9977
- if (size(signatureHex) !== 65)
9978
- throw new Error("invalid signature length");
9979
- const yParityOrV = hexToNumber(`0x${signatureHex.slice(130)}`);
9980
- const recoveryBit = toRecoveryBit(yParityOrV);
9981
- return secp256k12.Signature.fromCompact(signatureHex.substring(2, 130)).addRecoveryBit(recoveryBit);
9982
- })();
9983
- const publicKey = signature_.recoverPublicKey(hashHex.substring(2)).toHex(false);
9984
- return `0x${publicKey}`;
9985
- }
9986
- function toRecoveryBit(yParityOrV) {
9987
- if (yParityOrV === 0 || yParityOrV === 1)
9988
- return yParityOrV;
9989
- if (yParityOrV === 27)
9990
- return 0;
9991
- if (yParityOrV === 28)
9992
- return 1;
9993
- throw new Error("Invalid yParityOrV value");
9994
- }
9995
-
9996
- // ../../node_modules/viem/_esm/utils/signature/recoverAddress.js
9997
- async function recoverAddress({ hash, signature }) {
9998
- return publicKeyToAddress(await recoverPublicKey({ hash, signature }));
9999
- }
10000
-
10001
- // ../../node_modules/viem/_esm/utils/signature/hashTypedData.js
10002
- init_encodeAbiParameters();
10003
- init_concat();
10004
- init_toHex();
10005
- init_keccak256();
10006
-
10007
- // ../../node_modules/viem/_esm/utils/typedData.js
10008
- init_abi();
10009
- init_address();
10010
-
10011
- // ../../node_modules/viem/_esm/errors/typedData.js
10012
- init_stringify();
10013
- init_base();
10014
- var InvalidDomainError = class extends BaseError {
10015
- constructor({ domain }) {
10016
- super(`Invalid domain "${stringify(domain)}".`, {
10017
- metaMessages: ["Must be a valid EIP-712 domain."]
10018
- });
10103
+ };
10019
10104
  }
10020
- };
10021
- var InvalidPrimaryTypeError = class extends BaseError {
10022
- constructor({ primaryType, types }) {
10023
- super(`Invalid primary type \`${primaryType}\` must be one of \`${JSON.stringify(Object.keys(types))}\`.`, {
10024
- docsPath: "/api/glossary/Errors#typeddatainvalidprimarytypeerror",
10025
- metaMessages: ["Check that the primary type is a key in `types`."]
10026
- });
10105
+ /**
10106
+ * Send a message and wait for the complete response.
10107
+ * All tool calls and actions are executed automatically.
10108
+ */
10109
+ async ask(message) {
10110
+ this.responseParts = [];
10111
+ this.toolCalls = [];
10112
+ this.transactions = [];
10113
+ this.cards = [];
10114
+ this.warnings = [];
10115
+ this.outcome = void 0;
10116
+ this.error = void 0;
10117
+ this.errorIsTerminal = false;
10118
+ const callbacks = this.getCallbacks();
10119
+ await this.session.sendMessage(message, callbacks);
10120
+ return this.partialResult();
10027
10121
  }
10028
- };
10029
- var InvalidStructTypeError = class extends BaseError {
10030
- constructor({ type }) {
10031
- super(`Struct type "${type}" is invalid.`, {
10032
- metaMessages: ["Struct type must not be a Solidity type."],
10033
- name: "InvalidStructTypeError"
10034
- });
10122
+ /**
10123
+ * Snapshot of everything collected so far this turn. Identical to a normal
10124
+ * `ask()` return, but callable from a catch block when `ask()` THREW mid-turn
10125
+ * e.g. the follow-up request that reports recent_actions back to the backend
10126
+ * fails (timeout/5xx/auth) AFTER a tx has already broadcast and `onTxStatus`
10127
+ * fired. Lets the caller still surface the already-broadcast tx hash in the
10128
+ * error envelope instead of stranding funds the turn just moved.
10129
+ */
10130
+ partialResult() {
10131
+ return {
10132
+ sessionId: this.session.getConversationId() || "",
10133
+ response: this.responseParts[this.responseParts.length - 1] || "",
10134
+ toolCalls: this.toolCalls,
10135
+ transactions: this.transactions,
10136
+ cards: this.cards,
10137
+ warnings: this.warnings,
10138
+ error: this.error,
10139
+ ...this.outcome ? { outcome: this.outcome } : {}
10140
+ };
10035
10141
  }
10036
10142
  };
10037
10143
 
10038
- // ../../node_modules/viem/_esm/utils/typedData.js
10039
- init_isAddress();
10040
- init_size();
10041
- init_toHex();
10042
- init_regex();
10043
- function validateTypedData(parameters) {
10044
- const { domain, message, primaryType, types } = parameters;
10045
- const validateData = (struct, data) => {
10046
- for (const param of struct) {
10047
- const { name, type } = param;
10048
- const value = data[name];
10049
- const integerMatch = type.match(integerRegex);
10050
- if (integerMatch && (typeof value === "number" || typeof value === "bigint")) {
10051
- const [_type, base, size_] = integerMatch;
10052
- numberToHex(value, {
10053
- signed: base === "int",
10054
- size: Number.parseInt(size_, 10) / 8
10055
- });
10056
- }
10057
- if (type === "address" && typeof value === "string" && !isAddress(value))
10058
- throw new InvalidAddressError2({ address: value });
10059
- const bytesMatch = type.match(bytesRegex);
10060
- if (bytesMatch) {
10061
- const [_type, size_] = bytesMatch;
10062
- if (size_ && size(value) !== Number.parseInt(size_, 10))
10063
- throw new BytesSizeMismatchError({
10064
- expectedSize: Number.parseInt(size_, 10),
10065
- givenSize: size(value)
10066
- });
10144
+ // src/agent/auth.ts
10145
+ import { randomBytes as randomBytes3 } from "node:crypto";
10146
+ import { Chain as Chain10, computePersonalSignHash, formatEcdsaSignature65 } from "@vultisig/sdk";
10147
+ async function authenticateVault(client, vault, password, maxAttempts = 3) {
10148
+ const publicKey = vault.publicKeys.ecdsa;
10149
+ const chainCode = vault.hexChainCode;
10150
+ const ethAddress = await vault.address(Chain10.Ethereum);
10151
+ const nonce = "0x" + randomBytes3(16).toString("hex");
10152
+ const expiresAt = new Date(Date.now() + 15 * 60 * 1e3).toISOString();
10153
+ const authMessage = JSON.stringify({
10154
+ message: "Sign into Vultisig Plugin Marketplace",
10155
+ nonce,
10156
+ expiresAt,
10157
+ address: ethAddress
10158
+ });
10159
+ const messageHash = computePersonalSignHash(authMessage);
10160
+ let lastError = null;
10161
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
10162
+ try {
10163
+ if (attempt > 1) {
10164
+ process.stderr.write(` Retry ${attempt}/${maxAttempts}...
10165
+ `);
10067
10166
  }
10068
- const struct2 = types[type];
10069
- if (struct2) {
10070
- validateReference(type);
10071
- validateData(struct2, value);
10167
+ const signature = await vault.signBytes({ data: Buffer.from(messageHash), chain: Chain10.Ethereum }, {});
10168
+ if (signature.recovery === void 0) {
10169
+ throw new Error("Agent authentication requires an ECDSA recovery id");
10072
10170
  }
10073
- }
10074
- };
10075
- if (types.EIP712Domain && domain) {
10076
- if (typeof domain !== "object")
10077
- throw new InvalidDomainError({ domain });
10078
- validateData(types.EIP712Domain, domain);
10079
- }
10080
- if (primaryType !== "EIP712Domain") {
10081
- if (types[primaryType])
10082
- validateData(types[primaryType], message);
10083
- else
10084
- throw new InvalidPrimaryTypeError({ primaryType, types });
10085
- }
10086
- }
10087
- function getTypesForEIP712Domain({ domain }) {
10088
- return [
10089
- typeof domain?.name === "string" && { name: "name", type: "string" },
10090
- domain?.version && { name: "version", type: "string" },
10091
- (typeof domain?.chainId === "number" || typeof domain?.chainId === "bigint") && {
10092
- name: "chainId",
10093
- type: "uint256"
10094
- },
10095
- domain?.verifyingContract && {
10096
- name: "verifyingContract",
10097
- type: "address"
10098
- },
10099
- domain?.salt && { name: "salt", type: "bytes32" }
10100
- ].filter(Boolean);
10101
- }
10102
- function validateReference(type) {
10103
- if (type === "address" || type === "bool" || type === "string" || type.startsWith("bytes") || type.startsWith("uint") || type.startsWith("int"))
10104
- throw new InvalidStructTypeError({ type });
10171
+ const sigHex = formatEcdsaSignature65(signature.signature, signature.recovery);
10172
+ const authResponse = await client.authenticate({
10173
+ public_key: publicKey,
10174
+ chain_code_hex: chainCode,
10175
+ message: authMessage,
10176
+ signature: sigHex
10177
+ });
10178
+ return {
10179
+ token: authResponse.token,
10180
+ expiresAt: authResponse.expires_at,
10181
+ // Captured + persisted by the session token cache. The backend exposes
10182
+ // POST /auth/refresh to exchange it for a fresh access token without a
10183
+ // new MPC round; wiring that exchange is a future enhancement — today
10184
+ // the CLI re-auths via a full MPC re-sign (authenticateVault), which is
10185
+ // always available and avoids depending on refresh-token rotation.
10186
+ refreshToken: authResponse.refresh_token
10187
+ };
10188
+ } catch (err) {
10189
+ lastError = err;
10190
+ if (attempt < maxAttempts && err.message?.includes("timeout")) {
10191
+ continue;
10192
+ }
10193
+ throw err;
10194
+ }
10195
+ }
10196
+ throw lastError || new Error("Authentication failed after all attempts");
10105
10197
  }
10106
10198
 
10107
- // ../../node_modules/viem/_esm/utils/signature/hashTypedData.js
10108
- function hashTypedData(parameters) {
10109
- const { domain = {}, message, primaryType } = parameters;
10110
- const types = {
10111
- EIP712Domain: getTypesForEIP712Domain({ domain }),
10112
- ...parameters.types
10199
+ // src/agent/cards.ts
10200
+ import chalk8 from "chalk";
10201
+ var CLI_SUPPORTED_SURFACES = ["balance_summary", "turn_outcome"];
10202
+ function parseTurnOutcome(raw) {
10203
+ if (!raw || typeof raw !== "object") return null;
10204
+ const kind = raw.kind;
10205
+ if (kind !== "success" && kind !== "blocked" && kind !== "refusal" && kind !== "error") return null;
10206
+ const code = raw.code;
10207
+ const detail = raw.detail;
10208
+ return {
10209
+ kind,
10210
+ ...typeof code === "string" ? { code } : {},
10211
+ ...typeof detail === "string" ? { detail } : {}
10113
10212
  };
10114
- validateTypedData({
10115
- domain,
10116
- message,
10117
- primaryType,
10118
- types
10119
- });
10120
- const parts = ["0x1901"];
10121
- if (domain)
10122
- parts.push(hashDomain({
10123
- domain,
10124
- types
10125
- }));
10126
- if (primaryType !== "EIP712Domain")
10127
- parts.push(hashStruct({
10128
- data: message,
10129
- primaryType,
10130
- types
10131
- }));
10132
- return keccak256(concat(parts));
10133
10213
  }
10134
- function hashDomain({ domain, types }) {
10135
- return hashStruct({
10136
- data: domain,
10137
- primaryType: "EIP712Domain",
10138
- types
10139
- });
10214
+ function stripControlChars(s) {
10215
+ let out = "";
10216
+ for (const ch of s) {
10217
+ const code = ch.codePointAt(0) ?? 0;
10218
+ if (code <= 31 || code >= 127 && code <= 159) continue;
10219
+ out += ch;
10220
+ }
10221
+ return out;
10140
10222
  }
10141
- function hashStruct({ data, primaryType, types }) {
10142
- const encoded = encodeData({
10143
- data,
10144
- primaryType,
10145
- types
10146
- });
10147
- return keccak256(encoded);
10223
+ function asString(v) {
10224
+ return typeof v === "string" ? stripControlChars(v) : "";
10148
10225
  }
10149
- function encodeData({ data, primaryType, types }) {
10150
- const encodedTypes = [{ type: "bytes32" }];
10151
- const encodedValues = [hashType({ primaryType, types })];
10152
- for (const field of types[primaryType]) {
10153
- const [type, value] = encodeField({
10154
- types,
10155
- name: field.name,
10156
- type: field.type,
10157
- value: data[field.name]
10158
- });
10159
- encodedTypes.push(type);
10160
- encodedValues.push(value);
10161
- }
10162
- return encodeAbiParameters(encodedTypes, encodedValues);
10226
+ function parseToken(v) {
10227
+ if (!v || typeof v !== "object") return null;
10228
+ const o = v;
10229
+ const symbol = asString(o.symbol);
10230
+ const amountDecimal = asString(o.amountDecimal);
10231
+ if (!symbol && !amountDecimal) return null;
10232
+ const token = { symbol, amountDecimal };
10233
+ const amountUsd = asString(o.amountUsd);
10234
+ if (amountUsd) token.amountUsd = amountUsd;
10235
+ return token;
10163
10236
  }
10164
- function hashType({ primaryType, types }) {
10165
- const encodedHashType = toHex(encodeType({ primaryType, types }));
10166
- return keccak256(encodedHashType);
10237
+ function parseAccount(v) {
10238
+ if (!v || typeof v !== "object") return null;
10239
+ const o = v;
10240
+ const chainId = asString(o.chainId);
10241
+ if (!chainId) return null;
10242
+ const tokensRaw = Array.isArray(o.tokens) ? o.tokens : [];
10243
+ const tokens = tokensRaw.map(parseToken).filter((t) => t !== null);
10244
+ return { chainId, address: asString(o.address) || "\u2014", tokens };
10167
10245
  }
10168
- function encodeType({ primaryType, types }) {
10169
- let result = "";
10170
- const unsortedDeps = findTypeDependencies({ primaryType, types });
10171
- unsortedDeps.delete(primaryType);
10172
- const deps = [primaryType, ...Array.from(unsortedDeps).sort()];
10173
- for (const type of deps) {
10174
- result += `${type}(${types[type].map(({ name, type: t }) => `${t} ${name}`).join(",")})`;
10246
+ function parseBalanceSummaryEnvelope(value) {
10247
+ if (!value || typeof value !== "object") return null;
10248
+ const o = value;
10249
+ if (o.surface !== "balance_summary") return null;
10250
+ if (!Array.isArray(o.accounts)) return null;
10251
+ const accounts = o.accounts.map(parseAccount).filter((a) => a !== null);
10252
+ if (accounts.length === 0) return null;
10253
+ const card = { surface: "balance_summary", accounts };
10254
+ if (o.stale === true) {
10255
+ card.stale = true;
10256
+ if (typeof o.stale_secs === "number") card.staleSecs = o.stale_secs;
10175
10257
  }
10176
- return result;
10258
+ return card;
10177
10259
  }
10178
- function findTypeDependencies({ primaryType: primaryType_, types }, results = /* @__PURE__ */ new Set()) {
10179
- const match = primaryType_.match(/^\w*/u);
10180
- const primaryType = match?.[0];
10181
- if (results.has(primaryType) || types[primaryType] === void 0) {
10182
- return results;
10260
+ function matchBrace(text, start) {
10261
+ let depth = 0;
10262
+ let inString = false;
10263
+ let escaped = false;
10264
+ for (let i = start; i < text.length; i++) {
10265
+ const ch = text[i];
10266
+ if (inString) {
10267
+ if (escaped) escaped = false;
10268
+ else if (ch === "\\") escaped = true;
10269
+ else if (ch === '"') inString = false;
10270
+ continue;
10271
+ }
10272
+ if (ch === '"') inString = true;
10273
+ else if (ch === "{") depth++;
10274
+ else if (ch === "}") {
10275
+ depth--;
10276
+ if (depth === 0) return i;
10277
+ }
10183
10278
  }
10184
- results.add(primaryType);
10185
- for (const field of types[primaryType]) {
10186
- findTypeDependencies({ primaryType: field.type, types }, results);
10279
+ return -1;
10280
+ }
10281
+ function extractBalanceSummaryFromText(content) {
10282
+ if (!content || !content.includes("balance_summary")) return null;
10283
+ if (content.length > 2e5) return null;
10284
+ for (let i = content.indexOf("{"); i !== -1; i = content.indexOf("{", i + 1)) {
10285
+ const end = matchBrace(content, i);
10286
+ if (end === -1) break;
10287
+ const blob = content.slice(i, end + 1);
10288
+ if (!blob.includes("balance_summary")) continue;
10289
+ let parsed;
10290
+ try {
10291
+ parsed = JSON.parse(blob);
10292
+ } catch {
10293
+ continue;
10294
+ }
10295
+ const card = parseBalanceSummaryEnvelope(parsed);
10296
+ if (!card) continue;
10297
+ const before = content.slice(0, i).replace(/```(?:json)?\s*$/i, "");
10298
+ const after = content.slice(end + 1).replace(/^\s*```/, "");
10299
+ const remainingText = (before + after).trim();
10300
+ return { card, remainingText };
10187
10301
  }
10188
- return results;
10302
+ return null;
10189
10303
  }
10190
- function encodeField({ types, name, type, value }) {
10191
- if (types[type] !== void 0) {
10192
- return [
10193
- { type: "bytes32" },
10194
- keccak256(encodeData({ data: value, primaryType: type, types }))
10195
- ];
10304
+ function shortenAddress(address) {
10305
+ if (!address || address === "\u2014") return address || "\u2014";
10306
+ if (address.length <= 16) return address;
10307
+ return `${address.slice(0, 8)}\u2026${address.slice(-6)}`;
10308
+ }
10309
+ function parseUsd(amountUsd) {
10310
+ if (!amountUsd) return null;
10311
+ const cleaned = amountUsd.replace(/[$,\s]/g, "");
10312
+ if (!cleaned) return null;
10313
+ const n = Number(cleaned);
10314
+ return Number.isFinite(n) ? n : null;
10315
+ }
10316
+ function formatUsd(n) {
10317
+ return `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
10318
+ }
10319
+ function renderBalanceSummaryCard(card) {
10320
+ const lines = [];
10321
+ const staleCue = card.stale ? chalk8.gray(` (stale${card.staleSecs ? ` ~${Math.round(card.staleSecs / 60)}m` : ""}, refreshing\u2026)`) : "";
10322
+ lines.push(chalk8.bold(" Balances") + staleCue);
10323
+ let total = 0;
10324
+ let sawUsd = false;
10325
+ for (const account of card.accounts) {
10326
+ lines.push(` ${chalk8.cyan(account.chainId)} ${chalk8.gray(`(${shortenAddress(account.address)})`)}`);
10327
+ if (account.tokens.length === 0) {
10328
+ lines.push(chalk8.gray(" (no balances)"));
10329
+ continue;
10330
+ }
10331
+ for (const token of account.tokens) {
10332
+ const usd = parseUsd(token.amountUsd);
10333
+ if (usd !== null) {
10334
+ total += usd;
10335
+ sawUsd = true;
10336
+ }
10337
+ const symbol = token.symbol.padEnd(10);
10338
+ const amount = token.amountDecimal.padStart(16);
10339
+ const usdCol = token.amountUsd ? chalk8.gray(` ${token.amountUsd}`) : "";
10340
+ lines.push(` ${chalk8.bold(symbol)}${amount}${usdCol}`);
10341
+ }
10196
10342
  }
10197
- if (type === "bytes")
10198
- return [{ type: "bytes32" }, keccak256(value)];
10199
- if (type === "string")
10200
- return [{ type: "bytes32" }, keccak256(toHex(value))];
10201
- if (type.lastIndexOf("]") === type.length - 1) {
10202
- const parsedType = type.slice(0, type.lastIndexOf("["));
10203
- const typeValuePairs = value.map((item) => encodeField({
10204
- name,
10205
- type: parsedType,
10206
- types,
10207
- value: item
10208
- }));
10209
- return [
10210
- { type: "bytes32" },
10211
- keccak256(encodeAbiParameters(typeValuePairs.map(([t]) => t), typeValuePairs.map(([, v]) => v)))
10212
- ];
10343
+ if (sawUsd) {
10344
+ lines.push(chalk8.gray(" " + "\u2500".repeat(36)));
10345
+ lines.push(` ${chalk8.bold("Total")} ${chalk8.green(formatUsd(total))}`);
10213
10346
  }
10214
- return [{ type }, value];
10347
+ return lines.join("\n");
10215
10348
  }
10216
10349
 
10217
- // ../../node_modules/viem/_esm/index.js
10218
- init_formatUnits();
10350
+ // src/agent/client.ts
10351
+ import { randomUUID } from "node:crypto";
10352
+
10353
+ // src/agent/toolOutputSigning.ts
10354
+ import { getChainKind as getChainKind4 } from "@vultisig/sdk";
10355
+
10356
+ // src/agent/executor.ts
10357
+ import {
10358
+ Chain as Chain11,
10359
+ chainFeeCoin,
10360
+ getChainKind as getChainKind3,
10361
+ resolveChainReference,
10362
+ VaultError as VaultError3,
10363
+ VaultErrorCode as VaultErrorCode3,
10364
+ Vultisig as VultisigSdk
10365
+ } from "@vultisig/sdk";
10219
10366
 
10220
10367
  // src/core/VaultStateStore.ts
10221
10368
  import * as fs3 from "node:fs";
@@ -15132,7 +15279,7 @@ var cachedVersion = null;
15132
15279
  function getVersion() {
15133
15280
  if (cachedVersion) return cachedVersion;
15134
15281
  if (true) {
15135
- cachedVersion = "2.22.0";
15282
+ cachedVersion = "2.23.0";
15136
15283
  return cachedVersion;
15137
15284
  }
15138
15285
  try {
@@ -16529,7 +16676,7 @@ Error: ${error2.message}`));
16529
16676
  const [fromChainStr, toChainStr, amountStr, ...rest] = args;
16530
16677
  const fromChain = findChainByName(fromChainStr) || fromChainStr;
16531
16678
  const toChain = findChainByName(toChainStr) || toChainStr;
16532
- const amount = parseFloat(amountStr);
16679
+ const amount = amountStr;
16533
16680
  let fromToken;
16534
16681
  let toToken;
16535
16682
  for (let i = 0; i < rest.length; i++) {
@@ -16555,7 +16702,7 @@ Error: ${error2.message}`));
16555
16702
  const [fromChainStr, toChainStr, amountStr, ...rest] = args;
16556
16703
  const fromChain = findChainByName(fromChainStr) || fromChainStr;
16557
16704
  const toChain = findChainByName(toChainStr) || toChainStr;
16558
- const amount = parseFloat(amountStr);
16705
+ const amount = amountStr;
16559
16706
  let fromToken;
16560
16707
  let toToken;
16561
16708
  let slippage;
@@ -17558,7 +17705,7 @@ Examples:
17558
17705
  await executeSwapQuote(context, {
17559
17706
  fromChain,
17560
17707
  toChain,
17561
- amount: options.max ? "max" : parseFloat(amountStr),
17708
+ amount: options.max ? "max" : amountStr,
17562
17709
  fromToken: options.fromToken,
17563
17710
  toToken: options.toToken
17564
17711
  });
@@ -17583,7 +17730,7 @@ See also: swap-quote, swap-chains, balance`
17583
17730
  await executeSwap(context, {
17584
17731
  fromChain: findChainByName(fromChainStr) || fromChainStr,
17585
17732
  toChain: findChainByName(toChainStr) || toChainStr,
17586
- amount: options.max ? "max" : parseFloat(amountStr),
17733
+ amount: options.max ? "max" : amountStr,
17587
17734
  fromToken: options.fromToken,
17588
17735
  toToken: options.toToken,
17589
17736
  slippage: options.slippage ? parseFloat(options.slippage) : void 0,