@moly-mcp/lido 1.2.0 → 1.2.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/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # @moly-mcp/lido
2
+
3
+ Terminal assistant for managing Lido staking positions. Stake ETH, manage stETH/wstETH, track withdrawals, vote on governance, bridge from L2, and monitor your position with alerts.
4
+
5
+ ## Install
6
+
7
+ ```
8
+ npm i -g @moly-mcp/lido
9
+ ```
10
+
11
+ Or run directly:
12
+
13
+ ```
14
+ npx @moly-mcp/lido
15
+ ```
16
+
17
+ ## Setup
18
+
19
+ First run launches an interactive wizard that configures your wallet, network, and RPC.
20
+
21
+ ```
22
+ moly setup # re-run wizard
23
+ moly config # view current config (keys redacted)
24
+ moly reset # wipe config and start fresh
25
+ ```
26
+
27
+ Config is saved to `~/.moly/config.json` (chmod 600).
28
+
29
+ ## Commands
30
+
31
+ ### Position
32
+
33
+ ```
34
+ moly position # cross-chain summary (ETH + Base + Arbitrum)
35
+ moly position 0xAddress # specific address
36
+ ```
37
+
38
+ ### Bounds (Policy Limits)
39
+
40
+ Human-set guardrails enforced before any write operation.
41
+
42
+ ```
43
+ moly bounds # show current bounds
44
+ moly bounds set --max-stake-per-tx 1.0 --max-daily-stake 10 --min-eth-reserve 0.5
45
+ moly bounds reset # restore defaults
46
+ ```
47
+
48
+ Defaults: max 10 ETH/tx, 50 ETH/day, 0.5 ETH reserve, auto-vote off.
49
+
50
+ ### Alerts
51
+
52
+ ```
53
+ moly alert add balance_below 1.0
54
+ moly alert add withdrawal_ready
55
+ moly alert add reward_rate_below 0.01
56
+ moly alert add proposal_new
57
+ moly alert add balance_below 5.0 --channel webhook
58
+ moly alert list
59
+ moly alert remove <id>
60
+ moly alert channels --telegram-token <token> --telegram-chat <chat-id>
61
+ moly alert channels --webhook-url https://example.com/hook
62
+ ```
63
+
64
+ Alert types: `balance_below`, `balance_above`, `reward_rate_below`, `reward_rate_above`, `withdrawal_ready`, `proposal_new`, `conversion_rate_above`, `conversion_rate_below`, `reward_delta`, `governance_expiring`.
65
+
66
+ ### Monitor
67
+
68
+ Background daemon that checks alert conditions every 30s.
69
+
70
+ ```
71
+ moly monitor start
72
+ moly monitor status
73
+ moly monitor stop
74
+ ```
75
+
76
+ ### Ledger (Activity Log)
77
+
78
+ ```
79
+ moly ledger list # recent entries
80
+ moly ledger list --tool stake_eth --limit 10
81
+ moly ledger list --since 2025-01-01
82
+ moly ledger stats
83
+ moly ledger export --format json
84
+ moly ledger export --format csv
85
+ ```
86
+
87
+ ## MCP Server
88
+
89
+ Moly also runs as an MCP server exposing 28 tools for staking, withdrawals, wrapping, governance, bridging, alerts, and position tracking.
90
+
91
+ ```
92
+ moly --server
93
+ ```
94
+
95
+ All write operations support `dry_run` and are gated by your configured bounds.
96
+
97
+ ## Requirements
98
+
99
+ Node.js >= 18
100
+
101
+ ## License
102
+
103
+ MIT
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getBalance,
4
+ getRewards
5
+ } from "./chunk-2UMD5GE7.js";
6
+ import "./chunk-2MF5MDUT.js";
7
+ import "./chunk-P6VFMSPM.js";
8
+ import "./chunk-PDX44BCA.js";
9
+ export {
10
+ getBalance,
11
+ getRewards
12
+ };
package/dist/bin.js CHANGED
@@ -352,7 +352,7 @@ async function main() {
352
352
  case "setup": {
353
353
  const { cfg, terminalMode } = await runWizard();
354
354
  if (terminalMode) {
355
- const { startChatSession } = await import("./session-NTWLPEVH.js");
355
+ const { startChatSession } = await import("./session-FKRDTPLS.js");
356
356
  await startChatSession(cfg);
357
357
  } else {
358
358
  await startServer();
@@ -432,7 +432,7 @@ async function main() {
432
432
  console.log("No config. Run: moly setup");
433
433
  process.exit(1);
434
434
  }
435
- const { runDaemon } = await import("./daemon-FNV7KTYR.js");
435
+ const { runDaemon } = await import("./daemon-DAY6WJCJ.js");
436
436
  await runDaemon();
437
437
  break;
438
438
  }
@@ -590,7 +590,7 @@ async function main() {
590
590
  console.log("No config. Run: moly setup");
591
591
  process.exit(1);
592
592
  }
593
- const { getTotalPosition } = await import("./position-PZ363YFM.js");
593
+ const { getTotalPosition } = await import("./position-IA7ADOHU.js");
594
594
  const address = args[1];
595
595
  const pos = await getTotalPosition(address);
596
596
  console.log(JSON.stringify(pos, null, 2));
@@ -601,14 +601,14 @@ async function main() {
601
601
  if (!configExists()) {
602
602
  const { cfg, terminalMode } = await runWizard();
603
603
  if (terminalMode) {
604
- const { startChatSession } = await import("./session-NTWLPEVH.js");
604
+ const { startChatSession } = await import("./session-FKRDTPLS.js");
605
605
  await startChatSession(cfg);
606
606
  } else {
607
607
  await startServer();
608
608
  }
609
609
  } else {
610
610
  const cfg = loadConfig();
611
- const { startChatSession } = await import("./session-NTWLPEVH.js");
611
+ const { startChatSession } = await import("./session-FKRDTPLS.js");
612
612
  await startChatSession(cfg);
613
613
  }
614
614
  break;
@@ -620,39 +620,54 @@ async function main() {
620
620
  console.log(`
621
621
  moly \u2014 Lido MCP Server CLI
622
622
 
623
- Commands:
624
- setup Run the setup wizard
625
- config Show current configuration
626
- reset Delete configuration
627
- terminal Start interactive chat session
628
- --server Start MCP server (used in AI client configs)
623
+ Setup:
624
+ setup Run the setup wizard
625
+ config Show current configuration
626
+ reset Delete configuration
627
+ wallet Show wallet address
629
628
 
630
- alert Manage price/balance alerts
631
- add <cond> Add alert (e.g. "eth_below 2000")
632
- list List active alerts
633
- remove <id> Remove alert by ID
634
- channels Configure alert channels
635
- start Start alert daemon in background
629
+ Staking:
630
+ balance [addr] Get ETH, stETH, wstETH balances
631
+ rewards [addr] [d] Get staking rewards (default 7 days)
632
+ stake <amount> Stake ETH to receive stETH
633
+ withdraw <amount> Request stETH withdrawal
634
+ withdrawals [addr] List pending withdrawal requests
635
+ claim <id1> [...] Claim finalized withdrawals
636
+ wrap <amount> Wrap stETH to wstETH
637
+ unwrap <amount> Unwrap wstETH to stETH
638
+ rate Get stETH/wstETH conversion rate
639
+ position [addr] Cross-chain position summary
636
640
 
637
- monitor Daemon management
638
- start Start monitor daemon
639
- status Show daemon status
640
- stop Stop daemon
641
+ Governance:
642
+ proposals [count] List recent DAO proposals
643
+ proposal <id> Show proposal details
644
+ vote <id> yea|nay Vote on a proposal
641
645
 
642
- bounds Transaction safety bounds
643
- show Show current bounds
644
- set Set bounds (flags: --max-stake-per-tx, --max-daily-stake, --min-eth-reserve)
645
- reset Reset to defaults
646
+ Alerts:
647
+ alert add <cond> Add alert (e.g. "balance_below 0.5")
648
+ alert list List active alerts
649
+ alert remove <id> Remove alert
650
+ alert channels Configure Telegram/webhook
651
+ alert start Start alert daemon
646
652
 
647
- ledger Transaction history
648
- list List entries (--tool, --since, --limit)
649
- stats Show statistics
650
- export Export (--format json|csv)
653
+ Bounds:
654
+ bounds [show] Show safety bounds
655
+ bounds set Set bounds (--max-stake-per-tx, --max-daily-stake, --min-eth-reserve)
656
+ bounds reset Reset to defaults
651
657
 
652
- position [addr] Show total staking position
658
+ Ledger:
659
+ ledger list List entries (--tool, --since, --limit)
660
+ ledger stats Show statistics
661
+ ledger export Export (--format json|csv)
653
662
 
654
- help, --help Show this help
655
- --version Show version
663
+ Agent:
664
+ terminal Start interactive chat session
665
+ --server Start MCP server (for AI client configs)
666
+
667
+ help, --help Show this help
668
+ --version Show version
669
+
670
+ Add --dry-run to any write command to simulate without broadcasting.
656
671
  `);
657
672
  break;
658
673
  }
@@ -675,12 +690,182 @@ Commands:
675
690
  await startServer();
676
691
  break;
677
692
  }
693
+ // ── Direct tool commands ──────────────────────────────────────────
694
+ case "balance": {
695
+ if (!configExists()) {
696
+ console.log("No config. Run: moly setup");
697
+ process.exit(1);
698
+ }
699
+ const { getBalance } = await import("./balance-WQTVOX27.js");
700
+ const addr = args[1];
701
+ console.log(JSON.stringify(await getBalance(addr), null, 2));
702
+ break;
703
+ }
704
+ case "rewards": {
705
+ if (!configExists()) {
706
+ console.log("No config. Run: moly setup");
707
+ process.exit(1);
708
+ }
709
+ const { getRewards } = await import("./balance-WQTVOX27.js");
710
+ const addr = args[1];
711
+ const days = args[2] ? parseInt(args[2]) : 7;
712
+ console.log(JSON.stringify(await getRewards(addr, days), null, 2));
713
+ break;
714
+ }
715
+ case "stake": {
716
+ if (!configExists()) {
717
+ console.log("No config. Run: moly setup");
718
+ process.exit(1);
719
+ }
720
+ const amount = args[1];
721
+ if (!amount) {
722
+ console.log("Usage: moly stake <amount> [--dry-run]");
723
+ break;
724
+ }
725
+ const dryRun = args.includes("--dry-run");
726
+ const { stakeEth } = await import("./stake-ZYFXR35T.js");
727
+ console.log(JSON.stringify(await stakeEth(amount, dryRun), null, 2));
728
+ break;
729
+ }
730
+ case "wrap": {
731
+ if (!configExists()) {
732
+ console.log("No config. Run: moly setup");
733
+ process.exit(1);
734
+ }
735
+ const amount = args[1];
736
+ if (!amount) {
737
+ console.log("Usage: moly wrap <amount> [--dry-run]");
738
+ break;
739
+ }
740
+ const dryRun = args.includes("--dry-run");
741
+ const { wrapSteth } = await import("./wrap-67UFXAQW.js");
742
+ console.log(JSON.stringify(await wrapSteth(amount, dryRun), null, 2));
743
+ break;
744
+ }
745
+ case "unwrap": {
746
+ if (!configExists()) {
747
+ console.log("No config. Run: moly setup");
748
+ process.exit(1);
749
+ }
750
+ const amount = args[1];
751
+ if (!amount) {
752
+ console.log("Usage: moly unwrap <amount> [--dry-run]");
753
+ break;
754
+ }
755
+ const dryRun = args.includes("--dry-run");
756
+ const { unwrapWsteth } = await import("./wrap-67UFXAQW.js");
757
+ console.log(JSON.stringify(await unwrapWsteth(amount, dryRun), null, 2));
758
+ break;
759
+ }
760
+ case "withdraw": {
761
+ if (!configExists()) {
762
+ console.log("No config. Run: moly setup");
763
+ process.exit(1);
764
+ }
765
+ const amount = args[1];
766
+ if (!amount) {
767
+ console.log("Usage: moly withdraw <amount> [--dry-run]");
768
+ break;
769
+ }
770
+ const dryRun = args.includes("--dry-run");
771
+ const { requestWithdrawal } = await import("./unstake-D4K64YHK.js");
772
+ console.log(JSON.stringify(await requestWithdrawal(amount, dryRun), null, 2));
773
+ break;
774
+ }
775
+ case "claim": {
776
+ if (!configExists()) {
777
+ console.log("No config. Run: moly setup");
778
+ process.exit(1);
779
+ }
780
+ const ids = args.slice(1).filter((a) => !a.startsWith("--"));
781
+ if (!ids.length) {
782
+ console.log("Usage: moly claim <id1> [id2...] [--dry-run]");
783
+ break;
784
+ }
785
+ const dryRun = args.includes("--dry-run");
786
+ const { claimWithdrawals } = await import("./unstake-D4K64YHK.js");
787
+ console.log(JSON.stringify(await claimWithdrawals(ids, dryRun), null, 2));
788
+ break;
789
+ }
790
+ case "withdrawals": {
791
+ if (!configExists()) {
792
+ console.log("No config. Run: moly setup");
793
+ process.exit(1);
794
+ }
795
+ const addr = args[1];
796
+ const { getWithdrawalRequests } = await import("./unstake-D4K64YHK.js");
797
+ console.log(JSON.stringify(await getWithdrawalRequests(addr), null, 2));
798
+ break;
799
+ }
800
+ case "proposals": {
801
+ if (!configExists()) {
802
+ console.log("No config. Run: moly setup");
803
+ process.exit(1);
804
+ }
805
+ const count = args[1] ? parseInt(args[1]) : 5;
806
+ const { getProposals } = await import("./governance-RKBUYAG3.js");
807
+ console.log(JSON.stringify(await getProposals(count), null, 2));
808
+ break;
809
+ }
810
+ case "proposal": {
811
+ if (!configExists()) {
812
+ console.log("No config. Run: moly setup");
813
+ process.exit(1);
814
+ }
815
+ const id = args[1];
816
+ if (!id) {
817
+ console.log("Usage: moly proposal <id>");
818
+ break;
819
+ }
820
+ const { getProposal } = await import("./governance-RKBUYAG3.js");
821
+ console.log(JSON.stringify(await getProposal(parseInt(id)), null, 2));
822
+ break;
823
+ }
824
+ case "vote": {
825
+ if (!configExists()) {
826
+ console.log("No config. Run: moly setup");
827
+ process.exit(1);
828
+ }
829
+ const id = args[1];
830
+ const side = args[2];
831
+ if (!id || !side) {
832
+ console.log("Usage: moly vote <proposal_id> <yea|nay> [--dry-run]");
833
+ break;
834
+ }
835
+ const support = side.toLowerCase() === "yea" || side.toLowerCase() === "yes";
836
+ const dryRun = args.includes("--dry-run");
837
+ const { castVote } = await import("./governance-RKBUYAG3.js");
838
+ console.log(JSON.stringify(await castVote(parseInt(id), support, dryRun), null, 2));
839
+ break;
840
+ }
841
+ case "rate": {
842
+ if (!configExists()) {
843
+ console.log("No config. Run: moly setup");
844
+ process.exit(1);
845
+ }
846
+ const { getConversionRate } = await import("./wrap-67UFXAQW.js");
847
+ console.log(JSON.stringify(await getConversionRate(), null, 2));
848
+ break;
849
+ }
850
+ case "wallet": {
851
+ if (!configExists()) {
852
+ console.log("No config. Run: moly setup");
853
+ process.exit(1);
854
+ }
855
+ const { getRuntime } = await import("./runtime-PGSRZ7YU.js");
856
+ try {
857
+ console.log(getRuntime().getAddress());
858
+ } catch (e) {
859
+ console.log("No wallet configured:", e.message);
860
+ }
861
+ break;
862
+ }
678
863
  // ── moly (no args) — wizard if first run, else start server ───────
679
864
  default: {
680
865
  if (!configExists()) {
681
866
  const { cfg, terminalMode } = await runWizard();
682
867
  if (terminalMode) {
683
- const { startChatSession } = await import("./session-NTWLPEVH.js");
868
+ const { startChatSession } = await import("./session-FKRDTPLS.js");
684
869
  await startChatSession(cfg);
685
870
  } else {
686
871
  await startServer();
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getRuntime
4
+ } from "./chunk-2MF5MDUT.js";
5
+
6
+ // src/tools/balance.ts
7
+ import { formatEther } from "viem";
8
+ async function getBalance(address) {
9
+ const rt = getRuntime();
10
+ const addr = address ?? rt.getAddress();
11
+ const [eth, steth, wsteth] = await Promise.all([
12
+ rt.sdk.core.balanceETH(addr),
13
+ rt.sdk.steth.balance(addr),
14
+ rt.sdk.wsteth.balance(addr)
15
+ ]);
16
+ return {
17
+ address: addr,
18
+ mode: rt.config.mode,
19
+ network: rt.chainAddresses.name,
20
+ balances: {
21
+ eth: formatEther(eth),
22
+ stETH: formatEther(steth),
23
+ wstETH: formatEther(wsteth)
24
+ }
25
+ };
26
+ }
27
+ async function getRewards(address, days = 7) {
28
+ const rt = getRuntime();
29
+ const addr = address ?? rt.getAddress();
30
+ const rewards = await rt.sdk.rewards.getRewardsFromChain({
31
+ address: addr,
32
+ stepBlock: 1e3,
33
+ back: { days: BigInt(days) }
34
+ });
35
+ return {
36
+ address: addr,
37
+ period: `${days} days`,
38
+ totalRewards: formatEther(rewards.totalRewards),
39
+ baseBalance: formatEther(rewards.baseBalance),
40
+ rewards: rewards.rewards.slice(0, 10).map((e) => ({
41
+ type: e.type,
42
+ change: formatEther(e.change),
43
+ balance: formatEther(e.balance)
44
+ }))
45
+ };
46
+ }
47
+
48
+ export {
49
+ getBalance,
50
+ getRewards
51
+ };
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ applySettingsUpdate
4
+ } from "./chunk-2MF5MDUT.js";
5
+ import {
6
+ loadConfig,
7
+ redactedConfig
8
+ } from "./chunk-P6VFMSPM.js";
9
+
10
+ // src/tools/settings.ts
11
+ function getSettings() {
12
+ const cfg = loadConfig();
13
+ return {
14
+ ...redactedConfig(cfg),
15
+ note: "Use update_settings to change mode, network, or rpc. Private key and API keys can only be changed via: moly setup"
16
+ };
17
+ }
18
+ function updateSettings(patch) {
19
+ if (Object.keys(patch).length === 0) {
20
+ return { error: "No settings provided to update." };
21
+ }
22
+ const updated = applySettingsUpdate(patch);
23
+ return {
24
+ updated: true,
25
+ changes: patch,
26
+ current: redactedConfig(updated),
27
+ note: "Settings saved and applied. SDK reinitialized with new config."
28
+ };
29
+ }
30
+
31
+ export {
32
+ getSettings,
33
+ updateSettings
34
+ };
@@ -1,12 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- applySettingsUpdate,
4
3
  getRuntime
5
4
  } from "./chunk-2MF5MDUT.js";
6
- import {
7
- loadConfig,
8
- redactedConfig
9
- } from "./chunk-P6VFMSPM.js";
10
5
 
11
6
  // src/tools/stake.ts
12
7
  import { parseEther } from "viem";
@@ -71,29 +66,6 @@ async function stakeEth(amountEth, dryRun) {
71
66
  };
72
67
  }
73
68
 
74
- // src/tools/settings.ts
75
- function getSettings() {
76
- const cfg = loadConfig();
77
- return {
78
- ...redactedConfig(cfg),
79
- note: "Use update_settings to change mode, network, or rpc. Private key and API keys can only be changed via: moly setup"
80
- };
81
- }
82
- function updateSettings(patch) {
83
- if (Object.keys(patch).length === 0) {
84
- return { error: "No settings provided to update." };
85
- }
86
- const updated = applySettingsUpdate(patch);
87
- return {
88
- updated: true,
89
- changes: patch,
90
- current: redactedConfig(updated),
91
- note: "Settings saved and applied. SDK reinitialized with new config."
92
- };
93
- }
94
-
95
69
  export {
96
- stakeEth,
97
- getSettings,
98
- updateSettings
70
+ stakeEth
99
71
  };
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- getBalance,
3
+ getBalance
4
+ } from "./chunk-2UMD5GE7.js";
5
+ import {
4
6
  getConversionRate
5
- } from "./chunk-EG76IABG.js";
7
+ } from "./chunk-KS2ALUDE.js";
6
8
  import {
7
9
  L2_CHAINS,
8
10
  getRuntime
@@ -3,50 +3,8 @@ import {
3
3
  getRuntime
4
4
  } from "./chunk-2MF5MDUT.js";
5
5
 
6
- // src/tools/balance.ts
7
- import { formatEther } from "viem";
8
- async function getBalance(address) {
9
- const rt = getRuntime();
10
- const addr = address ?? rt.getAddress();
11
- const [eth, steth, wsteth] = await Promise.all([
12
- rt.sdk.core.balanceETH(addr),
13
- rt.sdk.steth.balance(addr),
14
- rt.sdk.wsteth.balance(addr)
15
- ]);
16
- return {
17
- address: addr,
18
- mode: rt.config.mode,
19
- network: rt.chainAddresses.name,
20
- balances: {
21
- eth: formatEther(eth),
22
- stETH: formatEther(steth),
23
- wstETH: formatEther(wsteth)
24
- }
25
- };
26
- }
27
- async function getRewards(address, days = 7) {
28
- const rt = getRuntime();
29
- const addr = address ?? rt.getAddress();
30
- const rewards = await rt.sdk.rewards.getRewardsFromChain({
31
- address: addr,
32
- stepBlock: 1e3,
33
- back: { days: BigInt(days) }
34
- });
35
- return {
36
- address: addr,
37
- period: `${days} days`,
38
- totalRewards: formatEther(rewards.totalRewards),
39
- baseBalance: formatEther(rewards.baseBalance),
40
- rewards: rewards.rewards.slice(0, 10).map((e) => ({
41
- type: e.type,
42
- change: formatEther(e.change),
43
- balance: formatEther(e.balance)
44
- }))
45
- };
46
- }
47
-
48
6
  // src/tools/wrap.ts
49
- import { parseEther, formatEther as formatEther2 } from "viem";
7
+ import { parseEther, formatEther } from "viem";
50
8
  async function wrapSteth(amountSteth, dryRun) {
51
9
  const rt = getRuntime();
52
10
  const amount = parseEther(amountSteth);
@@ -59,7 +17,7 @@ async function wrapSteth(amountSteth, dryRun) {
59
17
  network: rt.chainAddresses.name,
60
18
  action: "wrap_steth",
61
19
  amountSteth,
62
- expectedWstETH: formatEther2(expectedWstETH),
20
+ expectedWstETH: formatEther(expectedWstETH),
63
21
  note: "wstETH is non-rebasing \u2014 balance stays fixed while value grows. Better for DeFi."
64
22
  };
65
23
  }
@@ -77,7 +35,7 @@ async function wrapSteth(amountSteth, dryRun) {
77
35
  action: "wrap_steth",
78
36
  amountSteth,
79
37
  txHash: tx.hash,
80
- wstethReceived: formatEther2(tx.result?.wstethReceived ?? 0n)
38
+ wstethReceived: formatEther(tx.result?.wstethReceived ?? 0n)
81
39
  };
82
40
  }
83
41
  async function unwrapWsteth(amountWsteth, dryRun) {
@@ -92,7 +50,7 @@ async function unwrapWsteth(amountWsteth, dryRun) {
92
50
  network: rt.chainAddresses.name,
93
51
  action: "unwrap_wsteth",
94
52
  amountWsteth,
95
- expectedStETH: formatEther2(expectedStETH),
53
+ expectedStETH: formatEther(expectedStETH),
96
54
  note: "Unwrapping gives rebasing stETH back. Balance updates daily with rewards."
97
55
  };
98
56
  }
@@ -110,7 +68,7 @@ async function unwrapWsteth(amountWsteth, dryRun) {
110
68
  action: "unwrap_wsteth",
111
69
  amountWsteth,
112
70
  txHash: tx.hash,
113
- stethReceived: formatEther2(tx.result?.stethReceived ?? 0n)
71
+ stethReceived: formatEther(tx.result?.stethReceived ?? 0n)
114
72
  };
115
73
  }
116
74
  async function getConversionRate() {
@@ -123,15 +81,13 @@ async function getConversionRate() {
123
81
  return {
124
82
  mode: rt.config.mode,
125
83
  network: rt.chainAddresses.name,
126
- "1_stETH_in_wstETH": formatEther2(wstethPerSteth),
127
- "1_wstETH_in_stETH": formatEther2(stethPerWsteth),
84
+ "1_stETH_in_wstETH": formatEther(wstethPerSteth),
85
+ "1_wstETH_in_stETH": formatEther(stethPerWsteth),
128
86
  note: "wstETH/stETH ratio increases over time as staking rewards accumulate."
129
87
  };
130
88
  }
131
89
 
132
90
  export {
133
- getBalance,
134
- getRewards,
135
91
  wrapSteth,
136
92
  unwrapWsteth,
137
93
  getConversionRate
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getRuntime
4
+ } from "./chunk-2MF5MDUT.js";
5
+
6
+ // src/tools/unstake.ts
7
+ import { parseEther, formatEther } from "viem";
8
+ async function requestWithdrawal(amountSteth, dryRun) {
9
+ const rt = getRuntime();
10
+ const amount = parseEther(amountSteth);
11
+ const shouldDryRun = rt.config.mode === "simulation" ? dryRun !== false : !!dryRun;
12
+ if (shouldDryRun) {
13
+ return {
14
+ simulated: true,
15
+ mode: rt.config.mode,
16
+ network: rt.chainAddresses.name,
17
+ action: "request_withdrawal",
18
+ amountSteth,
19
+ minWithdrawal: "0.1 stETH",
20
+ maxWithdrawal: "1000 stETH per request",
21
+ note: "Withdrawal requests enter a queue. Finalization can take hours to days depending on validator exits. You will receive an ERC-721 NFT representing your position."
22
+ };
23
+ }
24
+ const account = rt.getAddress();
25
+ const tx = await rt.sdk.withdraw.request.requestWithdrawal({
26
+ amount,
27
+ token: "stETH",
28
+ account,
29
+ callback: () => {
30
+ }
31
+ });
32
+ return {
33
+ simulated: false,
34
+ mode: rt.config.mode,
35
+ network: rt.chainAddresses.name,
36
+ action: "request_withdrawal",
37
+ amountSteth,
38
+ txHash: tx.hash,
39
+ requestIds: tx.result?.requests?.map((r) => r.requestId.toString()),
40
+ note: "Check status with get_withdrawal_status. Claim when finalized."
41
+ };
42
+ }
43
+ async function claimWithdrawals(requestIds, dryRun) {
44
+ const rt = getRuntime();
45
+ const ids = requestIds.map(BigInt);
46
+ const shouldDryRun = rt.config.mode === "simulation" ? dryRun !== false : !!dryRun;
47
+ if (shouldDryRun) {
48
+ return {
49
+ simulated: true,
50
+ mode: rt.config.mode,
51
+ network: rt.chainAddresses.name,
52
+ action: "claim_withdrawals",
53
+ requestIds,
54
+ note: "Claims can only be made after requests are finalized. Use get_withdrawal_status first."
55
+ };
56
+ }
57
+ const account = rt.getAddress();
58
+ const tx = await rt.sdk.withdraw.claim.claimRequests({
59
+ requestsIds: ids,
60
+ account,
61
+ callback: () => {
62
+ }
63
+ });
64
+ return {
65
+ simulated: false,
66
+ mode: rt.config.mode,
67
+ network: rt.chainAddresses.name,
68
+ action: "claim_withdrawals",
69
+ requestIds,
70
+ txHash: tx.hash
71
+ };
72
+ }
73
+ async function getWithdrawalRequests(address) {
74
+ const rt = getRuntime();
75
+ const addr = address ?? rt.getAddress();
76
+ const requests = await rt.sdk.withdraw.views.getWithdrawalRequestsIds({ account: addr });
77
+ return {
78
+ address: addr,
79
+ mode: rt.config.mode,
80
+ network: rt.chainAddresses.name,
81
+ requests: requests.map((id) => id.toString()),
82
+ count: requests.length
83
+ };
84
+ }
85
+ async function getWithdrawalStatus(requestIds) {
86
+ const rt = getRuntime();
87
+ const ids = requestIds.map(BigInt);
88
+ const statuses = await rt.sdk.withdraw.views.getWithdrawalStatus({ requestsIds: ids });
89
+ return {
90
+ mode: rt.config.mode,
91
+ network: rt.chainAddresses.name,
92
+ statuses: statuses.map((s, i) => ({
93
+ requestId: requestIds[i],
94
+ amountOfStETH: formatEther(s.amountOfStETH),
95
+ amountOfShares: formatEther(s.amountOfShares),
96
+ owner: s.owner,
97
+ isFinalized: s.isFinalized,
98
+ isClaimed: s.isClaimed
99
+ }))
100
+ };
101
+ }
102
+
103
+ export {
104
+ requestWithdrawal,
105
+ claimWithdrawals,
106
+ getWithdrawalRequests,
107
+ getWithdrawalStatus
108
+ };
@@ -3,105 +3,8 @@ import {
3
3
  getRuntime
4
4
  } from "./chunk-2MF5MDUT.js";
5
5
 
6
- // src/tools/unstake.ts
7
- import { parseEther, formatEther } from "viem";
8
- async function requestWithdrawal(amountSteth, dryRun) {
9
- const rt = getRuntime();
10
- const amount = parseEther(amountSteth);
11
- const shouldDryRun = rt.config.mode === "simulation" ? dryRun !== false : !!dryRun;
12
- if (shouldDryRun) {
13
- return {
14
- simulated: true,
15
- mode: rt.config.mode,
16
- network: rt.chainAddresses.name,
17
- action: "request_withdrawal",
18
- amountSteth,
19
- minWithdrawal: "0.1 stETH",
20
- maxWithdrawal: "1000 stETH per request",
21
- note: "Withdrawal requests enter a queue. Finalization can take hours to days depending on validator exits. You will receive an ERC-721 NFT representing your position."
22
- };
23
- }
24
- const account = rt.getAddress();
25
- const tx = await rt.sdk.withdraw.request.requestWithdrawal({
26
- amount,
27
- token: "stETH",
28
- account,
29
- callback: () => {
30
- }
31
- });
32
- return {
33
- simulated: false,
34
- mode: rt.config.mode,
35
- network: rt.chainAddresses.name,
36
- action: "request_withdrawal",
37
- amountSteth,
38
- txHash: tx.hash,
39
- requestIds: tx.result?.requests?.map((r) => r.requestId.toString()),
40
- note: "Check status with get_withdrawal_status. Claim when finalized."
41
- };
42
- }
43
- async function claimWithdrawals(requestIds, dryRun) {
44
- const rt = getRuntime();
45
- const ids = requestIds.map(BigInt);
46
- const shouldDryRun = rt.config.mode === "simulation" ? dryRun !== false : !!dryRun;
47
- if (shouldDryRun) {
48
- return {
49
- simulated: true,
50
- mode: rt.config.mode,
51
- network: rt.chainAddresses.name,
52
- action: "claim_withdrawals",
53
- requestIds,
54
- note: "Claims can only be made after requests are finalized. Use get_withdrawal_status first."
55
- };
56
- }
57
- const account = rt.getAddress();
58
- const tx = await rt.sdk.withdraw.claim.claimRequests({
59
- requestsIds: ids,
60
- account,
61
- callback: () => {
62
- }
63
- });
64
- return {
65
- simulated: false,
66
- mode: rt.config.mode,
67
- network: rt.chainAddresses.name,
68
- action: "claim_withdrawals",
69
- requestIds,
70
- txHash: tx.hash
71
- };
72
- }
73
- async function getWithdrawalRequests(address) {
74
- const rt = getRuntime();
75
- const addr = address ?? rt.getAddress();
76
- const requests = await rt.sdk.withdraw.views.getWithdrawalRequestsIds({ account: addr });
77
- return {
78
- address: addr,
79
- mode: rt.config.mode,
80
- network: rt.chainAddresses.name,
81
- requests: requests.map((id) => id.toString()),
82
- count: requests.length
83
- };
84
- }
85
- async function getWithdrawalStatus(requestIds) {
86
- const rt = getRuntime();
87
- const ids = requestIds.map(BigInt);
88
- const statuses = await rt.sdk.withdraw.views.getWithdrawalStatus({ requestsIds: ids });
89
- return {
90
- mode: rt.config.mode,
91
- network: rt.chainAddresses.name,
92
- statuses: statuses.map((s, i) => ({
93
- requestId: requestIds[i],
94
- amountOfStETH: formatEther(s.amountOfStETH),
95
- amountOfShares: formatEther(s.amountOfShares),
96
- owner: s.owner,
97
- isFinalized: s.isFinalized,
98
- isClaimed: s.isClaimed
99
- }))
100
- };
101
- }
102
-
103
6
  // src/tools/governance.ts
104
- import { createPublicClient, http, parseAbi, formatEther as formatEther2 } from "viem";
7
+ import { createPublicClient, http, parseAbi, formatEther } from "viem";
105
8
  import { mainnet } from "viem/chains";
106
9
  import { defineChain } from "viem";
107
10
  var hoodi = defineChain({
@@ -142,9 +45,9 @@ async function getProposals(count = 5) {
142
45
  open: v[0],
143
46
  executed: v[1],
144
47
  startDate: new Date(Number(v[2]) * 1e3).toISOString(),
145
- yea: formatEther2(v[6]),
146
- nay: formatEther2(v[7]),
147
- votingPower: formatEther2(v[8])
48
+ yea: formatEther(v[6]),
49
+ nay: formatEther(v[7]),
50
+ votingPower: formatEther(v[8])
148
51
  };
149
52
  })
150
53
  );
@@ -178,9 +81,9 @@ async function getProposal(proposalId) {
178
81
  snapshotBlock: v[3].toString(),
179
82
  supportRequired: `${(Number(v[4]) / 1e16).toFixed(1)}%`,
180
83
  minAcceptQuorum: `${(Number(v[5]) / 1e16).toFixed(1)}%`,
181
- yea: formatEther2(v[6]),
182
- nay: formatEther2(v[7]),
183
- votingPower: formatEther2(v[8])
84
+ yea: formatEther(v[6]),
85
+ nay: formatEther(v[7]),
86
+ votingPower: formatEther(v[8])
184
87
  };
185
88
  }
186
89
  async function castVote(proposalId, support, dryRun) {
@@ -223,10 +126,6 @@ async function castVote(proposalId, support, dryRun) {
223
126
  }
224
127
 
225
128
  export {
226
- requestWithdrawal,
227
- claimWithdrawals,
228
- getWithdrawalRequests,
229
- getWithdrawalStatus,
230
129
  getProposals,
231
130
  getProposal,
232
131
  castVote
@@ -1,9 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- getProposals,
4
- getWithdrawalRequests,
5
- getWithdrawalStatus
6
- } from "./chunk-XXQ6WZSM.js";
7
2
  import {
8
3
  loadAlerts,
9
4
  loadChannelConfig,
@@ -11,9 +6,18 @@ import {
11
6
  } from "./chunk-6F64RPQQ.js";
12
7
  import {
13
8
  getBalance,
14
- getConversionRate,
15
9
  getRewards
16
- } from "./chunk-EG76IABG.js";
10
+ } from "./chunk-2UMD5GE7.js";
11
+ import {
12
+ getWithdrawalRequests,
13
+ getWithdrawalStatus
14
+ } from "./chunk-P66V3426.js";
15
+ import {
16
+ getConversionRate
17
+ } from "./chunk-KS2ALUDE.js";
18
+ import {
19
+ getProposals
20
+ } from "./chunk-RVYPT5AF.js";
17
21
  import {
18
22
  getRuntime
19
23
  } from "./chunk-2MF5MDUT.js";
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ castVote,
4
+ getProposal,
5
+ getProposals
6
+ } from "./chunk-RVYPT5AF.js";
7
+ import "./chunk-2MF5MDUT.js";
8
+ import "./chunk-P6VFMSPM.js";
9
+ import "./chunk-PDX44BCA.js";
10
+ export {
11
+ castVote,
12
+ getProposal,
13
+ getProposals
14
+ };
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getTotalPosition
4
- } from "./chunk-7CNNGPGY.js";
5
- import "./chunk-EG76IABG.js";
4
+ } from "./chunk-HH4NH4N5.js";
5
+ import "./chunk-2UMD5GE7.js";
6
+ import "./chunk-KS2ALUDE.js";
6
7
  import "./chunk-2MF5MDUT.js";
7
8
  import "./chunk-P6VFMSPM.js";
8
9
  import "./chunk-PDX44BCA.js";
@@ -1,43 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getSettings,
4
- stakeEth,
5
4
  updateSettings
6
- } from "../chunk-S4ONJBCI.js";
7
- import {
8
- castVote,
9
- claimWithdrawals,
10
- getProposal,
11
- getProposals,
12
- getWithdrawalRequests,
13
- getWithdrawalStatus,
14
- requestWithdrawal
15
- } from "../chunk-XXQ6WZSM.js";
5
+ } from "../chunk-DD4OV6ZQ.js";
16
6
  import {
17
7
  configureAlertChannels,
18
8
  listAlerts,
19
9
  removeAlertById,
20
10
  setAlert
21
11
  } from "../chunk-CZ3EIJUF.js";
22
- import "../chunk-6F64RPQQ.js";
23
12
  import {
24
13
  bridgeToEthereum,
25
14
  getBridgeQuote,
26
15
  getBridgeStatus,
27
16
  getL2Balance,
28
17
  getTotalPosition
29
- } from "../chunk-7CNNGPGY.js";
30
- import {
31
- getBalance,
32
- getConversionRate,
33
- getRewards,
34
- unwrapWsteth,
35
- wrapSteth
36
- } from "../chunk-EG76IABG.js";
37
- import "../chunk-2MF5MDUT.js";
38
- import {
39
- loadConfig
40
- } from "../chunk-P6VFMSPM.js";
18
+ } from "../chunk-HH4NH4N5.js";
41
19
  import {
42
20
  loadBounds,
43
21
  saveBounds
@@ -47,6 +25,34 @@ import {
47
25
  ledgerStats,
48
26
  queryLedger
49
27
  } from "../chunk-RR74UAKD.js";
28
+ import "../chunk-6F64RPQQ.js";
29
+ import {
30
+ getBalance,
31
+ getRewards
32
+ } from "../chunk-2UMD5GE7.js";
33
+ import {
34
+ stakeEth
35
+ } from "../chunk-E4XSJINX.js";
36
+ import {
37
+ claimWithdrawals,
38
+ getWithdrawalRequests,
39
+ getWithdrawalStatus,
40
+ requestWithdrawal
41
+ } from "../chunk-P66V3426.js";
42
+ import {
43
+ getConversionRate,
44
+ unwrapWsteth,
45
+ wrapSteth
46
+ } from "../chunk-KS2ALUDE.js";
47
+ import {
48
+ castVote,
49
+ getProposal,
50
+ getProposals
51
+ } from "../chunk-RVYPT5AF.js";
52
+ import "../chunk-2MF5MDUT.js";
53
+ import {
54
+ loadConfig
55
+ } from "../chunk-P6VFMSPM.js";
50
56
  import "../chunk-PDX44BCA.js";
51
57
 
52
58
  // src/server/index.ts
@@ -1,41 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getSettings,
4
- stakeEth,
5
4
  updateSettings
6
- } from "./chunk-S4ONJBCI.js";
7
- import {
8
- castVote,
9
- claimWithdrawals,
10
- getProposal,
11
- getProposals,
12
- getWithdrawalRequests,
13
- getWithdrawalStatus,
14
- requestWithdrawal
15
- } from "./chunk-XXQ6WZSM.js";
5
+ } from "./chunk-DD4OV6ZQ.js";
16
6
  import {
17
7
  configureAlertChannels,
18
8
  listAlerts,
19
9
  removeAlertById,
20
10
  setAlert
21
11
  } from "./chunk-CZ3EIJUF.js";
22
- import "./chunk-6F64RPQQ.js";
23
12
  import {
24
13
  bridgeToEthereum,
25
14
  getBridgeQuote,
26
15
  getBridgeStatus,
27
16
  getL2Balance,
28
17
  getTotalPosition
29
- } from "./chunk-7CNNGPGY.js";
30
- import {
31
- getBalance,
32
- getConversionRate,
33
- getRewards,
34
- unwrapWsteth,
35
- wrapSteth
36
- } from "./chunk-EG76IABG.js";
37
- import "./chunk-2MF5MDUT.js";
38
- import "./chunk-P6VFMSPM.js";
18
+ } from "./chunk-HH4NH4N5.js";
39
19
  import {
40
20
  loadBounds,
41
21
  saveBounds
@@ -46,6 +26,32 @@ import {
46
26
  logEntry,
47
27
  queryLedger
48
28
  } from "./chunk-RR74UAKD.js";
29
+ import "./chunk-6F64RPQQ.js";
30
+ import {
31
+ getBalance,
32
+ getRewards
33
+ } from "./chunk-2UMD5GE7.js";
34
+ import {
35
+ stakeEth
36
+ } from "./chunk-E4XSJINX.js";
37
+ import {
38
+ claimWithdrawals,
39
+ getWithdrawalRequests,
40
+ getWithdrawalStatus,
41
+ requestWithdrawal
42
+ } from "./chunk-P66V3426.js";
43
+ import {
44
+ getConversionRate,
45
+ unwrapWsteth,
46
+ wrapSteth
47
+ } from "./chunk-KS2ALUDE.js";
48
+ import {
49
+ castVote,
50
+ getProposal,
51
+ getProposals
52
+ } from "./chunk-RVYPT5AF.js";
53
+ import "./chunk-2MF5MDUT.js";
54
+ import "./chunk-P6VFMSPM.js";
49
55
  import "./chunk-PDX44BCA.js";
50
56
 
51
57
  // src/chat/session.ts
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ stakeEth
4
+ } from "./chunk-E4XSJINX.js";
5
+ import "./chunk-2MF5MDUT.js";
6
+ import "./chunk-P6VFMSPM.js";
7
+ import "./chunk-PDX44BCA.js";
8
+ export {
9
+ stakeEth
10
+ };
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ claimWithdrawals,
4
+ getWithdrawalRequests,
5
+ getWithdrawalStatus,
6
+ requestWithdrawal
7
+ } from "./chunk-P66V3426.js";
8
+ import "./chunk-2MF5MDUT.js";
9
+ import "./chunk-P6VFMSPM.js";
10
+ import "./chunk-PDX44BCA.js";
11
+ export {
12
+ claimWithdrawals,
13
+ getWithdrawalRequests,
14
+ getWithdrawalStatus,
15
+ requestWithdrawal
16
+ };
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getConversionRate,
4
+ unwrapWsteth,
5
+ wrapSteth
6
+ } from "./chunk-KS2ALUDE.js";
7
+ import "./chunk-2MF5MDUT.js";
8
+ import "./chunk-P6VFMSPM.js";
9
+ import "./chunk-PDX44BCA.js";
10
+ export {
11
+ getConversionRate,
12
+ unwrapWsteth,
13
+ wrapSteth
14
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@moly-mcp/lido",
3
- "version": "1.2.0",
4
- "description": "Lido MCP Server — stake, unstake, wrap, govern. Works with Claude Desktop, Cursor, Windsurf, and any MCP client.",
3
+ "version": "1.2.2",
4
+ "description": "Terminal assistant for Lido staking — stake, unstake, wrap, govern, bridge, and monitor positions.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {