当前位置: 首页 > news >正文

solana钱包管理模块

需求
  1. 批量创建solana钱包地址
  2. 获取私钥时需要输入密码
  3. 可以查询钱包地址
解决
  1. 生成钱包地址时需要输入自定义密码,使用钱包时也需要输入密码
  2. 生成的钱包地址保存到本地,保存内容是加密私钥的文件,使用时获取文件中的私钥进行解密
  3. 批量生成钱包地址文件保存到同一文件夹中,文件夹名称后缀用数字表示第几批钱包
  4. 查询钱包地址时,输入文件夹编号即可查看
  5. 查询某个地址私钥时,输入文件夹编号、地址编号和密码
实现效果

image.png

实现代码

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;

到此本文内容结束,更多相关文章,请, https://t.me/gtokentool

相关文章:

  • 自动化测试的框架有哪些?原理是什么?
  • 判断三方库是64位还是32位
  • IEEE Communications Magazine 2025年1-3月论文速览
  • 【调制识别】PGD攻击中参数的含义
  • ArkUI Tab组件开发深度解析与应用指南
  • lowcoder数据库操作2:新建数据库查询
  • Java:logback-classic与slf4j版本对应关系
  • 实战手册--如何针对衡石分析平台的数据集成详解
  • EmoBox:我与 CodeBuddy 共创的 Emoji 表情分类小工具
  • 【C++篇】揭秘STL vector:高效动态数组的深度解析(从使用到模拟实现)
  • 从技术层⾯来说深度SEO优化的⽅式有哪些?
  • Java 中Supplier延迟生成值的原因
  • 2025-5-19Vue3快速上手
  • java.lang.UnsupportedOperationException: null
  • 【java第18集】java引用数据类型详解
  • 进程退出 和 僵尸进程、孤儿进程
  • Linux错误处理集合 GLIBCXX_3.4.25‘ not found和 安装glibc-2.28和Error: rpmdb open failed
  • JQuery 禁止页面滚动(防止页面抖动)
  • VS中将控制台项目编程改为WINDOWS桌面程序
  • ai决策平台:AnKo如何推动引领智能化未来?
  • 住建部:推进“好房子、好小区、好社区、好城区”四好建设
  • 优质文化资源下基层,上海各区优秀群文团队“文化走亲”
  • “当代阿炳”甘柏林逝世,创办了国内第一所残疾人高等学府
  • 无人机企业从科技园区搬到乡村后,村子里变得不一样了
  • 殷墟出土鸮尊时隔50年首次聚首,北京新展“看·见殷商”
  • 外媒:哈马斯一名高级指挥官尸体被发现,系辛瓦尔弟弟