telegram-shop/src/services/walletService.js
2025-01-25 01:13:10 +00:00

309 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// walletService.js
import db from "../config/database.js";
import config from "../config/config.js";
import WalletUtils from "../utils/walletUtils.js";
import WalletGenerator from "../utils/walletGenerator.js";
import crypto from 'crypto';
class WalletService {
static async getArchivedWalletsCount(user) {
try {
const archivedWallets = await db.getAsync(
`SELECT COUNT(*) AS total
FROM crypto_wallets
WHERE user_id = ? AND wallet_type LIKE '%#_%' ESCAPE '#'`,
[user.id]
);
return archivedWallets.total;
} catch (error) {
console.error('Error fetching archived wallets count:', error);
throw new Error('Failed to fetch archived wallets count');
}
}
static async getActiveWalletsBalance(userId) {
try {
const wallets = await db.allAsync(
`SELECT wallet_type, balance
FROM crypto_wallets
WHERE user_id = ? AND wallet_type NOT LIKE '%#_%' ESCAPE '#'`,
[userId]
);
const prices = await WalletUtils.getCryptoPrices();
let totalBalance = 0;
for (const wallet of wallets) {
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
const balance = wallet.balance || 0;
switch (baseType) {
case 'BTC':
totalBalance += balance * prices.btc;
break;
case 'LTC':
totalBalance += balance * prices.ltc;
break;
case 'ETH':
totalBalance += balance * prices.eth;
break;
case 'USDT':
totalBalance += balance; // USDT is 1:1 with USD
break;
case 'USDC':
totalBalance += balance; // USDC is 1:1 with USD
break;
}
}
return totalBalance;
} catch (error) {
console.error('Error fetching active wallets balance:', error);
throw new Error('Failed to fetch active wallets balance');
}
}
static async getArchivedWalletsBalance(userId) {
try {
const wallets = await db.allAsync(
`SELECT wallet_type, balance
FROM crypto_wallets
WHERE user_id = ? AND wallet_type LIKE '%#_%' ESCAPE '#'`,
[userId]
);
const prices = await WalletUtils.getCryptoPrices();
let totalBalance = 0;
for (const wallet of wallets) {
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
const balance = wallet.balance || 0;
switch (baseType) {
case 'BTC':
totalBalance += balance * prices.btc;
break;
case 'LTC':
totalBalance += balance * prices.ltc;
break;
case 'ETH':
totalBalance += balance * prices.eth;
break;
case 'USDT':
totalBalance += balance; // USDT is 1:1 with USD
break;
case 'USDC':
totalBalance += balance; // USDC is 1:1 with USD
break;
}
}
return totalBalance;
} catch (error) {
console.error('Error fetching archived wallets balance:', error);
throw new Error('Failed to fetch archived wallets balance');
}
}
// Метод для получения кошельков по типу
static async getWalletsByType(walletType) {
try {
const wallets = await db.allAsync(
`SELECT *
FROM crypto_wallets
WHERE wallet_type = ? OR wallet_type LIKE ?`,
[walletType, `${walletType}_%`]
);
return wallets;
} catch (error) {
console.error('Error fetching wallets by type:', error);
throw new Error('Failed to fetch wallets by type');
}
}
static async createWallet(userId, walletType) {
try {
// Генерация нового кошелька
const mnemonic = await WalletGenerator.generateMnemonic();
const wallets = await WalletGenerator.generateWallets(mnemonic, userId);
if (!wallets || typeof wallets !== 'object') {
throw new Error('Failed to generate wallets');
}
// Проверяем наличие базового типа кошелька
const baseType = walletType === 'USDT' || walletType === 'USDC' ? 'ETH' : walletType;
if (!wallets[baseType.toUpperCase()]) {
throw new Error(`Unsupported wallet type: ${walletType}`);
}
// Проверяем наличие ключа шифрования
if (!config.ENCRYPTION_KEY || typeof config.ENCRYPTION_KEY !== 'string') {
throw new Error('Encryption key is not configured');
}
// Проверяем и преобразуем userId
if (typeof userId !== 'number' && typeof userId !== 'string') {
throw new Error('Invalid user ID');
}
const userIdStr = userId.toString();
// Создаем ключ шифрования с использованием хэша
const baseKey = config.ENCRYPTION_KEY;
const combinedKey = baseKey + userIdStr;
// Создаем ключ и IV с использованием SHA-256
const key = crypto.createHash('sha256')
.update(config.ENCRYPTION_KEY + userIdStr)
.digest();
const iv = crypto.randomBytes(16); // Генерируем случайный IV
// Создаем шифр
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
// Шифруем мнемонику
let encrypted = cipher.update(mnemonic, 'utf8', 'hex');
encrypted += cipher.final('hex');
// Сохраняем зашифрованные данные вместе с IV
const encryptedMnemonic = iv.toString('hex') + ':' + encrypted;
// Определяем путь деривации
let derivationPath;
if (walletType === 'USDT') {
derivationPath = "m/44'/60'/0'/0/1"; // Путь для USDT
} else if (walletType === 'USDC') {
derivationPath = "m/44'/60'/0'/0/2"; // Путь для USDC
} else {
derivationPath = wallets[walletType.toUpperCase()].path;
}
// Получаем адрес для базового типа
const walletData = wallets[baseType.toUpperCase()];
if (!walletData || !walletData.address) {
console.error('Wallet generation failed:', {
baseType,
wallets: Object.keys(wallets),
walletData
});
throw new Error('Failed to generate wallet address');
}
const address = walletData.address;
// Вставляем новый кошелек в базу данных
await db.runAsync(
`INSERT INTO crypto_wallets
(user_id, wallet_type, address, derivation_path, mnemonic)
VALUES (?, ?, ?, ?, ?)`,
[userId, walletType, address, derivationPath, encryptedMnemonic]
);
// Проверяем успешность вставки
const insertedWallet = await db.getAsync(
`SELECT * FROM crypto_wallets
WHERE user_id = ? AND wallet_type = ?`,
[userId, walletType]
);
if (!insertedWallet) {
throw new Error('Failed to verify wallet insertion');
}
// Проверяем целостность записанной мнемоники
// Разделяем IV и зашифрованные данные для проверки
const [verify_ivHex, verify_encryptedData] = insertedWallet.mnemonic.split(':');
const verify_iv = Buffer.from(verify_ivHex, 'hex');
// Создаем ключ для проверки
const verify_key = crypto.createHash('sha256')
.update(config.ENCRYPTION_KEY + userId)
.digest();
// Создаем дешифратор для проверки
const decipher = crypto.createDecipheriv('aes-256-cbc', verify_key, verify_iv);
// Дешифруем данные
let decryptedMnemonic;
try {
decryptedMnemonic = decipher.update(verify_encryptedData, 'hex', 'utf8');
decryptedMnemonic += decipher.final('utf8');
if (!decryptedMnemonic) {
throw new Error('Failed to decrypt mnemonic');
}
} catch (error) {
console.error('Decryption error:', error);
throw new Error('Failed to decrypt mnemonic: ' + error.message);
}
if (decryptedMnemonic !== mnemonic) {
console.error('Mnemonic verification failed for wallet:', walletType);
// Удаляем кошелек в случае ошибки
await db.runAsync(
`DELETE FROM crypto_wallets
WHERE user_id = ? AND wallet_type = ?`,
[userId, walletType]
);
throw new Error('Mnemonic verification failed');
}
console.log(`Successfully created and verified wallet: ${walletType}`, {
address,
derivationPath,
userId,
walletType
});
return {
address,
derivationPath,
userId,
walletType
};
} catch (error) {
console.error('Error creating wallet:', error);
throw new Error('Failed to create wallet: ' + error.message);
}
}
static async decryptMnemonic(encryptedMnemonic, userId) {
try {
// Проверяем наличие ключа шифрования
if (!config.ENCRYPTION_KEY || typeof config.ENCRYPTION_KEY !== 'string') {
throw new Error('Encryption key is not configured');
}
// Разделяем IV и зашифрованные данные
const [ivHex, encryptedData] = encryptedMnemonic.split(':');
if (!ivHex || !encryptedData) {
throw new Error('Invalid encrypted mnemonic format');
}
// Создаем ключ дешифрования
const key = crypto.createHash('sha256')
.update(config.ENCRYPTION_KEY + userId.toString())
.digest();
// Преобразуем IV из hex
const iv = Buffer.from(ivHex, 'hex');
// Создаем дешифратор
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
// Дешифруем данные
let decrypted = decipher.update(encryptedData, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
} catch (error) {
console.error('Error decrypting mnemonic:', error);
throw new Error('Failed to decrypt mnemonic: ' + error.message);
}
}
}
export default WalletService;