@execra/core 1.0.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.
- package/dist/cli/commands.d.ts +21 -0
- package/dist/cli/commands.js +686 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +1162 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompts.d.ts +12 -0
- package/dist/cli/prompts.js +98 -0
- package/dist/cli/prompts.js.map +1 -0
- package/dist/cli/ui.d.ts +38 -0
- package/dist/cli/ui.js +990 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/handler/index.d.ts +93 -0
- package/dist/handler/index.js +628 -0
- package/dist/handler/index.js.map +1 -0
- package/dist/handler/walletConnect.d.ts +6 -0
- package/dist/handler/walletConnect.js +623 -0
- package/dist/handler/walletConnect.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +2046 -0
- package/dist/index.js.map +1 -0
- package/dist/session/index.d.ts +20 -0
- package/dist/session/index.js +79 -0
- package/dist/session/index.js.map +1 -0
- package/dist/solana/index.d.ts +5 -0
- package/dist/solana/index.js +302 -0
- package/dist/solana/index.js.map +1 -0
- package/dist/solana/rpc.d.ts +45 -0
- package/dist/solana/rpc.js +120 -0
- package/dist/solana/rpc.js.map +1 -0
- package/dist/solana/simulate.d.ts +41 -0
- package/dist/solana/simulate.js +173 -0
- package/dist/solana/simulate.js.map +1 -0
- package/dist/solana/tx.d.ts +54 -0
- package/dist/solana/tx.js +141 -0
- package/dist/solana/tx.js.map +1 -0
- package/dist/vault/accounts.d.ts +88 -0
- package/dist/vault/accounts.js +126 -0
- package/dist/vault/accounts.js.map +1 -0
- package/dist/vault/config.d.ts +40 -0
- package/dist/vault/config.js +131 -0
- package/dist/vault/config.js.map +1 -0
- package/dist/vault/index.d.ts +122 -0
- package/dist/vault/index.js +580 -0
- package/dist/vault/index.js.map +1 -0
- package/dist/vault/keystore.d.ts +44 -0
- package/dist/vault/keystore.js +118 -0
- package/dist/vault/keystore.js.map +1 -0
- package/dist/vault/mnemonic.d.ts +43 -0
- package/dist/vault/mnemonic.js +37 -0
- package/dist/vault/mnemonic.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/vault/mnemonic.ts
|
|
4
|
+
import * as bip39 from "bip39";
|
|
5
|
+
function generateMnemonic2(strength = 12) {
|
|
6
|
+
const mnemonic = bip39.generateMnemonic(strength == 12 ? 128 : 256);
|
|
7
|
+
const wordCount = strength;
|
|
8
|
+
return { mnemonic, wordCount };
|
|
9
|
+
}
|
|
10
|
+
function validateMnemonic2(mnemonic) {
|
|
11
|
+
const cleaned = cleanMnemonic(mnemonic);
|
|
12
|
+
return bip39.validateMnemonic(cleaned);
|
|
13
|
+
}
|
|
14
|
+
async function mnemonicToSeed2(mnemonic, passphrase = "") {
|
|
15
|
+
const cleaned = cleanMnemonic(mnemonic);
|
|
16
|
+
if (!validateMnemonic2(cleaned)) {
|
|
17
|
+
throw new Error("Invalid mnemonic: failed wordlist or checksum validation");
|
|
18
|
+
}
|
|
19
|
+
return bip39.mnemonicToSeed(cleaned, passphrase);
|
|
20
|
+
}
|
|
21
|
+
function mnemonicToNumberedWords(mnemonic) {
|
|
22
|
+
return cleanMnemonic(mnemonic).split(" ").map((word, i) => `${i + 1}. ${word}`);
|
|
23
|
+
}
|
|
24
|
+
function wordsToMnemonic(words) {
|
|
25
|
+
return words.map((w) => w.trim().toLowerCase()).join(" ");
|
|
26
|
+
}
|
|
27
|
+
function cleanMnemonic(mnemonic) {
|
|
28
|
+
return mnemonic.trim().toLowerCase().replace(/\s+/g, " ");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/vault/keystore.ts
|
|
32
|
+
import crypto from "crypto";
|
|
33
|
+
import fs from "fs";
|
|
34
|
+
import os from "os";
|
|
35
|
+
import path from "path";
|
|
36
|
+
var VAULT_VERSION = 1;
|
|
37
|
+
var PBKDF2_ITERATIONS = 21e4;
|
|
38
|
+
var PBKDF2_DIGEST = "sha512";
|
|
39
|
+
var KEY_LENGTH = 32;
|
|
40
|
+
var SALT_LENGTH = 32;
|
|
41
|
+
var IV_LENGTH = 16;
|
|
42
|
+
var CIPHER = "aes-256-gcm";
|
|
43
|
+
function getWalletDir() {
|
|
44
|
+
return path.join(os.homedir(), ".wallet");
|
|
45
|
+
}
|
|
46
|
+
function getVaultPath() {
|
|
47
|
+
return path.join(getWalletDir(), "vault.enc");
|
|
48
|
+
}
|
|
49
|
+
function getConfigPath() {
|
|
50
|
+
return path.join(getWalletDir(), "config.json");
|
|
51
|
+
}
|
|
52
|
+
function getSessionsPath() {
|
|
53
|
+
return path.join(getWalletDir(), "sessions.json");
|
|
54
|
+
}
|
|
55
|
+
var SESSIONS_FILE = getSessionsPath();
|
|
56
|
+
function ensureWalletDir() {
|
|
57
|
+
const dir = getWalletDir();
|
|
58
|
+
if (!fs.existsSync(dir)) {
|
|
59
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function vaultExists() {
|
|
63
|
+
return fs.existsSync(getVaultPath());
|
|
64
|
+
}
|
|
65
|
+
async function saveVault(data, password) {
|
|
66
|
+
ensureWalletDir();
|
|
67
|
+
const salt = crypto.randomBytes(SALT_LENGTH);
|
|
68
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
69
|
+
const key = await deriveKey(password, salt);
|
|
70
|
+
const plaintext = JSON.stringify(data);
|
|
71
|
+
const cipher = crypto.createCipheriv(CIPHER, key, iv);
|
|
72
|
+
const encrypted = Buffer.concat([
|
|
73
|
+
cipher.update(plaintext, "utf8"),
|
|
74
|
+
cipher.final()
|
|
75
|
+
]);
|
|
76
|
+
const authTag = cipher.getAuthTag();
|
|
77
|
+
const vault = {
|
|
78
|
+
version: VAULT_VERSION,
|
|
79
|
+
salt: salt.toString("hex"),
|
|
80
|
+
iv: iv.toString("hex"),
|
|
81
|
+
authTag: authTag.toString("hex"),
|
|
82
|
+
ciphertext: encrypted.toString("hex")
|
|
83
|
+
};
|
|
84
|
+
fs.writeFileSync(getVaultPath(), JSON.stringify(vault, null, 2), {
|
|
85
|
+
mode: 384
|
|
86
|
+
// owner read/write only
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function loadVault(password) {
|
|
90
|
+
if (!vaultExists()) {
|
|
91
|
+
throw new Error("No vault found. Run `wallet init` to create one.");
|
|
92
|
+
}
|
|
93
|
+
const raw = fs.readFileSync(getVaultPath(), "utf8");
|
|
94
|
+
const vault = JSON.parse(raw);
|
|
95
|
+
if (vault.version !== VAULT_VERSION) {
|
|
96
|
+
throw new Error(`Unsupported vault version: ${vault.version}`);
|
|
97
|
+
}
|
|
98
|
+
const salt = Buffer.from(vault.salt, "hex");
|
|
99
|
+
const iv = Buffer.from(vault.iv, "hex");
|
|
100
|
+
const authTag = Buffer.from(vault.authTag, "hex");
|
|
101
|
+
const ciphertext = Buffer.from(vault.ciphertext, "hex");
|
|
102
|
+
const key = await deriveKey(password, salt);
|
|
103
|
+
try {
|
|
104
|
+
const decipher = crypto.createDecipheriv(CIPHER, key, iv);
|
|
105
|
+
decipher.setAuthTag(authTag);
|
|
106
|
+
const decrypted = Buffer.concat([
|
|
107
|
+
decipher.update(ciphertext),
|
|
108
|
+
decipher.final()
|
|
109
|
+
]);
|
|
110
|
+
return JSON.parse(decrypted.toString("utf8"));
|
|
111
|
+
} catch {
|
|
112
|
+
throw new Error("Decryption failed: wrong password or vault is corrupted.");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function changePassword(oldPassword, newPassword) {
|
|
116
|
+
const data = await loadVault(oldPassword);
|
|
117
|
+
await saveVault(data, newPassword);
|
|
118
|
+
}
|
|
119
|
+
function deriveKey(password, salt) {
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
crypto.pbkdf2(
|
|
122
|
+
password,
|
|
123
|
+
salt,
|
|
124
|
+
PBKDF2_ITERATIONS,
|
|
125
|
+
KEY_LENGTH,
|
|
126
|
+
PBKDF2_DIGEST,
|
|
127
|
+
(err, key) => {
|
|
128
|
+
if (err) reject(err);
|
|
129
|
+
else resolve(key);
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/vault/accounts.ts
|
|
136
|
+
import { derivePath } from "ed25519-hd-key";
|
|
137
|
+
import nacl from "tweetnacl";
|
|
138
|
+
import bs58 from "bs58";
|
|
139
|
+
function deriveAccount(seed, index, name = `Account ${index + 1}`) {
|
|
140
|
+
const path2 = derivationPath(index);
|
|
141
|
+
const { key: privateKeyBytes } = derivePath(path2, seed.toString("hex"));
|
|
142
|
+
const keypair = nacl.sign.keyPair.fromSeed(privateKeyBytes);
|
|
143
|
+
const publicKey = bs58.encode(Buffer.from(keypair.publicKey));
|
|
144
|
+
return {
|
|
145
|
+
index,
|
|
146
|
+
name,
|
|
147
|
+
publicKey,
|
|
148
|
+
secretKey: keypair.secretKey,
|
|
149
|
+
// 64 bytes: seed + public key
|
|
150
|
+
derivationPath: path2,
|
|
151
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function deriveAccounts(seed, accountStore) {
|
|
155
|
+
return accountStore.accounts.map(
|
|
156
|
+
(account) => deriveAccount(seed, account.index, account.name)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
async function deriveAccountFromMnemonic(mnemonic, index, name, passphrase) {
|
|
160
|
+
const seed = await mnemonicToSeed2(mnemonic, passphrase);
|
|
161
|
+
return deriveAccount(seed, index, name);
|
|
162
|
+
}
|
|
163
|
+
function createAccountStore(firstAccountName = "Account 1") {
|
|
164
|
+
return {
|
|
165
|
+
accounts: [
|
|
166
|
+
{
|
|
167
|
+
index: 0,
|
|
168
|
+
name: firstAccountName,
|
|
169
|
+
publicKey: "",
|
|
170
|
+
// filled in after derivation
|
|
171
|
+
derivationPath: derivationPath(0),
|
|
172
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
173
|
+
}
|
|
174
|
+
],
|
|
175
|
+
activeIndex: 0
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function addAccount(store, seed, name) {
|
|
179
|
+
const nextIndex = store.accounts.length > 0 ? Math.max(...store.accounts.map((a) => a.index)) + 1 : 0;
|
|
180
|
+
const accountName = name ?? `Account ${nextIndex + 1}`;
|
|
181
|
+
const keypair = deriveAccount(seed, nextIndex, accountName);
|
|
182
|
+
const newAccount = {
|
|
183
|
+
index: nextIndex,
|
|
184
|
+
name: accountName,
|
|
185
|
+
publicKey: keypair.publicKey,
|
|
186
|
+
derivationPath: keypair.derivationPath,
|
|
187
|
+
createdAt: keypair.createdAt
|
|
188
|
+
};
|
|
189
|
+
const updatedStore = {
|
|
190
|
+
...store,
|
|
191
|
+
accounts: [...store.accounts, newAccount]
|
|
192
|
+
};
|
|
193
|
+
return { store: updatedStore, keypair };
|
|
194
|
+
}
|
|
195
|
+
function setActiveAccount(store, index) {
|
|
196
|
+
const exists = store.accounts.find((a) => a.index === index);
|
|
197
|
+
if (!exists) {
|
|
198
|
+
throw new Error(`Account index ${index} does not exist.`);
|
|
199
|
+
}
|
|
200
|
+
return { ...store, activeIndex: index };
|
|
201
|
+
}
|
|
202
|
+
function renameAccount(store, index, newName) {
|
|
203
|
+
return {
|
|
204
|
+
...store,
|
|
205
|
+
accounts: store.accounts.map(
|
|
206
|
+
(a) => a.index === index ? { ...a, name: newName } : a
|
|
207
|
+
)
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function getActiveAccount(store) {
|
|
211
|
+
const account = store.accounts.find((a) => a.index === store.activeIndex);
|
|
212
|
+
if (!account) throw new Error("No active account found.");
|
|
213
|
+
return account;
|
|
214
|
+
}
|
|
215
|
+
function signMessage(message, secretKey) {
|
|
216
|
+
const signature = nacl.sign.detached(message, secretKey);
|
|
217
|
+
return bs58.encode(Buffer.from(signature));
|
|
218
|
+
}
|
|
219
|
+
function formatAccount(account, isActive) {
|
|
220
|
+
const activeMarker = isActive ? "\u25CF" : "\u25CB";
|
|
221
|
+
const shortKey = `${account.publicKey.slice(0, 4)}...${account.publicKey.slice(-4)}`;
|
|
222
|
+
return `${activeMarker} [${account.index}] ${account.name.padEnd(20)} ${shortKey}`;
|
|
223
|
+
}
|
|
224
|
+
function derivationPath(index) {
|
|
225
|
+
return `m/44'/501'/${index}'/0'`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/vault/config.ts
|
|
229
|
+
import fs2 from "fs";
|
|
230
|
+
var HELIUS_API_KEY = process.env.HELIUS_API_KEY;
|
|
231
|
+
var RPC_URLS = {
|
|
232
|
+
"mainnet-beta": HELIUS_API_KEY ? `https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}` : "https://api.mainnet-beta.solana.com",
|
|
233
|
+
devnet: HELIUS_API_KEY ? `https://devnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}` : "https://api.devnet.solana.com",
|
|
234
|
+
testnet: "https://api.testnet.solana.com",
|
|
235
|
+
localnet: "http://localhost:8899"
|
|
236
|
+
};
|
|
237
|
+
var CONFIG_VERSION = 1;
|
|
238
|
+
function configExists() {
|
|
239
|
+
return fs2.existsSync(getConfigPath());
|
|
240
|
+
}
|
|
241
|
+
function loadConfig() {
|
|
242
|
+
if (!configExists()) {
|
|
243
|
+
throw new Error("No config found. Run `wallet init` first.");
|
|
244
|
+
}
|
|
245
|
+
const raw = fs2.readFileSync(getConfigPath(), "utf8");
|
|
246
|
+
return JSON.parse(raw);
|
|
247
|
+
}
|
|
248
|
+
function saveConfig(config) {
|
|
249
|
+
ensureWalletDir();
|
|
250
|
+
const updated = { ...config, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
251
|
+
fs2.writeFileSync(getConfigPath(), JSON.stringify(updated, null, 2), {
|
|
252
|
+
mode: 384
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function createConfig(walletConnectProjectId = "", cluster = "devnet") {
|
|
256
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
257
|
+
return {
|
|
258
|
+
version: CONFIG_VERSION,
|
|
259
|
+
cluster,
|
|
260
|
+
rpcUrl: RPC_URLS[cluster],
|
|
261
|
+
walletConnectProjectId,
|
|
262
|
+
accountStore: createAccountStore("Account 1"),
|
|
263
|
+
createdAt: now,
|
|
264
|
+
updatedAt: now
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function updateCluster(cluster) {
|
|
268
|
+
const config = loadConfig();
|
|
269
|
+
config.cluster = cluster;
|
|
270
|
+
config.rpcUrl = RPC_URLS[cluster];
|
|
271
|
+
saveConfig(config);
|
|
272
|
+
}
|
|
273
|
+
function updateRpcUrl(url) {
|
|
274
|
+
const config = loadConfig();
|
|
275
|
+
config.rpcUrl = url;
|
|
276
|
+
saveConfig(config);
|
|
277
|
+
}
|
|
278
|
+
function updateAccountStore(accountStore) {
|
|
279
|
+
const config = loadConfig();
|
|
280
|
+
config.accountStore = accountStore;
|
|
281
|
+
saveConfig(config);
|
|
282
|
+
}
|
|
283
|
+
function getRpcUrl() {
|
|
284
|
+
return loadConfig().rpcUrl;
|
|
285
|
+
}
|
|
286
|
+
function getCluster() {
|
|
287
|
+
return loadConfig().cluster;
|
|
288
|
+
}
|
|
289
|
+
function getAccountStore() {
|
|
290
|
+
return loadConfig().accountStore;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/vault/index.ts
|
|
294
|
+
var WalletVault = class {
|
|
295
|
+
_keypairs = [];
|
|
296
|
+
_seed = null;
|
|
297
|
+
// cached for agent account derivation
|
|
298
|
+
_config = null;
|
|
299
|
+
_unlocked = false;
|
|
300
|
+
_mnemonic = null;
|
|
301
|
+
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
302
|
+
/**
|
|
303
|
+
* Initialize a brand new wallet.
|
|
304
|
+
* Generates a mnemonic, encrypts it, writes config.
|
|
305
|
+
* Returns the mnemonic for the user to write down — shown ONCE.
|
|
306
|
+
*/
|
|
307
|
+
async init(password, options = {}) {
|
|
308
|
+
if (vaultExists()) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
"Wallet already exists. Use `wallet unlock` to access it."
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
const { mnemonic } = generateMnemonic2(options.strength ?? 12);
|
|
314
|
+
const vaultData = {
|
|
315
|
+
mnemonic,
|
|
316
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
317
|
+
version: 1
|
|
318
|
+
};
|
|
319
|
+
await saveVault(vaultData, password);
|
|
320
|
+
const config = createConfig(
|
|
321
|
+
options.walletConnectProjectId ?? "",
|
|
322
|
+
options.cluster ?? "devnet"
|
|
323
|
+
);
|
|
324
|
+
if (options.firstAccountName) {
|
|
325
|
+
config.accountStore.accounts[0].name = options.firstAccountName;
|
|
326
|
+
}
|
|
327
|
+
const seed = await mnemonicToSeed2(mnemonic);
|
|
328
|
+
const firstKeypair = deriveAccount(
|
|
329
|
+
seed,
|
|
330
|
+
0,
|
|
331
|
+
config.accountStore.accounts[0].name
|
|
332
|
+
);
|
|
333
|
+
config.accountStore.accounts[0].publicKey = firstKeypair.publicKey;
|
|
334
|
+
saveConfig(config);
|
|
335
|
+
return mnemonic;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Unlock the wallet for a session.
|
|
339
|
+
* Decrypts the vault, derives all account keypairs into memory.
|
|
340
|
+
*/
|
|
341
|
+
async unlock(password) {
|
|
342
|
+
const vaultData = await loadVault(password);
|
|
343
|
+
const config = loadConfig();
|
|
344
|
+
const seed = await mnemonicToSeed2(vaultData.mnemonic);
|
|
345
|
+
this._seed = seed;
|
|
346
|
+
this._mnemonic = vaultData.mnemonic;
|
|
347
|
+
this._keypairs = deriveAccounts(seed, config.accountStore);
|
|
348
|
+
this._config = config;
|
|
349
|
+
this._unlocked = true;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Restore wallet from an existing mnemonic (import flow).
|
|
353
|
+
*/
|
|
354
|
+
async restore(mnemonic, password, options = {}) {
|
|
355
|
+
if (!validateMnemonic2(mnemonic)) {
|
|
356
|
+
throw new Error("Invalid mnemonic phrase.");
|
|
357
|
+
}
|
|
358
|
+
const vaultData = {
|
|
359
|
+
mnemonic,
|
|
360
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
361
|
+
version: 1
|
|
362
|
+
};
|
|
363
|
+
await saveVault(vaultData, password);
|
|
364
|
+
const config = createConfig("", options.cluster ?? "devnet");
|
|
365
|
+
const seed = await mnemonicToSeed2(mnemonic);
|
|
366
|
+
const firstKeypair = deriveAccount(seed, 0, "Account 1");
|
|
367
|
+
config.accountStore.accounts[0].publicKey = firstKeypair.publicKey;
|
|
368
|
+
saveConfig(config);
|
|
369
|
+
}
|
|
370
|
+
lock() {
|
|
371
|
+
this._keypairs = [];
|
|
372
|
+
this._mnemonic = null;
|
|
373
|
+
this._seed = null;
|
|
374
|
+
this._config = null;
|
|
375
|
+
this._unlocked = false;
|
|
376
|
+
}
|
|
377
|
+
// ── Account Management ─────────────────────────────────────────────────────
|
|
378
|
+
/**
|
|
379
|
+
* Get the currently active keypair (used for signing + dApp connections).
|
|
380
|
+
*/
|
|
381
|
+
getActiveKeypair() {
|
|
382
|
+
this.assertUnlocked();
|
|
383
|
+
const active = getActiveAccount(this._config.accountStore);
|
|
384
|
+
const keypair = this._keypairs.find((k) => k.index === active.index);
|
|
385
|
+
if (!keypair) throw new Error("Active keypair not found in session.");
|
|
386
|
+
return keypair;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Lock the wallet — wipe all keypairs from memory.
|
|
390
|
+
*/
|
|
391
|
+
/**
|
|
392
|
+
* Returns the mnemonic — only available while unlocked.
|
|
393
|
+
* Used by agent_connect to derive new accounts on demand.
|
|
394
|
+
*/
|
|
395
|
+
getMnemonic() {
|
|
396
|
+
if (!this._mnemonic) throw new Error("Wallet is locked");
|
|
397
|
+
return this._mnemonic;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Reload config and re-derive keypairs from seed.
|
|
401
|
+
* Call after adding new accounts so the vault picks them up.
|
|
402
|
+
*/
|
|
403
|
+
async reload() {
|
|
404
|
+
if (!this._seed) throw new Error("Wallet is locked");
|
|
405
|
+
this._config = loadConfig();
|
|
406
|
+
this._keypairs = deriveAccounts(this._seed, this._config.accountStore);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Get a keypair by account index.
|
|
410
|
+
*/
|
|
411
|
+
getKeypair(index) {
|
|
412
|
+
this.assertUnlocked();
|
|
413
|
+
const keypair = this._keypairs.find((k) => k.index === index);
|
|
414
|
+
if (!keypair) throw new Error(`Account ${index} not found.`);
|
|
415
|
+
return keypair;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Get all loaded keypairs.
|
|
419
|
+
*/
|
|
420
|
+
getAllKeypairs() {
|
|
421
|
+
this.assertUnlocked();
|
|
422
|
+
return [...this._keypairs];
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Add a new derived account.
|
|
426
|
+
*/
|
|
427
|
+
async addAccount(name, password) {
|
|
428
|
+
this.assertUnlocked();
|
|
429
|
+
if (!password) throw new Error("Password required to add a new account.");
|
|
430
|
+
const vaultData = await loadVault(password);
|
|
431
|
+
const seed = await mnemonicToSeed2(vaultData.mnemonic);
|
|
432
|
+
const { store, keypair } = addAccount(
|
|
433
|
+
this._config.accountStore,
|
|
434
|
+
seed,
|
|
435
|
+
name
|
|
436
|
+
);
|
|
437
|
+
this._config.accountStore = store;
|
|
438
|
+
this._keypairs.push(keypair);
|
|
439
|
+
updateAccountStore(store);
|
|
440
|
+
return keypair;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Switch the active account.
|
|
444
|
+
*/
|
|
445
|
+
setActiveAccount(index) {
|
|
446
|
+
this.assertUnlocked();
|
|
447
|
+
const updated = setActiveAccount(this._config.accountStore, index);
|
|
448
|
+
this._config.accountStore = updated;
|
|
449
|
+
updateAccountStore(updated);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Rename an account.
|
|
453
|
+
*/
|
|
454
|
+
renameAccount(index, newName) {
|
|
455
|
+
this.assertUnlocked();
|
|
456
|
+
const updated = renameAccount(this._config.accountStore, index, newName);
|
|
457
|
+
this._config.accountStore = updated;
|
|
458
|
+
this._keypairs = this._keypairs.map(
|
|
459
|
+
(k) => k.index === index ? { ...k, name: newName } : k
|
|
460
|
+
);
|
|
461
|
+
updateAccountStore(updated);
|
|
462
|
+
}
|
|
463
|
+
// ── Display ────────────────────────────────────────────────────────────────
|
|
464
|
+
/**
|
|
465
|
+
* Format all accounts for terminal display.
|
|
466
|
+
*/
|
|
467
|
+
listAccounts() {
|
|
468
|
+
this.assertUnlocked();
|
|
469
|
+
const activeIndex = this._config.accountStore.activeIndex;
|
|
470
|
+
return this._keypairs.map((k) => formatAccount(k, k.index === activeIndex));
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Sign a message with the active account.
|
|
474
|
+
*/
|
|
475
|
+
signMessage(message) {
|
|
476
|
+
const keypair = this.getActiveKeypair();
|
|
477
|
+
return signMessage(message, keypair.secretKey);
|
|
478
|
+
}
|
|
479
|
+
// ── State ──────────────────────────────────────────────────────────────────
|
|
480
|
+
get isUnlocked() {
|
|
481
|
+
return this._unlocked;
|
|
482
|
+
}
|
|
483
|
+
get config() {
|
|
484
|
+
this.assertUnlocked();
|
|
485
|
+
return this._config;
|
|
486
|
+
}
|
|
487
|
+
static exists() {
|
|
488
|
+
return vaultExists() && configExists();
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Display mnemonic as numbered words — for backup verification flow.
|
|
492
|
+
*/
|
|
493
|
+
static formatMnemonicForDisplay(mnemonic) {
|
|
494
|
+
const words = mnemonicToNumberedWords(mnemonic);
|
|
495
|
+
const cols = 3;
|
|
496
|
+
const rows = [];
|
|
497
|
+
for (let i = 0; i < words.length; i += cols) {
|
|
498
|
+
rows.push(
|
|
499
|
+
words.slice(i, i + cols).map((w) => w.padEnd(18)).join(" ")
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
return rows.join("\n");
|
|
503
|
+
}
|
|
504
|
+
// ── Internal ───────────────────────────────────────────────────────────────
|
|
505
|
+
assertUnlocked() {
|
|
506
|
+
if (!this._unlocked) {
|
|
507
|
+
throw new Error("Wallet is locked. Run `wallet unlock` first.");
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Find an account by name or create it if it doesn't exist.
|
|
512
|
+
* Uses the cached seed — no password needed.
|
|
513
|
+
* This is the primary way agents acquire their account.
|
|
514
|
+
*/
|
|
515
|
+
async findOrCreate(name) {
|
|
516
|
+
this.assertUnlocked();
|
|
517
|
+
const existing = this._keypairs.find((k) => k.name === name);
|
|
518
|
+
if (existing) return existing;
|
|
519
|
+
const nextIndex = this._config.accountStore.accounts.length;
|
|
520
|
+
const { store, keypair } = addAccount(
|
|
521
|
+
this._config.accountStore,
|
|
522
|
+
this._seed,
|
|
523
|
+
name
|
|
524
|
+
);
|
|
525
|
+
this._config.accountStore = store;
|
|
526
|
+
this._keypairs.push(keypair);
|
|
527
|
+
updateAccountStore(store);
|
|
528
|
+
console.log(
|
|
529
|
+
`[Vault] Created account "${name}" at index ${nextIndex} (${keypair.publicKey})`
|
|
530
|
+
);
|
|
531
|
+
return keypair;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Find an account by name. Returns null if not found.
|
|
535
|
+
*/
|
|
536
|
+
findByName(name) {
|
|
537
|
+
this.assertUnlocked();
|
|
538
|
+
return this._keypairs.find((k) => k.name === name) ?? null;
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
export {
|
|
542
|
+
RPC_URLS,
|
|
543
|
+
SESSIONS_FILE,
|
|
544
|
+
WalletVault,
|
|
545
|
+
addAccount,
|
|
546
|
+
changePassword,
|
|
547
|
+
configExists,
|
|
548
|
+
createAccountStore,
|
|
549
|
+
createConfig,
|
|
550
|
+
deriveAccount,
|
|
551
|
+
deriveAccountFromMnemonic,
|
|
552
|
+
deriveAccounts,
|
|
553
|
+
ensureWalletDir,
|
|
554
|
+
formatAccount,
|
|
555
|
+
generateMnemonic2 as generateMnemonic,
|
|
556
|
+
getAccountStore,
|
|
557
|
+
getActiveAccount,
|
|
558
|
+
getCluster,
|
|
559
|
+
getConfigPath,
|
|
560
|
+
getRpcUrl,
|
|
561
|
+
getSessionsPath,
|
|
562
|
+
getVaultPath,
|
|
563
|
+
getWalletDir,
|
|
564
|
+
loadConfig,
|
|
565
|
+
loadVault,
|
|
566
|
+
mnemonicToNumberedWords,
|
|
567
|
+
mnemonicToSeed2 as mnemonicToSeed,
|
|
568
|
+
renameAccount,
|
|
569
|
+
saveConfig,
|
|
570
|
+
saveVault,
|
|
571
|
+
setActiveAccount,
|
|
572
|
+
signMessage,
|
|
573
|
+
updateAccountStore,
|
|
574
|
+
updateCluster,
|
|
575
|
+
updateRpcUrl,
|
|
576
|
+
validateMnemonic2 as validateMnemonic,
|
|
577
|
+
vaultExists,
|
|
578
|
+
wordsToMnemonic
|
|
579
|
+
};
|
|
580
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/vault/mnemonic.ts","../../src/vault/keystore.ts","../../src/vault/accounts.ts","../../src/vault/config.ts","../../src/vault/index.ts"],"sourcesContent":["/**\n *\n * BIP-39 mnemonic generation, validation, and seed derivation.\n * Supports 12-word (128-bit) and 24-word (256-bit) mnemonics.\n *\n * Dependencies: bip39\n */\n\nimport * as bip39 from \"bip39\";\n\nexport type MnemonicStrength = 12 | 24; // 128 = 12 words, 256 = 24 words\n\nexport interface MnemonicResult {\n mnemonic: string;\n wordCount: 12 | 24;\n}\n\n/**\n * Generate a new cryptographically random BIP-39 mnemonic.\n * @param strength 128 for 12-word, 256 for 24-word (default: 128)\n */\nexport function generateMnemonic(\n strength: MnemonicStrength = 12,\n): MnemonicResult {\n const mnemonic = bip39.generateMnemonic(strength == 12 ? 128 : 256);\n const wordCount = strength;\n return { mnemonic, wordCount };\n}\n\n/**\n * Validate a BIP-39 mnemonic phrase.\n * Checks both wordlist membership and BIP-39 checksum.\n */\nexport function validateMnemonic(mnemonic: string): boolean {\n const cleaned = cleanMnemonic(mnemonic);\n return bip39.validateMnemonic(cleaned);\n}\n\n/**\n * Derive a 64-byte seed buffer from a mnemonic.\n * Optional passphrase adds extra security per BIP-39 spec.\n * This seed is the root of ALL derived accounts.\n */\nexport async function mnemonicToSeed(\n mnemonic: string,\n passphrase: string = \"\",\n): Promise<Buffer> {\n const cleaned = cleanMnemonic(mnemonic);\n\n if (!validateMnemonic(cleaned)) {\n throw new Error(\"Invalid mnemonic: failed wordlist or checksum validation\");\n }\n\n return bip39.mnemonicToSeed(cleaned, passphrase);\n}\n\n/**\n * Split mnemonic into a numbered word array.\n * Used for backup verification display in the terminal UI.\n *\n * Example output:\n * [ \"1. witch\", \"2. collapse\", \"3. practice\", ... ]\n */\nexport function mnemonicToNumberedWords(mnemonic: string): string[] {\n return cleanMnemonic(mnemonic)\n .split(\" \")\n .map((word, i) => `${i + 1}. ${word}`);\n}\n\n/**\n * Reconstruct mnemonic from a word array.\n * Used when user inputs words one by one during restore flow.\n */\nexport function wordsToMnemonic(words: string[]): string {\n return words.map((w) => w.trim().toLowerCase()).join(\" \");\n}\n\n// ── Internal ──────────────────────────────────────────────────────────────────\n\nfunction cleanMnemonic(mnemonic: string): string {\n return mnemonic.trim().toLowerCase().replace(/\\s+/g, \" \");\n}\n","/**\n *\n * AES-256-GCM encrypted keystore.\n * The mnemonic (seed phrase) is encrypted at rest in ~/.wallet/vault.enc\n * Private keys NEVER touch disk — they are derived in memory at runtime.\n *\n * Encryption scheme:\n * - Key derivation: PBKDF2 (SHA-512, 210,000 iterations) — OWASP recommended\n * - Cipher: AES-256-GCM (authenticated encryption — detects tampering)\n * - Salt: 32 random bytes (unique per vault)\n * - IV: 16 random bytes (unique per encryption)\n *\n * Dependencies: Node.js built-in `crypto`, `fs`, `os`, `path`\n */\n\nimport crypto from \"crypto\";\nimport fs from \"fs\";\nimport os from \"os\";\nimport path from \"path\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface VaultData {\n mnemonic: string;\n createdAt: string;\n version: number;\n}\n\ninterface EncryptedVault {\n version: number; // format version for future migrations\n salt: string; // hex — used for PBKDF2 key derivation\n iv: string; // hex — AES-GCM initialisation vector\n authTag: string; // hex — GCM authentication tag (detects tampering)\n ciphertext: string; // hex — encrypted vault data\n}\n\n// ── Constants ─────────────────────────────────────────────────────────────────\n\nconst VAULT_VERSION = 1;\nconst PBKDF2_ITERATIONS = 210_000; // OWASP 2024 recommendation for PBKDF2-SHA512\nconst PBKDF2_DIGEST = \"sha512\";\nconst KEY_LENGTH = 32; // 256 bits for AES-256\nconst SALT_LENGTH = 32; // 256 bits\nconst IV_LENGTH = 16; // 128 bits for AES-GCM\nconst CIPHER = \"aes-256-gcm\";\n\n// ── Vault Path ────────────────────────────────────────────────────────────────\n\nexport function getWalletDir(): string {\n // return path.join(__dirname, \".wallet\");\n return path.join(os.homedir(), \".wallet\");\n}\n\nexport function getVaultPath(): string {\n return path.join(getWalletDir(), \"vault.enc\");\n}\n\nexport function getConfigPath(): string {\n return path.join(getWalletDir(), \"config.json\");\n}\n\nexport function getSessionsPath(): string {\n return path.join(getWalletDir(), \"sessions.json\");\n}\n\nexport const SESSIONS_FILE = getSessionsPath();\n\nexport function ensureWalletDir(): void {\n const dir = getWalletDir();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); // owner-only access\n }\n}\n\nexport function vaultExists(): boolean {\n return fs.existsSync(getVaultPath());\n}\n\n// ── Encryption ────────────────────────────────────────────────────────────────\n\n/**\n * Encrypt and save the vault to disk.\n * @param data - vault contents (mnemonic + metadata)\n * @param password - user's wallet password\n */\nexport async function saveVault(\n data: VaultData,\n password: string,\n): Promise<void> {\n ensureWalletDir();\n\n const salt = crypto.randomBytes(SALT_LENGTH);\n const iv = crypto.randomBytes(IV_LENGTH);\n const key = await deriveKey(password, salt);\n\n const plaintext = JSON.stringify(data);\n const cipher = crypto.createCipheriv(CIPHER, key, iv);\n\n const encrypted = Buffer.concat([\n cipher.update(plaintext, \"utf8\"),\n cipher.final(),\n ]);\n\n const authTag = cipher.getAuthTag();\n\n const vault: EncryptedVault = {\n version: VAULT_VERSION,\n salt: salt.toString(\"hex\"),\n iv: iv.toString(\"hex\"),\n authTag: authTag.toString(\"hex\"),\n ciphertext: encrypted.toString(\"hex\"),\n };\n\n fs.writeFileSync(getVaultPath(), JSON.stringify(vault, null, 2), {\n mode: 0o600, // owner read/write only\n });\n}\n\n/**\n * Load and decrypt the vault from disk.\n * Throws if the password is wrong or the file has been tampered with.\n */\nexport async function loadVault(password: string): Promise<VaultData> {\n if (!vaultExists()) {\n throw new Error(\"No vault found. Run `wallet init` to create one.\");\n }\n\n const raw = fs.readFileSync(getVaultPath(), \"utf8\");\n const vault: EncryptedVault = JSON.parse(raw);\n\n if (vault.version !== VAULT_VERSION) {\n throw new Error(`Unsupported vault version: ${vault.version}`);\n }\n\n const salt = Buffer.from(vault.salt, \"hex\");\n const iv = Buffer.from(vault.iv, \"hex\");\n const authTag = Buffer.from(vault.authTag, \"hex\");\n const ciphertext = Buffer.from(vault.ciphertext, \"hex\");\n\n const key = await deriveKey(password, salt);\n\n try {\n const decipher = crypto.createDecipheriv(CIPHER, key, iv);\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([\n decipher.update(ciphertext),\n decipher.final(),\n ]);\n\n return JSON.parse(decrypted.toString(\"utf8\")) as VaultData;\n } catch {\n // GCM auth tag failure means wrong password OR tampered file\n throw new Error(\"Decryption failed: wrong password or vault is corrupted.\");\n }\n}\n\n/**\n * Change the vault password.\n * Decrypts with old password, re-encrypts with new password.\n */\nexport async function changePassword(\n oldPassword: string,\n newPassword: string,\n): Promise<void> {\n const data = await loadVault(oldPassword);\n await saveVault(data, newPassword);\n}\n\n// ── Internal ──────────────────────────────────────────────────────────────────\n\n/**\n * Derive a 256-bit AES key from a password using PBKDF2-SHA512.\n */\nfunction deriveKey(password: string, salt: Buffer): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n crypto.pbkdf2(\n password,\n salt,\n PBKDF2_ITERATIONS,\n KEY_LENGTH,\n PBKDF2_DIGEST,\n (err, key) => {\n if (err) reject(err);\n else resolve(key);\n },\n );\n });\n}\n","/**\n *\n * BIP-44 HD key derivation for Solana accounts.\n *\n * Derivation path: m/44'/501'/index'/0'\n * 44' = BIP-44 purpose\n * 501' = Solana's registered coin type (SLIP-44)\n * n' = account index (0 = first account, 1 = second, etc.)\n * 0' = change (always 0 for Solana)\n *\n * This is the exact path Phantom, Backpack, and Solflare use.\n * Same seed → same addresses as those wallets.\n *\n * Dependencies: ed25519-hd-key, tweetnacl, bs58\n */\n\nimport { derivePath } from \"ed25519-hd-key\";\nimport nacl from \"tweetnacl\";\nimport bs58 from \"bs58\";\nimport { mnemonicToSeed } from \"./mnemonic\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface Account {\n index: number; // derivation index\n name: string; // user-defined label\n publicKey: string; // Base58 encoded — this is the Solana address\n derivationPath: string; // m/44'/501'/index'/0'\n createdAt: string;\n}\n\n/**\n * In-memory keypair — private key NEVER persisted to disk.\n * Lives only for the duration of the daemon session.\n */\nexport interface AccountKeypair extends Account {\n secretKey: Uint8Array; // 64-byte Ed25519 secret key (seed + public key)\n}\n\nexport interface AccountStore {\n accounts: Account[];\n activeIndex: number;\n}\n\n// ── Derivation ────────────────────────────────────────────────────────────────\n\n/**\n * Derive an Ed25519 keypair at a specific BIP-44 index.\n * This is the core operation — called once per session after vault unlock.\n *\n * @param seed - 64-byte seed from BIP-39 mnemonic\n * @param index - account index (0-based)\n * @param name - label for this account\n */\nexport function deriveAccount(\n seed: Buffer,\n index: number,\n name: string = `Account ${index + 1}`,\n): AccountKeypair {\n const path = derivationPath(index);\n\n // SLIP-0010 Ed25519 derivation\n const { key: privateKeyBytes } = derivePath(path, seed.toString(\"hex\"));\n\n // nacl keypair from 32-byte seed\n const keypair = nacl.sign.keyPair.fromSeed(privateKeyBytes);\n\n const publicKey = bs58.encode(Buffer.from(keypair.publicKey));\n\n return {\n index,\n name,\n publicKey,\n secretKey: keypair.secretKey, // 64 bytes: seed + public key\n derivationPath: path,\n createdAt: new Date().toISOString(),\n };\n}\n\n/**\n * Derive multiple accounts at once.\n * Used on wallet startup to load all known accounts into memory.\n */\nexport function deriveAccounts(\n seed: Buffer,\n accountStore: AccountStore,\n): AccountKeypair[] {\n return accountStore.accounts.map((account) =>\n deriveAccount(seed, account.index, account.name),\n );\n}\n\n/**\n * Derive a single account directly from mnemonic.\n * Convenience wrapper used in tests and programmatic access.\n */\nexport async function deriveAccountFromMnemonic(\n mnemonic: string,\n index: number,\n name?: string,\n passphrase?: string,\n): Promise<AccountKeypair> {\n const seed = await mnemonicToSeed(mnemonic, passphrase);\n return deriveAccount(seed, index, name);\n}\n\n// ── Account Store ─────────────────────────────────────────────────────────────\n\n/**\n * Create a fresh account store with the first account.\n */\nexport function createAccountStore(\n firstAccountName: string = \"Account 1\",\n): AccountStore {\n return {\n accounts: [\n {\n index: 0,\n name: firstAccountName,\n publicKey: \"\", // filled in after derivation\n derivationPath: derivationPath(0),\n createdAt: new Date().toISOString(),\n },\n ],\n activeIndex: 0,\n };\n}\n\n/**\n * Add a new account to the store.\n * The next index is always max(existing indices) + 1.\n */\nexport function addAccount(\n store: AccountStore,\n seed: Buffer,\n name?: string,\n): { store: AccountStore; keypair: AccountKeypair } {\n const nextIndex =\n store.accounts.length > 0\n ? Math.max(...store.accounts.map((a) => a.index)) + 1\n : 0;\n\n const accountName = name ?? `Account ${nextIndex + 1}`;\n const keypair = deriveAccount(seed, nextIndex, accountName);\n\n const newAccount: Account = {\n index: nextIndex,\n name: accountName,\n publicKey: keypair.publicKey,\n derivationPath: keypair.derivationPath,\n createdAt: keypair.createdAt,\n };\n\n const updatedStore: AccountStore = {\n ...store,\n accounts: [...store.accounts, newAccount],\n };\n\n return { store: updatedStore, keypair };\n}\n\n/**\n * Set the active account by index.\n * The active account is used for all dApp connections and signing.\n */\nexport function setActiveAccount(\n store: AccountStore,\n index: number,\n): AccountStore {\n const exists = store.accounts.find((a) => a.index === index);\n if (!exists) {\n throw new Error(`Account index ${index} does not exist.`);\n }\n return { ...store, activeIndex: index };\n}\n\n/**\n * Rename an account.\n */\nexport function renameAccount(\n store: AccountStore,\n index: number,\n newName: string,\n): AccountStore {\n return {\n ...store,\n accounts: store.accounts.map((a) =>\n a.index === index ? { ...a, name: newName } : a,\n ),\n };\n}\n\n/**\n * Get the active account metadata from the store.\n */\nexport function getActiveAccount(store: AccountStore): Account {\n const account = store.accounts.find((a) => a.index === store.activeIndex);\n if (!account) throw new Error(\"No active account found.\");\n return account;\n}\n\n// ── Utilities ─────────────────────────────────────────────────────────────────\n\n/**\n * Sign a raw message with an account's secret key.\n * Returns a Base58 encoded signature.\n */\nexport function signMessage(\n message: Uint8Array,\n secretKey: Uint8Array,\n): string {\n const signature = nacl.sign.detached(message, secretKey);\n return bs58.encode(Buffer.from(signature));\n}\n\n/**\n * Format account for display in terminal.\n */\nexport function formatAccount(account: Account, isActive: boolean): string {\n const activeMarker = isActive ? \"●\" : \"○\";\n const shortKey = `${account.publicKey.slice(0, 4)}...${account.publicKey.slice(-4)}`;\n return `${activeMarker} [${account.index}] ${account.name.padEnd(20)} ${shortKey}`;\n}\n\n// ── Internal ──────────────────────────────────────────────────────────────────\n\nfunction derivationPath(index: number): string {\n return `m/44'/501'/${index}'/0'`;\n}\n","/**\n *\n * Reads and writes ~/.wallet/config.json\n * Stores non-sensitive wallet preferences:\n * - Active account index\n * - RPC endpoint (mainnet / devnet)\n * - Account metadata (names, indices — no keys)\n * - WalletConnect project ID\n *\n * Dependencies: Node.js built-in `fs`, `os`, `path`\n */\n\nimport fs from \"fs\";\nimport { getConfigPath, ensureWalletDir } from \"./keystore\";\nimport { AccountStore, createAccountStore } from \"./accounts\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type ClusterType = \"mainnet-beta\" | \"devnet\" | \"testnet\" | \"localnet\" | \"custom\";\n\nexport interface WalletConfig {\n version: number;\n cluster: ClusterType;\n rpcUrl: string;\n walletConnectProjectId: string;\n accountStore: AccountStore;\n createdAt: string;\n updatedAt: string;\n}\n\n// ── Defaults ──────────────────────────────────────────────────────────────────\n\nconst HELIUS_API_KEY = process.env.HELIUS_API_KEY;\nexport const RPC_URLS: Record<Exclude<ClusterType, \"custom\">, string> = {\n \"mainnet-beta\": HELIUS_API_KEY\n ? `https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}`\n : \"https://api.mainnet-beta.solana.com\",\n devnet: HELIUS_API_KEY\n ? `https://devnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}`\n : \"https://api.devnet.solana.com\",\n testnet: \"https://api.testnet.solana.com\",\n localnet: \"http://localhost:8899\",\n};\n\nconst CONFIG_VERSION = 1;\n\n// ── Read / Write ──────────────────────────────────────────────────────────────\n\nexport function configExists(): boolean {\n return fs.existsSync(getConfigPath());\n}\n\nexport function loadConfig(): WalletConfig {\n if (!configExists()) {\n throw new Error(\"No config found. Run `wallet init` first.\");\n }\n const raw = fs.readFileSync(getConfigPath(), \"utf8\");\n return JSON.parse(raw) as WalletConfig;\n}\n\nexport function saveConfig(config: WalletConfig): void {\n ensureWalletDir();\n const updated = { ...config, updatedAt: new Date().toISOString() };\n fs.writeFileSync(getConfigPath(), JSON.stringify(updated, null, 2), {\n mode: 0o600,\n });\n}\n\n/**\n * Create a fresh config on `wallet init`.\n */\nexport function createConfig(\n walletConnectProjectId: string = \"\",\n cluster: Exclude<ClusterType, \"custom\"> = \"devnet\",\n): WalletConfig {\n const now = new Date().toISOString();\n return {\n version: CONFIG_VERSION,\n cluster,\n rpcUrl: RPC_URLS[cluster],\n walletConnectProjectId,\n accountStore: createAccountStore(\"Account 1\"),\n createdAt: now,\n updatedAt: now,\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nexport function updateCluster(cluster: Exclude<ClusterType, \"custom\">): void {\n const config = loadConfig();\n config.cluster = cluster;\n config.rpcUrl = RPC_URLS[cluster];\n saveConfig(config);\n}\n\nexport function updateRpcUrl(url: string): void {\n const config = loadConfig();\n config.rpcUrl = url;\n saveConfig(config);\n}\n\nexport function updateAccountStore(accountStore: AccountStore): void {\n const config = loadConfig();\n config.accountStore = accountStore;\n saveConfig(config);\n}\n\nexport function getRpcUrl(): string {\n return loadConfig().rpcUrl;\n}\n\nexport function getCluster(): ClusterType {\n return loadConfig().cluster;\n}\n\nexport function getAccountStore(): AccountStore {\n return loadConfig().accountStore;\n}\n","/**\n *\n * Public API for the vault module.\n * All other modules import from here — not directly from sub-files.\n *\n * Usage:\n * import { WalletVault } from \"../vault\"\n *\n * const vault = new WalletVault()\n * await vault.init(\"my password\")\n * const account = vault.getActiveKeypair()\n */\n\nimport {\n generateMnemonic,\n validateMnemonic,\n mnemonicToSeed,\n mnemonicToNumberedWords,\n} from \"./mnemonic\";\nimport { saveVault, loadVault, vaultExists, VaultData } from \"./keystore\";\nimport {\n deriveAccount,\n deriveAccounts,\n addAccount,\n setActiveAccount,\n getActiveAccount,\n renameAccount,\n formatAccount,\n signMessage,\n AccountKeypair,\n AccountStore,\n} from \"./accounts\";\nimport {\n loadConfig,\n saveConfig,\n createConfig,\n updateAccountStore,\n configExists,\n WalletConfig,\n ClusterType,\n} from \"./config\";\n\nexport * from \"./mnemonic\";\nexport * from \"./keystore\";\nexport * from \"./accounts\";\nexport * from \"./config\";\n\n// ── WalletVault ───────────────────────────────────────────────────────────────\n\n/**\n * WalletVault is the in-memory session state of the wallet.\n * It is populated on `unlock()` and holds derived keypairs\n * for the duration of the daemon session.\n *\n * Private keys exist ONLY in this object — never on disk.\n */\nexport class WalletVault {\n private _keypairs: AccountKeypair[] = [];\n private _seed: Buffer | null = null; // cached for agent account derivation\n private _config: WalletConfig | null = null;\n private _unlocked: boolean = false;\n private _mnemonic: string | null = null;\n\n // ── Lifecycle ──────────────────────────────────────────────────────────────\n\n /**\n * Initialize a brand new wallet.\n * Generates a mnemonic, encrypts it, writes config.\n * Returns the mnemonic for the user to write down — shown ONCE.\n */\n async init(\n password: string,\n options: {\n strength?: 12 | 24;\n cluster?: Exclude<ClusterType, \"custom\">;\n walletConnectProjectId?: string;\n firstAccountName?: string;\n } = {},\n ): Promise<string> {\n if (vaultExists()) {\n throw new Error(\n \"Wallet already exists. Use `wallet unlock` to access it.\",\n );\n }\n\n const { mnemonic } = generateMnemonic(options.strength ?? 12);\n\n const vaultData: VaultData = {\n mnemonic,\n createdAt: new Date().toISOString(),\n version: 1,\n };\n\n // Save encrypted vault\n await saveVault(vaultData, password);\n\n // Create and save config\n const config = createConfig(\n options.walletConnectProjectId ?? \"\",\n options.cluster ?? \"devnet\",\n );\n\n if (options.firstAccountName) {\n config.accountStore.accounts[0].name = options.firstAccountName;\n }\n\n // Derive and store the first account's public key in config\n const seed = await mnemonicToSeed(mnemonic);\n const firstKeypair = deriveAccount(\n seed,\n 0,\n config.accountStore.accounts[0].name,\n );\n config.accountStore.accounts[0].publicKey = firstKeypair.publicKey;\n saveConfig(config);\n\n return mnemonic; // shown once to user, never stored in plaintext again\n }\n\n /**\n * Unlock the wallet for a session.\n * Decrypts the vault, derives all account keypairs into memory.\n */\n async unlock(password: string): Promise<void> {\n const vaultData = await loadVault(password); // throws on wrong password\n const config = loadConfig();\n\n const seed = await mnemonicToSeed(vaultData.mnemonic);\n this._seed = seed;\n this._mnemonic = vaultData.mnemonic;\n this._keypairs = deriveAccounts(seed, config.accountStore);\n this._config = config;\n this._unlocked = true;\n }\n\n /**\n * Restore wallet from an existing mnemonic (import flow).\n */\n async restore(\n mnemonic: string,\n password: string,\n options: { cluster?: Exclude<ClusterType, \"custom\"> } = {},\n ): Promise<void> {\n if (!validateMnemonic(mnemonic)) {\n throw new Error(\"Invalid mnemonic phrase.\");\n }\n\n const vaultData: VaultData = {\n mnemonic,\n createdAt: new Date().toISOString(),\n version: 1,\n };\n\n await saveVault(vaultData, password);\n const config = createConfig(\"\", options.cluster ?? \"devnet\");\n const seed = await mnemonicToSeed(mnemonic);\n const firstKeypair = deriveAccount(seed, 0, \"Account 1\");\n config.accountStore.accounts[0].publicKey = firstKeypair.publicKey;\n saveConfig(config);\n }\n\n lock(): void {\n this._keypairs = [];\n this._mnemonic = null;\n this._seed = null;\n this._config = null;\n this._unlocked = false;\n }\n\n // ── Account Management ─────────────────────────────────────────────────────\n\n /**\n * Get the currently active keypair (used for signing + dApp connections).\n */\n getActiveKeypair(): AccountKeypair {\n this.assertUnlocked();\n const active = getActiveAccount(this._config!.accountStore);\n const keypair = this._keypairs.find((k) => k.index === active.index);\n if (!keypair) throw new Error(\"Active keypair not found in session.\");\n return keypair;\n }\n\n /**\n * Lock the wallet — wipe all keypairs from memory.\n */\n /**\n * Returns the mnemonic — only available while unlocked.\n * Used by agent_connect to derive new accounts on demand.\n */\n\n getMnemonic(): string {\n if (!this._mnemonic) throw new Error(\"Wallet is locked\");\n return this._mnemonic;\n }\n\n /**\n * Reload config and re-derive keypairs from seed.\n * Call after adding new accounts so the vault picks them up.\n */\n async reload(): Promise<void> {\n if (!this._seed) throw new Error(\"Wallet is locked\");\n this._config = loadConfig();\n this._keypairs = deriveAccounts(this._seed, this._config.accountStore);\n }\n\n /**\n * Get a keypair by account index.\n */\n getKeypair(index: number): AccountKeypair {\n this.assertUnlocked();\n const keypair = this._keypairs.find((k) => k.index === index);\n if (!keypair) throw new Error(`Account ${index} not found.`);\n return keypair;\n }\n\n /**\n * Get all loaded keypairs.\n */\n getAllKeypairs(): AccountKeypair[] {\n this.assertUnlocked();\n return [...this._keypairs];\n }\n\n /**\n * Add a new derived account.\n */\n async addAccount(name?: string, password?: string): Promise<AccountKeypair> {\n this.assertUnlocked();\n\n // Need the seed to derive the new account\n if (!password) throw new Error(\"Password required to add a new account.\");\n const vaultData = await loadVault(password);\n const seed = await mnemonicToSeed(vaultData.mnemonic);\n\n const { store, keypair } = addAccount(\n this._config!.accountStore,\n seed,\n name,\n );\n\n this._config!.accountStore = store;\n this._keypairs.push(keypair);\n updateAccountStore(store);\n\n return keypair;\n }\n\n /**\n * Switch the active account.\n */\n setActiveAccount(index: number): void {\n this.assertUnlocked();\n const updated = setActiveAccount(this._config!.accountStore, index);\n this._config!.accountStore = updated;\n updateAccountStore(updated);\n }\n\n /**\n * Rename an account.\n */\n renameAccount(index: number, newName: string): void {\n this.assertUnlocked();\n const updated = renameAccount(this._config!.accountStore, index, newName);\n this._config!.accountStore = updated;\n this._keypairs = this._keypairs.map((k) =>\n k.index === index ? { ...k, name: newName } : k,\n );\n updateAccountStore(updated);\n }\n\n // ── Display ────────────────────────────────────────────────────────────────\n\n /**\n * Format all accounts for terminal display.\n */\n listAccounts(): string[] {\n this.assertUnlocked();\n const activeIndex = this._config!.accountStore.activeIndex;\n return this._keypairs.map((k) => formatAccount(k, k.index === activeIndex));\n }\n\n /**\n * Sign a message with the active account.\n */\n signMessage(message: Uint8Array): string {\n const keypair = this.getActiveKeypair();\n return signMessage(message, keypair.secretKey);\n }\n\n // ── State ──────────────────────────────────────────────────────────────────\n\n get isUnlocked(): boolean {\n return this._unlocked;\n }\n\n get config(): WalletConfig {\n this.assertUnlocked();\n return this._config!;\n }\n\n static exists(): boolean {\n return vaultExists() && configExists();\n }\n\n /**\n * Display mnemonic as numbered words — for backup verification flow.\n */\n static formatMnemonicForDisplay(mnemonic: string): string {\n const words = mnemonicToNumberedWords(mnemonic);\n // Format into 3 columns of 4 (12-word) or 6 columns of 4 (24-word)\n const cols = 3;\n const rows: string[] = [];\n for (let i = 0; i < words.length; i += cols) {\n rows.push(\n words\n .slice(i, i + cols)\n .map((w) => w.padEnd(18))\n .join(\" \"),\n );\n }\n return rows.join(\"\\n\");\n }\n\n // ── Internal ───────────────────────────────────────────────────────────────\n\n private assertUnlocked(): void {\n if (!this._unlocked) {\n throw new Error(\"Wallet is locked. Run `wallet unlock` first.\");\n }\n }\n\n /**\n * Find an account by name or create it if it doesn't exist.\n * Uses the cached seed — no password needed.\n * This is the primary way agents acquire their account.\n */\n async findOrCreate(name: string): Promise<AccountKeypair> {\n this.assertUnlocked();\n\n // Return existing account with this name\n const existing = this._keypairs.find((k) => k.name === name);\n if (existing) return existing;\n\n // Derive a new account at the next index\n const nextIndex = this._config!.accountStore.accounts.length;\n const { store, keypair } = addAccount(\n this._config!.accountStore,\n this._seed!,\n name,\n );\n\n this._config!.accountStore = store;\n this._keypairs.push(keypair);\n updateAccountStore(store);\n\n console.log(\n `[Vault] Created account \"${name}\" at index ${nextIndex} (${keypair.publicKey})`,\n );\n return keypair;\n }\n\n /**\n * Find an account by name. Returns null if not found.\n */\n findByName(name: string): AccountKeypair | null {\n this.assertUnlocked();\n return this._keypairs.find((k) => k.name === name) ?? null;\n }\n}\n"],"mappings":"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","names":["generateMnemonic","validateMnemonic","mnemonicToSeed","path","mnemonicToSeed","fs","fs","generateMnemonic","mnemonicToSeed","validateMnemonic"]}
|