@vultisig/cli 0.14.2 → 0.15.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.
- package/CHANGELOG.md +43 -0
- package/dist/index.js +218 -59
- package/package.json +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @vultisig/cli
|
|
2
2
|
|
|
3
|
+
## 0.15.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#263](https://github.com/vultisig/vultisig-sdk/pull/263) [`6585c38`](https://github.com/vultisig/vultisig-sdk/commit/6585c38431db063f600e133d1a23f84b7c19e934) Thanks [@rcoderdev](https://github.com/rcoderdev)! - fix(cli): align agent executor with backend payloads and harden action handling
|
|
8
|
+
- model `tx_ready` / non-streaming transaction payloads with `TxReadyPayload`
|
|
9
|
+
- optional `vultisig` on agent config for shared SDK state (e.g. address book)
|
|
10
|
+
- executor improvements (chain locks, calldata resolution, EVM gas refresh) and unit tests
|
|
11
|
+
|
|
12
|
+
- Updated dependencies [[`6585c38`](https://github.com/vultisig/vultisig-sdk/commit/6585c38431db063f600e133d1a23f84b7c19e934)]:
|
|
13
|
+
- @vultisig/sdk@0.15.2
|
|
14
|
+
- @vultisig/rujira@10.0.0
|
|
15
|
+
|
|
16
|
+
## 0.15.0
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- [#234](https://github.com/vultisig/vultisig-sdk/pull/234) [`9f71a0e`](https://github.com/vultisig/vultisig-sdk/commit/9f71a0e430aadcb96707448c5e5e077aa0b561e0) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Add Vitest for the CLI package and run CLI tests from the root `yarn test` script. Unimplemented agent actions now return `success: false` with an error message instead of `success: true` with a `data.message` field.
|
|
21
|
+
|
|
22
|
+
- [#235](https://github.com/vultisig/vultisig-sdk/pull/235) [`aea1c28`](https://github.com/vultisig/vultisig-sdk/commit/aea1c28051345ddef9c952108b203caa8b7fa032) Thanks [@rcoderdev](https://github.com/rcoderdev)! - ### Swap amounts (backward compatible)
|
|
23
|
+
- `SwapQuoteParams.amount` and `SwapTxParams.amount` now accept **`string | number`**. Call sites that already pass a **number** require no code changes.
|
|
24
|
+
- Human-readable swap amounts can be passed as **decimal strings** end-to-end (compound `vault.swap()`, `getSwapQuote`, `prepareSwapTx`, CLI agent), avoiding precision loss from `Number()` / `parseFloat()` on extreme magnitudes or fractional digits.
|
|
25
|
+
- `toChainAmount` accepts **`string | number`**; whitespace-only / empty strings throw instead of being treated as zero.
|
|
26
|
+
|
|
27
|
+
### Send preparation (stricter validation)
|
|
28
|
+
- `prepareSendTx` and `estimateSendFee` reject **zero or negative** `amount` in base units. This aligns with real transfers; payloads with `toAmount: "0"` are no longer built for native/token sends.
|
|
29
|
+
- **Zero-value EVM contract calls** are unchanged: use `prepareContractCallTx` (or `vault.contractCall()`), which still builds via the internal path that allows `value: 0n`.
|
|
30
|
+
|
|
31
|
+
### Other
|
|
32
|
+
- Swap approval sizing uses `toChainAmount` instead of float scaling for required allowance.
|
|
33
|
+
- `@vultisig/rujira` (source): `VultisigSignature.format` includes **`MLDSA`** to match SDK `Signature` — type-only widening, no runtime change; Rujira will pick up a **patch** version via normal dependency releases when published next.
|
|
34
|
+
- CLI: direct **`viem`** dependency; Solana local swap human amount via `formatUnits`; agent SSE `Transaction` typing includes optional `swap_tx` / `send_tx` / `tx`.
|
|
35
|
+
|
|
36
|
+
**Semver:** **Minor** for `@vultisig/core-chain`, `@vultisig/core-mpc`, and `@vultisig/sdk` (additive types + intentional validation tightening). **`@vultisig/cli` is linked to the SDK** in Changesets config, so it receives the same minor bump. This is **not** a SemVer **major** for integration purposes: swap inputs are only widened; `prepareSendTx({ amount: 0n })` was never a valid broadcast path.
|
|
37
|
+
|
|
38
|
+
**Release tooling note:** `yarn changeset status` may still propose a **major** version for `@vultisig/rujira` when the SDK minors, even though the only Rujira change is adding `'MLDSA'` to a string-literal union (fully backward compatible). Review the Version Packages PR and **downgrade Rujira to patch** if your policy is to reserve majors for real breaking API changes.
|
|
39
|
+
|
|
40
|
+
**`@vultisig/sdk` is 0.x:** per [SemVer](https://semver.org/#spec-item-4), minor releases on `0.y.z` may include behavior changes; consumers pinning `^0.14.0` should still accept `0.15.0` but should read changelog for validation tightening.
|
|
41
|
+
|
|
42
|
+
- Updated dependencies [[`9f71a0e`](https://github.com/vultisig/vultisig-sdk/commit/9f71a0e430aadcb96707448c5e5e077aa0b561e0), [`aea1c28`](https://github.com/vultisig/vultisig-sdk/commit/aea1c28051345ddef9c952108b203caa8b7fa032)]:
|
|
43
|
+
- @vultisig/sdk@0.15.0
|
|
44
|
+
- @vultisig/rujira@10.0.0
|
|
45
|
+
|
|
3
46
|
## 0.14.2
|
|
4
47
|
|
|
5
48
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -1430,10 +1430,29 @@ var init_sha3 = __esm({
|
|
|
1430
1430
|
}
|
|
1431
1431
|
});
|
|
1432
1432
|
|
|
1433
|
+
// node_modules/viem/_esm/utils/unit/formatUnits.js
|
|
1434
|
+
function formatUnits(value, decimals) {
|
|
1435
|
+
let display = value.toString();
|
|
1436
|
+
const negative = display.startsWith("-");
|
|
1437
|
+
if (negative)
|
|
1438
|
+
display = display.slice(1);
|
|
1439
|
+
display = display.padStart(decimals, "0");
|
|
1440
|
+
let [integer, fraction] = [
|
|
1441
|
+
display.slice(0, display.length - decimals),
|
|
1442
|
+
display.slice(display.length - decimals)
|
|
1443
|
+
];
|
|
1444
|
+
fraction = fraction.replace(/(0+)$/, "");
|
|
1445
|
+
return `${negative ? "-" : ""}${integer || "0"}${fraction ? `.${fraction}` : ""}`;
|
|
1446
|
+
}
|
|
1447
|
+
var init_formatUnits = __esm({
|
|
1448
|
+
"node_modules/viem/_esm/utils/unit/formatUnits.js"() {
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1433
1452
|
// src/index.ts
|
|
1434
1453
|
import "dotenv/config";
|
|
1435
1454
|
import { promises as fs4 } from "node:fs";
|
|
1436
|
-
import { parseKeygenQR, Vultisig as
|
|
1455
|
+
import { parseKeygenQR, Vultisig as Vultisig6 } from "@vultisig/sdk";
|
|
1437
1456
|
import chalk15 from "chalk";
|
|
1438
1457
|
import { program } from "commander";
|
|
1439
1458
|
import inquirer8 from "inquirer";
|
|
@@ -3915,7 +3934,12 @@ Address Book${options.chain ? ` (${options.chain})` : ""}:
|
|
|
3915
3934
|
}
|
|
3916
3935
|
|
|
3917
3936
|
// src/commands/rujira.ts
|
|
3918
|
-
import {
|
|
3937
|
+
import {
|
|
3938
|
+
getRoutesSummary,
|
|
3939
|
+
listEasyRoutes,
|
|
3940
|
+
RujiraClient,
|
|
3941
|
+
VultisigRujiraProvider
|
|
3942
|
+
} from "@vultisig/rujira";
|
|
3919
3943
|
async function createRujiraClient(ctx2, options = {}) {
|
|
3920
3944
|
const vault = await ctx2.ensureActiveVault();
|
|
3921
3945
|
const provider = new VultisigRujiraProvider(vault);
|
|
@@ -4547,8 +4571,11 @@ var AgentClient = class {
|
|
|
4547
4571
|
case "tx_ready":
|
|
4548
4572
|
if (this.verbose) process.stderr.write(`[SSE:tx_ready] raw: ${data.slice(0, 2e3)}
|
|
4549
4573
|
`);
|
|
4550
|
-
|
|
4551
|
-
|
|
4574
|
+
{
|
|
4575
|
+
const txReady = parsed;
|
|
4576
|
+
result.transactions.push(txReady);
|
|
4577
|
+
callbacks.onTxReady?.(txReady);
|
|
4578
|
+
}
|
|
4552
4579
|
break;
|
|
4553
4580
|
case "message":
|
|
4554
4581
|
result.message = parsed.message || parsed;
|
|
@@ -4763,7 +4790,10 @@ function getNativeTokenDecimals(chain) {
|
|
|
4763
4790
|
}
|
|
4764
4791
|
|
|
4765
4792
|
// src/agent/executor.ts
|
|
4766
|
-
import { Chain as Chain9, Vultisig as
|
|
4793
|
+
import { Chain as Chain9, evmCall, fiatCurrencies as fiatCurrencies3, Vultisig as VultisigSdk } from "@vultisig/sdk";
|
|
4794
|
+
|
|
4795
|
+
// node_modules/viem/_esm/index.js
|
|
4796
|
+
init_formatUnits();
|
|
4767
4797
|
|
|
4768
4798
|
// src/core/VaultStateStore.ts
|
|
4769
4799
|
import * as fs2 from "node:fs";
|
|
@@ -4977,6 +5007,8 @@ var EVM_GAS_RPC = {
|
|
|
4977
5007
|
};
|
|
4978
5008
|
var AgentExecutor = class {
|
|
4979
5009
|
vault;
|
|
5010
|
+
/** Owning SDK (optional); used for address book backed by app storage */
|
|
5011
|
+
vultisig;
|
|
4980
5012
|
pendingPayloads = /* @__PURE__ */ new Map();
|
|
4981
5013
|
password = null;
|
|
4982
5014
|
verbose;
|
|
@@ -4985,9 +5017,10 @@ var AgentExecutor = class {
|
|
|
4985
5017
|
chainLockReleases = /* @__PURE__ */ new Map();
|
|
4986
5018
|
/** Backend client for resolving calldata_id references. */
|
|
4987
5019
|
backendClient = null;
|
|
4988
|
-
constructor(vault, verbose = false, vaultId) {
|
|
5020
|
+
constructor(vault, verbose = false, vaultId, vultisig) {
|
|
4989
5021
|
this.vault = vault;
|
|
4990
5022
|
this.verbose = verbose;
|
|
5023
|
+
this.vultisig = vultisig;
|
|
4991
5024
|
if (vaultId) {
|
|
4992
5025
|
this.stateStore = new VaultStateStore(vaultId);
|
|
4993
5026
|
}
|
|
@@ -5001,6 +5034,8 @@ var AgentExecutor = class {
|
|
|
5001
5034
|
/**
|
|
5002
5035
|
* Store a server-built transaction (from tx_ready SSE event).
|
|
5003
5036
|
* This allows sign_tx to find and sign it when the backend requests signing.
|
|
5037
|
+
*
|
|
5038
|
+
* @returns true when a signable payload was stored; false for MCP errors or missing tx body
|
|
5004
5039
|
*/
|
|
5005
5040
|
storeServerTransaction(txReadyData) {
|
|
5006
5041
|
if (this.verbose)
|
|
@@ -5008,11 +5043,17 @@ var AgentExecutor = class {
|
|
|
5008
5043
|
`[executor] storeServerTransaction called, keys: ${Object.keys(txReadyData || {}).join(",")}
|
|
5009
5044
|
`
|
|
5010
5045
|
);
|
|
5011
|
-
const
|
|
5012
|
-
if (
|
|
5046
|
+
const nestedTx = txReadyData?.swap_tx || txReadyData?.send_tx || txReadyData?.tx;
|
|
5047
|
+
if (nestedTx?.status === "error" || nestedTx?.error) {
|
|
5048
|
+
if (this.verbose)
|
|
5049
|
+
process.stderr.write(`[executor] skipping error tx_ready: ${nestedTx.error || "unknown error"}
|
|
5050
|
+
`);
|
|
5051
|
+
return false;
|
|
5052
|
+
}
|
|
5053
|
+
if (!nestedTx) {
|
|
5013
5054
|
if (this.verbose) process.stderr.write(`[executor] storeServerTransaction: no swap_tx/send_tx/tx found in data
|
|
5014
5055
|
`);
|
|
5015
|
-
return;
|
|
5056
|
+
return false;
|
|
5016
5057
|
}
|
|
5017
5058
|
const chain = resolveChainFromTxReady(txReadyData) || Chain9.Ethereum;
|
|
5018
5059
|
this.pendingPayloads.clear();
|
|
@@ -5027,6 +5068,7 @@ var AgentExecutor = class {
|
|
|
5027
5068
|
`[executor] Stored server tx for chain ${chain}, pendingPayloads size=${this.pendingPayloads.size}
|
|
5028
5069
|
`
|
|
5029
5070
|
);
|
|
5071
|
+
return true;
|
|
5030
5072
|
}
|
|
5031
5073
|
hasPendingTransaction() {
|
|
5032
5074
|
return this.pendingPayloads.has("latest");
|
|
@@ -5085,7 +5127,7 @@ var AgentExecutor = class {
|
|
|
5085
5127
|
case "sign_tx":
|
|
5086
5128
|
return this.signTx(params);
|
|
5087
5129
|
case "get_address_book":
|
|
5088
|
-
return this.getAddressBook();
|
|
5130
|
+
return this.getAddressBook(params);
|
|
5089
5131
|
case "address_book_add":
|
|
5090
5132
|
return this.addAddressBookEntry(params);
|
|
5091
5133
|
case "address_book_remove":
|
|
@@ -5131,15 +5173,34 @@ var AgentExecutor = class {
|
|
|
5131
5173
|
}
|
|
5132
5174
|
return { balances: entries };
|
|
5133
5175
|
}
|
|
5134
|
-
async getPortfolio(
|
|
5135
|
-
const
|
|
5136
|
-
const
|
|
5137
|
-
|
|
5176
|
+
async getPortfolio(params) {
|
|
5177
|
+
const currencyRaw = String(params.currency ?? "USD").trim().toLowerCase();
|
|
5178
|
+
const fiatCurrency = fiatCurrencies3.includes(currencyRaw) ? currencyRaw : "usd";
|
|
5179
|
+
const portfolio = await this.vault.portfolio(fiatCurrency);
|
|
5180
|
+
const chainFilter = params.chain;
|
|
5181
|
+
const tickerFilter = params.ticker;
|
|
5182
|
+
let rows = portfolio.balances.map((b) => ({
|
|
5183
|
+
chain: b.chainId || "",
|
|
5138
5184
|
symbol: b.symbol || "",
|
|
5139
5185
|
amount: b.formattedAmount || b.amount?.toString() || "0",
|
|
5140
|
-
decimals: b.decimals
|
|
5186
|
+
decimals: b.decimals ?? 18,
|
|
5187
|
+
raw_amount: b.amount,
|
|
5188
|
+
fiatValue: b.fiatValue,
|
|
5189
|
+
fiatCurrency: b.fiatCurrency ?? portfolio.currency
|
|
5141
5190
|
}));
|
|
5142
|
-
|
|
5191
|
+
if (chainFilter) {
|
|
5192
|
+
const chain = resolveChain(chainFilter);
|
|
5193
|
+
if (!chain) throw new Error(`Unknown chain: ${chainFilter}`);
|
|
5194
|
+
rows = rows.filter((r) => r.chain.toLowerCase() === chain.toLowerCase());
|
|
5195
|
+
}
|
|
5196
|
+
if (tickerFilter) {
|
|
5197
|
+
rows = rows.filter((r) => r.symbol.toLowerCase() === String(tickerFilter).toLowerCase());
|
|
5198
|
+
}
|
|
5199
|
+
return {
|
|
5200
|
+
balances: rows,
|
|
5201
|
+
totalValue: portfolio.totalValue,
|
|
5202
|
+
currency: portfolio.currency
|
|
5203
|
+
};
|
|
5143
5204
|
}
|
|
5144
5205
|
// ============================================================================
|
|
5145
5206
|
// Chain & Token Management
|
|
@@ -5239,13 +5300,28 @@ var AgentExecutor = class {
|
|
|
5239
5300
|
};
|
|
5240
5301
|
const amount = parseAmount(amountStr, balance.decimals);
|
|
5241
5302
|
const memo = params.memo;
|
|
5242
|
-
const payload = await this.vault.prepareSendTx({
|
|
5303
|
+
const payload = await this.vault.prepareSendTx({
|
|
5304
|
+
coin,
|
|
5305
|
+
receiver: toAddress,
|
|
5306
|
+
amount,
|
|
5307
|
+
memo
|
|
5308
|
+
});
|
|
5243
5309
|
await this.patchEvmNonce(chain, payload);
|
|
5244
5310
|
const messageHashes = await this.vault.extractMessageHashes(payload);
|
|
5245
5311
|
this.pendingPayloads.clear();
|
|
5246
5312
|
const payloadId = `tx_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
5247
|
-
this.pendingPayloads.set(payloadId, {
|
|
5248
|
-
|
|
5313
|
+
this.pendingPayloads.set(payloadId, {
|
|
5314
|
+
payload,
|
|
5315
|
+
coin,
|
|
5316
|
+
chain,
|
|
5317
|
+
timestamp: Date.now()
|
|
5318
|
+
});
|
|
5319
|
+
this.pendingPayloads.set("latest", {
|
|
5320
|
+
payload,
|
|
5321
|
+
coin,
|
|
5322
|
+
chain,
|
|
5323
|
+
timestamp: Date.now()
|
|
5324
|
+
});
|
|
5249
5325
|
return {
|
|
5250
5326
|
keysign_payload: payloadId,
|
|
5251
5327
|
from_chain: chain.toString(),
|
|
@@ -5285,16 +5361,19 @@ var AgentExecutor = class {
|
|
|
5285
5361
|
const fromToken = params.from_contract || params.from_token_id;
|
|
5286
5362
|
const toToken = params.to_contract || params.to_token_id;
|
|
5287
5363
|
const fromCoin = { chain: fromChain, token: fromToken || void 0 };
|
|
5288
|
-
const toCoin = {
|
|
5364
|
+
const toCoin = {
|
|
5365
|
+
chain: toChain || fromChain,
|
|
5366
|
+
token: toToken || void 0
|
|
5367
|
+
};
|
|
5289
5368
|
const quote = await this.vault.getSwapQuote({
|
|
5290
5369
|
fromCoin,
|
|
5291
5370
|
toCoin,
|
|
5292
|
-
amount:
|
|
5371
|
+
amount: amountStr
|
|
5293
5372
|
});
|
|
5294
5373
|
const swapResult = await this.vault.prepareSwapTx({
|
|
5295
5374
|
fromCoin,
|
|
5296
5375
|
toCoin,
|
|
5297
|
-
amount:
|
|
5376
|
+
amount: amountStr,
|
|
5298
5377
|
swapQuote: quote,
|
|
5299
5378
|
autoApprove: true
|
|
5300
5379
|
});
|
|
@@ -5355,11 +5434,14 @@ var AgentExecutor = class {
|
|
|
5355
5434
|
chain: params.chain,
|
|
5356
5435
|
chain_id: params.chain_id
|
|
5357
5436
|
};
|
|
5358
|
-
this.storeServerTransaction({
|
|
5437
|
+
const stored = this.storeServerTransaction({
|
|
5359
5438
|
tx: txData,
|
|
5360
5439
|
chain: params.chain,
|
|
5361
5440
|
from_chain: params.chain
|
|
5362
5441
|
});
|
|
5442
|
+
if (!stored) {
|
|
5443
|
+
throw new Error("Could not stage calldata transaction for signing (invalid or empty tx payload)");
|
|
5444
|
+
}
|
|
5363
5445
|
const chain = resolveChain(params.chain) || Chain9.Ethereum;
|
|
5364
5446
|
const address = await this.vault.address(chain);
|
|
5365
5447
|
return {
|
|
@@ -5432,9 +5514,10 @@ var AgentExecutor = class {
|
|
|
5432
5514
|
}
|
|
5433
5515
|
const { payload, chain } = stored;
|
|
5434
5516
|
if (payload.__serverTx) {
|
|
5517
|
+
let result;
|
|
5435
5518
|
if (chain === "Solana" && (payload.swap_tx || payload.provider)) {
|
|
5436
5519
|
try {
|
|
5437
|
-
|
|
5520
|
+
result = await this.buildAndSignSolanaSwapLocally(payload);
|
|
5438
5521
|
} catch (e) {
|
|
5439
5522
|
if (e._phase === "prepare") {
|
|
5440
5523
|
if (this.verbose)
|
|
@@ -5445,7 +5528,9 @@ var AgentExecutor = class {
|
|
|
5445
5528
|
}
|
|
5446
5529
|
}
|
|
5447
5530
|
}
|
|
5448
|
-
|
|
5531
|
+
if (!result) result = await this.signServerTx(payload, chain, params);
|
|
5532
|
+
if (payload.sequence_id) result.sequence_id = payload.sequence_id;
|
|
5533
|
+
return result;
|
|
5449
5534
|
}
|
|
5450
5535
|
return this.signSdkTx(payload, chain, payloadId);
|
|
5451
5536
|
}
|
|
@@ -5481,7 +5566,7 @@ var AgentExecutor = class {
|
|
|
5481
5566
|
}
|
|
5482
5567
|
await this.releaseEvmLock(chain);
|
|
5483
5568
|
this.pendingPayloads.clear();
|
|
5484
|
-
const explorerUrl =
|
|
5569
|
+
const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, txHash);
|
|
5485
5570
|
return {
|
|
5486
5571
|
tx_hash: txHash,
|
|
5487
5572
|
chain: chain.toString(),
|
|
@@ -5578,7 +5663,7 @@ var AgentExecutor = class {
|
|
|
5578
5663
|
}
|
|
5579
5664
|
await this.releaseEvmLock(chain);
|
|
5580
5665
|
this.pendingPayloads.clear();
|
|
5581
|
-
const explorerUrl =
|
|
5666
|
+
const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, txHash);
|
|
5582
5667
|
return {
|
|
5583
5668
|
tx_hash: txHash,
|
|
5584
5669
|
chain: chain.toString(),
|
|
@@ -5595,12 +5680,23 @@ var AgentExecutor = class {
|
|
|
5595
5680
|
* Uses swap params from the tx_ready event to call vault.getSwapQuote → prepareSwapTx.
|
|
5596
5681
|
*/
|
|
5597
5682
|
async buildAndSignSolanaSwapLocally(serverTxData) {
|
|
5683
|
+
if (serverTxData._phase === "prepare") {
|
|
5684
|
+
throw Object.assign(new Error("tx_ready prepare phase: deferring to server sign path"), {
|
|
5685
|
+
_phase: "prepare"
|
|
5686
|
+
});
|
|
5687
|
+
}
|
|
5598
5688
|
const fromChainName = serverTxData.from_chain || serverTxData.chain || "Solana";
|
|
5599
5689
|
const toChainName = serverTxData.to_chain;
|
|
5600
5690
|
const fromChain = resolveChain(fromChainName);
|
|
5601
|
-
if (!fromChain)
|
|
5691
|
+
if (!fromChain)
|
|
5692
|
+
throw Object.assign(new Error(`Unknown from_chain: ${fromChainName}`), {
|
|
5693
|
+
_phase: "prepare"
|
|
5694
|
+
});
|
|
5602
5695
|
const toChain = toChainName ? resolveChain(toChainName) : fromChain;
|
|
5603
|
-
if (!toChain)
|
|
5696
|
+
if (!toChain)
|
|
5697
|
+
throw Object.assign(new Error(`Unknown to_chain: ${toChainName}`), {
|
|
5698
|
+
_phase: "prepare"
|
|
5699
|
+
});
|
|
5604
5700
|
const amountStr = serverTxData.amount;
|
|
5605
5701
|
if (!amountStr)
|
|
5606
5702
|
throw Object.assign(new Error("Missing amount in tx_ready data for local Solana swap build"), {
|
|
@@ -5615,10 +5711,17 @@ var AgentExecutor = class {
|
|
|
5615
5711
|
});
|
|
5616
5712
|
const fromCoin = { chain: fromChain, token: fromToken || void 0 };
|
|
5617
5713
|
const toCoin = { chain: toChain, token: toToken || void 0 };
|
|
5618
|
-
|
|
5714
|
+
let humanAmount;
|
|
5715
|
+
try {
|
|
5716
|
+
humanAmount = formatUnits(BigInt(amountStr), fromDecimals);
|
|
5717
|
+
} catch {
|
|
5718
|
+
throw Object.assign(new Error(`Invalid amount in tx_ready data for local Solana swap build: ${amountStr}`), {
|
|
5719
|
+
_phase: "prepare"
|
|
5720
|
+
});
|
|
5721
|
+
}
|
|
5619
5722
|
if (this.verbose)
|
|
5620
5723
|
process.stderr.write(
|
|
5621
|
-
`[solana_local_swap] from=${fromChainName} to=${toChainName || fromChainName} amount=${amountStr}
|
|
5724
|
+
`[solana_local_swap] from=${fromChainName} to=${toChainName || fromChainName} amount=${amountStr} human=${humanAmount}
|
|
5622
5725
|
`
|
|
5623
5726
|
);
|
|
5624
5727
|
if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
|
|
@@ -5660,7 +5763,7 @@ var AgentExecutor = class {
|
|
|
5660
5763
|
signature
|
|
5661
5764
|
});
|
|
5662
5765
|
this.pendingPayloads.clear();
|
|
5663
|
-
const explorerUrl =
|
|
5766
|
+
const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, txHash);
|
|
5664
5767
|
return {
|
|
5665
5768
|
tx_hash: txHash,
|
|
5666
5769
|
chain: chain.toString(),
|
|
@@ -5908,8 +6011,18 @@ var AgentExecutor = class {
|
|
|
5908
6011
|
// ============================================================================
|
|
5909
6012
|
// Address Book
|
|
5910
6013
|
// ============================================================================
|
|
5911
|
-
async getAddressBook() {
|
|
5912
|
-
|
|
6014
|
+
async getAddressBook(params) {
|
|
6015
|
+
if (!this.vultisig) {
|
|
6016
|
+
throw new Error(
|
|
6017
|
+
"get_address_book requires the CLI SDK instance. Ensure AgentConfig.vultisig is set when creating the session."
|
|
6018
|
+
);
|
|
6019
|
+
}
|
|
6020
|
+
const chainName = params.chain || params.chain_name;
|
|
6021
|
+
const chain = chainName ? resolveChain(chainName) : void 0;
|
|
6022
|
+
if (chainName && !chain) {
|
|
6023
|
+
throw new Error(`Unknown chain: ${chainName}`);
|
|
6024
|
+
}
|
|
6025
|
+
return await this.vultisig.getAddressBook(chain);
|
|
5913
6026
|
}
|
|
5914
6027
|
async addAddressBookEntry(_params) {
|
|
5915
6028
|
throw new Error("address_book_add is not yet implemented locally. The backend may handle this action server-side.");
|
|
@@ -5922,8 +6035,34 @@ var AgentExecutor = class {
|
|
|
5922
6035
|
// ============================================================================
|
|
5923
6036
|
// Token Search & Other
|
|
5924
6037
|
// ============================================================================
|
|
5925
|
-
async searchToken(
|
|
5926
|
-
|
|
6038
|
+
async searchToken(params) {
|
|
6039
|
+
const query = String(params.query ?? params.q ?? "").trim().toLowerCase();
|
|
6040
|
+
if (!query) {
|
|
6041
|
+
return { tokens: [] };
|
|
6042
|
+
}
|
|
6043
|
+
const limit = 20;
|
|
6044
|
+
const chainName = params.chain;
|
|
6045
|
+
const tokenMatchesQuery = (t) => {
|
|
6046
|
+
const tick = t.ticker.toLowerCase();
|
|
6047
|
+
const addr = (t.contractAddress ?? "").toLowerCase();
|
|
6048
|
+
const pid = (t.priceProviderId ?? "").toLowerCase();
|
|
6049
|
+
return tick.includes(query) || addr.includes(query) || pid.includes(query);
|
|
6050
|
+
};
|
|
6051
|
+
if (chainName) {
|
|
6052
|
+
const chain = resolveChain(chainName);
|
|
6053
|
+
if (!chain) throw new Error(`Unknown chain: ${chainName}`);
|
|
6054
|
+
const tokens = VultisigSdk.getKnownTokens(chain).filter(tokenMatchesQuery).slice(0, limit);
|
|
6055
|
+
return { tokens };
|
|
6056
|
+
}
|
|
6057
|
+
const out = [];
|
|
6058
|
+
for (const c of Object.values(Chain9)) {
|
|
6059
|
+
for (const t of VultisigSdk.getKnownTokens(c)) {
|
|
6060
|
+
if (!tokenMatchesQuery(t)) continue;
|
|
6061
|
+
out.push(t);
|
|
6062
|
+
if (out.length >= limit) return { tokens: out };
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
return { tokens: out };
|
|
5927
6066
|
}
|
|
5928
6067
|
async listVaults() {
|
|
5929
6068
|
return {
|
|
@@ -5940,8 +6079,25 @@ var AgentExecutor = class {
|
|
|
5940
6079
|
async scanTx(_params) {
|
|
5941
6080
|
throw new Error("scan_tx is not yet implemented locally. The backend may handle this action server-side.");
|
|
5942
6081
|
}
|
|
5943
|
-
async readEvmContract(
|
|
5944
|
-
|
|
6082
|
+
async readEvmContract(params) {
|
|
6083
|
+
const chainName = params.chain;
|
|
6084
|
+
if (!chainName) throw new Error("read_evm_contract requires chain");
|
|
6085
|
+
const contractRaw = params.contract_address || params.contractAddress;
|
|
6086
|
+
if (!contractRaw) throw new Error("read_evm_contract requires contract_address");
|
|
6087
|
+
const functionName = params.function_name || params.functionName;
|
|
6088
|
+
if (!functionName) throw new Error("read_evm_contract requires function_name");
|
|
6089
|
+
const chain = resolveChain(chainName);
|
|
6090
|
+
if (!chain) throw new Error(`Unknown chain: ${chainName}`);
|
|
6091
|
+
if (!EVM_CHAINS.has(chain)) {
|
|
6092
|
+
throw new Error(`read_evm_contract only supports EVM chains (got ${chain})`);
|
|
6093
|
+
}
|
|
6094
|
+
const callParams = params.params ?? [];
|
|
6095
|
+
const data = await encodeContractCall(functionName, callParams);
|
|
6096
|
+
const addr = contractRaw.startsWith("0x") ? contractRaw : `0x${contractRaw}`;
|
|
6097
|
+
const to = addr;
|
|
6098
|
+
const from = params.from;
|
|
6099
|
+
const result = await evmCall(chain, { to, data, from });
|
|
6100
|
+
return { result };
|
|
5945
6101
|
}
|
|
5946
6102
|
};
|
|
5947
6103
|
async function encodeContractCall(functionName, params) {
|
|
@@ -6215,7 +6371,10 @@ function parseDERSignature(sigHex) {
|
|
|
6215
6371
|
}
|
|
6216
6372
|
let offset = 0;
|
|
6217
6373
|
if (raw.slice(offset, offset + 2) !== "30") {
|
|
6218
|
-
return {
|
|
6374
|
+
return {
|
|
6375
|
+
r: raw.slice(0, 64).padStart(64, "0"),
|
|
6376
|
+
s: raw.slice(64).padStart(64, "0")
|
|
6377
|
+
};
|
|
6219
6378
|
}
|
|
6220
6379
|
offset += 2;
|
|
6221
6380
|
offset += 2;
|
|
@@ -6425,7 +6584,7 @@ var AgentSession = class {
|
|
|
6425
6584
|
this.config = config;
|
|
6426
6585
|
this.client = new AgentClient(config.backendUrl);
|
|
6427
6586
|
this.client.verbose = !!config.verbose;
|
|
6428
|
-
this.executor = new AgentExecutor(vault, !!config.verbose, vault.publicKeys.ecdsa);
|
|
6587
|
+
this.executor = new AgentExecutor(vault, !!config.verbose, vault.publicKeys.ecdsa, config.vultisig);
|
|
6429
6588
|
this.publicKey = vault.publicKeys.ecdsa;
|
|
6430
6589
|
if (config.password) {
|
|
6431
6590
|
this.executor.setPassword(config.password);
|
|
@@ -6582,6 +6741,7 @@ var AgentSession = class {
|
|
|
6582
6741
|
error: result.error || ""
|
|
6583
6742
|
};
|
|
6584
6743
|
}
|
|
6744
|
+
let serverTxStoredFromStream = 0;
|
|
6585
6745
|
const streamResult = await this.client.sendMessageStream(
|
|
6586
6746
|
this.conversationId,
|
|
6587
6747
|
request,
|
|
@@ -6602,16 +6762,11 @@ var AgentSession = class {
|
|
|
6602
6762
|
ui.onSuggestions(suggestions);
|
|
6603
6763
|
},
|
|
6604
6764
|
onTxReady: (tx) => {
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
if (this.config.
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
return;
|
|
6611
|
-
}
|
|
6612
|
-
this.executor.storeServerTransaction(tx);
|
|
6613
|
-
if (this.config.password) {
|
|
6614
|
-
this.executor.setPassword(this.config.password);
|
|
6765
|
+
if (this.executor.storeServerTransaction(tx)) {
|
|
6766
|
+
serverTxStoredFromStream++;
|
|
6767
|
+
if (this.config.password) {
|
|
6768
|
+
this.executor.setPassword(this.config.password);
|
|
6769
|
+
}
|
|
6615
6770
|
}
|
|
6616
6771
|
},
|
|
6617
6772
|
onMessage: (_msg) => {
|
|
@@ -6662,10 +6817,12 @@ var AgentSession = class {
|
|
|
6662
6817
|
return;
|
|
6663
6818
|
}
|
|
6664
6819
|
}
|
|
6665
|
-
if (
|
|
6820
|
+
if (serverTxStoredFromStream > 0) {
|
|
6666
6821
|
if (this.config.verbose)
|
|
6667
|
-
process.stderr.write(
|
|
6668
|
-
`
|
|
6822
|
+
process.stderr.write(
|
|
6823
|
+
`[session] ${serverTxStoredFromStream} stored server tx from tx_ready, signing client-side
|
|
6824
|
+
`
|
|
6825
|
+
);
|
|
6669
6826
|
const signAction = {
|
|
6670
6827
|
id: `tx_sign_${Date.now()}`,
|
|
6671
6828
|
type: "sign_tx",
|
|
@@ -7180,6 +7337,7 @@ async function executeAgent(ctx2, options) {
|
|
|
7180
7337
|
const config = {
|
|
7181
7338
|
backendUrl: options.backendUrl || process.env.VULTISIG_AGENT_URL || "https://abe.vultisig.com",
|
|
7182
7339
|
vaultName: vault.name,
|
|
7340
|
+
vultisig: ctx2.sdk,
|
|
7183
7341
|
password: options.password,
|
|
7184
7342
|
viaAgent: options.viaAgent,
|
|
7185
7343
|
sessionId: options.sessionId,
|
|
@@ -7221,6 +7379,7 @@ async function executeAgentAsk(ctx2, message, options) {
|
|
|
7221
7379
|
const config = {
|
|
7222
7380
|
backendUrl: options.backendUrl || process.env.VULTISIG_AGENT_URL || "https://abe.vultisig.com",
|
|
7223
7381
|
vaultName: vault.name,
|
|
7382
|
+
vultisig: ctx2.sdk,
|
|
7224
7383
|
password: options.password,
|
|
7225
7384
|
sessionId: options.session,
|
|
7226
7385
|
verbose: options.verbose,
|
|
@@ -7725,7 +7884,7 @@ var EventBuffer = class {
|
|
|
7725
7884
|
};
|
|
7726
7885
|
|
|
7727
7886
|
// src/interactive/session.ts
|
|
7728
|
-
import { fiatCurrencies as
|
|
7887
|
+
import { fiatCurrencies as fiatCurrencies4 } from "@vultisig/sdk";
|
|
7729
7888
|
import chalk12 from "chalk";
|
|
7730
7889
|
import ora3 from "ora";
|
|
7731
7890
|
import * as readline3 from "readline";
|
|
@@ -8543,9 +8702,9 @@ Error: ${error2.message}`));
|
|
|
8543
8702
|
i++;
|
|
8544
8703
|
}
|
|
8545
8704
|
}
|
|
8546
|
-
if (!
|
|
8705
|
+
if (!fiatCurrencies4.includes(currency)) {
|
|
8547
8706
|
console.log(chalk12.red(`Invalid currency: ${currency}`));
|
|
8548
|
-
console.log(chalk12.yellow(`Supported currencies: ${
|
|
8707
|
+
console.log(chalk12.yellow(`Supported currencies: ${fiatCurrencies4.join(", ")}`));
|
|
8549
8708
|
return;
|
|
8550
8709
|
}
|
|
8551
8710
|
const raw = args.includes("--raw");
|
|
@@ -8794,7 +8953,7 @@ var cachedVersion = null;
|
|
|
8794
8953
|
function getVersion() {
|
|
8795
8954
|
if (cachedVersion) return cachedVersion;
|
|
8796
8955
|
if (true) {
|
|
8797
|
-
cachedVersion = "0.
|
|
8956
|
+
cachedVersion = "0.15.2";
|
|
8798
8957
|
return cachedVersion;
|
|
8799
8958
|
}
|
|
8800
8959
|
try {
|
|
@@ -9289,7 +9448,7 @@ async function init(vaultOverride, unlockPassword, passwordTTL) {
|
|
|
9289
9448
|
}
|
|
9290
9449
|
const globalOptions = program.opts();
|
|
9291
9450
|
const serverEndpoints = resolveServerEndpoints(globalOptions);
|
|
9292
|
-
const sdk = new
|
|
9451
|
+
const sdk = new Vultisig6({
|
|
9293
9452
|
onPasswordRequired: createPasswordCallback(),
|
|
9294
9453
|
...serverEndpoints ? { serverEndpoints } : {},
|
|
9295
9454
|
...passwordTTL !== void 0 ? { passwordCache: { defaultTTL: passwordTTL } } : {}
|
|
@@ -9906,7 +10065,7 @@ program.command("update").description("Check for updates and show update command
|
|
|
9906
10065
|
setupCompletionCommand(program);
|
|
9907
10066
|
async function startInteractiveMode() {
|
|
9908
10067
|
const serverEndpoints = resolveServerEndpoints(parseServerEndpointOverridesFromArgv(process.argv.slice(2)));
|
|
9909
|
-
const sdk = new
|
|
10068
|
+
const sdk = new Vultisig6({
|
|
9910
10069
|
onPasswordRequired: createPasswordCallback(),
|
|
9911
10070
|
...serverEndpoints ? { serverEndpoints } : {}
|
|
9912
10071
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vultisig/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "Command-line wallet for Vultisig - multi-chain MPC wallet management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"cli:interactive": "npx tsx src/index.ts --interactive",
|
|
22
22
|
"repl": "npx tsx src/index.ts --interactive",
|
|
23
23
|
"typecheck": "tsc --noEmit",
|
|
24
|
+
"test": "vitest run",
|
|
24
25
|
"prepublishOnly": "npm run build"
|
|
25
26
|
},
|
|
26
27
|
"keywords": [
|
|
@@ -53,8 +54,8 @@
|
|
|
53
54
|
"@cosmjs/proto-signing": "^0.38.1",
|
|
54
55
|
"@cosmjs/stargate": "^0.38.1",
|
|
55
56
|
"@noble/hashes": "^2.0.1",
|
|
56
|
-
"@vultisig/rujira": "^
|
|
57
|
-
"@vultisig/sdk": "^0.
|
|
57
|
+
"@vultisig/rujira": "^10.0.0",
|
|
58
|
+
"@vultisig/sdk": "^0.15.3",
|
|
58
59
|
"chalk": "^5.6.2",
|
|
59
60
|
"cli-table3": "^0.6.5",
|
|
60
61
|
"commander": "^14.0.3",
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
"ora": "^9.3.0",
|
|
63
64
|
"qrcode-terminal": "^0.12.0",
|
|
64
65
|
"tabtab": "^3.0.2",
|
|
66
|
+
"viem": "^2.45.1",
|
|
65
67
|
"ws": "^8.19.0"
|
|
66
68
|
},
|
|
67
69
|
"devDependencies": {
|
|
@@ -71,7 +73,8 @@
|
|
|
71
73
|
"@types/ws": "^8.18.1",
|
|
72
74
|
"esbuild": "^0.27.4",
|
|
73
75
|
"tsx": "^4.21.0",
|
|
74
|
-
"typescript": "^5.9.3"
|
|
76
|
+
"typescript": "^5.9.3",
|
|
77
|
+
"vitest": "^3.2.4"
|
|
75
78
|
},
|
|
76
79
|
"engines": {
|
|
77
80
|
"node": ">=20.0.0"
|