@vultisig/cli 0.22.5 → 0.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 +27 -0
  2. package/dist/index.js +903 -31
  3. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @vultisig/cli
2
2
 
3
+ ## 0.23.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#464](https://github.com/vultisig/vultisig-sdk/pull/464) [`a6db82f`](https://github.com/vultisig/vultisig-sdk/commit/a6db82fd103ea8eea01a084cc8fbd787367db437) Thanks [@neavra](https://github.com/neavra)! - feat(sdk/vault): `signMsgDeposit` for THORChain/MayaChain LP add/remove; sdk-cli dispatches LP memos through it
8
+
9
+ Adds `vault.signMsgDeposit({chain, amountBaseUnits, memo})` to `VaultBase`, building a `THORChainDeposit` cosmos message via the existing keysign pipeline (passes `isDeposit: true` through `getChainSpecific`). Memo is opaque pass-through — LP add (`+:POOL[:PAIRED]`), LP remove (`-:POOL:BPS[:ASSET]`), and any future deposit-style intent flow through the same surface.
10
+
11
+ sdk-cli's `signNonEvmServerTx` now dispatches THORChain/MayaChain MsgDeposit envelopes by memo prefix: `=:` continues to route through `vault.swap` (Phase D), `+:` and `-:` route through the new `signThorMsgDepositLp` → `vault.signMsgDeposit`. Unsupported prefixes (LOAN, BOND, etc.) throw `NotImplemented` with the offending memo in the error message. Phase E in the envelope-parity progression; previously these memos threw at `parseThorSwapMemo`.
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [[`fde60dc`](https://github.com/vultisig/vultisig-sdk/commit/fde60dcc9f9822e21c2dbaeaacb9afb45cff0955), [`a6db82f`](https://github.com/vultisig/vultisig-sdk/commit/a6db82fd103ea8eea01a084cc8fbd787367db437)]:
16
+ - @vultisig/sdk@0.23.0
17
+ - @vultisig/client-shared@0.2.7
18
+ - @vultisig/rujira@18.0.0
19
+
20
+ ## 0.22.7
21
+
22
+ ### Patch Changes
23
+
24
+ - [#457](https://github.com/vultisig/vultisig-sdk/pull/457) [`680119e`](https://github.com/vultisig/vultisig-sdk/commit/680119e7392921b8aeaf859c85e811fb40a25054) Thanks [@rcoderdev](https://github.com/rcoderdev)! - Add regression tests and drift guards for Bitcoin PSBT compilation, ChainKind signing-input alignment, generated protobuf headers, and CLI agent action names aligned with AGENTS.md.
25
+
26
+ - Updated dependencies [[`680119e`](https://github.com/vultisig/vultisig-sdk/commit/680119e7392921b8aeaf859c85e811fb40a25054), [`5102976`](https://github.com/vultisig/vultisig-sdk/commit/5102976d7c13fa9578bbbc6e5122526cefc1ec66), [`b36eb62`](https://github.com/vultisig/vultisig-sdk/commit/b36eb62842051b8b2bae06f1e123a5ebcf6cad88)]:
27
+ - @vultisig/sdk@0.22.7
28
+ - @vultisig/core-chain@1.7.1
29
+
3
30
  ## 0.22.5
4
31
 
5
32
  ### Patch Changes
package/dist/index.js CHANGED
@@ -5395,8 +5395,399 @@ function getNativeTokenDecimals(chain) {
5395
5395
  return decimals[chain] || 18;
5396
5396
  }
5397
5397
 
5398
+ // ../../packages/core/chain/dist/Chain.js
5399
+ var EthereumL2Chain = {
5400
+ Arbitrum: "Arbitrum",
5401
+ Base: "Base",
5402
+ Blast: "Blast",
5403
+ Optimism: "Optimism",
5404
+ Zksync: "Zksync",
5405
+ Mantle: "Mantle"
5406
+ };
5407
+ var EvmChain = {
5408
+ ...EthereumL2Chain,
5409
+ Avalanche: "Avalanche",
5410
+ CronosChain: "CronosChain",
5411
+ BSC: "BSC",
5412
+ Ethereum: "Ethereum",
5413
+ Polygon: "Polygon",
5414
+ Hyperliquid: "Hyperliquid",
5415
+ Sei: "Sei"
5416
+ };
5417
+ var UtxoChain;
5418
+ (function(UtxoChain2) {
5419
+ UtxoChain2["Bitcoin"] = "Bitcoin";
5420
+ UtxoChain2["BitcoinCash"] = "Bitcoin-Cash";
5421
+ UtxoChain2["Litecoin"] = "Litecoin";
5422
+ UtxoChain2["Dogecoin"] = "Dogecoin";
5423
+ UtxoChain2["Dash"] = "Dash";
5424
+ UtxoChain2["Zcash"] = "Zcash";
5425
+ })(UtxoChain || (UtxoChain = {}));
5426
+ var cosmosChainsByKind = {
5427
+ ibcEnabled: {
5428
+ Cosmos: "Cosmos",
5429
+ Osmosis: "Osmosis",
5430
+ Dydx: "Dydx",
5431
+ Kujira: "Kujira",
5432
+ Terra: "Terra",
5433
+ TerraClassic: "TerraClassic",
5434
+ Noble: "Noble",
5435
+ Akash: "Akash"
5436
+ },
5437
+ vaultBased: {
5438
+ THORChain: "THORChain",
5439
+ MayaChain: "MayaChain"
5440
+ }
5441
+ };
5442
+ var IbcEnabledCosmosChain = cosmosChainsByKind.ibcEnabled;
5443
+ var VaultBasedCosmosChain = cosmosChainsByKind.vaultBased;
5444
+ var CosmosChain = {
5445
+ ...IbcEnabledCosmosChain,
5446
+ ...VaultBasedCosmosChain
5447
+ };
5448
+ var OtherChain;
5449
+ (function(OtherChain2) {
5450
+ OtherChain2["Sui"] = "Sui";
5451
+ OtherChain2["Solana"] = "Solana";
5452
+ OtherChain2["Polkadot"] = "Polkadot";
5453
+ OtherChain2["Bittensor"] = "Bittensor";
5454
+ OtherChain2["Ton"] = "Ton";
5455
+ OtherChain2["Ripple"] = "Ripple";
5456
+ OtherChain2["Tron"] = "Tron";
5457
+ OtherChain2["Cardano"] = "Cardano";
5458
+ OtherChain2["QBTC"] = "QBTC";
5459
+ })(OtherChain || (OtherChain = {}));
5460
+ var Chain9 = {
5461
+ ...EvmChain,
5462
+ ...UtxoChain,
5463
+ ...CosmosChain,
5464
+ ...OtherChain
5465
+ };
5466
+ var UtxoBasedChain = [
5467
+ ...Object.values(UtxoChain),
5468
+ OtherChain.Cardano
5469
+ ];
5470
+ var defaultChains = [
5471
+ Chain9.Bitcoin,
5472
+ Chain9.Ethereum,
5473
+ Chain9.THORChain,
5474
+ Chain9.Solana,
5475
+ Chain9.BSC
5476
+ ];
5477
+
5478
+ // ../../packages/core/chain/dist/ChainKind.js
5479
+ var chainKindRecord = {
5480
+ [EvmChain.Arbitrum]: "evm",
5481
+ [EvmChain.Avalanche]: "evm",
5482
+ [EvmChain.Base]: "evm",
5483
+ [EvmChain.CronosChain]: "evm",
5484
+ [EvmChain.BSC]: "evm",
5485
+ [EvmChain.Blast]: "evm",
5486
+ [EvmChain.Ethereum]: "evm",
5487
+ [EvmChain.Optimism]: "evm",
5488
+ [EvmChain.Polygon]: "evm",
5489
+ [EvmChain.Zksync]: "evm",
5490
+ [EvmChain.Mantle]: "evm",
5491
+ [EvmChain.Hyperliquid]: "evm",
5492
+ [EvmChain.Sei]: "evm",
5493
+ [UtxoChain.Bitcoin]: "utxo",
5494
+ [UtxoChain.BitcoinCash]: "utxo",
5495
+ [UtxoChain.Litecoin]: "utxo",
5496
+ [UtxoChain.Dogecoin]: "utxo",
5497
+ [UtxoChain.Dash]: "utxo",
5498
+ [UtxoChain.Zcash]: "utxo",
5499
+ [CosmosChain.THORChain]: "cosmos",
5500
+ [CosmosChain.Cosmos]: "cosmos",
5501
+ [CosmosChain.Osmosis]: "cosmos",
5502
+ [CosmosChain.MayaChain]: "cosmos",
5503
+ [CosmosChain.Dydx]: "cosmos",
5504
+ [CosmosChain.Kujira]: "cosmos",
5505
+ [CosmosChain.Terra]: "cosmos",
5506
+ [CosmosChain.TerraClassic]: "cosmos",
5507
+ [CosmosChain.Noble]: "cosmos",
5508
+ [CosmosChain.Akash]: "cosmos",
5509
+ [OtherChain.Sui]: "sui",
5510
+ [OtherChain.Solana]: "solana",
5511
+ [OtherChain.Polkadot]: "polkadot",
5512
+ [OtherChain.Bittensor]: "bittensor",
5513
+ [OtherChain.Ton]: "ton",
5514
+ [OtherChain.Ripple]: "ripple",
5515
+ [OtherChain.Tron]: "tron",
5516
+ [OtherChain.Cardano]: "cardano",
5517
+ [OtherChain.QBTC]: "qbtc"
5518
+ };
5519
+ function getChainKind(chain) {
5520
+ return chainKindRecord[chain];
5521
+ }
5522
+
5523
+ // ../../packages/lib/utils/dist/record/recordMap.js
5524
+ function recordMap(record, fn) {
5525
+ return Object.fromEntries(Object.entries(record).map(([key, value]) => [
5526
+ key,
5527
+ fn(value, key)
5528
+ ]));
5529
+ }
5530
+
5531
+ // ../../packages/lib/utils/dist/record/makeRecord/index.js
5532
+ var makeRecord = (keys, getValue) => {
5533
+ const record = {};
5534
+ keys.forEach((key, index) => {
5535
+ record[key] = getValue(key, index);
5536
+ });
5537
+ return record;
5538
+ };
5539
+
5540
+ // ../../packages/core/chain/dist/chains/cosmos/thor/kujira-merge/index.js
5541
+ var kujiraCoinsMigratedToThorChain = [
5542
+ "kuji",
5543
+ "rkuji",
5544
+ "fuzn",
5545
+ "nstk",
5546
+ "wink",
5547
+ "lvn"
5548
+ ];
5549
+ var kujiraCoinsMigratedToThorChainMetadata = {
5550
+ kuji: {
5551
+ ticker: "KUJI",
5552
+ logo: "kuji",
5553
+ priceProviderId: "kujira"
5554
+ },
5555
+ rkuji: {
5556
+ ticker: "rKUJI",
5557
+ logo: "rkuji.png",
5558
+ priceProviderId: "kujira"
5559
+ },
5560
+ fuzn: {
5561
+ ticker: "FUZN",
5562
+ logo: "fuzn.png",
5563
+ priceProviderId: "fuzion"
5564
+ },
5565
+ lvn: {
5566
+ ticker: "LVN",
5567
+ logo: "levana",
5568
+ priceProviderId: "levana-protocol"
5569
+ },
5570
+ nstk: {
5571
+ ticker: "NSTK",
5572
+ logo: "nstk.png",
5573
+ priceProviderId: "unstake-fi"
5574
+ },
5575
+ wink: {
5576
+ ticker: "WINK",
5577
+ logo: "wink.png",
5578
+ priceProviderId: "winkhub"
5579
+ }
5580
+ };
5581
+ var kujiraCoinMigratedToThorChainDestinationId = makeRecord(kujiraCoinsMigratedToThorChain, (id) => `thor.${id}`);
5582
+
5583
+ // ../../packages/core/chain/dist/coin/chainFeeCoin.js
5584
+ var ether = {
5585
+ ticker: "ETH",
5586
+ logo: "eth",
5587
+ decimals: 18,
5588
+ priceProviderId: "ethereum"
5589
+ };
5590
+ var leanChainFeeCoin = {
5591
+ [Chain9.Bitcoin]: {
5592
+ ticker: "BTC",
5593
+ logo: "btc",
5594
+ decimals: 8,
5595
+ priceProviderId: "bitcoin"
5596
+ },
5597
+ [Chain9.BitcoinCash]: {
5598
+ ticker: "BCH",
5599
+ logo: "bch",
5600
+ decimals: 8,
5601
+ priceProviderId: "bitcoin-cash"
5602
+ },
5603
+ [Chain9.Litecoin]: {
5604
+ ticker: "LTC",
5605
+ logo: "ltc",
5606
+ decimals: 8,
5607
+ priceProviderId: "litecoin"
5608
+ },
5609
+ [Chain9.Dogecoin]: {
5610
+ ticker: "DOGE",
5611
+ logo: "doge",
5612
+ decimals: 8,
5613
+ priceProviderId: "dogecoin"
5614
+ },
5615
+ [Chain9.Dash]: {
5616
+ ticker: "DASH",
5617
+ logo: "dash",
5618
+ decimals: 8,
5619
+ priceProviderId: "dash"
5620
+ },
5621
+ [Chain9.Ripple]: {
5622
+ ticker: "XRP",
5623
+ logo: "xrp",
5624
+ decimals: 6,
5625
+ priceProviderId: "ripple"
5626
+ },
5627
+ [Chain9.THORChain]: {
5628
+ ticker: "RUNE",
5629
+ logo: "rune",
5630
+ decimals: 8,
5631
+ priceProviderId: "thorchain"
5632
+ },
5633
+ [Chain9.MayaChain]: {
5634
+ ticker: "CACAO",
5635
+ logo: "cacao",
5636
+ decimals: 10,
5637
+ priceProviderId: "cacao"
5638
+ },
5639
+ [Chain9.Solana]: {
5640
+ ticker: "SOL",
5641
+ logo: "solana",
5642
+ decimals: 9,
5643
+ priceProviderId: "solana"
5644
+ },
5645
+ [Chain9.Ton]: {
5646
+ ticker: "TON",
5647
+ logo: "ton",
5648
+ decimals: 9,
5649
+ priceProviderId: "the-open-network"
5650
+ },
5651
+ [Chain9.Ethereum]: ether,
5652
+ [Chain9.Avalanche]: {
5653
+ ticker: "AVAX",
5654
+ logo: "avax",
5655
+ decimals: 18,
5656
+ priceProviderId: "avalanche-2"
5657
+ },
5658
+ [Chain9.BSC]: {
5659
+ ticker: "BNB",
5660
+ logo: "bsc",
5661
+ decimals: 18,
5662
+ priceProviderId: "binancecoin"
5663
+ },
5664
+ [Chain9.Polygon]: {
5665
+ ticker: "POL",
5666
+ logo: "polygon",
5667
+ decimals: 18,
5668
+ priceProviderId: "polygon-ecosystem-token"
5669
+ },
5670
+ [Chain9.CronosChain]: {
5671
+ ticker: "CRO",
5672
+ logo: "cro",
5673
+ decimals: 18,
5674
+ priceProviderId: "crypto-com-chain"
5675
+ },
5676
+ [Chain9.Dydx]: {
5677
+ ticker: "DYDX",
5678
+ logo: "dydx",
5679
+ decimals: 18,
5680
+ priceProviderId: "dydx-chain"
5681
+ },
5682
+ [Chain9.Kujira]: {
5683
+ ...kujiraCoinsMigratedToThorChainMetadata.kuji,
5684
+ decimals: 6
5685
+ },
5686
+ [Chain9.Terra]: {
5687
+ ticker: "LUNA",
5688
+ logo: "luna",
5689
+ decimals: 6,
5690
+ priceProviderId: "terra-luna-2"
5691
+ },
5692
+ [Chain9.TerraClassic]: {
5693
+ ticker: "LUNC",
5694
+ logo: "lunc",
5695
+ decimals: 6,
5696
+ priceProviderId: "terra-luna"
5697
+ },
5698
+ [Chain9.Sui]: {
5699
+ ticker: "SUI",
5700
+ logo: "sui",
5701
+ decimals: 9,
5702
+ priceProviderId: "sui"
5703
+ },
5704
+ [Chain9.Polkadot]: {
5705
+ ticker: "DOT",
5706
+ logo: "dot",
5707
+ decimals: 10,
5708
+ priceProviderId: "polkadot"
5709
+ },
5710
+ [Chain9.Bittensor]: {
5711
+ ticker: "TAO",
5712
+ logo: "bittensor",
5713
+ decimals: 9,
5714
+ priceProviderId: "bittensor"
5715
+ },
5716
+ [Chain9.Noble]: {
5717
+ ticker: "USDC",
5718
+ logo: "noble",
5719
+ decimals: 6,
5720
+ priceProviderId: "usd-coin"
5721
+ },
5722
+ [Chain9.Akash]: {
5723
+ ticker: "AKT",
5724
+ logo: "akash",
5725
+ decimals: 6,
5726
+ priceProviderId: "akash-network"
5727
+ },
5728
+ [Chain9.Cosmos]: {
5729
+ ticker: "ATOM",
5730
+ logo: "atom",
5731
+ decimals: 6,
5732
+ priceProviderId: "cosmos"
5733
+ },
5734
+ [Chain9.Osmosis]: {
5735
+ ticker: "OSMO",
5736
+ logo: "osmo",
5737
+ decimals: 6,
5738
+ priceProviderId: "osmosis"
5739
+ },
5740
+ [Chain9.Tron]: {
5741
+ ticker: "TRX",
5742
+ logo: "tron",
5743
+ decimals: 6,
5744
+ priceProviderId: "tron"
5745
+ },
5746
+ ...recordMap(EthereumL2Chain, () => ether),
5747
+ [Chain9.Zcash]: {
5748
+ ticker: "ZEC",
5749
+ logo: "zec",
5750
+ decimals: 8,
5751
+ priceProviderId: "zcash"
5752
+ },
5753
+ [Chain9.Cardano]: {
5754
+ ticker: "ADA",
5755
+ logo: "ada",
5756
+ decimals: 6,
5757
+ priceProviderId: "cardano"
5758
+ },
5759
+ [Chain9.Mantle]: {
5760
+ ticker: "MNT",
5761
+ logo: "mantle",
5762
+ decimals: 18,
5763
+ priceProviderId: "mantle"
5764
+ },
5765
+ [Chain9.Hyperliquid]: {
5766
+ ticker: "HYPE",
5767
+ logo: "hyperliquid",
5768
+ decimals: 18,
5769
+ priceProviderId: "hyperliquid"
5770
+ },
5771
+ [Chain9.Sei]: {
5772
+ ticker: "SEI",
5773
+ logo: "sei",
5774
+ decimals: 18,
5775
+ priceProviderId: "sei-network"
5776
+ },
5777
+ [Chain9.QBTC]: {
5778
+ ticker: "QBTC",
5779
+ logo: "qbtc",
5780
+ decimals: 6,
5781
+ priceProviderId: "qbtc-testnet"
5782
+ }
5783
+ };
5784
+ var chainFeeCoin = recordMap(leanChainFeeCoin, (coin, chain) => ({
5785
+ ...coin,
5786
+ chain
5787
+ }));
5788
+
5398
5789
  // src/agent/executor.ts
5399
- import { Chain as Chain9, Vultisig as VultisigSdk } from "@vultisig/sdk";
5790
+ import { Chain as Chain10, VaultError as VaultError3, VaultErrorCode as VaultErrorCode3, Vultisig as VultisigSdk } from "@vultisig/sdk";
5400
5791
 
5401
5792
  // ../../node_modules/viem/_esm/index.js
5402
5793
  init_formatUnits();
@@ -5557,6 +5948,45 @@ function sleep2(ms) {
5557
5948
  }
5558
5949
 
5559
5950
  // src/agent/executor.ts
5951
+ var THOR_MEMO_CHAIN_TO_ENUM = {
5952
+ BTC: Chain10.Bitcoin,
5953
+ ETH: Chain10.Ethereum,
5954
+ BSC: Chain10.BSC,
5955
+ AVAX: Chain10.Avalanche,
5956
+ BASE: Chain10.Base,
5957
+ // L2 — THORChain routinely quotes Base destinations (PR #439 review finding 1)
5958
+ ARB: Chain10.Arbitrum,
5959
+ // L1-via-bridge path (PR #439 review finding 1)
5960
+ BCH: Chain10.BitcoinCash,
5961
+ LTC: Chain10.Litecoin,
5962
+ DOGE: Chain10.Dogecoin,
5963
+ GAIA: Chain10.Cosmos,
5964
+ THOR: Chain10.THORChain,
5965
+ RUNE: Chain10.THORChain,
5966
+ XRP: Chain10.Ripple,
5967
+ DASH: Chain10.Dash,
5968
+ ZEC: Chain10.Zcash,
5969
+ MAYA: Chain10.MayaChain,
5970
+ CACAO: Chain10.MayaChain
5971
+ };
5972
+ var THOR_MEMO_ASSET_SHORTCUTS = {
5973
+ b: "BTC.BTC",
5974
+ e: "ETH.ETH",
5975
+ s: "BSC.BNB",
5976
+ a: "AVAX.AVAX",
5977
+ c: "BCH.BCH",
5978
+ l: "LTC.LTC",
5979
+ d: "DOGE.DOGE",
5980
+ g: "GAIA.ATOM",
5981
+ r: "THOR.RUNE",
5982
+ x: "XRP.XRP",
5983
+ cacao: "MAYA.CACAO",
5984
+ dash: "DASH.DASH",
5985
+ zec: "ZEC.ZEC"
5986
+ // BASE / ARB don't have documented single-letter shortcuts; THORChain
5987
+ // emits these as the full CHAIN.ASSET form in memos. Listed in
5988
+ // THOR_MEMO_CHAIN_TO_ENUM only.
5989
+ };
5560
5990
  var EVM_CHAINS = /* @__PURE__ */ new Set([
5561
5991
  "Ethereum",
5562
5992
  "BSC",
@@ -5592,6 +6022,12 @@ var AgentExecutor = class {
5592
6022
  /** Owning SDK (optional); used for address book backed by app storage */
5593
6023
  vultisig;
5594
6024
  pendingPayloads = /* @__PURE__ */ new Map();
6025
+ /**
6026
+ * Buffered legs for a 2-leg mcp-ts execute_* envelope (approve + main).
6027
+ * Populated by storeServerTransaction when both `approvalTxArgs` and
6028
+ * `txArgs` are present; consumed and cleared by signMultiLeg.
6029
+ */
6030
+ pendingLegs = [];
5595
6031
  password = null;
5596
6032
  verbose;
5597
6033
  stateStore = null;
@@ -5621,13 +6057,46 @@ var AgentExecutor = class {
5621
6057
  `[executor] storeServerTransaction called, keys: ${Object.keys(txReadyData || {}).join(",")}
5622
6058
  `
5623
6059
  );
5624
- if (txReadyData?.approvalTxArgs) {
5625
- if (this.verbose)
5626
- process.stderr.write(
5627
- `[executor] skipping multi-leg execute_swap envelope (approvalTxArgs present): not yet supported in sdk-cli \u2014 Phase B
6060
+ if (txReadyData?.approvalTxArgs && txReadyData?.txArgs) {
6061
+ const approvalChain = resolveChainFromTxReady(txReadyData.approvalTxArgs);
6062
+ const mainChain = resolveChainFromTxReady(txReadyData.txArgs);
6063
+ const parentChain = resolveChainFromTxReady(txReadyData);
6064
+ if (!approvalChain || !mainChain || approvalChain !== mainChain || parentChain && parentChain !== approvalChain) {
6065
+ if (this.verbose)
6066
+ process.stderr.write(
6067
+ `[executor] rejecting multi-leg envelope with inconsistent chain metadata: parent=${parentChain ?? "unresolved"} approval=${approvalChain ?? "unresolved"} main=${mainChain ?? "unresolved"}
5628
6068
  `
5629
- );
5630
- return false;
6069
+ );
6070
+ return false;
6071
+ }
6072
+ const chain2 = approvalChain;
6073
+ if (!EVM_CHAINS.has(chain2)) {
6074
+ if (this.verbose)
6075
+ process.stderr.write(
6076
+ `[executor] rejecting multi-leg envelope on non-EVM chain ${chain2}: signMultiLeg is EVM-only
6077
+ `
6078
+ );
6079
+ return false;
6080
+ }
6081
+ this.pendingLegs = [
6082
+ {
6083
+ txArgs: txReadyData.approvalTxArgs,
6084
+ parent: txReadyData,
6085
+ kind: "approve"
6086
+ },
6087
+ { txArgs: txReadyData.txArgs, parent: txReadyData, kind: "main" }
6088
+ ];
6089
+ this.pendingPayloads.clear();
6090
+ this.pendingPayloads.set("latest", {
6091
+ payload: { __serverTx: true, __multiLeg: true, ...txReadyData },
6092
+ coin: { chain: chain2, address: "", decimals: 18, ticker: "" },
6093
+ chain: chain2,
6094
+ timestamp: Date.now()
6095
+ });
6096
+ if (this.verbose)
6097
+ process.stderr.write(`[executor] stored multi-leg envelope: chain=${chain2}, legs=2 (approve, main)
6098
+ `);
6099
+ return true;
5631
6100
  }
5632
6101
  const nestedTx = extractNestedTx(txReadyData);
5633
6102
  if (nestedTx?.status === "error" || nestedTx?.error) {
@@ -5636,13 +6105,34 @@ var AgentExecutor = class {
5636
6105
  `);
5637
6106
  return false;
5638
6107
  }
6108
+ if (!nestedTx && txReadyData && typeof txReadyData === "object") {
6109
+ const txArgs = txReadyData.txArgs;
6110
+ if (txArgs && typeof txArgs === "object" && typeof txArgs.to === "string" && typeof txArgs.amount === "string") {
6111
+ const chain2 = resolveChainFromTxReady(txReadyData) || Chain10.Ethereum;
6112
+ if (getChainKind(chain2) !== "evm") {
6113
+ this.pendingPayloads.clear();
6114
+ this.pendingPayloads.set("latest", {
6115
+ payload: { __serverTx: true, ...txReadyData },
6116
+ coin: { chain: chain2, address: "", decimals: 18, ticker: "" },
6117
+ chain: chain2,
6118
+ timestamp: Date.now()
6119
+ });
6120
+ if (this.verbose)
6121
+ process.stderr.write(
6122
+ `[executor] Stored non-EVM server tx for chain ${chain2} (kind=${getChainKind(chain2)})
6123
+ `
6124
+ );
6125
+ return true;
6126
+ }
6127
+ }
6128
+ }
5639
6129
  if (!nestedTx) {
5640
6130
  if (this.verbose)
5641
6131
  process.stderr.write(`[executor] storeServerTransaction: no swap_tx/send_tx/tx/txArgs.tx found in data
5642
6132
  `);
5643
6133
  return false;
5644
6134
  }
5645
- const chain = resolveChainFromTxReady(txReadyData) || Chain9.Ethereum;
6135
+ const chain = resolveChainFromTxReady(txReadyData) || Chain10.Ethereum;
5646
6136
  this.pendingPayloads.clear();
5647
6137
  this.pendingPayloads.set("latest", {
5648
6138
  payload: { __serverTx: true, ...txReadyData },
@@ -5871,7 +6361,16 @@ var AgentExecutor = class {
5871
6361
  throw new Error("Pending transaction is not a server-built tx (no __serverTx flag).");
5872
6362
  }
5873
6363
  let result;
5874
- if (chain === "Solana" && (payload.swap_tx || payload.provider)) {
6364
+ if (payload.__multiLeg) {
6365
+ if (this.pendingLegs.length !== 2) {
6366
+ throw new VaultError3(
6367
+ VaultErrorCode3.InvalidConfig,
6368
+ `signMultiLeg: expected 2 pending legs, got ${this.pendingLegs.length}`
6369
+ );
6370
+ }
6371
+ result = await this.signMultiLeg(payload, chain, {});
6372
+ }
6373
+ if (!result && chain === "Solana" && (payload.swap_tx || payload.provider)) {
5875
6374
  try {
5876
6375
  result = await this.buildAndSignSolanaSwapLocally(payload);
5877
6376
  } catch (e) {
@@ -5890,10 +6389,223 @@ var AgentExecutor = class {
5890
6389
  });
5891
6390
  }
5892
6391
  /**
5893
- * Sign and broadcast a server-built transaction (raw EVM tx from tx_ready SSE).
5894
- * Uses vault.prepareSendTx with memo field to carry the calldata.
6392
+ * Dispatch a server-built tx_ready envelope to the chain-kind-specific
6393
+ * signer. EVM stays in `signEvmServerTx` (the existing PR #422 + PR #435
6394
+ * code, with EVM nonce/lock plumbing). Non-EVM kinds parse the envelope
6395
+ * via `parseNonEvmEnvelope` and route through `vault.send`, which is
6396
+ * already chain-agnostic via `VaultBase.prepareSendTx` virtuals.
6397
+ *
6398
+ * Phase D — see task `100526-sdk-cli-non-evm-signing.md`.
5895
6399
  */
5896
6400
  async signServerTx(serverTxData, defaultChain, params) {
6401
+ const chain = resolveChainFromTxReady(serverTxData) || defaultChain;
6402
+ const chainKind = getChainKind(chain);
6403
+ if (chainKind === "evm") {
6404
+ return this.signEvmServerTx(serverTxData, defaultChain, params);
6405
+ }
6406
+ return this.signNonEvmServerTx(serverTxData, chain);
6407
+ }
6408
+ /**
6409
+ * Non-EVM signing path: parse the agent's tx_ready envelope into a
6410
+ * `vault.send`-shaped argument bag and call through. The SDK already
6411
+ * handles per-chain prepare/sign/broadcast internally via
6412
+ * `VaultBase.prepareSendTx` virtuals — sdk-cli only owns envelope
6413
+ * parsing here, not chain-specific signing logic.
6414
+ *
6415
+ * THORChain / MayaChain MsgDeposit envelopes (msg_type='deposit',
6416
+ * to='') are routed through `vault.swap` because the agent's intent
6417
+ * is a swap — the memo (`=:CHAIN.ASSET:DEST::v0:slippage`) carries
6418
+ * the routing. We parse the memo to reconstruct vault.swap's
6419
+ * fromChain / fromSymbol / toChain / toSymbol / amount args. The SDK
6420
+ * then builds the MsgDeposit cosmos tx internally. Vultiagent uses an
6421
+ * equivalent custom helper (`buildSignBroadcastThorchainLpDeposit`);
6422
+ * we reuse the public `vault.swap` surface to avoid expanding the SDK.
6423
+ */
6424
+ async signNonEvmServerTx(serverTxData, chain) {
6425
+ if (this.vault.isEncrypted && !this.vault.isUnlocked?.()) {
6426
+ if (this.password) {
6427
+ await this.vault.unlock?.(this.password);
6428
+ }
6429
+ }
6430
+ const txArgs = serverTxData?.txArgs ?? {};
6431
+ if (typeof txArgs.chain === "string" && txArgs.chain !== chain) {
6432
+ throw new VaultError3(
6433
+ VaultErrorCode3.InvalidConfig,
6434
+ `signNonEvmServerTx: dispatcher chain '${chain}' disagrees with envelope chain '${txArgs.chain}'`
6435
+ );
6436
+ }
6437
+ if (txArgs.msg_type === "deposit" && (chain === Chain10.THORChain || chain === Chain10.MayaChain)) {
6438
+ const memo = typeof txArgs.memo === "string" ? txArgs.memo : "";
6439
+ if (memo.startsWith("=:")) {
6440
+ return this.signThorMsgDepositSwap(serverTxData, chain);
6441
+ }
6442
+ if (memo.startsWith("+:") || memo.startsWith("-:")) {
6443
+ return this.signThorMsgDepositLp(serverTxData, chain);
6444
+ }
6445
+ throw new VaultError3(
6446
+ VaultErrorCode3.NotImplemented,
6447
+ `signNonEvmServerTx: MsgDeposit memo prefix not supported on ${chain}: '${memo}'. Supported prefixes: '=:' (swap), '+:' (LP add), '-:' (LP remove). Loan / validator ops are out of scope.`
6448
+ );
6449
+ }
6450
+ const args = parseNonEvmEnvelope(serverTxData, chain);
6451
+ if (this.verbose)
6452
+ process.stderr.write(
6453
+ `[sign_non_evm_server_tx] chain=${chain}, to=${args.to}, amount=${args.amount}${args.symbol ? ` ${args.symbol}` : ""}, memo=${args.memo ? `"${args.memo}"` : "(none)"}
6454
+ `
6455
+ );
6456
+ const result = await this.vault.send({
6457
+ chain,
6458
+ to: args.to,
6459
+ amount: args.amount,
6460
+ symbol: args.symbol,
6461
+ memo: args.memo
6462
+ });
6463
+ if (result.dryRun) {
6464
+ throw new VaultError3(
6465
+ VaultErrorCode3.InvalidConfig,
6466
+ "signNonEvmServerTx: vault.send unexpectedly returned dryRun result"
6467
+ );
6468
+ }
6469
+ this.pendingPayloads.clear();
6470
+ const broadcast = result;
6471
+ const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, broadcast.txHash);
6472
+ return {
6473
+ tx_hash: broadcast.txHash,
6474
+ chain: chain.toString(),
6475
+ status: "pending",
6476
+ explorer_url: explorerUrl
6477
+ };
6478
+ }
6479
+ /**
6480
+ * Sign and broadcast a THORChain / MayaChain MsgDeposit-style swap
6481
+ * envelope by reconstructing `vault.swap` args from the memo.
6482
+ *
6483
+ * The agent emits envelopes shaped:
6484
+ * { txArgs: { chain: 'THORChain', tx_encoding: 'cosmos-msg',
6485
+ * to: '', amount: '<base>', denom: 'rune',
6486
+ * memo: '=:DEST_CHAIN.DEST_ASSET:DEST_ADDR::v0:slippage_bps',
6487
+ * msg_type: 'deposit' } }
6488
+ *
6489
+ * The memo is THORChain's standard swap memo. We parse out the
6490
+ * destination chain + asset, look up the corresponding `Chain` enum,
6491
+ * then call `vault.swap` which builds the MsgDeposit internally.
6492
+ *
6493
+ * IMPORTANT — destination address handling: `vault.swap` re-derives the
6494
+ * destination address from `vault.address(toChain)` when fetching the
6495
+ * native swap quote (see `findSwapQuote` → `getNativeSwapQuote` —
6496
+ * `destination: to.address`). It does NOT honor the destination address
6497
+ * encoded in the envelope's memo. As a fund-safety guard we therefore
6498
+ * require the memo's destination address to match the vault's own
6499
+ * destination address (self-swap) and throw otherwise — see Phase D
6500
+ * review F1. Cross-account routing must wait on a Phase E SDK extension
6501
+ * that lets `vault.swap` accept a user-supplied destination.
6502
+ */
6503
+ async signThorMsgDepositSwap(serverTxData, chain) {
6504
+ const txArgs = serverTxData?.txArgs ?? {};
6505
+ const memo = typeof txArgs.memo === "string" ? txArgs.memo : "";
6506
+ const parsed = parseThorSwapMemo(memo);
6507
+ const toChain = THOR_MEMO_CHAIN_TO_ENUM[parsed.destChainCode];
6508
+ if (!toChain) {
6509
+ throw new VaultError3(
6510
+ VaultErrorCode3.UnsupportedChain,
6511
+ `signThorMsgDepositSwap: unsupported destination chain code '${parsed.destChainCode}' in memo '${memo}'.`
6512
+ );
6513
+ }
6514
+ const vaultDestAddress = await this.vault.address(toChain);
6515
+ const normalizeForCompare = (addr) => EVM_CHAINS.has(toChain) ? addr.toLowerCase() : addr;
6516
+ if (parsed.destAddress && normalizeForCompare(parsed.destAddress) !== normalizeForCompare(vaultDestAddress)) {
6517
+ throw new VaultError3(
6518
+ VaultErrorCode3.NotImplemented,
6519
+ `signThorMsgDepositSwap: memo destination '${parsed.destAddress}' does not match vault address '${vaultDestAddress}' on ${toChain}. Phase D only supports self-swaps; cross-account routing requires a Phase E SDK extension that passes the user-supplied destination through to vault.swap.`
6520
+ );
6521
+ }
6522
+ const fromSymbol = chain === Chain10.THORChain ? "RUNE" : "CACAO";
6523
+ const amountRaw = typeof txArgs.amount === "string" ? txArgs.amount : void 0;
6524
+ if (!amountRaw) {
6525
+ throw new VaultError3(
6526
+ VaultErrorCode3.InvalidConfig,
6527
+ `signThorMsgDepositSwap: missing or non-string 'amount' field on ${chain} envelope`
6528
+ );
6529
+ }
6530
+ const amountDecimal = convertBaseUnitsToDecimal(chain, amountRaw, "signThorMsgDepositSwap");
6531
+ if (this.verbose)
6532
+ process.stderr.write(
6533
+ `[sign_thor_msg_deposit_swap] ${fromSymbol}@${chain} \u2192 ${parsed.destAsset}@${toChain}, amount=${amountDecimal}, memo='${memo}'
6534
+ `
6535
+ );
6536
+ const result = await this.vault.swap({
6537
+ fromChain: chain,
6538
+ fromSymbol,
6539
+ toChain,
6540
+ toSymbol: parsed.destAsset,
6541
+ amount: amountDecimal
6542
+ });
6543
+ if (result.dryRun) {
6544
+ throw new VaultError3(
6545
+ VaultErrorCode3.InvalidConfig,
6546
+ "signThorMsgDepositSwap: vault.swap unexpectedly returned dryRun result"
6547
+ );
6548
+ }
6549
+ this.pendingPayloads.clear();
6550
+ const broadcast = result;
6551
+ const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, broadcast.txHash);
6552
+ return {
6553
+ tx_hash: broadcast.txHash,
6554
+ chain: chain.toString(),
6555
+ status: "pending",
6556
+ explorer_url: explorerUrl
6557
+ };
6558
+ }
6559
+ /**
6560
+ * Sign and broadcast a THORChain / MayaChain MsgDeposit envelope whose
6561
+ * memo is an LP add (`+:POOL[:PAIRED]`) or remove (`-:POOL:BPS[:ASSET]`).
6562
+ *
6563
+ * The agent emits the same `cosmos-msg / msg_type: deposit` envelope as
6564
+ * the swap path; only the memo prefix differs. The memo is opaque
6565
+ * pass-through — sdk-cli doesn't parse pool / paired address / bps
6566
+ * because the SDK doesn't need to, the on-chain handler does.
6567
+ *
6568
+ * Uses `vault.signMsgDeposit` which builds a THORChainDeposit cosmos
6569
+ * message via the SDK's keysign payload pipeline. Amount is consumed
6570
+ * as base units directly (no decimal conversion) since the agent
6571
+ * already emits RUNE / CACAO in base units.
6572
+ */
6573
+ async signThorMsgDepositLp(serverTxData, chain) {
6574
+ const txArgs = serverTxData?.txArgs ?? {};
6575
+ const memo = typeof txArgs.memo === "string" ? txArgs.memo : "";
6576
+ const amountRaw = typeof txArgs.amount === "string" ? txArgs.amount : void 0;
6577
+ if (!amountRaw) {
6578
+ throw new VaultError3(
6579
+ VaultErrorCode3.InvalidConfig,
6580
+ `signThorMsgDepositLp: missing or non-string 'amount' field on ${chain} envelope`
6581
+ );
6582
+ }
6583
+ if (amountRaw.length > MAX_AMOUNT_DIGITS) {
6584
+ throw new VaultError3(
6585
+ VaultErrorCode3.InvalidAmount,
6586
+ `signThorMsgDepositLp: amount '${amountRaw}' for ${chain} exceeds ${MAX_AMOUNT_DIGITS}-digit safety bound. Likely a quote-side bug. Refusing to sign.`
6587
+ );
6588
+ }
6589
+ if (this.verbose)
6590
+ process.stderr.write(`[sign_thor_msg_deposit_lp] chain=${chain}, memo='${memo}', amountBaseUnits=${amountRaw}
6591
+ `);
6592
+ const result = await this.vault.signMsgDeposit({ chain, amountBaseUnits: amountRaw, memo });
6593
+ this.pendingPayloads.clear();
6594
+ const explorerUrl = VultisigSdk.getTxExplorerUrl(chain, result.txHash);
6595
+ return {
6596
+ tx_hash: result.txHash,
6597
+ chain: chain.toString(),
6598
+ status: "pending",
6599
+ explorer_url: explorerUrl
6600
+ };
6601
+ }
6602
+ /**
6603
+ * Sign and broadcast a server-built EVM transaction (raw EVM tx from
6604
+ * tx_ready SSE). Uses vault.prepareSendTx with memo field to carry the
6605
+ * calldata, plus EVM-specific nonce/lock plumbing that Phase B's
6606
+ * `signMultiLeg` depends on for back-to-back approve+main broadcasts.
6607
+ */
6608
+ async signEvmServerTx(serverTxData, defaultChain, params) {
5897
6609
  const swapTx = extractNestedTx(serverTxData);
5898
6610
  if (!swapTx?.to) {
5899
6611
  throw new Error("Server transaction missing required fields (to)");
@@ -5987,6 +6699,97 @@ var AgentExecutor = class {
5987
6699
  throw err;
5988
6700
  }
5989
6701
  }
6702
+ /**
6703
+ * Sign and broadcast a 2-leg ERC-20 approve + main flow originating from
6704
+ * mcp-ts `execute_*` envelopes that carry both `approvalTxArgs` and
6705
+ * `txArgs`. Mirrors vultiagent's `useTransactionFlow`: leg 1 (approve) is
6706
+ * signed and broadcast first, the receipt is awaited, then leg 2 (main)
6707
+ * is signed and broadcast. Fails closed if the approve doesn't confirm
6708
+ * — the main leg is NEVER broadcast against a stale or failed allowance.
6709
+ *
6710
+ * Phase B is intentionally EVM-only; non-EVM 2-leg flows are not a real
6711
+ * shape on mcp-ts today (Pattern 1 / Pattern 2 multi-leg flows are split
6712
+ * server-side via sequence_id and don't traverse this path).
6713
+ */
6714
+ async signMultiLeg(_payload, chain, params) {
6715
+ const [approveLeg, mainLeg] = this.pendingLegs;
6716
+ try {
6717
+ const approveEnvelope = {
6718
+ ...approveLeg.parent,
6719
+ txArgs: approveLeg.txArgs,
6720
+ approvalTxArgs: void 0,
6721
+ __multiLeg: void 0,
6722
+ swap_tx: void 0,
6723
+ send_tx: void 0,
6724
+ tx: void 0
6725
+ };
6726
+ const approveResult = await this.signServerTx(approveEnvelope, chain, params);
6727
+ const approveTxHash = approveResult.tx_hash;
6728
+ if (!approveTxHash) {
6729
+ throw new VaultError3(VaultErrorCode3.BroadcastFailed, "signMultiLeg: approve leg returned no tx_hash");
6730
+ }
6731
+ if (this.verbose)
6732
+ process.stderr.write(`[signMultiLeg] approve broadcast: ${approveTxHash}, waiting for receipt...
6733
+ `);
6734
+ try {
6735
+ await this.waitForEvmReceipt(chain, approveTxHash, { timeoutSec: 90 });
6736
+ } catch (err) {
6737
+ throw new VaultError3(
6738
+ VaultErrorCode3.Timeout,
6739
+ `signMultiLeg: approve leg ${approveTxHash} did not confirm: ${err?.message ?? err}`,
6740
+ err instanceof Error ? err : void 0
6741
+ );
6742
+ }
6743
+ if (this.verbose) process.stderr.write(`[signMultiLeg] approve confirmed, broadcasting main leg
6744
+ `);
6745
+ const mainEnvelope = {
6746
+ ...mainLeg.parent,
6747
+ txArgs: mainLeg.txArgs,
6748
+ approvalTxArgs: void 0,
6749
+ __multiLeg: void 0,
6750
+ swap_tx: void 0,
6751
+ send_tx: void 0,
6752
+ tx: void 0
6753
+ };
6754
+ const mainResult = await this.signServerTx(mainEnvelope, chain, params);
6755
+ return {
6756
+ tx_hash: mainResult.tx_hash,
6757
+ approval_tx_hash: approveTxHash,
6758
+ chain: mainResult.chain,
6759
+ status: "pending",
6760
+ explorer_url: mainResult.explorer_url
6761
+ };
6762
+ } finally {
6763
+ this.pendingLegs = [];
6764
+ }
6765
+ }
6766
+ /**
6767
+ * Poll vault.getTxStatus until the EVM tx confirms or the timeout fires.
6768
+ * Mirrors VaultBase's private `waitForConfirmation` (used by `vault.swap`
6769
+ * for its own approve-before-swap flow) — kept at the executor layer here
6770
+ * so we can stub it from unit tests without exposing private SDK methods.
6771
+ *
6772
+ * Throws on timeout or on receipt status === 'error' (revert). Returns on
6773
+ * success.
6774
+ */
6775
+ async waitForEvmReceipt(chain, txHash, opts) {
6776
+ const intervalMs = 3e3;
6777
+ const deadline = Date.now() + opts.timeoutSec * 1e3;
6778
+ while (Date.now() < deadline) {
6779
+ try {
6780
+ const result = await this.vault.getTxStatus({ chain, txHash });
6781
+ if (result?.status === "success") return;
6782
+ if (result?.status === "error") {
6783
+ throw new VaultError3(VaultErrorCode3.BroadcastFailed, `approve tx reverted (${txHash})`);
6784
+ }
6785
+ } catch (e) {
6786
+ if (e instanceof VaultError3 && e.code === VaultErrorCode3.BroadcastFailed) throw e;
6787
+ if (e?.message?.includes("reverted")) throw e;
6788
+ }
6789
+ await new Promise((r) => setTimeout(r, intervalMs));
6790
+ }
6791
+ throw new VaultError3(VaultErrorCode3.Timeout, `approve tx ${txHash} not confirmed within ${opts.timeoutSec}s`);
6792
+ }
5990
6793
  /**
5991
6794
  * Build, sign, and broadcast a Solana swap locally using the SDK's swap flow.
5992
6795
  * Uses swap params from the tx_ready event to call vault.getSwapQuote → prepareSwapTx.
@@ -6305,11 +7108,11 @@ var AgentExecutor = class {
6305
7108
  `);
6306
7109
  const chainName = params.chain;
6307
7110
  const chainId = domain.chainId;
6308
- let chain = Chain9.Ethereum;
7111
+ let chain = Chain10.Ethereum;
6309
7112
  if (chainName) {
6310
- chain = resolveChain(chainName) || Chain9.Ethereum;
7113
+ chain = resolveChain(chainName) || Chain10.Ethereum;
6311
7114
  } else if (chainId) {
6312
- chain = resolveChainId(chainId) || Chain9.Ethereum;
7115
+ chain = resolveChainId(chainId) || Chain10.Ethereum;
6313
7116
  }
6314
7117
  const sigResult = await this.vault.signBytes({
6315
7118
  data: eip712Hash,
@@ -6414,11 +7217,11 @@ var AgentExecutor = class {
6414
7217
  };
6415
7218
  function resolveChain(name) {
6416
7219
  if (!name) return null;
6417
- if (Object.values(Chain9).includes(name)) {
7220
+ if (Object.values(Chain10).includes(name)) {
6418
7221
  return name;
6419
7222
  }
6420
7223
  const lower = name.toLowerCase();
6421
- for (const [, value] of Object.entries(Chain9)) {
7224
+ for (const [, value] of Object.entries(Chain10)) {
6422
7225
  if (typeof value === "string" && value.toLowerCase() === lower) {
6423
7226
  return value;
6424
7227
  }
@@ -6444,7 +7247,7 @@ function resolveChain(name) {
6444
7247
  xrp: "Ripple"
6445
7248
  };
6446
7249
  const aliased = aliases[lower];
6447
- if (aliased && Object.values(Chain9).includes(aliased)) {
7250
+ if (aliased && Object.values(Chain10).includes(aliased)) {
6448
7251
  return aliased;
6449
7252
  }
6450
7253
  return null;
@@ -6480,20 +7283,89 @@ function resolveChainFromTxReady(txReadyData) {
6480
7283
  function extractNestedTx(txReadyData) {
6481
7284
  return txReadyData?.swap_tx || txReadyData?.send_tx || txReadyData?.tx || txReadyData?.txArgs?.tx;
6482
7285
  }
7286
+ var MAX_AMOUNT_DIGITS = 26;
7287
+ function convertBaseUnitsToDecimal(chain, amountRaw, context) {
7288
+ if (amountRaw.length > MAX_AMOUNT_DIGITS) {
7289
+ throw new VaultError3(
7290
+ VaultErrorCode3.InvalidAmount,
7291
+ `${context}: amount '${amountRaw}' for ${chain} exceeds ${MAX_AMOUNT_DIGITS}-digit safety bound. Likely a quote-side bug. Refusing to sign.`
7292
+ );
7293
+ }
7294
+ const decimals = chainFeeCoin[chain]?.decimals;
7295
+ if (decimals === void 0) {
7296
+ throw new VaultError3(VaultErrorCode3.UnsupportedChain, `${context}: no native decimals registered for ${chain}`);
7297
+ }
7298
+ try {
7299
+ return formatUnits(BigInt(amountRaw), decimals);
7300
+ } catch (err) {
7301
+ throw new VaultError3(
7302
+ VaultErrorCode3.InvalidAmount,
7303
+ `${context}: failed to convert amount '${amountRaw}' for ${chain}: ${err?.message ?? err}`
7304
+ );
7305
+ }
7306
+ }
7307
+ function parseNonEvmEnvelope(serverTxData, chain) {
7308
+ const txArgs = serverTxData?.txArgs ?? serverTxData;
7309
+ if (!txArgs || typeof txArgs !== "object") {
7310
+ throw new VaultError3(VaultErrorCode3.InvalidConfig, "parseNonEvmEnvelope: envelope missing txArgs");
7311
+ }
7312
+ const to = typeof txArgs.to === "string" ? txArgs.to : void 0;
7313
+ if (!to) {
7314
+ throw new VaultError3(VaultErrorCode3.InvalidConfig, `parseNonEvmEnvelope: missing 'to' field for ${chain}`);
7315
+ }
7316
+ const amountRaw = typeof txArgs.amount === "string" ? txArgs.amount : void 0;
7317
+ if (!amountRaw) {
7318
+ throw new VaultError3(VaultErrorCode3.InvalidConfig, `parseNonEvmEnvelope: missing 'amount' field for ${chain}`);
7319
+ }
7320
+ const amountDecimal = convertBaseUnitsToDecimal(chain, amountRaw, "parseNonEvmEnvelope");
7321
+ let symbol;
7322
+ const tokenResolved = serverTxData?.resolved?.labels?.token_resolved;
7323
+ const nativeTicker = chainFeeCoin[chain]?.ticker;
7324
+ if (typeof tokenResolved === "string" && tokenResolved !== nativeTicker) {
7325
+ symbol = tokenResolved;
7326
+ }
7327
+ const memo = typeof txArgs.memo === "string" && txArgs.memo.length > 0 ? txArgs.memo : void 0;
7328
+ return { chain, to, amount: amountDecimal, symbol, memo };
7329
+ }
7330
+ function parseThorSwapMemo(memo) {
7331
+ if (!memo.startsWith("=:")) {
7332
+ throw new VaultError3(
7333
+ VaultErrorCode3.NotImplemented,
7334
+ `parseThorSwapMemo: only swap memos (=:CHAIN.ASSET:DEST...) supported on this path; got memo='${memo}'. LP memos (+:/-:) route through signThorMsgDepositLp; loan / validator ops out of scope.`
7335
+ );
7336
+ }
7337
+ const memoBody = memo.slice(2);
7338
+ const parts = memoBody.split(":");
7339
+ let chainAsset = parts[0];
7340
+ if (chainAsset && !chainAsset.includes(".")) {
7341
+ const expanded = THOR_MEMO_ASSET_SHORTCUTS[chainAsset.toLowerCase()];
7342
+ if (expanded) chainAsset = expanded;
7343
+ }
7344
+ if (!chainAsset || !chainAsset.includes(".")) {
7345
+ throw new VaultError3(
7346
+ VaultErrorCode3.InvalidConfig,
7347
+ `parseThorSwapMemo: malformed swap memo '${memo}': missing CHAIN.ASSET in first segment.`
7348
+ );
7349
+ }
7350
+ const [destChainCode, destAssetRaw] = chainAsset.split(".");
7351
+ const destAsset = destAssetRaw?.split("-")[0] ?? "";
7352
+ const destAddress = typeof parts[1] === "string" ? parts[1] : "";
7353
+ return { destChainCode, destAsset, destAddress };
7354
+ }
6483
7355
  function resolveChainId(chainId) {
6484
7356
  const id = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
6485
7357
  if (isNaN(id)) return null;
6486
7358
  const chainIdMap = {
6487
- 1: Chain9.Ethereum,
6488
- 56: Chain9.BSC,
6489
- 137: Chain9.Polygon,
6490
- 43114: Chain9.Avalanche,
6491
- 42161: Chain9.Arbitrum,
6492
- 10: Chain9.Optimism,
6493
- 8453: Chain9.Base,
6494
- 81457: Chain9.Blast,
6495
- 324: Chain9.Zksync,
6496
- 25: Chain9.CronosChain
7359
+ 1: Chain10.Ethereum,
7360
+ 56: Chain10.BSC,
7361
+ 137: Chain10.Polygon,
7362
+ 43114: Chain10.Avalanche,
7363
+ 42161: Chain10.Arbitrum,
7364
+ 10: Chain10.Optimism,
7365
+ 8453: Chain10.Base,
7366
+ 81457: Chain10.Blast,
7367
+ 324: Chain10.Zksync,
7368
+ 25: Chain10.CronosChain
6497
7369
  };
6498
7370
  return chainIdMap[id] || null;
6499
7371
  }
@@ -7858,7 +8730,7 @@ var cachedVersion = null;
7858
8730
  function getVersion() {
7859
8731
  if (cachedVersion) return cachedVersion;
7860
8732
  if (true) {
7861
- cachedVersion = "0.22.5";
8733
+ cachedVersion = "0.23.0";
7862
8734
  return cachedVersion;
7863
8735
  }
7864
8736
  try {
@@ -8141,7 +9013,7 @@ function readArgValue(args, optionName) {
8141
9013
  }
8142
9014
 
8143
9015
  // src/interactive/completer.ts
8144
- import { Chain as Chain10 } from "@vultisig/sdk";
9016
+ import { Chain as Chain11 } from "@vultisig/sdk";
8145
9017
  import fs3 from "fs";
8146
9018
  import path3 from "path";
8147
9019
  var COMMANDS = [
@@ -8285,7 +9157,7 @@ function completeVaultName(ctx2, partial) {
8285
9157
  return [show, partial];
8286
9158
  }
8287
9159
  function completeChainName(partial) {
8288
- const allChains = Object.values(Chain10);
9160
+ const allChains = Object.values(Chain11);
8289
9161
  const partialLower = partial.toLowerCase();
8290
9162
  const matches = allChains.filter((chain) => chain.toLowerCase().startsWith(partialLower));
8291
9163
  matches.sort();
@@ -8293,7 +9165,7 @@ function completeChainName(partial) {
8293
9165
  return [show, partial];
8294
9166
  }
8295
9167
  function findChainByName(name) {
8296
- const allChains = Object.values(Chain10);
9168
+ const allChains = Object.values(Chain11);
8297
9169
  const nameLower = name.toLowerCase();
8298
9170
  const found = allChains.find((chain) => chain.toLowerCase() === nameLower);
8299
9171
  return found ? found : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/cli",
3
- "version": "0.22.5",
3
+ "version": "0.23.0",
4
4
  "description": "The self-custody MPC wallet CLI for AI coding agents (Claude Code, Cursor, OpenCode). Natural-language agent mode, 36+ chains, DKLS23 threshold signatures. Seedless.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -73,10 +73,10 @@
73
73
  "@cosmjs/stargate": "^0.38.1",
74
74
  "@napi-rs/keyring": "^1.3.0",
75
75
  "@noble/hashes": "^2.0.1",
76
- "@vultisig/client-shared": "^0.2.6",
77
- "@vultisig/core-chain": "^1.6.1",
78
- "@vultisig/rujira": "^17.0.1",
79
- "@vultisig/sdk": "^0.22.5",
76
+ "@vultisig/client-shared": "^0.2.7",
77
+ "@vultisig/core-chain": "^1.7.1",
78
+ "@vultisig/rujira": "^18.0.0",
79
+ "@vultisig/sdk": "^0.23.0",
80
80
  "chalk": "^5.6.2",
81
81
  "cli-table3": "^0.6.5",
82
82
  "commander": "^14.0.3",