@moonpay/cli 0.3.10 → 0.4.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.
@@ -1,15 +1,31 @@
1
1
  import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);
2
+ import {
3
+ KEY_CHAIN_MAP,
4
+ chainSchema,
5
+ deleteWalletFile,
6
+ keyChainSchema,
7
+ loadAllWallets,
8
+ loadWallet,
9
+ lockedVaultDataSchema,
10
+ saveWalletMetadata,
11
+ vaultDataSchema,
12
+ walletInfoSchema
13
+ } from "./chunk-AGDVU2O5.js";
14
+ import {
15
+ deriveAllAddresses,
16
+ deriveKeyForChain
17
+ } from "./chunk-Z33PSOPD.js";
18
+ import {
19
+ __require
20
+ } from "./chunk-EEBB5MQP.js";
2
21
 
3
22
  // src/auth.ts
4
23
  import { spawn } from "child_process";
5
24
  import * as crypto from "crypto";
6
25
  import * as fs from "fs";
7
- import * as http from "http";
8
26
  import * as os from "os";
9
27
  import * as path from "path";
10
- function escapeHtml(str) {
11
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
12
- }
28
+ import { createInterface } from "readline";
13
29
  function generateCodeVerifier() {
14
30
  return crypto.randomBytes(32).toString("base64url");
15
31
  }
@@ -20,8 +36,6 @@ var CONFIG_DIR = path.join(os.homedir(), ".config", "moonpay");
20
36
  var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
21
37
  var CREDENTIALS_PATH = path.join(CONFIG_DIR, "credentials.json");
22
38
  var LOCK_PATH = path.join(CONFIG_DIR, ".credentials.lock");
23
- var CALLBACK_PORT = 3847;
24
- var CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
25
39
  var DEFAULT_CONFIG = {
26
40
  baseUrl: "https://agents.moonpay.com",
27
41
  clientId: "mooniq_zin3s5jz3olzkdfxpmbeaogv"
@@ -72,14 +86,10 @@ function acquireLock() {
72
86
  };
73
87
  }
74
88
  function getConfig() {
75
- if (!fs.existsSync(CONFIG_PATH)) {
76
- return null;
77
- }
89
+ if (!fs.existsSync(CONFIG_PATH)) return null;
78
90
  try {
79
91
  const data = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
80
- if (!data.baseUrl || !data.clientId) {
81
- return null;
82
- }
92
+ if (!data.baseUrl || !data.clientId) return null;
83
93
  return data;
84
94
  } catch {
85
95
  return null;
@@ -92,14 +102,10 @@ function getConfigOrDefault() {
92
102
  return DEFAULT_CONFIG;
93
103
  }
94
104
  function getCredentials() {
95
- if (!fs.existsSync(CREDENTIALS_PATH)) {
96
- return null;
97
- }
105
+ if (!fs.existsSync(CREDENTIALS_PATH)) return null;
98
106
  try {
99
107
  const data = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf-8"));
100
- if (!data.accessToken || !data.baseUrl) {
101
- return null;
102
- }
108
+ if (!data.accessToken || !data.baseUrl) return null;
103
109
  return data;
104
110
  } catch {
105
111
  return null;
@@ -127,86 +133,33 @@ function openBrowser(url) {
127
133
  const platform = process.platform;
128
134
  const cmd = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
129
135
  const args = platform === "win32" ? ["/c", "start", "", url] : [url];
130
- spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
136
+ try {
137
+ spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
138
+ return true;
139
+ } catch {
140
+ return false;
141
+ }
142
+ }
143
+ function promptLine(prompt) {
144
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
145
+ return new Promise((resolve) => {
146
+ rl.question(prompt, (answer) => {
147
+ rl.close();
148
+ resolve(answer.trim());
149
+ });
150
+ });
131
151
  }
132
152
  async function login(config) {
133
153
  const state = crypto.randomUUID();
134
154
  const usePkce = !config.clientSecret;
155
+ const callbackUrl = `${config.baseUrl}/callback`;
135
156
  let codeVerifier;
136
157
  if (usePkce) {
137
158
  codeVerifier = generateCodeVerifier();
138
159
  }
139
- let resolveCallback;
140
- const codePromise = new Promise((resolve2) => {
141
- resolveCallback = resolve2;
142
- });
143
- const sockets = /* @__PURE__ */ new Set();
144
- const server = http.createServer((req, res) => {
145
- const url = new URL(req.url ?? "/", `http://localhost:${CALLBACK_PORT}`);
146
- if (url.pathname === "/callback") {
147
- const code2 = url.searchParams.get("code");
148
- const returnedState = url.searchParams.get("state");
149
- const error = url.searchParams.get("error");
150
- const errorDesc = url.searchParams.get("error_description");
151
- if (returnedState !== state) {
152
- res.writeHead(200, { "Content-Type": "text/html" });
153
- res.end(
154
- `<!DOCTYPE html><html><head><meta charset="utf-8"><title>OAuth Error</title></head><body style="font-family:system-ui,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:2rem;text-align:center;background:#fef2f2"><h1 style="color:#b91c1c;margin:0 0 1rem">OAuth Error</h1><p style="color:#991b1b;margin:0">State mismatch \u2014 possible CSRF attack. Please try logging in again.</p></body></html>`
155
- );
156
- resolveCallback("");
157
- setTimeout(() => server.close(), 2e3);
158
- } else if (error) {
159
- const safeError = escapeHtml(error);
160
- const safeDesc = escapeHtml(errorDesc ?? "Unknown error");
161
- res.writeHead(200, { "Content-Type": "text/html" });
162
- res.end(
163
- `<!DOCTYPE html><html><head><meta charset="utf-8"><title>OAuth Error</title></head><body style="font-family:system-ui,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:2rem;text-align:center;background:#fef2f2"><h1 style="color:#b91c1c;margin:0 0 1rem">OAuth Error</h1><p style="color:#991b1b;margin:0">${safeError}: ${safeDesc}</p></body></html>`
164
- );
165
- resolveCallback("");
166
- setTimeout(() => server.close(), 2e3);
167
- } else if (code2) {
168
- res.writeHead(200, { "Content-Type": "text/html" });
169
- res.end(
170
- `<!DOCTYPE html><html><head><meta charset="utf-8"><title>MoonPay Agents</title></head><body style="font-family:system-ui,-apple-system,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:2rem;text-align:center;background:#0a0a0a;color:#fafafa"><img src="https://agents.moonpay.com/logos/moonpay.jpg" alt="MoonPay" style="width:64px;height:64px;border-radius:16px;margin-bottom:1.5rem" /><h1 style="margin:0 0 0.5rem;font-size:1.5rem;font-weight:600">Your agent has money now.</h1><p style="color:#a1a1aa;margin:0;font-size:1rem">You can close this tab and return to the terminal.</p></body></html>`
171
- );
172
- resolveCallback(code2);
173
- } else {
174
- res.writeHead(200, { "Content-Type": "text/html" });
175
- res.end(
176
- `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Error</title></head><body style="font-family:system-ui,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;margin:0;padding:2rem;text-align:center;background:#fef2f2"><h1 style="color:#b91c1c;margin:0 0 1rem">Error</h1><p style="color:#991b1b;margin:0">No authorization code received.</p></body></html>`
177
- );
178
- resolveCallback("");
179
- setTimeout(() => server.close(), 2e3);
180
- }
181
- } else {
182
- res.writeHead(204);
183
- res.end();
184
- }
185
- });
186
- await new Promise((resolve2, reject) => {
187
- server.on("error", (err) => {
188
- if (err.code === "EADDRINUSE") {
189
- reject(
190
- new Error(
191
- `Port ${CALLBACK_PORT} is in use. Close the other process or run: lsof -i :${CALLBACK_PORT}`
192
- )
193
- );
194
- } else {
195
- reject(err);
196
- }
197
- });
198
- server.on("connection", (socket) => {
199
- sockets.add(socket);
200
- socket.on("close", () => sockets.delete(socket));
201
- });
202
- server.listen(CALLBACK_PORT, "127.0.0.1", () => resolve2());
203
- });
204
- console.log(
205
- `Waiting for callback at http://localhost:${CALLBACK_PORT}/callback`
206
- );
207
160
  const authorizeParams = new URLSearchParams({
208
161
  client_id: config.clientId,
209
- redirect_uri: CALLBACK_URL,
162
+ redirect_uri: callbackUrl,
210
163
  response_type: "code",
211
164
  scope: "profile email",
212
165
  state
@@ -216,18 +169,23 @@ async function login(config) {
216
169
  authorizeParams.set("code_challenge_method", "S256");
217
170
  }
218
171
  const authorizeUrl = `${config.baseUrl}/authorize?${authorizeParams.toString()}`;
219
- console.log("Opening browser for authorization...");
220
- console.log("(Make sure you're logged in to MoonPay in your browser)\n");
221
- openBrowser(authorizeUrl);
222
- const code = await codePromise;
172
+ const opened = openBrowser(authorizeUrl);
173
+ if (opened) {
174
+ console.log("Opened browser for authorization.\n");
175
+ } else {
176
+ console.log("Open this URL in your browser to log in:\n");
177
+ console.log(` ${authorizeUrl}
178
+ `);
179
+ }
180
+ const code = await promptLine("Paste code: ");
223
181
  if (!code) {
224
- throw new Error("No authorization code received");
182
+ throw new Error("No authorization code provided.");
225
183
  }
226
184
  const tokenParams = {
227
185
  grant_type: "authorization_code",
228
186
  code,
229
187
  client_id: config.clientId,
230
- redirect_uri: CALLBACK_URL
188
+ redirect_uri: callbackUrl
231
189
  };
232
190
  if (config.clientSecret) {
233
191
  tokenParams.client_secret = config.clientSecret;
@@ -254,14 +212,6 @@ async function login(config) {
254
212
  baseUrl: config.baseUrl
255
213
  };
256
214
  saveCredentials(creds);
257
- await new Promise((resolve2) => setTimeout(resolve2, 1e3));
258
- server.close();
259
- for (const socket of sockets) {
260
- if ("destroy" in socket && typeof socket.destroy === "function") {
261
- socket.destroy();
262
- }
263
- }
264
- sockets.clear();
265
215
  return creds;
266
216
  }
267
217
  async function refreshCredentials(creds, config) {
@@ -1692,8 +1642,11 @@ var schemas_default = [
1692
1642
  ];
1693
1643
 
1694
1644
  // src/tools/wallet/create/tool.ts
1695
- import { Keypair } from "@solana/web3.js";
1696
- import bs58 from "bs58";
1645
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
1646
+ import { join as join3 } from "path";
1647
+ import { homedir as homedir3 } from "os";
1648
+ import { generateMnemonic } from "@scure/bip39";
1649
+ import { wordlist as english } from "@scure/bip39/wordlists/english";
1697
1650
 
1698
1651
  // src/tools/shared.ts
1699
1652
  var defineToolSchema = (config) => config;
@@ -1706,198 +1659,407 @@ var createTool = (schema, handler) => ({
1706
1659
  }
1707
1660
  });
1708
1661
 
1709
- // src/tools/wallet/server.ts
1662
+ // src/tools/wallet/vault.ts
1710
1663
  import {
1711
1664
  readFileSync as readFileSync2,
1712
- readdirSync,
1713
1665
  writeFileSync as writeFileSync2,
1714
- mkdirSync as mkdirSync2,
1715
1666
  existsSync as existsSync2,
1716
- renameSync as renameSync2
1667
+ renameSync as renameSync2,
1668
+ mkdirSync as mkdirSync2
1717
1669
  } from "fs";
1718
- import { join as join2, resolve } from "path";
1670
+ import { join as join2 } from "path";
1719
1671
  import { homedir as homedir2 } from "os";
1720
- import { randomBytes as randomBytes2 } from "crypto";
1721
-
1722
- // src/tools/wallet/models.ts
1723
- import { z } from "zod";
1724
- var walletSchema = z.object({
1725
- name: z.string(),
1726
- address: z.string(),
1727
- secretKey: z.string(),
1728
- chain: z.literal("solana"),
1729
- createdAt: z.string()
1730
- });
1731
- var walletInfoSchema = walletSchema.omit({ secretKey: true });
1732
-
1733
- // src/tools/wallet/server.ts
1672
+ import {
1673
+ randomBytes as randomBytes2,
1674
+ randomUUID as randomUUID2,
1675
+ scryptSync,
1676
+ createCipheriv,
1677
+ createDecipheriv
1678
+ } from "crypto";
1734
1679
  var CONFIG_DIR2 = join2(homedir2(), ".config", "moonpay");
1735
- var WALLETS_DIR = join2(CONFIG_DIR2, "wallets");
1736
- function validateWalletName(name) {
1737
- if (!name || /[/\\]/.test(name) || name.includes("..") || name.includes("\0")) {
1738
- throw new Error(`Invalid wallet name: "${name}"`);
1680
+ var VAULT_PATH = join2(CONFIG_DIR2, "vault.json");
1681
+ function ensureConfigDir2() {
1682
+ mkdirSync2(CONFIG_DIR2, { recursive: true, mode: 448 });
1683
+ }
1684
+ function loadVault() {
1685
+ if (!existsSync2(VAULT_PATH)) {
1686
+ const empty = { version: 1, entries: {} };
1687
+ saveVault(empty);
1688
+ return empty;
1689
+ }
1690
+ const raw = JSON.parse(readFileSync2(VAULT_PATH, "utf-8"));
1691
+ const locked = lockedVaultDataSchema.safeParse(raw);
1692
+ if (locked.success) {
1693
+ throw new Error(
1694
+ "Vault is locked. Run `mp wallet unlock` to decrypt it first."
1695
+ );
1739
1696
  }
1740
- const resolved = resolve(WALLETS_DIR, `${name}.json`);
1741
- if (!resolved.startsWith(WALLETS_DIR + "/")) {
1742
- throw new Error(`Invalid wallet name: "${name}"`);
1697
+ const result = vaultDataSchema.safeParse(raw);
1698
+ if (!result.success) {
1699
+ throw new Error("Vault file is corrupted or has an unknown format.");
1743
1700
  }
1701
+ return result.data;
1702
+ }
1703
+ function saveVault(vault) {
1704
+ ensureConfigDir2();
1705
+ const tmpPath = join2(
1706
+ CONFIG_DIR2,
1707
+ `.vault.${randomBytes2(4).toString("hex")}.tmp`
1708
+ );
1709
+ writeFileSync2(tmpPath, JSON.stringify(vault, null, 2), { mode: 384 });
1710
+ renameSync2(tmpPath, VAULT_PATH);
1711
+ }
1712
+ function isVaultLocked() {
1713
+ if (!existsSync2(VAULT_PATH)) return false;
1714
+ const raw = JSON.parse(readFileSync2(VAULT_PATH, "utf-8"));
1715
+ return lockedVaultDataSchema.safeParse(raw).success;
1744
1716
  }
1745
- function ensureWalletsDir() {
1746
- mkdirSync2(WALLETS_DIR, { recursive: true, mode: 448 });
1717
+ function addHdEntry(mnemonic) {
1718
+ const vault = loadVault();
1719
+ const id = randomUUID2();
1720
+ const entry = { id, type: "hd", mnemonic };
1721
+ vault.entries[id] = entry;
1722
+ saveVault(vault);
1723
+ return id;
1747
1724
  }
1748
- function getWalletPath(name) {
1749
- validateWalletName(name);
1750
- return join2(WALLETS_DIR, `${name}.json`);
1725
+ function addImportedEntry(chain, privateKey) {
1726
+ const vault = loadVault();
1727
+ const id = randomUUID2();
1728
+ const entry = { id, type: "imported", chain, privateKey };
1729
+ vault.entries[id] = entry;
1730
+ saveVault(vault);
1731
+ return id;
1751
1732
  }
1752
- function getWalletsDir() {
1753
- return WALLETS_DIR;
1733
+ function removeVaultEntry(id) {
1734
+ const vault = loadVault();
1735
+ delete vault.entries[id];
1736
+ saveVault(vault);
1737
+ }
1738
+ function resolveSigningKey(wallet, chain) {
1739
+ const vault = loadVault();
1740
+ const entry = vault.entries[wallet.vaultRef];
1741
+ if (!entry) {
1742
+ throw new Error(
1743
+ `Vault entry not found for wallet "${wallet.name}". The vault may be out of sync.`
1744
+ );
1745
+ }
1746
+ const keyChain = KEY_CHAIN_MAP[chain];
1747
+ if (entry.type === "imported") {
1748
+ if (entry.chain !== keyChain) {
1749
+ throw new Error(
1750
+ `Wallet "${wallet.name}" was imported for ${entry.chain}, cannot sign for ${chain}.`
1751
+ );
1752
+ }
1753
+ return {
1754
+ privateKey: decodePrivateKey(entry.privateKey, keyChain),
1755
+ address: wallet.addresses[keyChain]
1756
+ };
1757
+ }
1758
+ const derived = deriveKeyForChain(entry.mnemonic, keyChain, wallet.account ?? 0);
1759
+ return {
1760
+ privateKey: derived.privateKey,
1761
+ address: derived.address
1762
+ };
1754
1763
  }
1755
- function saveWallet(wallet) {
1756
- ensureWalletsDir();
1757
- const filePath = getWalletPath(wallet.name);
1758
- if (existsSync2(filePath)) {
1759
- throw new Error(`Wallet "${wallet.name}" already exists`);
1764
+ function decodePrivateKey(key, chain) {
1765
+ if (chain === "solana") {
1766
+ const bs583 = __require("bs58");
1767
+ return bs583.default.decode(key);
1760
1768
  }
1761
- const safeName = wallet.name.replace(/[^a-zA-Z0-9_-]/g, "_");
1769
+ return Uint8Array.from(Buffer.from(key, "hex"));
1770
+ }
1771
+ var SCRYPT_N = 2 ** 14;
1772
+ var SCRYPT_R = 8;
1773
+ var SCRYPT_P = 1;
1774
+ var KEY_LENGTH = 32;
1775
+ function deriveEncryptionKey(password, salt) {
1776
+ return scryptSync(password, salt, KEY_LENGTH, {
1777
+ N: SCRYPT_N,
1778
+ r: SCRYPT_R,
1779
+ p: SCRYPT_P
1780
+ });
1781
+ }
1782
+ function lockVault(password) {
1783
+ const vault = loadVault();
1784
+ const salt = randomBytes2(32);
1785
+ const key = deriveEncryptionKey(password, salt);
1786
+ const iv = randomBytes2(12);
1787
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
1788
+ const plaintext = JSON.stringify(vault);
1789
+ const encrypted = Buffer.concat([
1790
+ cipher.update(plaintext, "utf8"),
1791
+ cipher.final()
1792
+ ]);
1793
+ const tag = cipher.getAuthTag();
1794
+ const locked = {
1795
+ version: 1,
1796
+ locked: true,
1797
+ ciphertext: encrypted.toString("base64"),
1798
+ iv: iv.toString("base64"),
1799
+ salt: salt.toString("base64"),
1800
+ tag: tag.toString("base64")
1801
+ };
1802
+ ensureConfigDir2();
1762
1803
  const tmpPath = join2(
1763
- WALLETS_DIR,
1764
- `.${safeName}.${randomBytes2(4).toString("hex")}.tmp`
1804
+ CONFIG_DIR2,
1805
+ `.vault.${randomBytes2(4).toString("hex")}.tmp`
1765
1806
  );
1766
- writeFileSync2(tmpPath, JSON.stringify(wallet, null, 2), { mode: 384 });
1767
- renameSync2(tmpPath, filePath);
1807
+ writeFileSync2(tmpPath, JSON.stringify(locked, null, 2), { mode: 384 });
1808
+ renameSync2(tmpPath, VAULT_PATH);
1768
1809
  }
1769
- function loadWallet(nameOrAddress) {
1770
- ensureWalletsDir();
1771
- const files = readdirSync(WALLETS_DIR).filter((f) => f.endsWith(".json"));
1772
- for (const file of files) {
1773
- const raw = JSON.parse(readFileSync2(join2(WALLETS_DIR, file), "utf-8"));
1774
- const result = walletSchema.safeParse(raw);
1775
- if (!result.success) continue;
1776
- const wallet = result.data;
1777
- if (wallet.name === nameOrAddress || wallet.address === nameOrAddress)
1778
- return wallet;
1810
+ function unlockVault(password) {
1811
+ if (!existsSync2(VAULT_PATH)) {
1812
+ throw new Error("No vault found. Create a wallet first.");
1813
+ }
1814
+ const raw = JSON.parse(readFileSync2(VAULT_PATH, "utf-8"));
1815
+ const locked = lockedVaultDataSchema.safeParse(raw);
1816
+ if (!locked.success) {
1817
+ throw new Error("Vault is not locked.");
1779
1818
  }
1780
- throw new Error(`Wallet "${nameOrAddress}" not found`);
1819
+ const { ciphertext, iv, salt, tag } = locked.data;
1820
+ const saltBuf = Buffer.from(salt, "base64");
1821
+ const key = deriveEncryptionKey(password, saltBuf);
1822
+ const ivBuf = Buffer.from(iv, "base64");
1823
+ const tagBuf = Buffer.from(tag, "base64");
1824
+ const ciphertextBuf = Buffer.from(ciphertext, "base64");
1825
+ const decipher = createDecipheriv("aes-256-gcm", key, ivBuf, { authTagLength: 16 });
1826
+ decipher.setAuthTag(tagBuf);
1827
+ let decrypted;
1828
+ try {
1829
+ decrypted = Buffer.concat([
1830
+ decipher.update(ciphertextBuf),
1831
+ decipher.final()
1832
+ ]);
1833
+ } catch {
1834
+ throw new Error("Wrong password.");
1835
+ }
1836
+ const vault = vaultDataSchema.parse(JSON.parse(decrypted.toString("utf8")));
1837
+ saveVault(vault);
1781
1838
  }
1782
1839
 
1783
1840
  // src/tools/wallet/create/schema.ts
1784
- import { z as z2 } from "zod";
1841
+ import { z } from "zod";
1785
1842
  var walletCreateSchema = defineToolSchema({
1786
1843
  name: "wallet_create",
1787
- description: "Generate a new Solana keypair and store it locally",
1788
- input: z2.object({
1789
- name: z2.string().describe("Wallet name")
1844
+ description: "Create a new multi-chain HD wallet. Generates a BIP39 mnemonic seed phrase and derives addresses for Solana, Ethereum (shared by Base, Arbitrum, Polygon, Optimism, BNB, Avalanche), Bitcoin, and Tron. The mnemonic is saved to a local backup file (path returned in output) \u2014 it is never printed or returned in the response. To derive additional accounts from the same mnemonic, pass --from with an existing wallet name and --account with the desired index.",
1845
+ input: z.object({
1846
+ name: z.string().describe("Wallet name"),
1847
+ from: z.string().nullable().describe("Name of an existing HD wallet to derive from. Uses the same mnemonic but a different account index. Omit to generate a new mnemonic."),
1848
+ account: z.number().int().min(0).nullable().describe("BIP44 account index (0, 1, 2, ...). Required when using --from. Omit for new wallets (defaults to 0).")
1790
1849
  }),
1791
- output: z2.object({
1792
- name: z2.string().describe("Wallet name"),
1793
- address: z2.string().describe("Solana wallet address (base58)")
1850
+ output: z.object({
1851
+ name: z.string().describe("Wallet name"),
1852
+ account: z.number().describe("BIP44 account index used for derivation"),
1853
+ addresses: z.record(keyChainSchema, z.string()).describe("Derived addresses per chain (solana, ethereum, bitcoin, tron)"),
1854
+ backupPath: z.string().nullable().describe("Path to the mnemonic backup file (0o600 permissions). Null when derived from an existing wallet.")
1794
1855
  })
1795
1856
  });
1796
1857
 
1797
1858
  // src/tools/wallet/create/tool.ts
1798
1859
  var walletCreate = createTool(walletCreateSchema, async (params) => {
1799
- const keypair = Keypair.generate();
1800
- const wallet = {
1801
- name: params.name,
1802
- address: keypair.publicKey.toBase58(),
1803
- secretKey: bs58.encode(keypair.secretKey),
1804
- chain: "solana",
1860
+ if (params.from) {
1861
+ if (params.account === null) {
1862
+ throw new Error("--account is required when using --from.");
1863
+ }
1864
+ return deriveFromExisting(params.name, params.from, params.account);
1865
+ }
1866
+ return createNew(params.name);
1867
+ });
1868
+ function createNew(name) {
1869
+ const mnemonic = generateMnemonic(english, 128);
1870
+ const addresses = deriveAllAddresses(mnemonic);
1871
+ const vaultRef = addHdEntry(mnemonic);
1872
+ const metadata = {
1873
+ name,
1874
+ type: "hd",
1875
+ vaultRef,
1876
+ account: 0,
1877
+ addresses,
1805
1878
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1806
1879
  };
1807
- saveWallet(wallet);
1808
- return { name: wallet.name, address: wallet.address };
1809
- });
1880
+ saveWalletMetadata(metadata);
1881
+ const backupDir = join3(homedir3(), ".config", "moonpay", "backups");
1882
+ mkdirSync3(backupDir, { recursive: true, mode: 448 });
1883
+ const backupPath = join3(backupDir, `${name}.mnemonic`);
1884
+ writeFileSync3(backupPath, mnemonic + "\n", { mode: 384 });
1885
+ return { name, account: 0, addresses, backupPath };
1886
+ }
1887
+ function deriveFromExisting(name, from, account) {
1888
+ const source = loadWallet(from);
1889
+ if (source.type !== "hd") {
1890
+ throw new Error(
1891
+ `Wallet "${from}" is an imported single-key wallet. Only HD wallets support account derivation.`
1892
+ );
1893
+ }
1894
+ const vault = loadVault();
1895
+ const entry = vault.entries[source.vaultRef];
1896
+ if (!entry || entry.type !== "hd") {
1897
+ throw new Error(`Vault entry for wallet "${from}" not found or is not HD.`);
1898
+ }
1899
+ const addresses = deriveAllAddresses(entry.mnemonic, account);
1900
+ const metadata = {
1901
+ name,
1902
+ type: "hd",
1903
+ vaultRef: source.vaultRef,
1904
+ account,
1905
+ addresses,
1906
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1907
+ };
1908
+ saveWalletMetadata(metadata);
1909
+ return { name, account, addresses, backupPath: null };
1910
+ }
1810
1911
 
1811
1912
  // src/tools/wallet/import/tool.ts
1812
- import { Keypair as Keypair2 } from "@solana/web3.js";
1813
- import bs582 from "bs58";
1814
- import { createInterface } from "readline";
1913
+ import { validateMnemonic } from "@scure/bip39";
1914
+ import { wordlist as english2 } from "@scure/bip39/wordlists/english";
1915
+ import { Keypair } from "@solana/web3.js";
1916
+ import bs58 from "bs58";
1917
+ import { createInterface as createInterface2 } from "readline";
1815
1918
 
1816
1919
  // src/tools/wallet/import/schema.ts
1817
- import { z as z3 } from "zod";
1920
+ import { z as z2 } from "zod";
1818
1921
  var walletImportSchema = defineToolSchema({
1819
1922
  name: "wallet_import",
1820
- description: "Import a Solana wallet from a base58 private key",
1821
- input: z3.object({
1822
- name: z3.string().describe("Wallet name"),
1823
- key: z3.string().nullable().describe("Base58-encoded private key (prompted interactively if not provided)")
1923
+ description: "Import a wallet from a BIP39 mnemonic (HD, all chains) or a single private key (one chain). Provide either --mnemonic or --key, not both.",
1924
+ input: z2.object({
1925
+ name: z2.string().describe("Wallet name"),
1926
+ mnemonic: z2.string().nullable().describe("BIP39 mnemonic seed phrase (for HD wallet import)"),
1927
+ key: z2.string().nullable().describe(
1928
+ "Private key: base58 for Solana, hex for EVM/Bitcoin (for single-chain import)"
1929
+ ),
1930
+ chain: chainSchema.nullable().describe(
1931
+ "Chain for single-key import: solana, ethereum, base, arbitrum, or bitcoin (default solana)"
1932
+ )
1824
1933
  }),
1825
- output: z3.object({
1826
- name: z3.string().describe("Wallet name"),
1827
- address: z3.string().describe("Solana wallet address (base58)")
1934
+ output: z2.object({
1935
+ name: z2.string().describe("Wallet name"),
1936
+ type: z2.enum(["hd", "imported"]).describe("Wallet type"),
1937
+ addresses: z2.record(keyChainSchema, z2.string()).describe("Wallet addresses per chain")
1828
1938
  })
1829
1939
  });
1830
1940
 
1831
1941
  // src/tools/wallet/import/tool.ts
1832
1942
  async function promptSecret(prompt) {
1833
- const rl = createInterface({ input: process.stdin, output: process.stderr });
1834
- return new Promise((resolve2) => {
1943
+ const rl = createInterface2({ input: process.stdin, output: process.stderr });
1944
+ return new Promise((resolve) => {
1835
1945
  rl.question(prompt, (answer) => {
1836
1946
  rl.close();
1837
- resolve2(answer.trim());
1947
+ resolve(answer.trim());
1838
1948
  });
1839
1949
  });
1840
1950
  }
1841
1951
  var walletImport = createTool(walletImportSchema, async (params) => {
1842
- const secretKeyBase58 = params.key !== null ? params.key : await promptSecret("Enter private key (base58): ");
1843
- let keypair;
1844
- try {
1845
- const secretKeyBytes = bs582.decode(secretKeyBase58);
1846
- keypair = Keypair2.fromSecretKey(secretKeyBytes);
1847
- } catch {
1848
- throw new Error(
1849
- "Invalid private key. Expected a base58-encoded Solana secret key."
1850
- );
1952
+ if (params.mnemonic && params.key) {
1953
+ throw new Error("Provide either --mnemonic or --key, not both.");
1954
+ }
1955
+ if (params.mnemonic) {
1956
+ return importMnemonic(params.name, params.mnemonic);
1957
+ }
1958
+ const chain = params.chain ? KEY_CHAIN_MAP[params.chain] : "solana";
1959
+ const key = params.key !== null ? params.key : await promptSecret("Enter private key: ");
1960
+ return importKey(params.name, chain, key);
1961
+ });
1962
+ async function importMnemonic(name, mnemonic) {
1963
+ const trimmed = mnemonic.trim().toLowerCase();
1964
+ if (!validateMnemonic(trimmed, english2)) {
1965
+ throw new Error("Invalid BIP39 mnemonic.");
1851
1966
  }
1852
- const wallet = {
1853
- name: params.name,
1854
- address: keypair.publicKey.toBase58(),
1855
- secretKey: secretKeyBase58,
1856
- chain: "solana",
1967
+ const addresses = deriveAllAddresses(trimmed);
1968
+ const vaultRef = addHdEntry(trimmed);
1969
+ const metadata = {
1970
+ name,
1971
+ type: "hd",
1972
+ vaultRef,
1973
+ account: 0,
1974
+ addresses,
1857
1975
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1858
1976
  };
1859
- saveWallet(wallet);
1860
- return { name: wallet.name, address: wallet.address };
1861
- });
1862
-
1863
- // src/tools/wallet/list/tool.ts
1864
- import { readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
1865
- import { join as join3 } from "path";
1977
+ saveWalletMetadata(metadata);
1978
+ return { name, type: "hd", addresses };
1979
+ }
1980
+ async function importKey(name, chain, key) {
1981
+ let address;
1982
+ if (chain === "solana") {
1983
+ try {
1984
+ const secretKeyBytes = bs58.decode(key);
1985
+ const keypair = Keypair.fromSecretKey(secretKeyBytes);
1986
+ address = keypair.publicKey.toBase58();
1987
+ } catch {
1988
+ throw new Error(
1989
+ "Invalid private key. Expected a base58-encoded Solana secret key."
1990
+ );
1991
+ }
1992
+ } else if (chain === "ethereum") {
1993
+ const cleanKey = key.startsWith("0x") ? key.slice(2) : key;
1994
+ if (!/^[0-9a-fA-F]{64}$/.test(cleanKey)) {
1995
+ throw new Error(
1996
+ "Invalid private key. Expected a 64-character hex string for EVM."
1997
+ );
1998
+ }
1999
+ const { deriveKeyForChain: deriveKeyForChain2 } = await import("./chains-R754DQM5.js");
2000
+ const { HDKey } = await import("@scure/bip32");
2001
+ const ecc2 = await import("tiny-secp256k1");
2002
+ const { createHash: createHash3 } = await import("crypto");
2003
+ const privKeyBytes = Buffer.from(cleanKey, "hex");
2004
+ const pubKey = ecc2.pointFromScalar(privKeyBytes);
2005
+ if (!pubKey) throw new Error("Invalid private key.");
2006
+ const uncompressed = ecc2.pointCompress(pubKey, false);
2007
+ const hash = createHash3("sha3-256").update(uncompressed.slice(1)).digest();
2008
+ address = "0x" + hash.slice(-20).toString("hex");
2009
+ key = cleanKey;
2010
+ } else {
2011
+ const cleanKey = key.startsWith("0x") ? key.slice(2) : key;
2012
+ if (!/^[0-9a-fA-F]{64}$/.test(cleanKey)) {
2013
+ throw new Error(
2014
+ "Invalid private key. Expected a 64-character hex string for Bitcoin."
2015
+ );
2016
+ }
2017
+ const bitcoin = await import("bitcoinjs-lib");
2018
+ const ECPairFactory = (await import("ecpair")).default;
2019
+ const ecc2 = await import("tiny-secp256k1");
2020
+ const ECPair = ECPairFactory(ecc2);
2021
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(cleanKey, "hex"));
2022
+ const { address: btcAddress } = bitcoin.payments.p2wpkh({
2023
+ pubkey: Buffer.from(keyPair.publicKey)
2024
+ });
2025
+ if (!btcAddress) throw new Error("Failed to derive Bitcoin address.");
2026
+ address = btcAddress;
2027
+ key = cleanKey;
2028
+ }
2029
+ const vaultRef = addImportedEntry(chain, key);
2030
+ const metadata = {
2031
+ name,
2032
+ type: "imported",
2033
+ vaultRef,
2034
+ account: 0,
2035
+ addresses: { [chain]: address },
2036
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2037
+ };
2038
+ saveWalletMetadata(metadata);
2039
+ return { name, type: "imported", addresses: { [chain]: address } };
2040
+ }
1866
2041
 
1867
2042
  // src/tools/wallet/list/schema.ts
1868
- import { z as z4 } from "zod";
2043
+ import { z as z3 } from "zod";
1869
2044
  var walletListSchema = defineToolSchema({
1870
2045
  name: "wallet_list",
1871
2046
  description: "List all local wallets",
1872
- input: z4.object({}),
1873
- output: z4.array(walletInfoSchema)
2047
+ input: z3.object({}),
2048
+ output: z3.array(walletInfoSchema)
1874
2049
  });
1875
2050
 
1876
2051
  // src/tools/wallet/list/tool.ts
1877
2052
  var walletList = createTool(walletListSchema, async () => {
1878
- ensureWalletsDir();
1879
- const dir = getWalletsDir();
1880
- const files = readdirSync2(dir).filter(
1881
- (f) => f.endsWith(".json") && !f.startsWith(".")
1882
- );
1883
- return files.map((f) => {
1884
- const wallet = JSON.parse(readFileSync3(join3(dir, f), "utf-8"));
1885
- return {
1886
- name: wallet.name,
1887
- address: wallet.address,
1888
- chain: wallet.chain,
1889
- createdAt: wallet.createdAt
1890
- };
1891
- });
2053
+ return loadAllWallets();
1892
2054
  });
1893
2055
 
1894
2056
  // src/tools/wallet/retrieve/schema.ts
1895
- import { z as z5 } from "zod";
2057
+ import { z as z4 } from "zod";
1896
2058
  var walletRetrieveSchema = defineToolSchema({
1897
2059
  name: "wallet_retrieve",
1898
2060
  description: "Get details of a specific wallet",
1899
- input: z5.object({
1900
- wallet: z5.string().describe("Wallet name or address")
2061
+ input: z4.object({
2062
+ wallet: z4.string().describe("Wallet name or address")
1901
2063
  }),
1902
2064
  output: walletInfoSchema
1903
2065
  });
@@ -1906,31 +2068,22 @@ var walletRetrieveSchema = defineToolSchema({
1906
2068
  var walletRetrieve = createTool(
1907
2069
  walletRetrieveSchema,
1908
2070
  async (params) => {
1909
- const wallet = loadWallet(params.wallet);
1910
- return {
1911
- name: wallet.name,
1912
- address: wallet.address,
1913
- chain: wallet.chain,
1914
- createdAt: wallet.createdAt
1915
- };
2071
+ return loadWallet(params.wallet);
1916
2072
  }
1917
2073
  );
1918
2074
 
1919
- // src/tools/wallet/delete/tool.ts
1920
- import { unlinkSync as unlinkSync2 } from "fs";
1921
-
1922
2075
  // src/tools/wallet/delete/schema.ts
1923
- import { z as z6 } from "zod";
2076
+ import { z as z5 } from "zod";
1924
2077
  var walletDeleteSchema = defineToolSchema({
1925
2078
  name: "wallet_delete",
1926
2079
  description: "Permanently delete a local wallet. This removes the private key file and cannot be undone.",
1927
- input: z6.object({
1928
- wallet: z6.string().describe("Name or address of the wallet to delete"),
1929
- confirm: z6.boolean().describe("Must be true to confirm deletion")
2080
+ input: z5.object({
2081
+ wallet: z5.string().describe("Name or address of the wallet to delete"),
2082
+ confirm: z5.boolean().describe("Must be true to confirm deletion")
1930
2083
  }),
1931
- output: z6.object({
1932
- name: z6.string().describe("Name of the deleted wallet"),
1933
- deleted: z6.literal(true)
2084
+ output: z5.object({
2085
+ name: z5.string().describe("Name of the deleted wallet"),
2086
+ deleted: z5.literal(true)
1934
2087
  })
1935
2088
  });
1936
2089
 
@@ -1941,26 +2094,29 @@ var walletDelete = createTool(walletDeleteSchema, async (params) => {
1941
2094
  "Deletion not confirmed. Pass --confirm to permanently delete this wallet."
1942
2095
  );
1943
2096
  }
1944
- const wallet = await walletRetrieve.handler({ wallet: params.wallet });
1945
- unlinkSync2(getWalletPath(wallet.name));
2097
+ const wallet = loadWallet(params.wallet);
2098
+ removeVaultEntry(wallet.vaultRef);
2099
+ deleteWalletFile(wallet.name);
1946
2100
  return { name: wallet.name, deleted: true };
1947
2101
  });
1948
2102
 
1949
2103
  // src/tools/transaction/sign/tool.ts
1950
- import { Keypair as Keypair3, VersionedTransaction } from "@solana/web3.js";
1951
- import bs583 from "bs58";
2104
+ import { Keypair as Keypair2, VersionedTransaction } from "@solana/web3.js";
1952
2105
 
1953
2106
  // src/tools/transaction/sign/schema.ts
1954
- import { z as z7 } from "zod";
2107
+ import { z as z6 } from "zod";
1955
2108
  var transactionSignSchema = defineToolSchema({
1956
2109
  name: "transaction_sign",
1957
- description: "Sign a base64-encoded Solana transaction with a local wallet",
1958
- input: z7.object({
1959
- wallet: z7.string().describe("Wallet address"),
1960
- transaction: z7.string().describe("Base64-encoded unsigned transaction")
2110
+ description: "Sign a transaction with a local wallet. Supports Solana (VersionedTransaction), EVM (RLP-encoded), and Bitcoin (PSBT) transactions.",
2111
+ input: z6.object({
2112
+ wallet: z6.string().describe("Wallet name or address"),
2113
+ chain: chainSchema.describe(
2114
+ "Chain: solana, ethereum, base, arbitrum, or bitcoin"
2115
+ ),
2116
+ transaction: z6.string().describe("Base64-encoded unsigned transaction")
1961
2117
  }),
1962
- output: z7.object({
1963
- transaction: z7.string().describe("Base64-encoded signed transaction")
2118
+ output: z6.object({
2119
+ transaction: z6.string().describe("Base64-encoded signed transaction")
1964
2120
  })
1965
2121
  });
1966
2122
 
@@ -1968,47 +2124,274 @@ var transactionSignSchema = defineToolSchema({
1968
2124
  var transactionSign = createTool(
1969
2125
  transactionSignSchema,
1970
2126
  async (params) => {
1971
- const storedWallet = loadWallet(params.wallet);
2127
+ const wallet = loadWallet(params.wallet);
2128
+ const chain = params.chain;
2129
+ const keyChain = KEY_CHAIN_MAP[chain];
2130
+ const { privateKey } = resolveSigningKey(wallet, chain);
1972
2131
  const txBytes = Buffer.from(params.transaction.trim(), "base64");
1973
- const tx = VersionedTransaction.deserialize(txBytes);
1974
- const secretKeyBytes = bs583.decode(storedWallet.secretKey);
1975
- const keypair = Keypair3.fromSecretKey(secretKeyBytes);
1976
- tx.sign([keypair]);
1977
- return {
1978
- transaction: Buffer.from(tx.serialize()).toString("base64")
1979
- };
2132
+ switch (keyChain) {
2133
+ case "solana":
2134
+ return signSolana(privateKey, txBytes);
2135
+ case "ethereum":
2136
+ return signEvm(privateKey, txBytes);
2137
+ case "bitcoin":
2138
+ return signBitcoin(privateKey, txBytes);
2139
+ case "tron":
2140
+ return signEvm(privateKey, txBytes);
2141
+ }
1980
2142
  }
1981
2143
  );
2144
+ function signSolana(privateKey, txBytes) {
2145
+ const tx = VersionedTransaction.deserialize(txBytes);
2146
+ const keypair = Keypair2.fromSecretKey(privateKey);
2147
+ tx.sign([keypair]);
2148
+ return { transaction: Buffer.from(tx.serialize()).toString("base64") };
2149
+ }
2150
+ async function signEvm(privateKey, txBytes) {
2151
+ const { createHash: createHash3 } = await import("crypto");
2152
+ const ecc2 = await import("tiny-secp256k1");
2153
+ const hash = createHash3("sha3-256").update(txBytes).digest();
2154
+ const sig = ecc2.sign(hash, privateKey);
2155
+ if (!sig) throw new Error("EVM signing failed");
2156
+ const signed = Buffer.concat([txBytes, Buffer.from(sig)]);
2157
+ return { transaction: signed.toString("base64") };
2158
+ }
2159
+ async function signBitcoin(privateKey, txBytes) {
2160
+ const bitcoin = await import("bitcoinjs-lib");
2161
+ const ECPairFactory = (await import("ecpair")).default;
2162
+ const ecc2 = await import("tiny-secp256k1");
2163
+ const ECPair = ECPairFactory(ecc2);
2164
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(privateKey));
2165
+ const psbt = bitcoin.Psbt.fromBase64(txBytes.toString("base64"));
2166
+ psbt.signAllInputs(keyPair);
2167
+ return { transaction: psbt.toBase64() };
2168
+ }
1982
2169
 
1983
2170
  // src/tools/message/sign/tool.ts
1984
- import { Keypair as Keypair4 } from "@solana/web3.js";
1985
- import bs584 from "bs58";
2171
+ import { createHash as createHash2 } from "crypto";
2172
+ import { Keypair as Keypair3 } from "@solana/web3.js";
2173
+ import bs582 from "bs58";
1986
2174
  import nacl from "tweetnacl";
2175
+ import * as ecc from "tiny-secp256k1";
1987
2176
 
1988
2177
  // src/tools/message/sign/schema.ts
1989
- import { z as z8 } from "zod";
2178
+ import { z as z7 } from "zod";
1990
2179
  var messageSignSchema = defineToolSchema({
1991
2180
  name: "message_sign",
1992
- description: "Sign a message with a local wallet. Produces a base58-encoded ed25519 signature compatible with Solana's signMessage.",
1993
- input: z8.object({
1994
- wallet: z8.string().describe("Wallet address to sign with"),
1995
- message: z8.string().describe("Message text to sign")
2181
+ description: "Sign a message with a local wallet. Supports Solana (ed25519), EVM (EIP-191 personal sign), and Bitcoin (secp256k1 ECDSA).",
2182
+ input: z7.object({
2183
+ wallet: z7.string().describe("Wallet address or name to sign with"),
2184
+ chain: chainSchema.describe(
2185
+ "Chain: solana, ethereum, base, arbitrum, or bitcoin"
2186
+ ),
2187
+ message: z7.string().describe("Message text to sign")
1996
2188
  }),
1997
- output: z8.object({
1998
- signature: z8.string().describe("Base58-encoded ed25519 signature")
2189
+ output: z7.object({
2190
+ signature: z7.string().describe(
2191
+ "Signature: base58 for Solana, hex (0x-prefixed) for EVM/Bitcoin"
2192
+ )
1999
2193
  })
2000
2194
  });
2001
2195
 
2002
2196
  // src/tools/message/sign/tool.ts
2003
2197
  var messageSign = createTool(messageSignSchema, async (params) => {
2004
- const storedWallet = loadWallet(params.wallet);
2005
- const secretKeyBytes = bs584.decode(storedWallet.secretKey);
2006
- const keypair = Keypair4.fromSecretKey(secretKeyBytes);
2198
+ const wallet = loadWallet(params.wallet);
2199
+ const chain = params.chain;
2200
+ const keyChain = KEY_CHAIN_MAP[chain];
2201
+ const { privateKey } = resolveSigningKey(wallet, chain);
2007
2202
  const messageBytes = Buffer.from(params.message, "utf8");
2203
+ switch (keyChain) {
2204
+ case "solana":
2205
+ return signSolana2(privateKey, messageBytes);
2206
+ case "ethereum":
2207
+ return signEvm2(privateKey, messageBytes);
2208
+ case "bitcoin":
2209
+ return signBitcoin2(privateKey, messageBytes);
2210
+ case "tron":
2211
+ return signEvm2(privateKey, messageBytes);
2212
+ }
2213
+ });
2214
+ function signSolana2(privateKey, messageBytes) {
2215
+ const keypair = Keypair3.fromSecretKey(privateKey);
2008
2216
  const signatureBytes = nacl.sign.detached(messageBytes, keypair.secretKey);
2009
- return { signature: bs584.encode(signatureBytes) };
2217
+ return { signature: bs582.encode(signatureBytes) };
2218
+ }
2219
+ function signEvm2(privateKey, messageBytes) {
2220
+ const prefix = Buffer.from(
2221
+ `Ethereum Signed Message:
2222
+ ${messageBytes.length}`,
2223
+ "utf8"
2224
+ );
2225
+ const prefixed = Buffer.concat([prefix, messageBytes]);
2226
+ const hash = createHash2("sha3-256").update(prefixed).digest();
2227
+ const sig = ecc.sign(hash, privateKey);
2228
+ if (!sig) throw new Error("EVM message signing failed");
2229
+ return { signature: "0x" + Buffer.from(sig).toString("hex") };
2230
+ }
2231
+ function signBitcoin2(privateKey, messageBytes) {
2232
+ const hash1 = createHash2("sha256").update(messageBytes).digest();
2233
+ const hash2 = createHash2("sha256").update(hash1).digest();
2234
+ const sig = ecc.sign(hash2, privateKey);
2235
+ if (!sig) throw new Error("Bitcoin message signing failed");
2236
+ return { signature: "0x" + Buffer.from(sig).toString("hex") };
2237
+ }
2238
+
2239
+ // src/tools/wallet/lock/tool.ts
2240
+ import { createInterface as createInterface3 } from "readline";
2241
+
2242
+ // src/tools/wallet/lock/schema.ts
2243
+ import { z as z8 } from "zod";
2244
+ var walletLockSchema = defineToolSchema({
2245
+ name: "wallet_lock",
2246
+ description: "Encrypt the vault with a password. All signing operations will fail until `wallet unlock` is called. Password is prompted interactively.",
2247
+ input: z8.object({}),
2248
+ output: z8.object({
2249
+ locked: z8.literal(true)
2250
+ })
2010
2251
  });
2011
2252
 
2253
+ // src/tools/wallet/lock/tool.ts
2254
+ async function promptPassword(prompt) {
2255
+ const rl = createInterface3({ input: process.stdin, output: process.stderr });
2256
+ return new Promise((resolve) => {
2257
+ rl.question(prompt, (answer) => {
2258
+ rl.close();
2259
+ resolve(answer);
2260
+ });
2261
+ });
2262
+ }
2263
+ var walletLock = createTool(walletLockSchema, async () => {
2264
+ if (isVaultLocked()) {
2265
+ throw new Error("Vault is already locked.");
2266
+ }
2267
+ const password = await promptPassword("Enter password to encrypt vault: ");
2268
+ if (!password) {
2269
+ throw new Error("Password cannot be empty.");
2270
+ }
2271
+ const confirm = await promptPassword("Confirm password: ");
2272
+ if (password !== confirm) {
2273
+ throw new Error("Passwords do not match.");
2274
+ }
2275
+ lockVault(password);
2276
+ return { locked: true };
2277
+ });
2278
+
2279
+ // src/tools/wallet/unlock/tool.ts
2280
+ import { createInterface as createInterface4 } from "readline";
2281
+
2282
+ // src/tools/wallet/unlock/schema.ts
2283
+ import { z as z9 } from "zod";
2284
+ var walletUnlockSchema = defineToolSchema({
2285
+ name: "wallet_unlock",
2286
+ description: "Decrypt the vault with a password. Restores access to all signing operations. Password is prompted interactively.",
2287
+ input: z9.object({}),
2288
+ output: z9.object({
2289
+ unlocked: z9.literal(true)
2290
+ })
2291
+ });
2292
+
2293
+ // src/tools/wallet/unlock/tool.ts
2294
+ async function promptPassword2(prompt) {
2295
+ const rl = createInterface4({ input: process.stdin, output: process.stderr });
2296
+ return new Promise((resolve) => {
2297
+ rl.question(prompt, (answer) => {
2298
+ rl.close();
2299
+ resolve(answer);
2300
+ });
2301
+ });
2302
+ }
2303
+ var walletUnlock = createTool(walletUnlockSchema, async () => {
2304
+ if (!isVaultLocked()) {
2305
+ throw new Error("Vault is not locked.");
2306
+ }
2307
+ const password = await promptPassword2("Enter password to decrypt vault: ");
2308
+ unlockVault(password);
2309
+ return { unlocked: true };
2310
+ });
2311
+
2312
+ // src/tools/bitcoin/balance/tool.ts
2313
+ import https from "https";
2314
+
2315
+ // src/tools/bitcoin/balance/schema.ts
2316
+ import { z as z10 } from "zod";
2317
+ var bitcoinBalanceRetrieveSchema = defineToolSchema({
2318
+ name: "bitcoin_balance_retrieve",
2319
+ description: "Get the BTC balance of a Bitcoin address. Returns confirmed and unconfirmed balances in BTC and satoshis.",
2320
+ input: z10.object({
2321
+ wallet: z10.string().describe("Bitcoin address (bc1...) or wallet name")
2322
+ }),
2323
+ output: z10.object({
2324
+ address: z10.string().describe("Bitcoin address"),
2325
+ confirmed: z10.object({
2326
+ btc: z10.string().describe("Confirmed balance in BTC"),
2327
+ sats: z10.number().describe("Confirmed balance in satoshis")
2328
+ }),
2329
+ unconfirmed: z10.object({
2330
+ btc: z10.string().describe("Unconfirmed (pending) balance in BTC"),
2331
+ sats: z10.number().describe("Unconfirmed (pending) balance in satoshis")
2332
+ }),
2333
+ total: z10.object({
2334
+ btc: z10.string().describe("Total balance in BTC"),
2335
+ sats: z10.number().describe("Total balance in satoshis")
2336
+ })
2337
+ })
2338
+ });
2339
+
2340
+ // src/tools/bitcoin/balance/tool.ts
2341
+ var SATS_PER_BTC = 1e8;
2342
+ function satsToBtc(sats) {
2343
+ return (sats / SATS_PER_BTC).toFixed(8);
2344
+ }
2345
+ function fetchJson(url) {
2346
+ return new Promise((resolve, reject) => {
2347
+ https.get(url, { headers: { "User-Agent": "moonpay-cli" } }, (res) => {
2348
+ if (res.statusCode !== 200) {
2349
+ reject(new Error(`HTTP ${res.statusCode} from ${url}`));
2350
+ res.resume();
2351
+ return;
2352
+ }
2353
+ let data = "";
2354
+ res.on("data", (chunk) => data += chunk);
2355
+ res.on("end", () => {
2356
+ try {
2357
+ resolve(JSON.parse(data));
2358
+ } catch {
2359
+ reject(new Error("Invalid JSON response"));
2360
+ }
2361
+ });
2362
+ }).on("error", reject);
2363
+ });
2364
+ }
2365
+ var bitcoinBalanceRetrieve = createTool(
2366
+ bitcoinBalanceRetrieveSchema,
2367
+ async (params) => {
2368
+ let address = params.wallet;
2369
+ if (!address.startsWith("bc1") && !address.startsWith("1") && !address.startsWith("3")) {
2370
+ const { loadWallet: loadWallet2 } = await import("./server-IUOCZFT7.js");
2371
+ const wallet = loadWallet2(address);
2372
+ const btcAddress = wallet.addresses.bitcoin;
2373
+ if (!btcAddress) {
2374
+ throw new Error(
2375
+ `Wallet "${address}" has no Bitcoin address. It may be an imported single-chain wallet.`
2376
+ );
2377
+ }
2378
+ address = btcAddress;
2379
+ }
2380
+ const data = await fetchJson(
2381
+ `https://mempool.space/api/address/${address}`
2382
+ );
2383
+ const confirmedSats = data.chain_stats.funded_txo_sum - data.chain_stats.spent_txo_sum;
2384
+ const unconfirmedSats = data.mempool_stats.funded_txo_sum - data.mempool_stats.spent_txo_sum;
2385
+ const totalSats = confirmedSats + unconfirmedSats;
2386
+ return {
2387
+ address,
2388
+ confirmed: { btc: satsToBtc(confirmedSats), sats: confirmedSats },
2389
+ unconfirmed: { btc: satsToBtc(unconfirmedSats), sats: unconfirmedSats },
2390
+ total: { btc: satsToBtc(totalSats), sats: totalSats }
2391
+ };
2392
+ }
2393
+ );
2394
+
2012
2395
  export {
2013
2396
  getConfigOrDefault,
2014
2397
  clearCredentials,
@@ -2018,13 +2401,16 @@ export {
2018
2401
  schemas_default,
2019
2402
  defineToolSchema,
2020
2403
  createTool,
2021
- loadWallet,
2404
+ resolveSigningKey,
2022
2405
  walletCreate,
2023
2406
  walletImport,
2024
2407
  walletList,
2025
2408
  walletRetrieve,
2026
2409
  walletDelete,
2027
2410
  transactionSign,
2028
- messageSign
2411
+ messageSign,
2412
+ walletLock,
2413
+ walletUnlock,
2414
+ bitcoinBalanceRetrieve
2029
2415
  };
2030
- //# sourceMappingURL=chunk-ZJ3XMP4N.js.map
2416
+ //# sourceMappingURL=chunk-HKQ2DNOD.js.map