148 lines
5.5 KiB
JavaScript
148 lines
5.5 KiB
JavaScript
/**
|
||
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||
* All rights reserved.
|
||
*
|
||
* This software is proprietary and confidential.
|
||
* Unauthorized copying, modification, or distribution is prohibited.
|
||
*
|
||
* For licensing inquiries: info@hb3-accelerator.com
|
||
* Website: https://hb3-accelerator.com
|
||
* GitHub: https://github.com/HB3-ACCELERATOR
|
||
*/
|
||
|
||
const { ethers } = require('ethers');
|
||
const logger = require('../utils/logger');
|
||
const db = require('../db');
|
||
const authTokenService = require('./authTokenService');
|
||
const rpcProviderService = require('./rpcProviderService');
|
||
|
||
// Минимальный ABI для ERC20
|
||
const ERC20_ABI = [
|
||
'function balanceOf(address owner) view returns (uint256)'
|
||
];
|
||
|
||
/**
|
||
* Основной метод проверки роли админа
|
||
* @param {string} address - Адрес кошелька
|
||
* @returns {Promise<boolean>} - Является ли пользователь админом
|
||
*/
|
||
async function checkAdminRole(address) {
|
||
if (!address) return false;
|
||
logger.info(`Checking admin role for address: ${address}`);
|
||
|
||
try {
|
||
let foundTokens = false;
|
||
let errorCount = 0;
|
||
const balances = {};
|
||
// Получаем ключ шифрования
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
let encryptionKey = 'default-key';
|
||
|
||
try {
|
||
const keyPath = path.join(__dirname, '../ssl/keys/full_db_encryption.key');
|
||
if (fs.existsSync(keyPath)) {
|
||
encryptionKey = fs.readFileSync(keyPath, 'utf8').trim();
|
||
}
|
||
} catch (keyError) {
|
||
// console.error('Error reading encryption key:', keyError);
|
||
}
|
||
|
||
// Получаем токены и RPC из базы с расшифровкой
|
||
const tokensResult = await db.getQuery()(
|
||
'SELECT id, min_balance, created_at, updated_at, decrypt_text(name_encrypted, $1) as name, decrypt_text(address_encrypted, $1) as address, decrypt_text(network_encrypted, $1) as network FROM auth_tokens',
|
||
[encryptionKey]
|
||
);
|
||
const tokens = tokensResult.rows;
|
||
|
||
const rpcProvidersResult = await db.getQuery()(
|
||
'SELECT id, chain_id, created_at, updated_at, decrypt_text(network_id_encrypted, $1) as network_id, decrypt_text(rpc_url_encrypted, $1) as rpc_url FROM rpc_providers',
|
||
[encryptionKey]
|
||
);
|
||
const rpcProviders = rpcProvidersResult.rows;
|
||
|
||
logger.info(`Retrieved ${tokens.length} tokens and ${rpcProviders.length} RPC providers`);
|
||
logger.info('Tokens:', JSON.stringify(tokens, null, 2));
|
||
logger.info('RPC Providers:', JSON.stringify(rpcProviders, null, 2));
|
||
const rpcMap = {};
|
||
for (const rpc of rpcProviders) {
|
||
rpcMap[rpc.network_id] = rpc.rpc_url;
|
||
}
|
||
const checkPromises = tokens.map(async (token) => {
|
||
try {
|
||
const rpcUrl = rpcMap[token.network];
|
||
if (!rpcUrl) {
|
||
logger.error(`No RPC URL for network ${token.network}`);
|
||
balances[token.network] = 'Error: No RPC URL';
|
||
errorCount++;
|
||
return null;
|
||
}
|
||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||
// Проверяем доступность сети с таймаутом
|
||
try {
|
||
const networkCheckPromise = provider.getNetwork();
|
||
const timeoutPromise = new Promise((_, reject) =>
|
||
setTimeout(() => reject(new Error('Network check timeout')), 3000)
|
||
);
|
||
await Promise.race([networkCheckPromise, timeoutPromise]);
|
||
} catch (networkError) {
|
||
logger.error(`Provider for ${token.network} is not available: ${networkError.message}`);
|
||
balances[token.network] = 'Error: Network unavailable';
|
||
errorCount++;
|
||
return null;
|
||
}
|
||
const tokenContract = new ethers.Contract(token.address, ERC20_ABI, provider);
|
||
const balancePromise = tokenContract.balanceOf(address);
|
||
const timeoutPromise = new Promise((_, reject) =>
|
||
setTimeout(() => reject(new Error('Timeout')), 3000)
|
||
);
|
||
const balance = await Promise.race([balancePromise, timeoutPromise]);
|
||
const formattedBalance = ethers.formatUnits(balance, 18);
|
||
balances[token.network] = formattedBalance;
|
||
logger.info(`Token balance on ${token.network}:`, {
|
||
address,
|
||
contract: token.address,
|
||
balance: formattedBalance,
|
||
minBalance: token.min_balance,
|
||
hasTokens: parseFloat(formattedBalance) >= parseFloat(token.min_balance),
|
||
});
|
||
if (parseFloat(formattedBalance) >= parseFloat(token.min_balance)) {
|
||
logger.info(`Found admin tokens on ${token.network}`);
|
||
foundTokens = true;
|
||
}
|
||
return { network: token.network, balance: formattedBalance };
|
||
} catch (error) {
|
||
logger.error(`Error checking balance in ${token.network}:`, {
|
||
address,
|
||
contract: token.address,
|
||
error: error.message || 'Unknown error',
|
||
});
|
||
balances[token.network] = 'Error';
|
||
errorCount++;
|
||
return null;
|
||
}
|
||
});
|
||
await Promise.all(checkPromises);
|
||
if (errorCount === tokens.length) {
|
||
logger.error(`All network checks for ${address} failed. Cannot verify admin status.`);
|
||
return false;
|
||
}
|
||
if (foundTokens) {
|
||
logger.info(`Admin role summary for ${address}:`, {
|
||
networks: Object.keys(balances).filter(
|
||
(net) => parseFloat(balances[net]) > 0 && balances[net] !== 'Error'
|
||
),
|
||
balances,
|
||
});
|
||
logger.info(`Admin role granted for ${address}`);
|
||
return true;
|
||
}
|
||
logger.info(`Admin role denied - no tokens found for ${address}`);
|
||
return false;
|
||
} catch (error) {
|
||
logger.error(`Error in checkAdminRole for ${address}:`, error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
module.exports = { checkAdminRole };
|