使用web3js实现solana钱包管理
const { Keypair } = require('@solana/web3.js');
const bs58 = require('bs58');
const CryptoJS = require('crypto-js');
const fs = require('fs-extra');
const path = require('path');
const inquirer = require('inquirer');
const moment = require('moment');
const { Connection, PublicKey } = require('@solana/web3.js');
const { clusterApiUrl } = require('@solana/web3.js');
// 引入配置管理器
const ConfigManager = require('./config-manager').default;
const configManager = new ConfigManager();
if (typeof configManager === 'object') {
console.log('配置管理器属性:', Object.keys(configManager));
if (configManager.network) {
console.log('网络配置:', configManager.network);
}
}
class SolanaAddressGenerator {
constructor() {
this.walletDir = '';
this.configManager = configManager;
}
// 加密私钥
encryptPrivateKey(privateKey, password) {
try {
// 使用密码生成加密密钥
const key = CryptoJS.PBKDF2(password, 'salt', {
keySize: 256 / 32,
iterations: 100000
});
// 使用密钥加密私钥
const encrypted = CryptoJS.AES.encrypt(privateKey, key.toString());
return encrypted.toString();
} catch (error) {
throw new Error(`加密失败: ${error.message}`);
}
}
// 解密私钥
decryptPrivateKey(encryptedData, password) {
try {
// 使用相同的方法从密码生成密钥
const key = CryptoJS.PBKDF2(password, 'salt', {
keySize: 256 / 32,
iterations: 100000
});
// 使用密钥解密
const decrypted = CryptoJS.AES.decrypt(encryptedData, key.toString());
const privateKey = decrypted.toString(CryptoJS.enc.Utf8);
if (!privateKey) {
throw new Error('密码错误');
}
return privateKey;
} catch (error) {
throw new Error('密码错误或数据已损坏');
}
}
// 生成新的Solana地址
generateSolanaAddress(password) {
// 生成新的密钥对
const keypair = Keypair.generate();
// 获取公钥(地址)和私钥
const publicKey = keypair.publicKey.toString();
const secretKey = bs58.encode(keypair.secretKey);
// 加密私钥
const encryptedPrivateKey = this.encryptPrivateKey(secretKey, password);
return {
address: publicKey,
encryptedPrivateKey
};
}
// 获取钱包目录
async getWalletDirectory(checkExists = true) {
const folderNumStr = await inquirer.prompt([
{
type: 'input',
name: 'folderNum',
message: '请输入文件夹编号 (例如: 1):',
validate: input => !isNaN(parseInt(input)) ? true : '请输入有效的数字'
}
]);
const folderNum = folderNumStr.folderNum.trim(); // 直接使用返回的字符串,而不是尝试解构
// 创建主钱包目录
const mainWalletDir = path.join(process.cwd(), 'wallets');
if (!fs.existsSync(mainWalletDir)) {
fs.mkdirSync(mainWalletDir, { recursive: true });
console.log(`创建主钱包目录: ${mainWalletDir}`);
}
const folderName = `wallets${folderNum}`;
const walletDir = path.join(mainWalletDir, folderName);
console.log(`调试信息: 钱包目录路径: ${walletDir}`);
if (fs.existsSync(walletDir)) {
if (checkExists) {
const useExistingStr = await inquirer.prompt([
{
type: 'confirm',
name: 'useExisting',
message: '文件夹 ' + folderName + ' 已存在。是否使用该文件夹? (y/n):'
}
]);
const useExisting = useExistingStr.useExisting;
if (useExisting === false) {
return this.getWalletDirectory(checkExists);
}
}
} else {
if (checkExists) {
fs.mkdirSync(walletDir, { recursive: true });
console.log(`创建文件夹: ${walletDir}`);
} else {
throw new Error(`找不到文件夹 ${folderName}`);
}
}
this.walletDir = walletDir;
return walletDir;
}
// 生成文件名
getFilename(address) {
// 生成时间戳(精确到毫秒)
const timestamp = moment().format('YYYYMMDD_HHmmss_SSS');
// 使用时间戳和地址后8位组合成文件名
return `wallet_${timestamp}_${address.slice(-8)}.json`;
}
// 保存钱包到文件
saveWalletToFile(wallet, walletDir) {
// 生成文件名
const filename = this.getFilename(wallet.address);
const filepath = path.join(walletDir, filename);
// 保存到文件
fs.writeJsonSync(filepath, {
address: wallet.address,
encryptedPrivateKey: wallet.encryptedPrivateKey
}, { spaces: 2 });
return filepath;
}
// 从文件加载钱包
loadWalletFromFile(filepath) {
return fs.readJsonSync(filepath);
}
// 列出钱包文件
listWalletFiles(walletDir) {
return fs.readdirSync(walletDir)
.filter(file => file.endsWith('.json'))
.sort();
}
// 查看私钥
async viewPrivateKeys(folderNum, walletIndex, password) {
try {
// 创建主钱包目录
const mainWalletDir = path.join(process.cwd(), 'wallets');
if (!fs.existsSync(mainWalletDir)) {
fs.mkdirSync(mainWalletDir, { recursive: true });
console.log(`创建主钱包目录: ${mainWalletDir}`);
}
// 构建文件夹路径
const folderName = `wallets${folderNum}`;
const folderPath = path.join(mainWalletDir, folderName);
console.log(`调试信息: 查询私钥的文件夹路径: ${folderPath}`);
if (!fs.existsSync(folderPath)) {
throw new Error(`找不到文件夹: ${folderPath}`);
}
// 获取所有钱包文件
const walletFiles = fs.readdirSync(folderPath)
.filter(file => file.endsWith('.json'))
.sort();
if (walletFiles.length === 0) {
throw new Error(`文件夹 ${folderName} 中没有钱包文件`);
}
// 如果指定了钱包索引,只查询该钱包的私钥
if (walletIndex) {
if (walletIndex < 1 || walletIndex > walletFiles.length) {
throw new Error(`无效的钱包索引,应为 1 到 ${walletFiles.length}`);
}
const walletFile = walletFiles[walletIndex - 1];
const walletPath = path.join(folderPath, walletFile);
const walletData = fs.readJsonSync(walletPath);
// 解密私钥
const privateKey = this.decryptPrivateKey(walletData.encryptedPrivateKey, password);
return [{
index: walletIndex,
address: walletData.address,
privateKey: privateKey
}];
} else {
// 获取所有钱包文件
const wallets = [];
for (let i = 0; i < walletFiles.length; i++) {
try {
const walletFile = walletFiles[i];
const walletPath = path.join(folderPath, walletFile);
const walletData = fs.readJsonSync(walletPath);
// 解密私钥
const privateKey = this.decryptPrivateKey(walletData.encryptedPrivateKey, password);
wallets.push({
index: i + 1,
address: walletData.address,
privateKey: privateKey
});
} catch (error) {
console.error(`处理钱包 #${i + 1} 失败: ${error.message}`);
}
}
if (wallets.length === 0) {
throw new Error('没有成功解密任何钱包,请检查密码是否正确');
}
return wallets;
}
} catch (error) {
throw new Error(`查看私钥失败: ${error.message}`);
}
}
// 获取项目根目录(与solana-js同级的目录)
getProjectRoot() {
// 当前工作目录是 solana-js/src,需要回退两级到项目根目录
return path.join(process.cwd(), '..');
}
// 生成钱包
async generateWallets(folderNum, count, password) {
try {
// 如果是交互模式,获取参数
if (arguments.length === 0) {
const folderNumStr = await inquirer.prompt([
{
type: 'input',
name: 'folderNum',
message: '请输入文件夹编号:',
validate: input => !isNaN(parseInt(input)) ? true : '请输入有效的数字'
}
]);
folderNum = folderNumStr.folderNum.trim();
const countStr = await inquirer.prompt([
{
type: 'input',
name: 'count',
message: '请输入要生成的钱包数量:',
default: '1',
validate: input => !isNaN(parseInt(input)) && parseInt(input) > 0 ? true : '请输入有效的正整数'
}
]);
count = parseInt(countStr.count);
// 添加密码输入和确认
const passwordPrompt = await inquirer.prompt([
{
type: 'password',
name: 'password',
message: '请输入钱包密码:',
mask: '*',
validate: input => input.length >= 6 ? true : '密码长度至少为6个字符'
}
]);
// 添加密码确认
const confirmPasswordPrompt = await inquirer.prompt([
{
type: 'password',
name: 'confirmPassword',
message: '请再次输入钱包密码:',
mask: '*',
validate: input => {
if (input !== passwordPrompt.password) {
return '两次输入的密码不一致,请重新输入';
}
return true;
}
}
]);
password = passwordPrompt.password;
// 最后确认一次所有信息
console.log('\n请确认以下信息:');
console.log(`文件夹编号: wallets${folderNum}`);
console.log(`钱包数量: ${count}`);
console.log(`密码长度: ${password.length}位`);
const confirmGenerate = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: '确认生成钱包?',
default: true
}
]);
if (!confirmGenerate.confirm) {
console.log('已取消生成钱包');
return [];
}
}
// 创建主钱包目录
const mainWalletDir = path.join(process.cwd(), 'wallets');
if (!fs.existsSync(mainWalletDir)) {
fs.mkdirSync(mainWalletDir, { recursive: true });
console.log(`创建主钱包目录: ${mainWalletDir}`);
}
// 创建钱包文件夹
const folderName = `wallets${folderNum}`;
const folderPath = path.join(mainWalletDir, folderName);
console.log(`调试信息: 检查钱包文件夹路径: ${folderPath}`);
// 检查文件夹是否已存在,如果存在则直接返回错误
if (fs.existsSync(folderPath)) {
console.error(`错误: 文件夹 ${folderName} 已存在,为避免覆盖现有钱包,请使用其他文件夹编号`);
throw new Error(`文件夹 ${folderName} 已存在,为避免覆盖现有钱包,请使用其他文件夹编号`);
}
// 创建新文件夹
fs.mkdirSync(folderPath, { recursive: true });
console.log(`创建文件夹: ${folderPath}`);
console.log(`开始生成 ${count} 个钱包`);
// 生成钱包
const wallets = [];
for (let i = 0; i < count; i++) {
const keypair = Keypair.generate();
const publicKey = keypair.publicKey.toString();
// 将私钥转换为base58格式
const privateKey = bs58.encode(keypair.secretKey);
// 加密私钥
const key = CryptoJS.PBKDF2(password, 'salt', {
keySize: 256 / 32,
iterations: 100000
});
const encrypted = CryptoJS.AES.encrypt(privateKey, key.toString()).toString();
// 创建钱包文件 - 使用日期_时间_地址后8位的格式
const timestamp = moment().format('YYYYMMDD_HHmmss_SSS');
const walletFileName = `wallet_${timestamp}_${publicKey.slice(-8)}.json`;
const walletFilePath = path.join(folderPath, walletFileName);
// 保存钱包信息
const walletData = {
address: publicKey,
encryptedPrivateKey: encrypted,
createdAt: new Date().toISOString()
};
fs.writeFileSync(walletFilePath, JSON.stringify(walletData, null, 2));
wallets.push({
index: i + 1,
address: publicKey,
filePath: walletFilePath
});
console.log(`生成钱包 #${i + 1}: ${publicKey}`);
}
console.log(`\n成功生成 ${count} 个钱包,保存在文件夹 ${folderPath}`);
// 保存地址列表到文件
await this.saveAddressListFile(folderNum, wallets);
return wallets;
} catch (error) {
console.error(`生成钱包失败: ${error.message}`);
throw new Error(`生成钱包失败: ${error.message}`);
}
}
/**
* 保存地址列表文件
* @param {string} folderNum 文件夹编号
* @param {Array} wallets 钱包数组
*/
async saveAddressListFile(folderNum, wallets) {
try {
// 创建主钱包目录
const mainWalletDir = path.join(process.cwd(), 'wallets');
if (!fs.existsSync(mainWalletDir)) {
fs.mkdirSync(mainWalletDir, { recursive: true });
console.log(`创建主钱包目录: ${mainWalletDir}`);
}
// 在主钱包目录下创建导出目录
const exportDir = path.join(mainWalletDir, 'address_exports');
if (!fs.existsSync(exportDir)) {
fs.mkdirSync(exportDir, { recursive: true });
}
const timestamp = moment().format('YYYYMMDD_HHmmss');
const fileName = `export_${timestamp}_wallets${folderNum}_${wallets[0].index}-${wallets[wallets.length - 1].index}.txt`;
const filePath = path.join(exportDir, fileName);
// 创建地址列表内容
let content = '';
wallets.forEach(wallet => {
content += `${wallet.index}: ${wallet.address}\n`;
});
// 写入文件
fs.writeFileSync(filePath, content);
console.log(`地址列表已保存到: ${filePath}`);
return filePath;
} catch (error) {
console.error(`保存地址列表失败: ${error.message}`);
return null;
}
}
// 查找地址在所有钱包文件夹中
async findAddressInAllFolders(address) {
try {
// 创建主钱包目录
const mainWalletDir = path.join(process.cwd(), 'wallets');
if (!fs.existsSync(mainWalletDir)) {
fs.mkdirSync(mainWalletDir, { recursive: true });
console.log(`创建主钱包目录: ${mainWalletDir}`);
return { found: false, folders: [] };
}
// 获取所有钱包文件夹
const folders = fs.readdirSync(mainWalletDir)
.filter(folder => folder.startsWith('wallets') && fs.statSync(path.join(mainWalletDir, folder)).isDirectory());
if (folders.length === 0) {
console.log('没有找到任何钱包文件夹');
return { found: false, folders: [] };
}
console.log(`找到 ${folders.length} 个钱包文件夹,开始搜索...`);
const results = [];
let found = false;
// 在每个文件夹中查找地址
for (const folder of folders) {
const folderPath = path.join(mainWalletDir, folder);
const walletFiles = fs.readdirSync(folderPath)
.filter(file => file.endsWith('.json'));
if (walletFiles.length === 0) {
console.log(`文件夹 ${folder} 中没有钱包文件,跳过`);
continue;
}
// 检查每个钱包文件
for (const file of walletFiles) {
try {
const walletPath = path.join(folderPath, file);
const data = fs.readJsonSync(walletPath);
if (data.address === address) {
found = true;
const folderNum = folder.replace('wallets', '');
results.push({
folder: folder,
folderNum: folderNum,
file: file,
path: walletPath
});
console.log(`✅ 在文件夹 ${folder} 中找到地址: ${file}`);
}
} catch (error) {
console.error(`读取钱包文件 ${folder}/${file} 失败: ${error.message}`);
}
}
}
return { found, folders: results };
} catch (error) {
throw new Error(`搜索地址失败: ${error.message}`);
}
}
}
module.exports = SolanaAddressGenerator;
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!