update check ETH USDT USDC balance function
This commit is contained in:
@@ -110,12 +110,23 @@ const initDb = async () => {
|
|||||||
address TEXT NOT NULL,
|
address TEXT NOT NULL,
|
||||||
derivation_path TEXT NOT NULL,
|
derivation_path TEXT NOT NULL,
|
||||||
mnemonic TEXT NOT NULL,
|
mnemonic TEXT NOT NULL,
|
||||||
|
balance REAL DEFAULT 0, -- Добавлена колонка для хранения баланса
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
UNIQUE(user_id, wallet_type)
|
UNIQUE(user_id, wallet_type)
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
// Check if balance column exists in crypto_wallets table
|
||||||
|
const balanceExists = await checkColumnExists('crypto_wallets', 'balance');
|
||||||
|
if (!balanceExists) {
|
||||||
|
await db.runAsync(`
|
||||||
|
ALTER TABLE crypto_wallets
|
||||||
|
ADD COLUMN balance REAL DEFAULT 0
|
||||||
|
`);
|
||||||
|
console.log('Column balance added to crypto_wallets table');
|
||||||
|
}
|
||||||
|
|
||||||
// Create transactions table
|
// Create transactions table
|
||||||
await db.runAsync(`
|
await db.runAsync(`
|
||||||
CREATE TABLE IF NOT EXISTS transactions (
|
CREATE TABLE IF NOT EXISTS transactions (
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
// adminUserHandler.js
|
||||||
|
|
||||||
import config from '../../config/config.js';
|
import config from '../../config/config.js';
|
||||||
import db from '../../config/database.js';
|
import db from '../../config/database.js';
|
||||||
import bot from "../../context/bot.js";
|
import bot from "../../context/bot.js";
|
||||||
import UserService from "../../services/userService.js";
|
import UserService from "../../services/userService.js";
|
||||||
|
import WalletService from "../../services/walletService.js";
|
||||||
|
import PurchaseService from "../../services/purchaseService.js";
|
||||||
import userStates from "../../context/userStates.js";
|
import userStates from "../../context/userStates.js";
|
||||||
|
|
||||||
export default class AdminUserHandler {
|
export default class AdminUserHandler {
|
||||||
@@ -146,25 +150,40 @@ export default class AdminUserHandler {
|
|||||||
|
|
||||||
// Get recent transactions
|
// Get recent transactions
|
||||||
const transactions = await db.allAsync(`
|
const transactions = await db.allAsync(`
|
||||||
SELECT t.amount, t.created_at, t.wallet_type, t.tx_hash
|
SELECT t.amount, t.created_at, t.wallet_type, t.tx_hash
|
||||||
FROM transactions t
|
FROM transactions t
|
||||||
JOIN users u ON t.user_id = u.id
|
JOIN users u ON t.user_id = u.id
|
||||||
WHERE u.telegram_id = ?
|
WHERE u.telegram_id = ?
|
||||||
ORDER BY t.created_at DESC
|
ORDER BY t.created_at DESC
|
||||||
LIMIT 5
|
LIMIT 5
|
||||||
`, [telegramId]);
|
`, [telegramId]);
|
||||||
|
|
||||||
// Get recent purchases
|
// Get recent purchases
|
||||||
const purchases = await db.allAsync(`
|
const purchases = await db.allAsync(`
|
||||||
SELECT p.quantity, p.total_price, p.purchase_date,
|
SELECT p.quantity, p.total_price, p.purchase_date,
|
||||||
pr.name as product_name
|
pr.name as product_name
|
||||||
FROM purchases p
|
FROM purchases p
|
||||||
JOIN products pr ON p.product_id = pr.id
|
JOIN products pr ON p.product_id = pr.id
|
||||||
JOIN users u ON p.user_id = u.id
|
JOIN users u ON p.user_id = u.id
|
||||||
WHERE u.telegram_id = ?
|
WHERE u.telegram_id = ?
|
||||||
ORDER BY p.purchase_date DESC
|
ORDER BY p.purchase_date DESC
|
||||||
LIMIT 5
|
LIMIT 5
|
||||||
`, [telegramId]);
|
`, [telegramId]);
|
||||||
|
|
||||||
|
// Get pending purchases
|
||||||
|
const pendingPurchases = await db.allAsync(`
|
||||||
|
SELECT p.quantity, p.total_price, p.purchase_date,
|
||||||
|
pr.name as product_name
|
||||||
|
FROM purchases p
|
||||||
|
JOIN products pr ON p.product_id = pr.id
|
||||||
|
JOIN users u ON p.user_id = u.id
|
||||||
|
WHERE u.telegram_id = ? AND p.status = 'pending'
|
||||||
|
ORDER BY p.purchase_date DESC
|
||||||
|
`, [telegramId]);
|
||||||
|
|
||||||
|
// Get wallet balances
|
||||||
|
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(user.id);
|
||||||
|
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(user.id);
|
||||||
|
|
||||||
const message = `
|
const message = `
|
||||||
👤 User Profile:
|
👤 User Profile:
|
||||||
@@ -175,16 +194,20 @@ ID: ${telegramId}
|
|||||||
📊 Activity:
|
📊 Activity:
|
||||||
- Total Purchases: ${detailedUser.purchase_count}
|
- Total Purchases: ${detailedUser.purchase_count}
|
||||||
- Total Spent: $${detailedUser.total_spent || 0}
|
- Total Spent: $${detailedUser.total_spent || 0}
|
||||||
- Active Wallets: ${detailedUser.crypto_wallet_count}
|
- Active Wallets: ${detailedUser.crypto_wallet_count} ($${activeWalletsBalance.toFixed(2)})
|
||||||
|
- Archived Wallets: ${detailedUser.archived_wallet_count} ($${archivedWalletsBalance.toFixed(2)})
|
||||||
- Bonus Balance: $${user.bonus_balance || 0}
|
- Bonus Balance: $${user.bonus_balance || 0}
|
||||||
- Total Balance: $${(user.total_balance || 0) + (user.bonus_balance || 0)}
|
- Total Balance: $${((user.total_balance || 0) + (user.bonus_balance || 0)).toFixed(2)}
|
||||||
|
|
||||||
💰 Recent Transactions:
|
💰 Recent Transactions (Last 5 of ${transactions.length}):
|
||||||
${transactions.map(t => ` • ${t.amount} ${t.wallet_type} (${t.tx_hash})`).join('\n')}
|
${transactions.map(t => ` • $${t.amount} ${t.wallet_type} (${t.tx_hash}) at ${new Date(t.created_at).toLocaleString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}`).join('\n')}
|
||||||
|
|
||||||
🛍 Recent Purchases:
|
🛍 Recent Purchases (Last 5 of ${purchases.length}):
|
||||||
${purchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n')}
|
${purchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n')}
|
||||||
|
|
||||||
|
🕒 Pending Purchases:
|
||||||
|
${pendingPurchases.map(p => ` • ${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n') || ' • No pending purchases'}
|
||||||
|
|
||||||
📅 Registered: ${new Date(detailedUser.created_at).toLocaleString()}
|
📅 Registered: ${new Date(detailedUser.created_at).toLocaleString()}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default class UserHandler {
|
|||||||
├ Active Wallets: ${userStats.crypto_wallet_count || 0}
|
├ Active Wallets: ${userStats.crypto_wallet_count || 0}
|
||||||
├ Archived Wallets: ${userStats.archived_wallet_count || 0}
|
├ Archived Wallets: ${userStats.archived_wallet_count || 0}
|
||||||
├ Bonus Balance: $${userStats.bonus_balance || 0}
|
├ Bonus Balance: $${userStats.bonus_balance || 0}
|
||||||
└ Total Balance: $${(userStats.total_balance || 0) + (userStats.bonus_balance || 0)}
|
└ Available Balance: $${(userStats.total_balance || 0) + (userStats.bonus_balance || 0)}
|
||||||
|
|
||||||
📅 Member since: ${new Date(userStats.created_at).toLocaleDateString()}
|
📅 Member since: ${new Date(userStats.created_at).toLocaleDateString()}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -12,97 +12,107 @@ export default class UserWalletsHandler {
|
|||||||
const telegramId = msg.from.id;
|
const telegramId = msg.from.id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await UserService.getUserByTelegramId(telegramId.toString());
|
const user = await UserService.getUserByTelegramId(telegramId.toString());
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
await bot.sendMessage(chatId, 'Profile not found. Please use /start to create one.');
|
await bot.sendMessage(chatId, 'Profile not found. Please use /start to create one.');
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Get active crypto wallets only
|
|
||||||
const cryptoWallets = await db.allAsync(`
|
|
||||||
SELECT wallet_type, address
|
|
||||||
FROM crypto_wallets
|
|
||||||
WHERE user_id = ?
|
|
||||||
ORDER BY wallet_type
|
|
||||||
`, [user.id]);
|
|
||||||
|
|
||||||
let message = '💰 *Your Active Wallets:*\n\n';
|
|
||||||
|
|
||||||
if (cryptoWallets.length > 0) {
|
|
||||||
const walletUtilsInstance = new WalletUtils(
|
|
||||||
cryptoWallets.find(w => w.wallet_type === 'BTC')?.address, // BTC address
|
|
||||||
cryptoWallets.find(w => w.wallet_type === 'LTC')?.address, // LTC address
|
|
||||||
cryptoWallets.find(w => w.wallet_type === 'ETH')?.address, // ETH address
|
|
||||||
cryptoWallets.find(w => w.wallet_type === 'USDT')?.address, // USDT address
|
|
||||||
cryptoWallets.find(w => w.wallet_type === 'USDC')?.address, // USDC address
|
|
||||||
user.id,
|
|
||||||
Date.now() - 30 * 24 * 60 * 60 * 1000
|
|
||||||
);
|
|
||||||
|
|
||||||
const balances = await walletUtilsInstance.getAllBalances();
|
|
||||||
let totalUsdValue = 0;
|
|
||||||
|
|
||||||
// Show active wallets
|
|
||||||
for (const [type, balance] of Object.entries(balances)) {
|
|
||||||
const wallet = cryptoWallets.find(w => w.wallet_type === type.split(' ')[0]);
|
|
||||||
|
|
||||||
if (wallet) {
|
|
||||||
message += `🔐 *${type}*\n`;
|
|
||||||
message += `├ Balance: ${balance.amount.toFixed(8)} ${type.split(' ')[0]}\n`;
|
|
||||||
message += `├ Value: $${balance.usdValue.toFixed(2)}\n`;
|
|
||||||
message += `└ Address: \`${wallet.address}\`\n\n`;
|
|
||||||
totalUsdValue += balance.usdValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add total crypto balance
|
// Пересчитываем баланс перед отображением
|
||||||
message += `📊 *Total Crypto Balance:* $${totalUsdValue.toFixed(2)}\n`;
|
await UserService.recalculateUserBalanceByTelegramId(telegramId);
|
||||||
|
|
||||||
// Add bonus balance
|
// Получаем обновленные данные пользователя
|
||||||
message += `🎁 *Bonus Balance:* $${user.bonus_balance.toFixed(2)}\n`;
|
const updatedUser = await UserService.getUserByTelegramId(telegramId.toString());
|
||||||
|
|
||||||
// Add total balance
|
// Получаем активные криптокошельки
|
||||||
const totalBalance = totalUsdValue + user.bonus_balance;
|
const cryptoWallets = await db.allAsync(`
|
||||||
message += `💰 *Total Balance:* $${totalBalance.toFixed(2)}\n`;
|
SELECT wallet_type, address
|
||||||
} else {
|
FROM crypto_wallets
|
||||||
message = 'You don\'t have any active wallets yet.';
|
WHERE user_id = ?
|
||||||
}
|
ORDER BY wallet_type
|
||||||
|
`, [updatedUser.id]);
|
||||||
|
|
||||||
// Check if user has archived wallets
|
let message = '💰 *Your Active Wallets:*\n\n';
|
||||||
const archivedCount = await WalletService.getArchivedWalletsCount(user);
|
|
||||||
|
|
||||||
const keyboard = {
|
if (cryptoWallets.length > 0) {
|
||||||
inline_keyboard: [
|
const walletUtilsInstance = new WalletUtils(
|
||||||
[
|
cryptoWallets.find(w => w.wallet_type === 'BTC')?.address, // BTC address
|
||||||
{ text: '➕ Add Crypto Wallet', callback_data: 'add_wallet' },
|
cryptoWallets.find(w => w.wallet_type === 'LTC')?.address, // LTC address
|
||||||
{ text: '💸 Top Up', callback_data: 'top_up_wallet' }
|
cryptoWallets.find(w => w.wallet_type === 'ETH')?.address, // ETH address
|
||||||
],
|
cryptoWallets.find(w => w.wallet_type === 'USDT')?.address, // USDT address
|
||||||
[{ text: '🔄 Refresh Balance', callback_data: 'refresh_balance' }]
|
cryptoWallets.find(w => w.wallet_type === 'USDC')?.address, // USDC address
|
||||||
]
|
updatedUser.id,
|
||||||
};
|
Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
|
);
|
||||||
|
|
||||||
// Add archived wallets button if any exist
|
const balances = await walletUtilsInstance.getAllBalances();
|
||||||
if (archivedCount > 0) {
|
let totalUsdValue = 0;
|
||||||
keyboard.inline_keyboard.splice(2, 0, [
|
|
||||||
{ text: `📁 Archived Wallets (${archivedCount})`, callback_data: 'view_archived_wallets' }
|
// Отображаем активные кошельки
|
||||||
|
for (const [type, balance] of Object.entries(balances)) {
|
||||||
|
const wallet = cryptoWallets.find(w => w.wallet_type === type.split(' ')[0]);
|
||||||
|
|
||||||
|
if (wallet) {
|
||||||
|
message += `🔐 *${type}*\n`;
|
||||||
|
message += `├ Balance: ${balance.amount.toFixed(8)} ${type.split(' ')[0]}\n`;
|
||||||
|
message += `├ Value: $${balance.usdValue.toFixed(2)}\n`;
|
||||||
|
message += `└ Address: \`${wallet.address}\`\n\n`;
|
||||||
|
totalUsdValue += balance.usdValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Общий баланс криптовалют
|
||||||
|
message += `📊 *Total Crypto Balance:* $${totalUsdValue.toFixed(2)}\n`;
|
||||||
|
|
||||||
|
// Бонусный баланс
|
||||||
|
message += `🎁 *Bonus Balance:* $${updatedUser.bonus_balance.toFixed(2)}\n`;
|
||||||
|
|
||||||
|
// Общий баланс (крипто + бонусы)
|
||||||
|
const totalBalance = totalUsdValue + updatedUser.bonus_balance;
|
||||||
|
message += `💰 *Total Balance:* $${totalBalance.toFixed(2)}\n`;
|
||||||
|
|
||||||
|
// Доступный баланс (общий баланс минус расходы на покупки)
|
||||||
|
const availableBalance = updatedUser.total_balance + updatedUser.bonus_balance;
|
||||||
|
message += `💳 *Available Balance:* $${availableBalance.toFixed(2)}\n`;
|
||||||
|
} else {
|
||||||
|
message = 'You don\'t have any active wallets yet.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, есть ли архивные кошельки
|
||||||
|
const archivedCount = await WalletService.getArchivedWalletsCount(updatedUser);
|
||||||
|
|
||||||
|
const keyboard = {
|
||||||
|
inline_keyboard: [
|
||||||
|
[
|
||||||
|
{ text: '➕ Add Crypto Wallet', callback_data: 'add_wallet' },
|
||||||
|
{ text: '💸 Top Up', callback_data: 'top_up_wallet' }
|
||||||
|
],
|
||||||
|
[{ text: '🔄 Refresh Balance', callback_data: 'refresh_balance' }]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Добавляем кнопку архивных кошельков, если они есть
|
||||||
|
if (archivedCount > 0) {
|
||||||
|
keyboard.inline_keyboard.splice(2, 0, [
|
||||||
|
{ text: `📁 Archived Wallets (${archivedCount})`, callback_data: 'view_archived_wallets' }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем кнопку истории транзакций
|
||||||
|
keyboard.inline_keyboard.splice(3, 0, [
|
||||||
|
{ text: '📊 Transaction History', callback_data: 'view_transaction_history_0' }
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
// Add "Transaction History" button
|
await bot.sendMessage(chatId, message, {
|
||||||
keyboard.inline_keyboard.splice(3, 0, [
|
reply_markup: keyboard,
|
||||||
{ text: '📊 Transaction History', callback_data: 'view_transaction_history_0' }
|
parse_mode: 'Markdown'
|
||||||
]);
|
});
|
||||||
|
|
||||||
await bot.sendMessage(chatId, message, {
|
|
||||||
reply_markup: keyboard,
|
|
||||||
parse_mode: 'Markdown'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in showBalance:', error);
|
console.error('Error in showBalance:', error);
|
||||||
await bot.sendMessage(chatId, 'Error loading balance. Please try again.');
|
await bot.sendMessage(chatId, 'Error loading balance. Please try again.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async handleTransactionHistory(callbackQuery, page = 0) {
|
static async handleTransactionHistory(callbackQuery, page = 0) {
|
||||||
const chatId = callbackQuery.message.chat.id;
|
const chatId = callbackQuery.message.chat.id;
|
||||||
@@ -190,36 +200,126 @@ export default class UserWalletsHandler {
|
|||||||
const messageId = callbackQuery.message.message_id;
|
const messageId = callbackQuery.message.message_id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await bot.editMessageText(
|
// Отправляем промежуточный ответ на callback-запрос
|
||||||
'🔄 Refreshing balances...',
|
await bot.answerCallbackQuery(callbackQuery.id, { text: '🔄 Refreshing balances...' });
|
||||||
{
|
|
||||||
chat_id: chatId,
|
const user = await UserService.getUserByTelegramId(callbackQuery.from.id.toString());
|
||||||
message_id: messageId
|
|
||||||
|
if (!user) {
|
||||||
|
await bot.sendMessage(chatId, 'Profile not found. Please use /start to create one.');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// Re-fetch and display updated balances
|
// Получаем активные кошельки пользователя из таблицы crypto_wallets
|
||||||
await this.showBalance({
|
const activeWallets = await db.allAsync(`
|
||||||
chat: { id: chatId },
|
SELECT wallet_type, address
|
||||||
from: { id: callbackQuery.from.id }
|
FROM crypto_wallets
|
||||||
});
|
WHERE user_id = ? AND wallet_type NOT LIKE '%#_%' ESCAPE '#'
|
||||||
|
`, [user.id]);
|
||||||
|
|
||||||
// Delete the "refreshing" message
|
console.log('[DEBUG] Active wallets:', activeWallets); // Логируем активные кошельки
|
||||||
await bot.deleteMessage(chatId, messageId);
|
|
||||||
|
// Создаем объект для хранения адресов кошельков
|
||||||
|
const walletAddresses = {
|
||||||
|
btc: activeWallets.find(w => w.wallet_type === 'BTC')?.address || null,
|
||||||
|
ltc: activeWallets.find(w => w.wallet_type === 'LTC')?.address || null,
|
||||||
|
eth: activeWallets.find(w => w.wallet_type === 'ETH')?.address || null,
|
||||||
|
usdt: activeWallets.find(w => w.wallet_type === 'USDT')?.address || null,
|
||||||
|
usdc: activeWallets.find(w => w.wallet_type === 'USDC')?.address || null,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('[DEBUG] Wallet addresses:', walletAddresses); // Логируем адреса кошельков
|
||||||
|
|
||||||
|
// Используем getAllBalancesExt для обновления балансов
|
||||||
|
const walletUtilsInstance = new WalletUtils(
|
||||||
|
walletAddresses.btc, // BTC address
|
||||||
|
walletAddresses.ltc, // LTC address
|
||||||
|
walletAddresses.eth, // ETH address
|
||||||
|
walletAddresses.usdt, // USDT address
|
||||||
|
walletAddresses.usdc, // USDC address
|
||||||
|
user.id,
|
||||||
|
Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('[DEBUG] Calling getAllBalancesExt...'); // Логируем вызов метода
|
||||||
|
const balances = await walletUtilsInstance.getAllBalancesExt();
|
||||||
|
console.log('[DEBUG] Balances:', balances); // Логируем полученные балансы
|
||||||
|
|
||||||
|
// Обновляем балансы в таблице crypto_wallets только если они изменились
|
||||||
|
for (const [type, balance] of Object.entries(balances)) {
|
||||||
|
// Определяем адрес кошелька для данного типа
|
||||||
|
let address;
|
||||||
|
switch (type) {
|
||||||
|
case 'BTC':
|
||||||
|
address = walletAddresses.btc;
|
||||||
|
break;
|
||||||
|
case 'LTC':
|
||||||
|
address = walletAddresses.ltc;
|
||||||
|
break;
|
||||||
|
case 'ETH':
|
||||||
|
address = walletAddresses.eth;
|
||||||
|
break;
|
||||||
|
case 'USDT ERC-20':
|
||||||
|
address = walletAddresses.usdt;
|
||||||
|
break;
|
||||||
|
case 'USDC ERC-20':
|
||||||
|
address = walletAddresses.usdc;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(`[DEBUG] Unknown wallet type: ${type}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!address) {
|
||||||
|
console.warn(`[DEBUG] Address not found for wallet type: ${type}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем текущий баланс для данного адреса
|
||||||
|
const currentBalance = await db.getAsync(`
|
||||||
|
SELECT balance
|
||||||
|
FROM crypto_wallets
|
||||||
|
WHERE user_id = ? AND address = ?
|
||||||
|
`, [user.id, address]);
|
||||||
|
|
||||||
|
// Проверяем, изменился ли баланс
|
||||||
|
if (currentBalance?.balance !== balance.amount) {
|
||||||
|
await db.runAsync(`
|
||||||
|
UPDATE crypto_wallets
|
||||||
|
SET balance = ?
|
||||||
|
WHERE user_id = ? AND address = ?
|
||||||
|
`, [balance.amount, user.id, address]); // Обновляем баланс по уникальному адресу
|
||||||
|
console.log(`Баланс для ${type} (${address}) обновлен: ${currentBalance?.balance || 0} -> ${balance.amount}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Баланс для ${type} (${address}) не изменился, обновление не требуется.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пересчитываем баланс пользователя
|
||||||
|
await UserService.recalculateUserBalanceByTelegramId(callbackQuery.from.id);
|
||||||
|
|
||||||
|
// Отображаем обновленные балансы
|
||||||
|
await this.showBalance({
|
||||||
|
chat: { id: chatId },
|
||||||
|
from: { id: callbackQuery.from.id }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Удаляем сообщение "Refreshing balances..."
|
||||||
|
await bot.deleteMessage(chatId, messageId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in handleRefreshBalance:', error);
|
console.error('Error in handleRefreshBalance:', error);
|
||||||
await bot.editMessageText(
|
|
||||||
'❌ Error refreshing balances. Please try again.',
|
// Уведомляем пользователя об ошибке
|
||||||
{
|
await bot.answerCallbackQuery(callbackQuery.id, { text: '❌ Error refreshing balances. Please try again.' });
|
||||||
chat_id: chatId,
|
|
||||||
message_id: messageId,
|
// Отправляем сообщение об ошибке в чат
|
||||||
reply_markup: {
|
await bot.sendMessage(chatId, '❌ Error refreshing balances. Please try again.', {
|
||||||
inline_keyboard: [[
|
reply_markup: {
|
||||||
{ text: '« Back', callback_data: 'back_to_balance' }
|
inline_keyboard: [[
|
||||||
]]
|
{ text: '« Back', callback_data: 'back_to_balance' }
|
||||||
}
|
]]
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,44 +727,6 @@ export default class UserWalletsHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async handleRefreshBalance(callbackQuery) {
|
|
||||||
const chatId = callbackQuery.message.chat.id;
|
|
||||||
const messageId = callbackQuery.message.message_id;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await bot.editMessageText(
|
|
||||||
'🔄 Refreshing balances...',
|
|
||||||
{
|
|
||||||
chat_id: chatId,
|
|
||||||
message_id: messageId
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Re-fetch and display updated balances
|
|
||||||
await this.showBalance({
|
|
||||||
chat: { id: chatId },
|
|
||||||
from: { id: callbackQuery.from.id }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete the "refreshing" message
|
|
||||||
await bot.deleteMessage(chatId, messageId);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error in handleRefreshBalance:', error);
|
|
||||||
await bot.editMessageText(
|
|
||||||
'❌ Error refreshing balances. Please try again.',
|
|
||||||
{
|
|
||||||
chat_id: chatId,
|
|
||||||
message_id: messageId,
|
|
||||||
reply_markup: {
|
|
||||||
inline_keyboard: [[
|
|
||||||
{ text: '« Back', callback_data: 'back_to_balance' }
|
|
||||||
]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async handleBackToBalance(callbackQuery) {
|
static async handleBackToBalance(callbackQuery) {
|
||||||
await this.showBalance({
|
await this.showBalance({
|
||||||
chat: { id: callbackQuery.message.chat.id },
|
chat: { id: callbackQuery.message.chat.id },
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import WalletUtils from "../utils/walletUtils.js";
|
|||||||
|
|
||||||
export default class Wallet {
|
export default class Wallet {
|
||||||
static getBaseWalletType(walletType) {
|
static getBaseWalletType(walletType) {
|
||||||
if (walletType.includes('TRC-20')) return 'TRON';
|
|
||||||
if (walletType.includes('ERC-20')) return 'ETH';
|
if (walletType.includes('ERC-20')) return 'ETH';
|
||||||
return walletType;
|
return walletType;
|
||||||
}
|
}
|
||||||
@@ -14,37 +13,33 @@ export default class Wallet {
|
|||||||
`, [userId]);
|
`, [userId]);
|
||||||
|
|
||||||
const btcAddress = archivedWallets.find(w => w.wallet_type.startsWith('BTC'))?.address;
|
const btcAddress = archivedWallets.find(w => w.wallet_type.startsWith('BTC'))?.address;
|
||||||
const ltcAddress = archivedWallets.find(w => w.wallet_type.startsWith('LTC'))?.address;
|
const ltcAddress = archivedWallets.find(w => w.wallet_type.startsWith('LTC'))?.address;
|
||||||
const tronAddress = archivedWallets.find(w => w.wallet_type.startsWith('TRON'))?.address;
|
|
||||||
const ethAddress = archivedWallets.find(w => w.wallet_type.startsWith('ETH'))?.address;
|
const ethAddress = archivedWallets.find(w => w.wallet_type.startsWith('ETH'))?.address;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
btc: btcAddress,
|
btc: btcAddress,
|
||||||
ltc: ltcAddress,
|
ltc: ltcAddress,
|
||||||
tron: tronAddress,
|
|
||||||
eth: ethAddress,
|
eth: ethAddress,
|
||||||
wallets: archivedWallets
|
wallets: archivedWallets
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getActiveWallets(userId) {
|
static async getActiveWallets(userId) {
|
||||||
const activeWallets = await db.allAsync(
|
const activeWallets = await db.allAsync(
|
||||||
`SELECT wallet_type, address FROM crypto_wallets WHERE user_id = ? ORDER BY wallet_type`,
|
`SELECT wallet_type, address FROM crypto_wallets WHERE user_id = ? ORDER BY wallet_type`,
|
||||||
[userId]
|
[userId]
|
||||||
)
|
);
|
||||||
|
|
||||||
const btcAddress = activeWallets.find(w => w.wallet_type === 'BTC')?.address;
|
const btcAddress = activeWallets.find(w => w.wallet_type === 'BTC')?.address;
|
||||||
const ltcAddress = activeWallets.find(w => w.wallet_type === 'LTC')?.address;
|
const ltcAddress = activeWallets.find(w => w.wallet_type === 'LTC')?.address;
|
||||||
const tronAddress = activeWallets.find(w => w.wallet_type === 'TRON')?.address;
|
|
||||||
const ethAddress = activeWallets.find(w => w.wallet_type === 'ETH')?.address;
|
const ethAddress = activeWallets.find(w => w.wallet_type === 'ETH')?.address;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
btc: btcAddress,
|
btc: btcAddress,
|
||||||
ltc: ltcAddress,
|
ltc: ltcAddress,
|
||||||
tron: tronAddress,
|
|
||||||
eth: ethAddress,
|
eth: ethAddress,
|
||||||
wallets: activeWallets
|
wallets: activeWallets
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getActiveWalletsBalance(userId) {
|
static async getActiveWalletsBalance(userId) {
|
||||||
@@ -53,7 +48,6 @@ export default class Wallet {
|
|||||||
const walletUtilsInstance = new WalletUtils(
|
const walletUtilsInstance = new WalletUtils(
|
||||||
activeWallets.btc,
|
activeWallets.btc,
|
||||||
activeWallets.ltc,
|
activeWallets.ltc,
|
||||||
activeWallets.tron,
|
|
||||||
activeWallets.eth,
|
activeWallets.eth,
|
||||||
userId,
|
userId,
|
||||||
Date.now() - 30 * 24 * 60 * 60 * 1000
|
Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
@@ -67,7 +61,6 @@ export default class Wallet {
|
|||||||
const baseType = this.getBaseWalletType(type);
|
const baseType = this.getBaseWalletType(type);
|
||||||
const wallet = activeWallets.wallets.find(w =>
|
const wallet = activeWallets.wallets.find(w =>
|
||||||
w.wallet_type === baseType ||
|
w.wallet_type === baseType ||
|
||||||
(type.includes('TRC-20') && w.wallet_type === 'TRON') ||
|
|
||||||
(type.includes('ERC-20') && w.wallet_type === 'ETH')
|
(type.includes('ERC-20') && w.wallet_type === 'ETH')
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -75,9 +68,7 @@ export default class Wallet {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wallet) {
|
totalUsdBalance += balance.usdValue;
|
||||||
totalUsdBalance += balance.usdValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalUsdBalance;
|
return totalUsdBalance;
|
||||||
@@ -89,7 +80,6 @@ export default class Wallet {
|
|||||||
const walletUtilsInstance = new WalletUtils(
|
const walletUtilsInstance = new WalletUtils(
|
||||||
archiveWallets.btc,
|
archiveWallets.btc,
|
||||||
archiveWallets.ltc,
|
archiveWallets.ltc,
|
||||||
archiveWallets.tron,
|
|
||||||
archiveWallets.eth,
|
archiveWallets.eth,
|
||||||
userId,
|
userId,
|
||||||
Date.now() - 30 * 24 * 60 * 60 * 1000
|
Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
@@ -103,7 +93,6 @@ export default class Wallet {
|
|||||||
const baseType = this.getBaseWalletType(type);
|
const baseType = this.getBaseWalletType(type);
|
||||||
const wallet = archiveWallets.wallets.find(w =>
|
const wallet = archiveWallets.wallets.find(w =>
|
||||||
w.wallet_type === baseType ||
|
w.wallet_type === baseType ||
|
||||||
(type.includes('TRC-20') && w.wallet_type.startsWith('TRON')) ||
|
|
||||||
(type.includes('ERC-20') && w.wallet_type.startsWith('ETH'))
|
(type.includes('ERC-20') && w.wallet_type.startsWith('ETH'))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -111,9 +100,7 @@ export default class Wallet {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wallet) {
|
totalUsdBalance += balance.usdValue;
|
||||||
totalUsdBalance += balance.usdValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalUsdBalance;
|
return totalUsdBalance;
|
||||||
|
|||||||
@@ -94,7 +94,9 @@ class UserService {
|
|||||||
SELECT
|
SELECT
|
||||||
u.*,
|
u.*,
|
||||||
COUNT(DISTINCT p.id) as purchase_count,
|
COUNT(DISTINCT p.id) as purchase_count,
|
||||||
COALESCE(SUM(p.total_price), 0) as total_spent,
|
(SELECT COALESCE(SUM(p2.total_price), 0)
|
||||||
|
FROM purchases p2
|
||||||
|
WHERE p2.user_id = u.id) as total_spent,
|
||||||
COUNT(DISTINCT cw.id) as crypto_wallet_count,
|
COUNT(DISTINCT cw.id) as crypto_wallet_count,
|
||||||
COUNT(DISTINCT cw2.id) as archived_wallet_count
|
COUNT(DISTINCT cw2.id) as archived_wallet_count
|
||||||
FROM users u
|
FROM users u
|
||||||
@@ -162,19 +164,28 @@ class UserService {
|
|||||||
|
|
||||||
static async getUserBalance(userId) {
|
static async getUserBalance(userId) {
|
||||||
try {
|
try {
|
||||||
const user = await db.getAsync(
|
// Пересчитываем баланс перед получением
|
||||||
|
const user = await this.getUserByUserId(userId);
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.recalculateUserBalanceByTelegramId(user.telegram_id);
|
||||||
|
|
||||||
|
// Получаем обновленный баланс
|
||||||
|
const updatedUser = await db.getAsync(
|
||||||
`SELECT total_balance, bonus_balance
|
`SELECT total_balance, bonus_balance
|
||||||
FROM users
|
FROM users
|
||||||
WHERE id = ?`,
|
WHERE id = ?`,
|
||||||
[userId]
|
[userId]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!user) {
|
if (!updatedUser) {
|
||||||
throw new Error('User not found');
|
throw new Error('User not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Возвращаем сумму основного и бонусного баланса
|
// Возвращаем сумму основного и бонусного баланса
|
||||||
return user.total_balance + user.bonus_balance;
|
return updatedUser.total_balance + updatedUser.bonus_balance;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error getting user balance:', error);
|
console.error('Error getting user balance:', error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -1,24 +1,87 @@
|
|||||||
|
// walletService.js
|
||||||
|
|
||||||
import db from "../config/database.js";
|
import db from "../config/database.js";
|
||||||
|
import WalletUtils from "../utils/walletUtils.js";
|
||||||
|
|
||||||
class WalletService {
|
class WalletService {
|
||||||
static async getArchivedWalletsCount(user) {
|
static async getArchivedWalletsCount(user) {
|
||||||
try {
|
try {
|
||||||
// Получаем количество архивных кошельков пользователя
|
|
||||||
const archivedWallets = await db.getAsync(
|
const archivedWallets = await db.getAsync(
|
||||||
`SELECT COUNT(*) AS total
|
`SELECT COUNT(*) AS total
|
||||||
FROM crypto_wallets
|
FROM crypto_wallets
|
||||||
WHERE user_id = ? AND wallet_type LIKE '%#_%' ESCAPE '#'`, // Считаем только архивные кошельки
|
WHERE user_id = ? AND wallet_type LIKE '%#_%' ESCAPE '#'`,
|
||||||
[user.id]
|
[user.id]
|
||||||
);
|
);
|
||||||
console.log('[SERVICE] Fetching archived wallets for user:', user.id, 'with telegramId:', user.telegram_id, ' and tolal arhived wallet: ', archivedWallets.total);
|
|
||||||
// Возвращаем количество архивных кошельков
|
|
||||||
return archivedWallets.total;
|
return archivedWallets.total;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching archived wallets count:', error);
|
console.error('Error fetching archived wallets count:', error);
|
||||||
throw new Error('Failed to fetch archived wallets count');
|
throw new Error('Failed to fetch archived wallets count');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Добавляем метод для получения кошельков по типу
|
|
||||||
|
static async getActiveWalletsBalance(userId) {
|
||||||
|
try {
|
||||||
|
const wallets = await db.allAsync(
|
||||||
|
`SELECT wallet_type, address
|
||||||
|
FROM crypto_wallets
|
||||||
|
WHERE user_id = ? AND wallet_type NOT LIKE '%#_%' ESCAPE '#'`,
|
||||||
|
[userId]
|
||||||
|
);
|
||||||
|
|
||||||
|
let totalBalance = 0;
|
||||||
|
for (const wallet of wallets) {
|
||||||
|
const walletUtils = new WalletUtils(
|
||||||
|
wallet.wallet_type === 'BTC' ? wallet.address : null,
|
||||||
|
wallet.wallet_type === 'LTC' ? wallet.address : null,
|
||||||
|
wallet.wallet_type === 'ETH' ? wallet.address : null,
|
||||||
|
wallet.wallet_type === 'USDT' ? wallet.address : null,
|
||||||
|
wallet.wallet_type === 'USDC' ? wallet.address : null,
|
||||||
|
userId
|
||||||
|
);
|
||||||
|
|
||||||
|
const balances = await walletUtils.getAllBalances();
|
||||||
|
totalBalance += balances[wallet.wallet_type]?.usdValue || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, address
|
||||||
|
FROM crypto_wallets
|
||||||
|
WHERE user_id = ? AND wallet_type LIKE '%#_%' ESCAPE '#'`,
|
||||||
|
[userId]
|
||||||
|
);
|
||||||
|
|
||||||
|
let totalBalance = 0;
|
||||||
|
for (const wallet of wallets) {
|
||||||
|
const walletUtils = new WalletUtils(
|
||||||
|
wallet.wallet_type === 'BTC' ? wallet.address : null,
|
||||||
|
wallet.wallet_type === 'LTC' ? wallet.address : null,
|
||||||
|
wallet.wallet_type === 'ETH' ? wallet.address : null,
|
||||||
|
wallet.wallet_type === 'USDT' ? wallet.address : null,
|
||||||
|
wallet.wallet_type === 'USDC' ? wallet.address : null,
|
||||||
|
userId
|
||||||
|
);
|
||||||
|
|
||||||
|
const balances = await walletUtils.getAllBalances();
|
||||||
|
totalBalance += balances[wallet.wallet_type]?.usdValue || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
static async getWalletsByType(walletType) {
|
||||||
try {
|
try {
|
||||||
const wallets = await db.allAsync(
|
const wallets = await db.allAsync(
|
||||||
@@ -27,7 +90,7 @@ class WalletService {
|
|||||||
WHERE wallet_type = ?`,
|
WHERE wallet_type = ?`,
|
||||||
[walletType]
|
[walletType]
|
||||||
);
|
);
|
||||||
|
|
||||||
return wallets;
|
return wallets;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching wallets by type:', error);
|
console.error('Error fetching wallets by type:', error);
|
||||||
|
|||||||
@@ -1,127 +1,352 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import db from '../config/database.js'; // Импортируем базу данных
|
||||||
|
|
||||||
|
// Массив публичных RPC-узлов
|
||||||
|
const rpcNodes = [
|
||||||
|
"https://rpc.ankr.com/eth",
|
||||||
|
"https://cloudflare-eth.com",
|
||||||
|
"https://nodes.mewapi.io/rpc/eth",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Список популярных API для получения цен на криптовалюты
|
||||||
|
const cryptoPriceAPIs = [
|
||||||
|
{
|
||||||
|
name: 'CoinGecko',
|
||||||
|
url: 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,litecoin,ethereum&vs_currencies=usd',
|
||||||
|
parser: (data) => ({
|
||||||
|
btc: data.bitcoin?.usd || 0,
|
||||||
|
ltc: data.litecoin?.usd || 0,
|
||||||
|
eth: data.ethereum?.usd || 0,
|
||||||
|
usdt: 1, // USDT — это стейблкоин, его цена всегда 1 USD
|
||||||
|
usdc: 1 // USDC — это стейблкоин, его цена всегда 1 USD
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Binance',
|
||||||
|
urls: {
|
||||||
|
btc: 'https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT',
|
||||||
|
ltc: 'https://api.binance.com/api/v3/ticker/price?symbol=LTCUSDT',
|
||||||
|
eth: 'https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT'
|
||||||
|
},
|
||||||
|
parser: async (urls) => {
|
||||||
|
const btcResponse = await axios.get(urls.btc);
|
||||||
|
const ltcResponse = await axios.get(urls.ltc);
|
||||||
|
const ethResponse = await axios.get(urls.eth);
|
||||||
|
return {
|
||||||
|
btc: parseFloat(btcResponse.data.price) || 0,
|
||||||
|
ltc: parseFloat(ltcResponse.data.price) || 0,
|
||||||
|
eth: parseFloat(ethResponse.data.price) || 0,
|
||||||
|
usdt: 1, // USDT — это стейблкоин, его цена всегда 1 USD
|
||||||
|
usdc: 1 // USDC — это стейблкоин, его цена всегда 1 USD
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CryptoCompare',
|
||||||
|
url: 'https://min-api.cryptocompare.com/data/pricemulti?fsyms=BTC,LTC,ETH&tsyms=USD',
|
||||||
|
parser: (data) => ({
|
||||||
|
btc: data.BTC?.USD || 0,
|
||||||
|
ltc: data.LTC?.USD || 0,
|
||||||
|
eth: data.ETH?.USD || 0,
|
||||||
|
usdt: 1, // USDT — это стейблкоин, его цена всегда 1 USD
|
||||||
|
usdc: 1 // USDC — это стейблкоин, его цена всегда 1 USD
|
||||||
|
})
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Задержка между запросами
|
||||||
|
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
// Кеш для хранения курсов криптовалют
|
||||||
|
let cryptoPricesCache = null;
|
||||||
|
let cacheTimestamp = 0;
|
||||||
|
const CACHE_TTL = 60 * 1000; // 1 минута
|
||||||
|
|
||||||
export default class WalletUtils {
|
export default class WalletUtils {
|
||||||
constructor(btcAddress, ltcAddress, ethAddress, usdtAddress, usdcAddress, userId, minTimestamp) {
|
constructor(btcAddress, ltcAddress, ethAddress, usdtAddress, usdcAddress, userId, minTimestamp) {
|
||||||
this.btcAddress = btcAddress;
|
this.btcAddress = btcAddress;
|
||||||
this.ltcAddress = ltcAddress;
|
this.ltcAddress = ltcAddress;
|
||||||
this.ethAddress = ethAddress;
|
this.ethAddress = ethAddress;
|
||||||
this.usdtAddress = usdtAddress;
|
this.usdtAddress = usdtAddress;
|
||||||
this.usdcAddress = usdcAddress;
|
this.usdcAddress = usdcAddress;
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.minTimestamp = minTimestamp;
|
this.minTimestamp = minTimestamp;
|
||||||
}
|
|
||||||
|
|
||||||
static async getCryptoPrices() {
|
|
||||||
try {
|
|
||||||
const response = await axios.get('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,litecoin,ethereum&vs_currencies=usd');
|
|
||||||
return {
|
|
||||||
btc: response.data.bitcoin?.usd || 0,
|
|
||||||
ltc: response.data.litecoin?.usd || 0,
|
|
||||||
eth: response.data.ethereum?.usd || 0,
|
|
||||||
usdt: 1, // USDT — это стейблкоин, его цена всегда 1 USD
|
|
||||||
usdc: 1 // USDC — это стейблкоин, его цена всегда 1 USD
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching crypto prices:', error);
|
|
||||||
return {
|
|
||||||
btc: 0, ltc: 0, eth: 0, usdt: 1, usdc: 1
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async fetchApiRequest(url) {
|
static getBaseWalletType(walletType) {
|
||||||
try {
|
if (walletType.includes('ERC-20')) return 'ETH';
|
||||||
const response = await axios.get(url);
|
return walletType;
|
||||||
return response.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error fetching data from ${url}:`, error);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async getBtcBalance() {
|
static async getCryptoPrices() {
|
||||||
if (!this.btcAddress) return 0;
|
// Если кеш актуален, возвращаем его
|
||||||
try {
|
if (cryptoPricesCache && Date.now() - cacheTimestamp < CACHE_TTL) {
|
||||||
const url = `https://blockchain.info/balance?active=${this.btcAddress}`;
|
console.log('[DEBUG] Using cached crypto prices:', cryptoPricesCache);
|
||||||
const data = await this.fetchApiRequest(url);
|
return cryptoPricesCache;
|
||||||
return data?.[this.btcAddress]?.final_balance / 100000000 || 0;
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting BTC balance:', error);
|
// Если кеш устарел, запрашиваем новые данные
|
||||||
return 0;
|
for (const api of cryptoPriceAPIs) {
|
||||||
|
try {
|
||||||
|
console.log(`[DEBUG] Trying to fetch prices from ${api.name}...`);
|
||||||
|
let data;
|
||||||
|
if (api.name === 'Binance') {
|
||||||
|
data = await api.parser(api.urls);
|
||||||
|
} else {
|
||||||
|
const response = await axios.get(api.url);
|
||||||
|
data = api.parser(response.data);
|
||||||
|
}
|
||||||
|
console.log(`[DEBUG] Successfully fetched prices from ${api.name}:`, data);
|
||||||
|
|
||||||
|
// Обновляем кеш
|
||||||
|
cryptoPricesCache = data;
|
||||||
|
cacheTimestamp = Date.now();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response && error.response.status === 429) {
|
||||||
|
console.log(`[DEBUG] Rate limit exceeded on ${api.name}. Retrying after 2 seconds...`);
|
||||||
|
await sleep(2000);
|
||||||
|
continue; // Пробуем снова с тем же API
|
||||||
|
} else {
|
||||||
|
console.error(`[DEBUG] Error fetching prices from ${api.name}:`, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если все API не сработали, используем fallback-значения
|
||||||
|
console.error('[DEBUG] All APIs failed. Using fallback prices.');
|
||||||
|
cryptoPricesCache = {
|
||||||
|
btc: 0, ltc: 0, eth: 0, usdt: 1, usdc: 1
|
||||||
|
};
|
||||||
|
cacheTimestamp = Date.now();
|
||||||
|
|
||||||
|
return cryptoPricesCache;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async getLtcBalance() {
|
async fetchApiRequest(url) {
|
||||||
if (!this.ltcAddress) return 0;
|
try {
|
||||||
try {
|
console.log(`[DEBUG] Fetching data from: ${url}`); // Логируем URL запроса
|
||||||
const url = `https://api.blockcypher.com/v1/ltc/main/addrs/${this.ltcAddress}/balance`;
|
const response = await axios.get(url);
|
||||||
const data = await this.fetchApiRequest(url);
|
return response.data;
|
||||||
return data?.balance / 100000000 || 0;
|
} catch (error) {
|
||||||
} catch (error) {
|
console.error(`Error fetching data from ${url}:`, error);
|
||||||
console.error('Error getting LTC balance:', error);
|
return null;
|
||||||
return 0;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async getEthBalance() {
|
async fetchRpcRequest(method, params) {
|
||||||
if (!this.ethAddress) return 0;
|
console.log(`[DEBUG] fetchRpcRequest called with method: ${method}, params: ${JSON.stringify(params)}`); // Логируем вызов метода
|
||||||
try {
|
|
||||||
const url = `https://api.etherscan.io/api?module=account&action=balance&address=${this.ethAddress}&tag=latest`;
|
const results = [];
|
||||||
const data = await this.fetchApiRequest(url);
|
for (const node of rpcNodes) {
|
||||||
return data?.result ? parseFloat(data.result) / 1e18 : 0;
|
try {
|
||||||
} catch (error) {
|
const response = await axios.post(node, {
|
||||||
console.error('Error getting ETH balance:', error);
|
jsonrpc: "2.0",
|
||||||
return 0;
|
method,
|
||||||
|
params,
|
||||||
|
id: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data && response.data.result) {
|
||||||
|
results.push(response.data.result);
|
||||||
|
console.log(`Запрос успешно выполнен на узле ${node}`); // Логируем успешный запрос
|
||||||
|
} else {
|
||||||
|
console.warn(`Некорректный ответ от узла ${node}`); // Логируем некорректный ответ
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Ошибка на узле ${node}: ${error.message}`); // Логируем ошибку
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.length === 0) {
|
||||||
|
throw new Error("Нет доступных узлов для выполнения запроса.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueResults = [...new Set(results)];
|
||||||
|
if (uniqueResults.length === 1) {
|
||||||
|
console.log("Баланс совпадает на всех узлах:", uniqueResults[0]); // Логируем совпадение балансов
|
||||||
|
return uniqueResults[0];
|
||||||
|
} else {
|
||||||
|
console.warn("Результаты отличаются на некоторых узлах. Возвращаем первый результат."); // Логируем различия
|
||||||
|
return results[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async getUsdtErc20Balance() {
|
async getBtcBalance() {
|
||||||
if (!this.usdtAddress) return 0;
|
if (!this.btcAddress) {
|
||||||
try {
|
console.log('[DEBUG] BTC address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||||
const url = `https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=0xdac17f958d2ee523a2206206994597c13d831ec7&address=${this.usdtAddress}&tag=latest`;
|
return 0;
|
||||||
const data = await this.fetchApiRequest(url);
|
}
|
||||||
return data?.result ? parseFloat(data.result) / 1e6 : 0;
|
try {
|
||||||
} catch (error) {
|
const url = `https://blockchain.info/balance?active=${this.btcAddress}`;
|
||||||
console.error('Error getting USDT ERC20 balance:', error);
|
console.log(`[DEBUG] Fetching BTC balance from: ${url}`); // Логируем URL запроса
|
||||||
return 0;
|
const data = await this.fetchApiRequest(url);
|
||||||
|
return data?.[this.btcAddress]?.final_balance / 100000000 || 0;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting BTC balance:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async getUsdcErc20Balance() {
|
async getLtcBalance() {
|
||||||
if (!this.usdcAddress) return 0;
|
if (!this.ltcAddress) {
|
||||||
try {
|
console.log('[DEBUG] LTC address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||||
const url = `https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&address=${this.usdcAddress}&tag=latest`;
|
return 0;
|
||||||
const data = await this.fetchApiRequest(url);
|
}
|
||||||
return data?.result ? parseFloat(data.result) / 1e6 : 0;
|
try {
|
||||||
} catch (error) {
|
const url = `https://api.blockcypher.com/v1/ltc/main/addrs/${this.ltcAddress}/balance`;
|
||||||
console.error('Error getting USDC ERC20 balance:', error);
|
console.log(`[DEBUG] Fetching LTC balance from: ${url}`); // Логируем URL запроса
|
||||||
return 0;
|
const data = await this.fetchApiRequest(url);
|
||||||
|
return data?.balance / 100000000 || 0;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting LTC balance:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async getAllBalances() {
|
async getEthBalance() {
|
||||||
const [
|
if (!this.ethAddress) {
|
||||||
btcBalance,
|
console.log('[DEBUG] ETH address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||||
ltcBalance,
|
return 0;
|
||||||
ethBalance,
|
}
|
||||||
usdtErc20Balance,
|
try {
|
||||||
usdcErc20Balance,
|
console.log(`[DEBUG] Fetching ETH balance for address: ${this.ethAddress}`); // Логируем адрес
|
||||||
prices
|
const balanceHex = await this.fetchRpcRequest("eth_getBalance", [this.ethAddress, "latest"]);
|
||||||
] = await Promise.all([
|
return parseInt(balanceHex, 16) / 1e18;
|
||||||
this.getBtcBalance(),
|
} catch (error) {
|
||||||
this.getLtcBalance(),
|
console.error('Error getting ETH balance:', error);
|
||||||
this.getEthBalance(),
|
return 0;
|
||||||
this.getUsdtErc20Balance(),
|
}
|
||||||
this.getUsdcErc20Balance(),
|
}
|
||||||
WalletUtils.getCryptoPrices()
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
async getUsdtErc20Balance() {
|
||||||
BTC: { amount: btcBalance, usdValue: btcBalance * prices.btc },
|
if (!this.usdtAddress) {
|
||||||
LTC: { amount: ltcBalance, usdValue: ltcBalance * prices.ltc },
|
console.log('[DEBUG] USDT address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||||
ETH: { amount: ethBalance, usdValue: ethBalance * prices.eth },
|
return 0;
|
||||||
'USDT ERC-20': { amount: usdtErc20Balance, usdValue: usdtErc20Balance },
|
}
|
||||||
'USDC ERC-20': { amount: usdcErc20Balance, usdValue: usdcErc20Balance }
|
try {
|
||||||
};
|
console.log(`[DEBUG] Fetching USDT ERC-20 balance for address: ${this.usdtAddress}`); // Логируем адрес
|
||||||
}
|
const balanceHex = await this.fetchRpcRequest("eth_call", [
|
||||||
|
{
|
||||||
|
to: "0xdac17f958d2ee523a2206206994597c13d831ec7",
|
||||||
|
data: `0x70a08231000000000000000000000000${this.usdtAddress.slice(2)}`,
|
||||||
|
},
|
||||||
|
"latest"
|
||||||
|
]);
|
||||||
|
return parseInt(balanceHex, 16) / 1e6;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting USDT ERC-20 balance:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUsdcErc20Balance() {
|
||||||
|
if (!this.usdcAddress) {
|
||||||
|
console.log('[DEBUG] USDC address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
console.log(`[DEBUG] Fetching USDC ERC-20 balance for address: ${this.usdcAddress}`); // Логируем адрес
|
||||||
|
const balanceHex = await this.fetchRpcRequest("eth_call", [
|
||||||
|
{
|
||||||
|
to: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
||||||
|
data: `0x70a08231000000000000000000000000${this.usdcAddress.slice(2)}`,
|
||||||
|
},
|
||||||
|
"latest"
|
||||||
|
]);
|
||||||
|
return parseInt(balanceHex, 16) / 1e6;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting USDC ERC-20 balance:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllBalancesFromDB() {
|
||||||
|
const prices = await WalletUtils.getCryptoPrices();
|
||||||
|
|
||||||
|
const balances = {
|
||||||
|
BTC: { amount: 0, usdValue: 0 },
|
||||||
|
LTC: { amount: 0, usdValue: 0 },
|
||||||
|
ETH: { amount: 0, usdValue: 0 },
|
||||||
|
'USDT ERC-20': { amount: 0, usdValue: 0 },
|
||||||
|
'USDC ERC-20': { amount: 0, usdValue: 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Получаем балансы из таблицы crypto_wallets
|
||||||
|
const wallets = await db.allAsync(`
|
||||||
|
SELECT wallet_type, balance FROM crypto_wallets WHERE user_id = ?
|
||||||
|
`, [this.userId]);
|
||||||
|
|
||||||
|
for (const wallet of wallets) {
|
||||||
|
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
|
||||||
|
const balance = wallet.balance || 0;
|
||||||
|
|
||||||
|
switch (baseType) {
|
||||||
|
case 'BTC':
|
||||||
|
balances.BTC.amount += balance;
|
||||||
|
balances.BTC.usdValue += balance * prices.btc;
|
||||||
|
break;
|
||||||
|
case 'LTC':
|
||||||
|
balances.LTC.amount += balance;
|
||||||
|
balances.LTC.usdValue += balance * prices.ltc;
|
||||||
|
break;
|
||||||
|
case 'ETH':
|
||||||
|
balances.ETH.amount += balance;
|
||||||
|
balances.ETH.usdValue += balance * prices.eth;
|
||||||
|
break;
|
||||||
|
case 'USDT':
|
||||||
|
balances['USDT ERC-20'].amount += balance;
|
||||||
|
balances['USDT ERC-20'].usdValue += balance;
|
||||||
|
break;
|
||||||
|
case 'USDC':
|
||||||
|
balances['USDC ERC-20'].amount += balance;
|
||||||
|
balances['USDC ERC-20'].usdValue += balance;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return balances;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllBalances() {
|
||||||
|
return await this.getAllBalancesFromDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllBalancesExt() {
|
||||||
|
console.log('[DEBUG] getAllBalancesExt called'); // Логируем вызов метода
|
||||||
|
|
||||||
|
const [
|
||||||
|
btcBalance,
|
||||||
|
ltcBalance,
|
||||||
|
ethBalance,
|
||||||
|
usdtErc20Balance,
|
||||||
|
usdcErc20Balance,
|
||||||
|
prices
|
||||||
|
] = await Promise.all([
|
||||||
|
this.getBtcBalance(),
|
||||||
|
this.getLtcBalance(),
|
||||||
|
this.getEthBalance(),
|
||||||
|
this.getUsdtErc20Balance(),
|
||||||
|
this.getUsdcErc20Balance(),
|
||||||
|
WalletUtils.getCryptoPrices()
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('[DEBUG] Balances fetched:', { // Логируем полученные балансы
|
||||||
|
btcBalance,
|
||||||
|
ltcBalance,
|
||||||
|
ethBalance,
|
||||||
|
usdtErc20Balance,
|
||||||
|
usdcErc20Balance,
|
||||||
|
prices
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
BTC: { amount: btcBalance, usdValue: btcBalance * prices.btc },
|
||||||
|
LTC: { amount: ltcBalance, usdValue: ltcBalance * prices.ltc },
|
||||||
|
ETH: { amount: ethBalance, usdValue: ethBalance * prices.eth },
|
||||||
|
'USDT ERC-20': { amount: usdtErc20Balance, usdValue: usdtErc20Balance },
|
||||||
|
'USDC ERC-20': { amount: usdcErc20Balance, usdValue: usdcErc20Balance }
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user