udpdate wallet function

This commit is contained in:
NW 2024-12-24 09:19:14 +00:00
parent 3129525a1e
commit c9bcb09221
6 changed files with 155 additions and 69 deletions

View File

@ -109,7 +109,7 @@ const initDb = async () => {
wallet_type TEXT NOT NULL,
address TEXT NOT NULL,
derivation_path TEXT NOT NULL,
encrypted_mnemonic TEXT NOT NULL,
mnemonic TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE(user_id, wallet_type)

View File

@ -6,6 +6,11 @@ import fs from 'fs';
import csvWriter from 'csv-writer';
export default class AdminWalletsHandler {
// Метод для проверки, является ли пользователь администратором
static isAdmin(userId) {
return config.ADMIN_IDS.includes(userId.toString());
}
static async handleWalletManagement(msg) {
const chatId = msg.chat.id;
@ -42,6 +47,9 @@ export default class AdminWalletsHandler {
const walletType = action.split('_').pop();
try {
// Удаляем предыдущее сообщение перед отправкой нового
await bot.deleteMessage(chatId, callbackQuery.message.message_id);
// Получаем все кошельки выбранного типа
const wallets = await WalletService.getWalletsByType(walletType);
@ -62,14 +70,32 @@ export default class AdminWalletsHandler {
}
static async displayWalletsPage(chatId, wallets, walletType, totalBalance, page) {
const pageSize = 50;
const pageSize = 5;
const startIndex = page * pageSize;
const endIndex = startIndex + pageSize;
const walletsPage = wallets.slice(startIndex, endIndex);
// Формируем список кошельков с балансами
const walletList = walletsPage.map(wallet => `${wallet.address} - ${wallet.balance}`).join('\n');
let walletList = '';
for (const wallet of walletsPage) {
const walletUtilsInstance = new WalletUtils(
wallet.wallet_type.startsWith('BTC') ? wallet.address : null,
wallet.wallet_type.startsWith('LTC') ? wallet.address : null,
wallet.wallet_type.startsWith('TRON') ? wallet.address : null,
wallet.wallet_type.startsWith('ETH') ? wallet.address : null,
null, // userId не нужен для админки
Date.now() - 30 * 24 * 60 * 60 * 1000
);
const balances = await walletUtilsInstance.getAllBalances();
const balance = balances[wallet.wallet_type] || { amount: 0, usdValue: 0 };
walletList += `💰 *${wallet.wallet_type}*\n`;
walletList += `├ Balance: ${balance.amount.toFixed(8)} ${wallet.wallet_type.split(' ')[0]}\n`;
walletList += `├ Value: $${balance.usdValue.toFixed(2)}\n`;
walletList += `└ Address: \`${wallet.address}\`\n\n`;
}
// Создаем клавиатуру с пагинацией
const keyboard = {
inline_keyboard: [
@ -83,23 +109,22 @@ export default class AdminWalletsHandler {
]
]
};
// Убираем кнопку "Назад", если это первая страница
if (page === 0) {
keyboard.inline_keyboard[0].shift();
}
// Убираем кнопку "Далее", если это последняя страница
if (endIndex >= wallets.length) {
keyboard.inline_keyboard[0].pop();
}
// Отправляем сообщение с суммарным балансом и списком кошельков
await bot.editMessageText(
await bot.sendMessage(
chatId,
`Total Balance for ${walletType}: $${totalBalance.toFixed(2)}\n\nWallets (${startIndex + 1}-${endIndex} of ${wallets.length}):\n${walletList}`,
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
parse_mode: 'Markdown',
reply_markup: keyboard
}
@ -130,46 +155,97 @@ export default class AdminWalletsHandler {
static async handlePagination(callbackQuery) {
const action = callbackQuery.data;
const chatId = callbackQuery.message.chat.id;
const [actionType, walletType, page] = action.split('_');
// Получаем все кошельки выбранного типа
const wallets = await WalletService.getWalletsByType(walletType);
// Вычисляем суммарный баланс
const totalBalance = await this.calculateTotalBalance(wallets);
// Отображаем страницу с учетом пагинации
await this.displayWalletsPage(chatId, wallets, walletType, totalBalance, parseInt(page));
// Используем регулярное выражение для извлечения номера страницы
const match = action.match(/next_page_(.+)_(\d+)/) || action.match(/prev_page_(.+)_(\d+)/);
if (!match) {
console.error('Invalid pagination action:', action);
await bot.sendMessage(chatId, 'Invalid pagination action. Please try again.');
return;
}
const walletType = match[1]; // Тип кошелька (например, BTC)
const pageNumber = parseInt(match[2]); // Номер страницы
try {
// Удаляем предыдущее сообщение перед отправкой нового
await bot.deleteMessage(chatId, callbackQuery.message.message_id);
// Получаем все кошельки выбранного типа
const wallets = await WalletService.getWalletsByType(walletType);
// Вычисляем суммарный баланс
const totalBalance = await this.calculateTotalBalance(wallets);
// Отображаем страницу с учетом пагинации
await this.displayWalletsPage(chatId, wallets, walletType, totalBalance, pageNumber);
} catch (error) {
console.error('Error fetching wallets:', error);
await bot.sendMessage(chatId, 'Failed to fetch wallets. Please try again later.');
}
}
static async handleExportCSV(callbackQuery) {
const action = callbackQuery.data;
const chatId = callbackQuery.message.chat.id;
const walletType = action.split('_').pop();
try {
// Удаляем предыдущее сообщение перед отправкой нового
await bot.deleteMessage(chatId, callbackQuery.message.message_id);
// Получаем все кошельки выбранного типа
const wallets = await WalletService.getWalletsByType(walletType);
if (wallets.length === 0) {
await bot.sendMessage(chatId, `No wallets found for ${walletType}.`);
return;
}
// Расшифровываем мнемоническую фразу и получаем балансы
const walletsWithData = await Promise.all(wallets.map(async (wallet) => {
if (!wallet.mnemonic) {
console.error(`Mnemonic is missing for wallet with ID ${wallet.id}`);
return null; // Пропускаем кошелек, если mnemonic отсутствует
}
const walletUtilsInstance = new WalletUtils(
wallet.wallet_type.startsWith('BTC') ? wallet.address : null,
wallet.wallet_type.startsWith('LTC') ? wallet.address : null,
wallet.wallet_type.startsWith('TRON') ? wallet.address : null,
wallet.wallet_type.startsWith('ETH') ? wallet.address : null,
null, // userId не нужен для админки
Date.now() - 30 * 24 * 60 * 60 * 1000
);
const balances = await walletUtilsInstance.getAllBalances();
const balance = balances[wallet.wallet_type] || { amount: 0, usdValue: 0 };
return {
address: wallet.address,
balance: balance.amount.toFixed(8),
mnemonic: wallet.mnemonic
};
}));
// Убираем null значения из массива
const filteredWallets = walletsWithData.filter(wallet => wallet !== null);
// Создаем CSV-файл
const csv = csvWriter.createObjectCsvWriter({
path: `wallets_${walletType}.csv`,
header: [
{ id: 'address', title: 'Address' },
{ id: 'balance', title: 'Balance' }
{ id: 'balance', title: 'Balance' },
{ id: 'mnemonic', title: 'Mnemonic' }
]
});
await csv.writeRecords(wallets);
await csv.writeRecords(filteredWallets);
// Отправляем файл пользователю
await bot.sendDocument(chatId, fs.createReadStream(`wallets_${walletType}.csv`));
// Удаляем временный файл
fs.unlinkSync(`wallets_${walletType}.csv`);
} catch (error) {
@ -177,8 +253,38 @@ export default class AdminWalletsHandler {
await bot.sendMessage(chatId, 'Failed to export wallets to CSV. Please try again later.');
}
}
// Метод для возврата к списку типов кошельков
static async handleBackToWalletTypes(callbackQuery) {
const chatId = callbackQuery.message.chat.id;
static isAdmin(userId) {
return config.ADMIN_IDS.includes(userId.toString());
try {
// Удаляем предыдущее сообщение перед отправкой нового
await bot.deleteMessage(chatId, callbackQuery.message.message_id);
const keyboard = {
reply_markup: {
inline_keyboard: [
[
{ text: 'Bitcoin (BTC)', callback_data: 'wallet_type_BTC' },
{ text: 'Litecoin (LTC)', callback_data: 'wallet_type_LTC' }
],
[
{ text: 'Tether (USDT)', callback_data: 'wallet_type_USDT' },
{ text: 'Ethereum (ETH)', callback_data: 'wallet_type_ETH' }
],
[
{ text: 'TRON (TRX)', callback_data: 'wallet_type_TRON' }
]
]
}
};
// Отправляем новое сообщение с клавиатурой
await bot.sendMessage(chatId, 'Select wallet type:', keyboard);
} catch (error) {
console.error('Error in handleBackToWalletTypes:', error);
await bot.sendMessage(chatId, 'An error occurred. Please try again later.');
}
}
}

View File

@ -275,7 +275,6 @@ export default class UserWalletsHandler {
// Generate new wallets
const mnemonic = await WalletGenerator.generateMnemonic();
const wallets = await WalletGenerator.generateWallets(mnemonic);
const encryptedMnemonic = await WalletGenerator.encryptMnemonic(mnemonic, telegramId);
// Get the base wallet type (ETH for ERC-20, TRON for TRC-20)
const baseType = this.getBaseWalletType(walletType);
@ -298,14 +297,14 @@ export default class UserWalletsHandler {
// Store the new wallet
await db.runAsync(
`INSERT INTO crypto_wallets (
user_id, wallet_type, address, derivation_path, encrypted_mnemonic
user_id, wallet_type, address, derivation_path, mnemonic
) VALUES (?, ?, ?, ?, ?)`,
[
user.id,
baseType,
wallets[baseType].address,
wallets[baseType].path,
encryptedMnemonic
mnemonic
]
);

View File

@ -350,8 +350,8 @@ bot.on('callback_query', async (callbackQuery) => {
logDebug(action, 'handleExportCSV');
await adminWalletsHandler.handleExportCSV(callbackQuery);
} else if (action === 'back_to_wallet_types') {
logDebug(action, 'handleWalletManagement');
await adminWalletsHandler.handleWalletManagement(msg);
logDebug(action, 'handleBackToWalletTypes');
await adminWalletsHandler.handleBackToWalletTypes(callbackQuery);
}
// Dump manage
else if (action === "export_database") {

View File

@ -20,18 +20,20 @@ class WalletService {
}
// Добавляем метод для получения кошельков по типу
static async getWalletsByType(walletType) {
try {
const wallets = await db.allAsync(
`SELECT * FROM crypto_wallets WHERE wallet_type = ?`,
[walletType]
);
return wallets;
} catch (error) {
console.error('Error fetching wallets by type:', error);
throw new Error('Failed to fetch wallets by type');
try {
const wallets = await db.allAsync(
`SELECT *
FROM crypto_wallets
WHERE wallet_type = ?`,
[walletType]
);
return wallets;
} catch (error) {
console.error('Error fetching wallets by type:', error);
throw new Error('Failed to fetch wallets by type');
}
}
}
}
export default WalletService;

View File

@ -11,34 +11,13 @@ const ECPair = ECPairFactory(ecc);
export default class WalletGenerator {
static async generateMnemonic() {
try {
return bip39.generateMnemonic(256); // 24 words for maximum security
return bip39.generateMnemonic(128); // 12 words for maximum security
} catch (error) {
console.error('Error generating mnemonic:', error);
throw new Error('Failed to generate mnemonic');
}
}
static async encryptMnemonic(mnemonic, userId) {
try {
const key = process.env.ENCRYPTION_KEY || 'default-key-12345';
return CryptoJS.AES.encrypt(mnemonic, key + userId.toString()).toString();
} catch (error) {
console.error('Error encrypting mnemonic:', error);
throw new Error('Failed to encrypt mnemonic');
}
}
static async decryptMnemonic(encryptedMnemonic, userId) {
try {
const key = process.env.ENCRYPTION_KEY || 'default-key-12345';
const bytes = CryptoJS.AES.decrypt(encryptedMnemonic, key + userId.toString());
return bytes.toString(CryptoJS.enc.Utf8);
} catch (error) {
console.error('Error decrypting mnemonic:', error);
throw new Error('Failed to decrypt mnemonic');
}
}
static async generateWallets(mnemonic) {
try {
const seed = await bip39.mnemonicToSeed(mnemonic);