feat: новая функция
This commit is contained in:
40
DEPLOYMENT_FIXES.md
Normal file
40
DEPLOYMENT_FIXES.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Исправления ошибок деплоя DLE
|
||||||
|
|
||||||
|
## Проблема
|
||||||
|
Ошибки `Cannot access 'chainId' before initialization` в скриптах деплоя из-за неправильного использования переменных.
|
||||||
|
|
||||||
|
## Источник данных
|
||||||
|
**База данных `deploy_params`:**
|
||||||
|
- `supported_chain_ids`: `[421614, 84532, 11155111, 17000]` (числовые chainId)
|
||||||
|
- `rpc_urls`: `["https://sepolia-rollup.arbitrum.io/rpc", ...]` (строки URL)
|
||||||
|
|
||||||
|
## Исправления
|
||||||
|
|
||||||
|
### 1. deploy-multichain.js
|
||||||
|
- **Функция `deployInNetwork(chainId, ...)`**
|
||||||
|
- **Было:** `const networkChainId = Number(net.chainId)` + использование `networkChainId`
|
||||||
|
- **Стало:** Использование параметра `chainId` напрямую
|
||||||
|
- **Причина:** `chainId` уже приходит как числовой параметр из `supportedChainIds` базы данных
|
||||||
|
|
||||||
|
### 2. deploy-modules.js
|
||||||
|
- **Функция `deployModuleInNetwork(rpcUrl, ...)`**
|
||||||
|
- **Было:** `createRPCConnection(rpcUrl, ...)` - неправильно
|
||||||
|
- **Стало:** Получение `chainId` из RPC URL + `createRPCConnection(chainId, ...)`
|
||||||
|
- **Функция `deployAllModulesInNetwork(chainId, ...)`**
|
||||||
|
- **Было:** Создание `networkChainId` + использование его
|
||||||
|
- **Стало:** Использование параметра `chainId` напрямую
|
||||||
|
|
||||||
|
### 3. DleDeployFormView.vue
|
||||||
|
- **Было:** `adminTokenCheck` использовался в watcher до объявления
|
||||||
|
- **Стало:** Объявление `adminTokenCheck` перед watcher'ом
|
||||||
|
|
||||||
|
## Логика работы
|
||||||
|
1. База данных → `params.supportedChainIds` (числовые chainId)
|
||||||
|
2. `createMultipleRPCConnections(supportedChainIds, ...)`
|
||||||
|
3. `connection.network.chainId` возвращает тот же числовой chainId
|
||||||
|
4. `deployInNetwork(chainId, ...)` получает числовой chainId как параметр
|
||||||
|
5. **Внутри функций используем `chainId` (параметр), НЕ создаем `networkChainId`**
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
✅ Все ошибки инициализации переменных исправлены
|
||||||
|
✅ Система готова к работе без ошибок `Cannot access before initialization`
|
||||||
@@ -330,6 +330,12 @@ const initializeDbSettingsService = async () => {
|
|||||||
// Инициализируем сервис настроек БД при запуске
|
// Инициализируем сервис настроек БД при запуске
|
||||||
if (process.env.NODE_ENV !== 'migration') {
|
if (process.env.NODE_ENV !== 'migration') {
|
||||||
initializeDbSettingsService();
|
initializeDbSettingsService();
|
||||||
|
|
||||||
|
// Загружаем RPC URL из базы данных
|
||||||
|
const { loadRpcFromDatabase } = require('./utils/loadRpcFromDatabase');
|
||||||
|
loadRpcFromDatabase().catch(error => {
|
||||||
|
logger.error('[App] Ошибка загрузки RPC URL из базы данных:', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { app, nonceStore };
|
module.exports = { app, nonceStore };
|
||||||
|
|||||||
@@ -29,54 +29,8 @@ function getNetworks() {
|
|||||||
|
|
||||||
// console.log удален - может мешать flatten
|
// console.log удален - может мешать flatten
|
||||||
|
|
||||||
// Базовые сети
|
// Базовые сети - УБРАНО, используем только базу данных
|
||||||
const baseNetworks = {
|
const baseNetworks = {}; // Пустой объект - никаких хардкод цепочек
|
||||||
sepolia: {
|
|
||||||
url: process.env.SEPOLIA_RPC_URL || 'https://1rpc.io/sepolia',
|
|
||||||
chainId: 11155111,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
holesky: {
|
|
||||||
url: process.env.HOLESKY_RPC_URL || 'https://ethereum-holesky.publicnode.com',
|
|
||||||
chainId: 17000,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
mainnet: {
|
|
||||||
url: process.env.MAINNET_RPC_URL || 'https://eth-mainnet.nodereal.io/v1/YOUR_NODEREAL_KEY',
|
|
||||||
chainId: 1,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
arbitrumSepolia: {
|
|
||||||
url: process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc',
|
|
||||||
chainId: 421614,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
baseSepolia: {
|
|
||||||
url: process.env.BASE_SEPOLIA_RPC_URL || 'https://sepolia.base.org',
|
|
||||||
chainId: 84532,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
arbitrumOne: {
|
|
||||||
url: process.env.ARBITRUM_ONE_RPC_URL || 'https://arb1.arbitrum.io/rpc',
|
|
||||||
chainId: 42161,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
base: {
|
|
||||||
url: process.env.BASE_RPC_URL || 'https://mainnet.base.org',
|
|
||||||
chainId: 8453,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
polygon: {
|
|
||||||
url: process.env.POLYGON_RPC_URL || 'https://polygon-rpc.com',
|
|
||||||
chainId: 137,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
bsc: {
|
|
||||||
url: process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org',
|
|
||||||
chainId: 56,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Если есть supported_chain_ids, фильтруем только нужные сети
|
// Если есть supported_chain_ids, фильтруем только нужные сети
|
||||||
if (supportedChainIds.length > 0) {
|
if (supportedChainIds.length > 0) {
|
||||||
@@ -104,35 +58,9 @@ function getNetworks() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для получения базовых сетей (fallback)
|
// Функция для получения базовых сетей (fallback) - УБРАНО, используем только базу данных
|
||||||
function getBaseNetworks() {
|
function getBaseNetworks() {
|
||||||
return {
|
return {}; // Пустой объект - никаких хардкод цепочек
|
||||||
sepolia: {
|
|
||||||
url: process.env.SEPOLIA_RPC_URL || 'https://1rpc.io/sepolia',
|
|
||||||
chainId: 11155111,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
holesky: {
|
|
||||||
url: process.env.HOLESKY_RPC_URL || 'https://ethereum-holesky.publicnode.com',
|
|
||||||
chainId: 17000,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
mainnet: {
|
|
||||||
url: process.env.MAINNET_RPC_URL || 'https://eth-mainnet.nodereal.io/v1/YOUR_NODEREAL_KEY',
|
|
||||||
chainId: 1,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
arbitrumSepolia: {
|
|
||||||
url: process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc',
|
|
||||||
chainId: 421614,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
},
|
|
||||||
baseSepolia: {
|
|
||||||
url: process.env.BASE_SEPOLIA_RPC_URL || 'https://sepolia.base.org',
|
|
||||||
chainId: 84532,
|
|
||||||
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ router.post('/read-dle-info', async (req, res) => {
|
|||||||
if (!rpcUrl) {
|
if (!rpcUrl) {
|
||||||
return res.status(500).json({ success: false, error: `RPC URL для сети ${targetChainId} не найден` });
|
return res.status(500).json({ success: false, error: `RPC URL для сети ${targetChainId} не найден` });
|
||||||
}
|
}
|
||||||
provider = new ethers.JsonRpcProvider(rpcUrl);
|
provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const code = await provider.getCode(dleAddress);
|
const code = await provider.getCode(dleAddress);
|
||||||
if (!code || code === '0x') {
|
if (!code || code === '0x') {
|
||||||
return res.status(400).json({ success: false, error: `По адресу ${dleAddress} нет контракта в сети ${targetChainId}` });
|
return res.status(400).json({ success: false, error: `По адресу ${dleAddress} нет контракта в сети ${targetChainId}` });
|
||||||
@@ -358,7 +358,7 @@ router.post('/get-proposals', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// ABI для чтения предложений (используем правильные функции из смарт-контракта)
|
// ABI для чтения предложений (используем правильные функции из смарт-контракта)
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
@@ -520,7 +520,7 @@ router.post('/get-proposal-info', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// ABI для чтения информации о предложении
|
// ABI для чтения информации о предложении
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
@@ -614,7 +614,7 @@ router.post('/deactivate-dle', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// ABI для проверки деактивации DLE
|
// ABI для проверки деактивации DLE
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
@@ -707,7 +707,7 @@ router.post('/check-deactivation-proposal-result', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function checkDeactivationProposalResult(uint256 _proposalId) public view returns (bool passed, bool quorumReached)"
|
"function checkDeactivationProposalResult(uint256 _proposalId) public view returns (bool passed, bool quorumReached)"
|
||||||
@@ -785,7 +785,7 @@ router.post('/load-deactivation-proposals', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function deactivationProposalCounter() external view returns (uint256)",
|
"function deactivationProposalCounter() external view returns (uint256)",
|
||||||
@@ -889,7 +889,7 @@ router.post('/execute-proposal', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const wallet = new ethers.Wallet(privateKey, provider);
|
const wallet = new ethers.Wallet(privateKey, provider);
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
@@ -965,7 +965,7 @@ router.post('/cancel-proposal', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function cancelProposal(uint256 _proposalId, string calldata reason) external"
|
"function cancelProposal(uint256 _proposalId, string calldata reason) external"
|
||||||
@@ -1050,7 +1050,7 @@ router.post('/get-governance-params', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getGovernanceParams() external view returns (uint256 quorumPct, uint256 chainId, uint256 supportedCount)"
|
"function getGovernanceParams() external view returns (uint256 quorumPct, uint256 chainId, uint256 supportedCount)"
|
||||||
@@ -1126,7 +1126,7 @@ router.post('/get-proposal-state', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getProposalState(uint256 _proposalId) public view returns (uint8 state)"
|
"function getProposalState(uint256 _proposalId) public view returns (uint8 state)"
|
||||||
@@ -1201,7 +1201,7 @@ router.post('/get-proposal-votes', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getProposalVotes(uint256 _proposalId) external view returns (uint256 forVotes, uint256 againstVotes, uint256 totalVotes, uint256 quorumRequired)"
|
"function getProposalVotes(uint256 _proposalId) external view returns (uint256 forVotes, uint256 againstVotes, uint256 totalVotes, uint256 quorumRequired)"
|
||||||
@@ -1279,7 +1279,7 @@ router.post('/get-proposals-count', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getProposalsCount() external view returns (uint256)"
|
"function getProposalsCount() external view returns (uint256)"
|
||||||
@@ -1353,7 +1353,7 @@ router.post('/list-proposals', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)"
|
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)"
|
||||||
@@ -1429,7 +1429,7 @@ router.post('/get-voting-power-at', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getVotingPowerAt(address voter, uint256 timepoint) external view returns (uint256)"
|
"function getVotingPowerAt(address voter, uint256 timepoint) external view returns (uint256)"
|
||||||
@@ -1505,7 +1505,7 @@ router.post('/get-quorum-at', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getQuorumAt(uint256 timepoint) external view returns (uint256)"
|
"function getQuorumAt(uint256 timepoint) external view returns (uint256)"
|
||||||
@@ -1580,7 +1580,7 @@ router.post('/get-token-balance', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function balanceOf(address account) external view returns (uint256)"
|
"function balanceOf(address account) external view returns (uint256)"
|
||||||
@@ -1655,7 +1655,7 @@ router.post('/get-total-supply', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function totalSupply() external view returns (uint256)"
|
"function totalSupply() external view returns (uint256)"
|
||||||
@@ -1729,7 +1729,7 @@ router.post('/is-active', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function isActive() external view returns (bool)"
|
"function isActive() external view returns (bool)"
|
||||||
@@ -1809,7 +1809,7 @@ router.post('/get-dle-analytics', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function totalSupply() external view returns (uint256)",
|
"function totalSupply() external view returns (uint256)",
|
||||||
@@ -1956,7 +1956,7 @@ router.post('/get-dle-history', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getProposalsCount() external view returns (uint256)",
|
"function getProposalsCount() external view returns (uint256)",
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ router.post('/get-dle-analytics', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function totalSupply() external view returns (uint256)",
|
"function totalSupply() external view returns (uint256)",
|
||||||
@@ -242,7 +242,7 @@ router.post('/get-dle-history', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getProposalsCount() external view returns (uint256)",
|
"function getProposalsCount() external view returns (uint256)",
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ router.post('/read-dle-info', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// ABI для чтения данных DLE
|
// ABI для чтения данных DLE
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
@@ -254,7 +254,7 @@ router.post('/get-governance-params', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getGovernanceParams() external view returns (uint256 quorumPct, uint256 chainId, uint256 supportedCount)"
|
"function getGovernanceParams() external view returns (uint256 quorumPct, uint256 chainId, uint256 supportedCount)"
|
||||||
@@ -323,7 +323,7 @@ router.post('/is-active', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function isActive() external view returns (bool)"
|
"function isActive() external view returns (bool)"
|
||||||
@@ -408,7 +408,7 @@ router.post('/deactivate-dle', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// ABI для проверки деактивации DLE
|
// ABI для проверки деактивации DLE
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ router.post('/get-extended-history', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getDLEInfo() external view returns (tuple(string name, string symbol, string location, string coordinates, uint256 jurisdiction, uint256 oktmo, string[] okvedCodes, uint256 kpp, uint256 creationTimestamp, bool isActive))",
|
"function getDLEInfo() external view returns (tuple(string name, string symbol, string location, string coordinates, uint256 jurisdiction, uint256 oktmo, string[] okvedCodes, uint256 kpp, uint256 creationTimestamp, bool isActive))",
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ router.post('/is-module-active', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function isModuleActive(bytes32 _moduleId) external view returns (bool)"
|
"function isModuleActive(bytes32 _moduleId) external view returns (bool)"
|
||||||
@@ -296,7 +296,7 @@ router.post('/get-module-address', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getModuleAddress(bytes32 _moduleId) external view returns (address)"
|
"function getModuleAddress(bytes32 _moduleId) external view returns (address)"
|
||||||
@@ -348,7 +348,13 @@ router.post('/prepare-initialize-modules-all-networks', async (req, res) => {
|
|||||||
const results = [];
|
const results = [];
|
||||||
for (const network of supportedNetworks) {
|
for (const network of supportedNetworks) {
|
||||||
try {
|
try {
|
||||||
const provider = new ethers.JsonRpcProvider(network.rpcUrl);
|
// Получаем RPC URL из базы данных
|
||||||
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(network.chainId);
|
||||||
|
if (!rpcUrl) {
|
||||||
|
console.warn(`[DLE Modules] RPC URL не найден для chainId ${network.chainId}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const dle = new ethers.Contract(
|
const dle = new ethers.Contract(
|
||||||
dleAddress,
|
dleAddress,
|
||||||
[
|
[
|
||||||
@@ -576,14 +582,16 @@ router.post('/get-all-modules', async (req, res) => {
|
|||||||
return networks[chainId] || `Chain ${chainId}`;
|
return networks[chainId] || `Chain ${chainId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFallbackRpcUrl(chainId) {
|
async function getFallbackRpcUrl(chainId) {
|
||||||
const fallbackUrls = {
|
try {
|
||||||
11155111: process.env.SEPOLIA_RPC_URL || 'https://eth-sepolia.nodereal.io/v1/YOUR_NODEREAL_KEY',
|
// Получаем RPC URL из базы данных
|
||||||
17000: 'https://ethereum-holesky.publicnode.com',
|
const rpcService = require('../services/rpcProviderService');
|
||||||
421614: 'https://sepolia-rollup.arbitrum.io/rpc',
|
const rpcUrl = await rpcService.getRpcUrlByChainId(chainId);
|
||||||
84532: 'https://sepolia.base.org'
|
return rpcUrl;
|
||||||
};
|
} catch (error) {
|
||||||
return fallbackUrls[chainId] || null;
|
console.error(`[DLE Modules] Ошибка получения RPC из базы данных для chain_id ${chainId}:`, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEtherscanUrl(chainId) {
|
function getEtherscanUrl(chainId) {
|
||||||
@@ -621,48 +629,19 @@ router.post('/get-all-modules', async (req, res) => {
|
|||||||
const supportedChainIds = params.supportedChainIds || [];
|
const supportedChainIds = params.supportedChainIds || [];
|
||||||
const rpcUrls = params.rpcUrls || params.rpc_urls || {};
|
const rpcUrls = params.rpcUrls || params.rpc_urls || {};
|
||||||
|
|
||||||
supportedNetworks = supportedChainIds.map((chainId, index) => ({
|
supportedNetworks = await Promise.all(supportedChainIds.map(async (chainId, index) => ({
|
||||||
chainId: Number(chainId),
|
chainId: Number(chainId),
|
||||||
networkName: getNetworkName(Number(chainId)),
|
networkName: getNetworkName(Number(chainId)),
|
||||||
rpcUrl: rpcUrls[chainId] || getFallbackRpcUrl(chainId),
|
rpcUrl: rpcUrls[chainId] || await getFallbackRpcUrl(chainId),
|
||||||
etherscanUrl: getEtherscanUrl(chainId),
|
etherscanUrl: getEtherscanUrl(chainId),
|
||||||
networkIndex: index
|
networkIndex: index
|
||||||
}));
|
})));
|
||||||
}
|
}
|
||||||
await deployParamsService.close();
|
await deployParamsService.close();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка получения параметров деплоя:', error);
|
console.error('❌ Ошибка получения параметров деплоя:', error);
|
||||||
// Fallback для совместимости
|
// НЕ показываем fallback цепочки - только те, что выбрал пользователь
|
||||||
supportedNetworks = [
|
supportedNetworks = [];
|
||||||
{
|
|
||||||
chainId: 11155111,
|
|
||||||
networkName: 'Sepolia',
|
|
||||||
rpcUrl: process.env.SEPOLIA_RPC_URL || 'https://eth-sepolia.nodereal.io/v1/YOUR_NODEREAL_KEY',
|
|
||||||
etherscanUrl: 'https://sepolia.etherscan.io',
|
|
||||||
networkIndex: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
chainId: 17000,
|
|
||||||
networkName: 'Holesky',
|
|
||||||
rpcUrl: 'https://ethereum-holesky.publicnode.com',
|
|
||||||
etherscanUrl: 'https://holesky.etherscan.io',
|
|
||||||
networkIndex: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
chainId: 421614,
|
|
||||||
networkName: 'Arbitrum Sepolia',
|
|
||||||
rpcUrl: 'https://sepolia-rollup.arbitrum.io/rpc',
|
|
||||||
etherscanUrl: 'https://sepolia.arbiscan.io',
|
|
||||||
networkIndex: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
chainId: 84532,
|
|
||||||
networkName: 'Base Sepolia',
|
|
||||||
rpcUrl: 'https://sepolia.base.org',
|
|
||||||
etherscanUrl: 'https://sepolia.basescan.org',
|
|
||||||
networkIndex: 3
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
@@ -754,7 +733,7 @@ router.post('/create-add-module-proposal', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// Получаем приватный ключ из параметров деплоя
|
// Получаем приватный ключ из параметров деплоя
|
||||||
let privateKey;
|
let privateKey;
|
||||||
@@ -1003,7 +982,7 @@ router.post('/create-remove-module-proposal', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function createRemoveModuleProposal(string memory _description, uint256 _duration, bytes32 _moduleId, uint256 _chainId) external returns (uint256)"
|
"function createRemoveModuleProposal(string memory _description, uint256 _duration, bytes32 _moduleId, uint256 _chainId) external returns (uint256)"
|
||||||
@@ -1276,7 +1255,7 @@ router.post('/verify-module', async (req, res) => {
|
|||||||
|
|
||||||
// Получаем ABI и bytecode для модуля
|
// Получаем ABI и bytecode для модуля
|
||||||
const { ethers } = require('ethers');
|
const { ethers } = require('ethers');
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// Получаем код контракта для проверки существования
|
// Получаем код контракта для проверки существования
|
||||||
const code = await provider.getCode(moduleAddress);
|
const code = await provider.getCode(moduleAddress);
|
||||||
@@ -1389,7 +1368,11 @@ router.post('/check-modules-status', async (req, res) => {
|
|||||||
|
|
||||||
// Проверяем первую доступную сеть
|
// Проверяем первую доступную сеть
|
||||||
const network = supportedNetworks[0];
|
const network = supportedNetworks[0];
|
||||||
const provider = new ethers.JsonRpcProvider(network.rpcUrl);
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(network.chainId);
|
||||||
|
if (!rpcUrl) {
|
||||||
|
return res.status(400).json({ success: false, message: `RPC URL не найден для chainId ${network.chainId}` });
|
||||||
|
}
|
||||||
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function initializer() external view returns (address)",
|
"function initializer() external view returns (address)",
|
||||||
@@ -1603,7 +1586,12 @@ router.post('/initialize-modules-all-networks', async (req, res) => {
|
|||||||
console.log(`[DLE Modules] Инициализация модулей в сети: ${network.networkName} (${network.chainId})`);
|
console.log(`[DLE Modules] Инициализация модулей в сети: ${network.networkName} (${network.chainId})`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const provider = new ethers.JsonRpcProvider(network.rpcUrl);
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(network.chainId);
|
||||||
|
if (!rpcUrl) {
|
||||||
|
console.warn(`[DLE Modules] RPC URL не найден для chainId ${network.chainId}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const wallet = new ethers.Wallet(privateKey, provider);
|
const wallet = new ethers.Wallet(privateKey, provider);
|
||||||
const dle = new ethers.Contract(dleAddress, dleAbi, wallet);
|
const dle = new ethers.Contract(dleAddress, dleAbi, wallet);
|
||||||
|
|
||||||
@@ -1723,7 +1711,12 @@ router.post('/verify-modules-all-networks', async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const provider = new ethers.JsonRpcProvider(network.rpcUrl);
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(network.chainId);
|
||||||
|
if (!rpcUrl) {
|
||||||
|
console.warn(`[DLE Modules] RPC URL не найден для chainId ${network.chainId}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
|
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
|
||||||
|
|
||||||
for (const [moduleKey, moduleId] of Object.entries(moduleIds)) {
|
for (const [moduleKey, moduleId] of Object.entries(moduleIds)) {
|
||||||
@@ -1892,7 +1885,7 @@ router.post('/check-dle-deployment-status', async (req, res) => {
|
|||||||
throw new Error(`RPC URL не найден для сети ${chainId}`);
|
throw new Error(`RPC URL не найден для сети ${chainId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// Проверяем, что контракт существует и имеет код
|
// Проверяем, что контракт существует и имеет код
|
||||||
const code = await provider.getCode(dleAddress);
|
const code = await provider.getCode(dleAddress);
|
||||||
@@ -2019,7 +2012,7 @@ router.post('/check-module-deployment-status', async (req, res) => {
|
|||||||
throw new Error(`RPC URL не найден для сети ${chainId}`);
|
throw new Error(`RPC URL не найден для сети ${chainId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getModuleAddress(bytes32 _moduleId) external view returns (address)",
|
"function getModuleAddress(bytes32 _moduleId) external view returns (address)",
|
||||||
@@ -2179,7 +2172,7 @@ router.post('/deploy-module-all-networks', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const result = await executeWithRetries(
|
const result = await executeWithRetries(
|
||||||
async () => {
|
async () => {
|
||||||
const provider = new ethers.JsonRpcProvider(network.rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(network.chainId));
|
||||||
const wallet = new ethers.Wallet(privateKey, provider);
|
const wallet = new ethers.Wallet(privateKey, provider);
|
||||||
|
|
||||||
// Используем NonceManager для правильного управления nonce
|
// Используем NonceManager для правильного управления nonce
|
||||||
@@ -2558,7 +2551,7 @@ router.post('/verify-module-all-networks', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const result = await executeWithRetries(
|
const result = await executeWithRetries(
|
||||||
async () => {
|
async () => {
|
||||||
const provider = new ethers.JsonRpcProvider(network.rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(network.chainId));
|
||||||
const dle = new ethers.Contract(dleAddress, [
|
const dle = new ethers.Contract(dleAddress, [
|
||||||
"function getModuleAddress(bytes32 _moduleId) external view returns (address)"
|
"function getModuleAddress(bytes32 _moduleId) external view returns (address)"
|
||||||
], provider);
|
], provider);
|
||||||
@@ -2698,7 +2691,7 @@ router.post('/initialize-module-all-networks', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const result = await executeWithRetries(
|
const result = await executeWithRetries(
|
||||||
async () => {
|
async () => {
|
||||||
const provider = new ethers.JsonRpcProvider(network.rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(network.chainId));
|
||||||
const wallet = new ethers.Wallet(privateKey, provider);
|
const wallet = new ethers.Wallet(privateKey, provider);
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
@@ -2826,7 +2819,7 @@ router.post('/final-deployment-check', async (req, res) => {
|
|||||||
throw new Error(`RPC URL не найден для сети ${chainId}`);
|
throw new Error(`RPC URL не найден для сети ${chainId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function name() external view returns (string)",
|
"function name() external view returns (string)",
|
||||||
@@ -3025,7 +3018,7 @@ router.post('/get-deployment-status', async (req, res) => {
|
|||||||
const result = await executeWithRetries(
|
const result = await executeWithRetries(
|
||||||
async () => {
|
async () => {
|
||||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const dleCode = await provider.getCode(dleAddress);
|
const dleCode = await provider.getCode(dleAddress);
|
||||||
return dleCode !== '0x';
|
return dleCode !== '0x';
|
||||||
},
|
},
|
||||||
@@ -3045,7 +3038,7 @@ router.post('/get-deployment-status', async (req, res) => {
|
|||||||
const verificationResult = await executeWithRetries(
|
const verificationResult = await executeWithRetries(
|
||||||
async () => {
|
async () => {
|
||||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
// Простая проверка - если код контракта не пустой, считаем верифицированным
|
// Простая проверка - если код контракта не пустой, считаем верифицированным
|
||||||
const code = await provider.getCode(dleAddress);
|
const code = await provider.getCode(dleAddress);
|
||||||
return code !== '0x' && code.length > 2;
|
return code !== '0x' && code.length > 2;
|
||||||
@@ -3102,7 +3095,7 @@ router.post('/get-deployment-status', async (req, res) => {
|
|||||||
const result = await executeWithRetries(
|
const result = await executeWithRetries(
|
||||||
async () => {
|
async () => {
|
||||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const dle = new ethers.Contract(dleAddress, [
|
const dle = new ethers.Contract(dleAddress, [
|
||||||
"function getModuleAddress(bytes32 _moduleId) external view returns (address)",
|
"function getModuleAddress(bytes32 _moduleId) external view returns (address)",
|
||||||
"function isModuleActive(bytes32 _moduleId) external view returns (bool)"
|
"function isModuleActive(bytes32 _moduleId) external view returns (bool)"
|
||||||
@@ -3129,7 +3122,7 @@ router.post('/get-deployment-status', async (req, res) => {
|
|||||||
const verificationResult = await executeWithRetries(
|
const verificationResult = await executeWithRetries(
|
||||||
async () => {
|
async () => {
|
||||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const moduleCode = await provider.getCode(moduleAddress);
|
const moduleCode = await provider.getCode(moduleAddress);
|
||||||
return moduleCode !== '0x';
|
return moduleCode !== '0x';
|
||||||
},
|
},
|
||||||
@@ -3168,7 +3161,7 @@ router.post('/get-deployment-status', async (req, res) => {
|
|||||||
const result = await executeWithRetries(
|
const result = await executeWithRetries(
|
||||||
async () => {
|
async () => {
|
||||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId);
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const dle = new ethers.Contract(dleAddress, [
|
const dle = new ethers.Contract(dleAddress, [
|
||||||
], provider);
|
], provider);
|
||||||
|
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ router.post('/get-multichain-contracts', async (req, res) => {
|
|||||||
// Если не найден в параметрах, используем fallback
|
// Если не найден в параметрах, используем fallback
|
||||||
if (!rpcUrl) {
|
if (!rpcUrl) {
|
||||||
const fallbackConfigs = {
|
const fallbackConfigs = {
|
||||||
'11155111': 'https://1rpc.io/sepolia',
|
'11155111': null,
|
||||||
'17000': 'https://ethereum-holesky.publicnode.com',
|
'17000': null,
|
||||||
'421614': 'https://sepolia-rollup.arbitrum.io/rpc',
|
'421614': null,
|
||||||
'84532': 'https://sepolia.base.org'
|
'84532': null
|
||||||
};
|
};
|
||||||
rpcUrl = fallbackConfigs[targetChainId];
|
rpcUrl = fallbackConfigs[targetChainId];
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ router.post('/get-multichain-contracts', async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const contractCode = await provider.getCode(originalContract);
|
const contractCode = await provider.getCode(originalContract);
|
||||||
|
|
||||||
if (contractCode && contractCode !== '0x') {
|
if (contractCode && contractCode !== '0x') {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ router.post('/get-proposal-multichain-info', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function proposals(uint256) external view returns (uint256 id, string memory description, uint256 forVotes, uint256 againstVotes, bool executed, bool canceled, uint256 deadline, address initiator, bytes memory operation, uint256 governanceChainId, uint256 snapshotTimepoint, uint256[] memory targetChains)",
|
"function proposals(uint256) external view returns (uint256 id, string memory description, uint256 forVotes, uint256 againstVotes, bool executed, bool canceled, uint256 deadline, address initiator, bytes memory operation, uint256 governanceChainId, uint256 snapshotTimepoint, uint256[] memory targetChains)",
|
||||||
@@ -294,7 +294,7 @@ async function executeProposalInChain(dleAddress, proposalId, chainId, privateKe
|
|||||||
throw new Error(`RPC URL для сети ${chainId} не найден`);
|
throw new Error(`RPC URL для сети ${chainId} не найден`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const wallet = new ethers.Wallet(privateKey, provider);
|
const wallet = new ethers.Wallet(privateKey, provider);
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ router.post('/get-proposals', async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rpcUrl) {
|
if (rpcUrl) {
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getSupportedChainCount() external view returns (uint256)",
|
"function getSupportedChainCount() external view returns (uint256)",
|
||||||
"function getSupportedChainId(uint256 _index) external view returns (uint256)"
|
"function getSupportedChainId(uint256 _index) external view returns (uint256)"
|
||||||
@@ -97,7 +97,7 @@ router.post('/get-proposals', async (req, res) => {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// ABI для чтения предложений (используем getProposalSummary для мультиконтрактов)
|
// ABI для чтения предложений (используем getProposalSummary для мультиконтрактов)
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
@@ -369,7 +369,7 @@ router.post('/get-proposal-info', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// ABI для чтения информации о предложении
|
// ABI для чтения информации о предложении
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
@@ -447,7 +447,7 @@ router.post('/get-proposal-state', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getProposalState(uint256 _proposalId) public view returns (uint8 state)"
|
"function getProposalState(uint256 _proposalId) public view returns (uint8 state)"
|
||||||
@@ -499,7 +499,7 @@ router.post('/get-proposal-votes', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function checkProposalResult(uint256 _proposalId) external view returns (bool passed, bool quorumReached)",
|
"function checkProposalResult(uint256 _proposalId) external view returns (bool passed, bool quorumReached)",
|
||||||
@@ -560,7 +560,7 @@ router.post('/get-proposals-count', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getProposalsCount() external view returns (uint256)"
|
"function getProposalsCount() external view returns (uint256)"
|
||||||
@@ -611,7 +611,7 @@ router.post('/list-proposals', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)"
|
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)"
|
||||||
@@ -664,7 +664,7 @@ router.post('/get-voting-power-at', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getVotingPowerAt(address voter, uint256 timepoint) external view returns (uint256)"
|
"function getVotingPowerAt(address voter, uint256 timepoint) external view returns (uint256)"
|
||||||
@@ -717,7 +717,7 @@ router.post('/get-quorum-at', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getQuorumAt(uint256 timepoint) external view returns (uint256)"
|
"function getQuorumAt(uint256 timepoint) external view returns (uint256)"
|
||||||
@@ -772,7 +772,7 @@ router.post('/execute-proposal', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function executeProposal(uint256 _proposalId) external"
|
"function executeProposal(uint256 _proposalId) external"
|
||||||
@@ -827,7 +827,7 @@ router.post('/cancel-proposal', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function cancelProposal(uint256 _proposalId, string calldata reason) external"
|
"function cancelProposal(uint256 _proposalId, string calldata reason) external"
|
||||||
@@ -879,7 +879,7 @@ router.post('/get-proposals-count', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function getProposalsCount() external view returns (uint256)"
|
"function getProposalsCount() external view returns (uint256)"
|
||||||
@@ -929,7 +929,7 @@ router.post('/list-proposals', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)",
|
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)",
|
||||||
@@ -1030,7 +1030,7 @@ router.post('/vote-proposal', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function vote(uint256 _proposalId, bool _support) external"
|
"function vote(uint256 _proposalId, bool _support) external"
|
||||||
@@ -1088,7 +1088,7 @@ router.post('/check-vote-status', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// Функция hasVoted не существует в контракте DLE
|
// Функция hasVoted не существует в контракте DLE
|
||||||
console.log(`[DLE Proposals] Функция hasVoted не поддерживается в контракте DLE`);
|
console.log(`[DLE Proposals] Функция hasVoted не поддерживается в контракте DLE`);
|
||||||
@@ -1135,7 +1135,7 @@ router.post('/track-vote-transaction', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// Ждем подтверждения транзакции
|
// Ждем подтверждения транзакции
|
||||||
const receipt = await provider.waitForTransaction(txHash, 1, 60000); // 60 секунд таймаут
|
const receipt = await provider.waitForTransaction(txHash, 1, 60000); // 60 секунд таймаут
|
||||||
@@ -1193,7 +1193,7 @@ router.post('/track-execution-transaction', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// Ждем подтверждения транзакции
|
// Ждем подтверждения транзакции
|
||||||
const receipt = await provider.waitForTransaction(txHash, 1, 60000); // 60 секунд таймаут
|
const receipt = await provider.waitForTransaction(txHash, 1, 60000); // 60 секунд таймаут
|
||||||
@@ -1252,7 +1252,7 @@ router.post('/decode-proposal-data', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
// Получаем данные транзакции
|
// Получаем данные транзакции
|
||||||
const tx = await provider.getTransaction(transactionHash);
|
const tx = await provider.getTransaction(transactionHash);
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ router.post('/get-token-balance', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function balanceOf(address account) external view returns (uint256)"
|
"function balanceOf(address account) external view returns (uint256)"
|
||||||
@@ -158,7 +158,7 @@ router.post('/get-total-supply', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function totalSupply() external view returns (uint256)"
|
"function totalSupply() external view returns (uint256)"
|
||||||
@@ -243,7 +243,7 @@ router.post('/get-token-holders', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
|
||||||
|
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
"function totalSupply() external view returns (uint256)",
|
"function totalSupply() external view returns (uint256)",
|
||||||
|
|||||||
@@ -17,9 +17,23 @@ const express = require('express');
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const { ethers } = require('ethers');
|
const { ethers } = require('ethers');
|
||||||
|
|
||||||
function getMainnetProvider() {
|
async function getMainnetProvider() {
|
||||||
const url = process.env.MAINNET_RPC_URL || process.env.ETH_MAINNET_RPC || 'https://ethereum.publicnode.com';
|
try {
|
||||||
return new ethers.JsonRpcProvider(url);
|
// Получаем RPC URL из базы данных для mainnet (chain_id = 1)
|
||||||
|
const rpcService = require('../services/rpcProviderService');
|
||||||
|
const rpcUrl = await rpcService.getRpcUrlByChainId(1);
|
||||||
|
|
||||||
|
if (!rpcUrl) {
|
||||||
|
throw new Error('RPC URL для mainnet не найден в базе данных');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[ENS] Используем RPC из базы данных: ${rpcUrl}`);
|
||||||
|
return new ethers.JsonRpcProvider(await rpcService.getRpcUrlByChainId(1));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[ENS] Ошибка получения RPC из базы данных:`, error);
|
||||||
|
throw new Error(`Не удалось получить RPC провайдер: ${error.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET /api/ens/avatar?name=vc-hb3-accelerator.eth
|
// GET /api/ens/avatar?name=vc-hb3-accelerator.eth
|
||||||
@@ -29,7 +43,7 @@ router.get('/avatar', async (req, res) => {
|
|||||||
if (!name || !name.endsWith('.eth')) {
|
if (!name || !name.endsWith('.eth')) {
|
||||||
return res.status(400).json({ success: false, message: 'ENS name is required (e.g., example.eth)' });
|
return res.status(400).json({ success: false, message: 'ENS name is required (e.g., example.eth)' });
|
||||||
}
|
}
|
||||||
const provider = getMainnetProvider();
|
const provider = await getMainnetProvider();
|
||||||
const url = await provider.getAvatar(name);
|
const url = await provider.getAvatar(name);
|
||||||
return res.json({ success: true, data: { url: url || null } });
|
return res.json({ success: true, data: { url: url || null } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ async function checkModules() {
|
|||||||
const dleAddress = '0xCaa85e96a6929F0373442e31FD9888d985869EcE';
|
const dleAddress = '0xCaa85e96a6929F0373442e31FD9888d985869EcE';
|
||||||
|
|
||||||
// RPC URL для Sepolia
|
// RPC URL для Sepolia
|
||||||
const rpcUrl = process.env.SEPOLIA_RPC_URL || 'https://eth-sepolia.nodereal.io/v1/YOUR_NODEREAL_KEY';
|
// Получаем RPC URL из базы данных
|
||||||
|
const rpcService = require('../services/rpcProviderService');
|
||||||
|
const rpcUrl = await rpcService.getRpcUrlByChainId(11155111);
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcService.getRpcUrlByChainId(11155111));
|
||||||
|
|
||||||
// ABI для DLE контракта
|
// ABI для DLE контракта
|
||||||
const dleAbi = [
|
const dleAbi = [
|
||||||
|
|||||||
@@ -179,27 +179,30 @@ async function verifyModuleAfterDeploy(chainId, contractAddress, moduleType, con
|
|||||||
async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce, moduleInit, moduleType) {
|
async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce, moduleInit, moduleType) {
|
||||||
const { ethers } = hre;
|
const { ethers } = hre;
|
||||||
|
|
||||||
|
// Создаем временный провайдер для получения chainId
|
||||||
|
const tempProvider = new ethers.JsonRpcProvider(rpcUrl);
|
||||||
|
const network = await tempProvider.getNetwork();
|
||||||
|
const chainId = Number(network.chainId);
|
||||||
|
|
||||||
// Используем новый менеджер RPC с retry логикой
|
// Используем новый менеджер RPC с retry логикой
|
||||||
const { provider, wallet, network } = await createRPCConnection(rpcUrl, pk, {
|
const { provider, wallet, network: rpcNetwork } = await createRPCConnection(chainId, pk, {
|
||||||
maxRetries: 3,
|
maxRetries: 3,
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
});
|
});
|
||||||
|
|
||||||
const net = network;
|
const net = rpcNetwork;
|
||||||
|
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} deploying ${moduleType}...`);
|
|
||||||
|
|
||||||
// 1) Используем NonceManager для правильного управления nonce
|
// 1) Используем NonceManager для правильного управления nonce
|
||||||
const chainId = Number(net.chainId);
|
logger.info(`[MODULES_DBG] chainId=${chainId} deploying ${moduleType}...`);
|
||||||
let current = await nonceManager.getNonce(wallet.address, rpcUrl, chainId);
|
let current = await nonceManager.getNonce(wallet.address, rpcUrl, chainId);
|
||||||
logger.info(`[MODULES_DBG] chainId=${chainId} current nonce=${current} target=${targetNonce}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} current nonce=${current} target=${targetNonce}`);
|
||||||
|
|
||||||
if (current > targetNonce) {
|
if (current > targetNonce) {
|
||||||
throw new Error(`Current nonce ${current} > targetNonce ${targetNonce} on chainId=${Number(net.chainId)}`);
|
throw new Error(`Current nonce ${current} > targetNonce ${targetNonce} on chainId=${chainId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current < targetNonce) {
|
if (current < targetNonce) {
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} aligning nonce from ${current} to ${targetNonce} (${targetNonce - current} transactions needed)`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} aligning nonce from ${current} to ${targetNonce} (${targetNonce - current} transactions needed)`);
|
||||||
|
|
||||||
// Используем burn address для более надежных транзакций
|
// Используем burn address для более надежных транзакций
|
||||||
const burnAddress = "0x000000000000000000000000000000000000dEaD";
|
const burnAddress = "0x000000000000000000000000000000000000dEaD";
|
||||||
@@ -219,15 +222,15 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
gasLimit,
|
gasLimit,
|
||||||
...overrides
|
...overrides
|
||||||
};
|
};
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} sending filler tx nonce=${current} attempt=${attempt + 1}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} sending filler tx nonce=${current} attempt=${attempt + 1}`);
|
||||||
const rpcManager = new RPCConnectionManager();
|
const rpcManager = new RPCConnectionManager();
|
||||||
const { tx: txFill, receipt } = await rpcManager.sendTransactionWithRetry(wallet, txReq, { maxRetries: 3 });
|
const { tx: txFill, receipt } = await rpcManager.sendTransactionWithRetry(wallet, txReq, { maxRetries: 3 });
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} filler tx sent, hash=${txFill.hash}, waiting for confirmation...`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} filler tx sent, hash=${txFill.hash}, waiting for confirmation...`);
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} filler tx nonce=${current} confirmed, hash=${txFill.hash}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} filler tx nonce=${current} confirmed, hash=${txFill.hash}`);
|
||||||
sent = true;
|
sent = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
lastErr = e;
|
lastErr = e;
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} filler tx nonce=${current} attempt=${attempt + 1} failed: ${e?.message || e}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} filler tx nonce=${current} attempt=${attempt + 1} failed: ${e?.message || e}`);
|
||||||
|
|
||||||
if (String(e?.message || '').toLowerCase().includes('intrinsic gas too low') && attempt < 2) {
|
if (String(e?.message || '').toLowerCase().includes('intrinsic gas too low') && attempt < 2) {
|
||||||
gasLimit = 50000;
|
gasLimit = 50000;
|
||||||
@@ -236,13 +239,13 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
|
|
||||||
if (String(e?.message || '').toLowerCase().includes('nonce too low') && attempt < 2) {
|
if (String(e?.message || '').toLowerCase().includes('nonce too low') && attempt < 2) {
|
||||||
// Сбрасываем кэш и получаем актуальный nonce
|
// Сбрасываем кэш и получаем актуальный nonce
|
||||||
nonceManager.resetNonce(wallet.address, Number(net.chainId));
|
nonceManager.resetNonce(wallet.address, chainId);
|
||||||
current = await provider.getTransactionCount(wallet.address, 'pending');
|
current = await provider.getTransactionCount(wallet.address, 'pending');
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} updated nonce to ${current}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} updated nonce to ${current}`);
|
||||||
|
|
||||||
// Если новый nonce больше целевого, это критическая ошибка
|
// Если новый nonce больше целевого, это критическая ошибка
|
||||||
if (current > targetNonce) {
|
if (current > targetNonce) {
|
||||||
throw new Error(`Current nonce ${current} > target nonce ${targetNonce} on chainId=${Number(net.chainId)}. Cannot proceed with module deployment.`);
|
throw new Error(`Current nonce ${current} > target nonce ${targetNonce} on chainId=${chainId}. Cannot proceed with module deployment.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -253,20 +256,20 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sent) {
|
if (!sent) {
|
||||||
logger.error(`[MODULES_DBG] chainId=${Number(net.chainId)} failed to send filler tx for nonce=${current}`);
|
logger.error(`[MODULES_DBG] chainId=${chainId} failed to send filler tx for nonce=${current}`);
|
||||||
throw lastErr || new Error('filler tx failed');
|
throw lastErr || new Error('filler tx failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
current++;
|
current++;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} nonce alignment completed, current nonce=${current}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} nonce alignment completed, current nonce=${current}`);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} nonce already aligned at ${current}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} nonce already aligned at ${current}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Деплой модуля напрямую на согласованном nonce
|
// 2) Деплой модуля напрямую на согласованном nonce
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} deploying ${moduleType} directly with nonce=${targetNonce}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} deploying ${moduleType} directly with nonce=${targetNonce}`);
|
||||||
|
|
||||||
const feeOverrides = await getFeeOverrides(provider);
|
const feeOverrides = await getFeeOverrides(provider);
|
||||||
let gasLimit;
|
let gasLimit;
|
||||||
@@ -283,7 +286,7 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
const fallbackGas = maxByBalance > 2_000_000n ? 2_000_000n : (maxByBalance < 500_000n ? 500_000n : maxByBalance);
|
const fallbackGas = maxByBalance > 2_000_000n ? 2_000_000n : (maxByBalance < 500_000n ? 500_000n : maxByBalance);
|
||||||
gasLimit = est ? (est + est / 5n) : fallbackGas;
|
gasLimit = est ? (est + est / 5n) : fallbackGas;
|
||||||
|
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} estGas=${est?.toString?.()||'null'} effGasPrice=${effPrice?.toString?.()||'0'} maxByBalance=${maxByBalance.toString()} chosenGasLimit=${gasLimit.toString()}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} estGas=${est?.toString?.()||'null'} effGasPrice=${effPrice?.toString?.()||'0'} maxByBalance=${maxByBalance.toString()} chosenGasLimit=${gasLimit.toString()}`);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
gasLimit = 1_000_000n;
|
gasLimit = 1_000_000n;
|
||||||
}
|
}
|
||||||
@@ -293,13 +296,13 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
from: wallet.address,
|
from: wallet.address,
|
||||||
nonce: targetNonce
|
nonce: targetNonce
|
||||||
});
|
});
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} predicted ${moduleType} address=${predictedAddress}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} predicted ${moduleType} address=${predictedAddress}`);
|
||||||
|
|
||||||
// Проверяем, не развернут ли уже контракт
|
// Проверяем, не развернут ли уже контракт
|
||||||
const existingCode = await provider.getCode(predictedAddress);
|
const existingCode = await provider.getCode(predictedAddress);
|
||||||
if (existingCode && existingCode !== '0x') {
|
if (existingCode && existingCode !== '0x') {
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleType} already exists at predictedAddress, skip deploy`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} ${moduleType} already exists at predictedAddress, skip deploy`);
|
||||||
return { address: predictedAddress, chainId: Number(net.chainId) };
|
return { address: predictedAddress, chainId: chainId };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Деплоим модуль с retry логикой для обработки race conditions
|
// Деплоим модуль с retry логикой для обработки race conditions
|
||||||
@@ -312,8 +315,8 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
deployAttempts++;
|
deployAttempts++;
|
||||||
|
|
||||||
// Получаем актуальный nonce прямо перед отправкой транзакции
|
// Получаем актуальный nonce прямо перед отправкой транзакции
|
||||||
const currentNonce = await nonceManager.getNonce(wallet.address, rpcUrl, Number(net.chainId), { timeout: 15000, maxRetries: 5 });
|
const currentNonce = await nonceManager.getNonce(wallet.address, rpcUrl, chainId, { timeout: 15000, maxRetries: 5 });
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} deploy attempt ${deployAttempts}/${maxDeployAttempts} with current nonce=${currentNonce} (target was ${targetNonce})`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} deploy attempt ${deployAttempts}/${maxDeployAttempts} with current nonce=${currentNonce} (target was ${targetNonce})`);
|
||||||
|
|
||||||
const txData = {
|
const txData = {
|
||||||
data: moduleInit,
|
data: moduleInit,
|
||||||
@@ -326,26 +329,26 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
const result = await rpcManager.sendTransactionWithRetry(wallet, txData, { maxRetries: 3 });
|
const result = await rpcManager.sendTransactionWithRetry(wallet, txData, { maxRetries: 3 });
|
||||||
tx = result.tx;
|
tx = result.tx;
|
||||||
|
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} deploy successful on attempt ${deployAttempts}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} deploy successful on attempt ${deployAttempts}`);
|
||||||
break; // Успешно отправили, выходим из цикла
|
break; // Успешно отправили, выходим из цикла
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errorMsg = e?.message || e;
|
const errorMsg = e?.message || e;
|
||||||
logger.warn(`[MODULES_DBG] chainId=${Number(net.chainId)} deploy attempt ${deployAttempts} failed: ${errorMsg}`);
|
logger.warn(`[MODULES_DBG] chainId=${chainId} deploy attempt ${deployAttempts} failed: ${errorMsg}`);
|
||||||
|
|
||||||
// Проверяем, является ли это ошибкой nonce
|
// Проверяем, является ли это ошибкой nonce
|
||||||
if (String(errorMsg).toLowerCase().includes('nonce too low') && deployAttempts < maxDeployAttempts) {
|
if (String(errorMsg).toLowerCase().includes('nonce too low') && deployAttempts < maxDeployAttempts) {
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} nonce race condition detected, retrying...`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} nonce race condition detected, retrying...`);
|
||||||
|
|
||||||
// Получаем актуальный nonce из сети
|
// Получаем актуальный nonce из сети
|
||||||
const currentNonce = await nonceManager.getNonce(wallet.address, rpcUrl, Number(net.chainId), { timeout: 15000, maxRetries: 5 });
|
const currentNonce = await nonceManager.getNonce(wallet.address, rpcUrl, chainId, { timeout: 15000, maxRetries: 5 });
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} current nonce: ${currentNonce}, target: ${targetNonce}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} current nonce: ${currentNonce}, target: ${targetNonce}`);
|
||||||
|
|
||||||
// Если текущий nonce больше целевого, обновляем targetNonce
|
// Если текущий nonce больше целевого, обновляем targetNonce
|
||||||
if (currentNonce > targetNonce) {
|
if (currentNonce > targetNonce) {
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} current nonce ${currentNonce} > target nonce ${targetNonce}, updating target`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} current nonce ${currentNonce} > target nonce ${targetNonce}, updating target`);
|
||||||
targetNonce = currentNonce;
|
targetNonce = currentNonce;
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} updated targetNonce to: ${targetNonce}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} updated targetNonce to: ${targetNonce}`);
|
||||||
|
|
||||||
// Короткая задержка перед следующей попыткой
|
// Короткая задержка перед следующей попыткой
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
@@ -354,7 +357,7 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
|
|
||||||
// Если текущий nonce меньше целевого, выравниваем его
|
// Если текущий nonce меньше целевого, выравниваем его
|
||||||
if (currentNonce < targetNonce) {
|
if (currentNonce < targetNonce) {
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} aligning nonce from ${currentNonce} to ${targetNonce}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} aligning nonce from ${currentNonce} to ${targetNonce}`);
|
||||||
|
|
||||||
// Выравниваем nonce нулевыми транзакциями
|
// Выравниваем nonce нулевыми транзакциями
|
||||||
for (let i = currentNonce; i < targetNonce; i++) {
|
for (let i = currentNonce; i < targetNonce; i++) {
|
||||||
@@ -368,13 +371,13 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
});
|
});
|
||||||
|
|
||||||
await fillerTx.wait();
|
await fillerTx.wait();
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} filler tx ${i} confirmed`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} filler tx ${i} confirmed`);
|
||||||
|
|
||||||
// Обновляем nonce в кэше
|
// Обновляем nonce в кэше
|
||||||
nonceManager.reserveNonce(wallet.address, Number(net.chainId), i);
|
nonceManager.reserveNonce(wallet.address, chainId, i);
|
||||||
|
|
||||||
} catch (fillerError) {
|
} catch (fillerError) {
|
||||||
logger.error(`[MODULES_DBG] chainId=${Number(net.chainId)} filler tx ${i} failed: ${fillerError.message}`);
|
logger.error(`[MODULES_DBG] chainId=${chainId} filler tx ${i} failed: ${fillerError.message}`);
|
||||||
throw fillerError;
|
throw fillerError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,7 +385,7 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
|
|
||||||
// ВАЖНО: Обновляем targetNonce на актуальный nonce для следующей попытки
|
// ВАЖНО: Обновляем targetNonce на актуальный nonce для следующей попытки
|
||||||
targetNonce = currentNonce;
|
targetNonce = currentNonce;
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} updated targetNonce to: ${targetNonce}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} updated targetNonce to: ${targetNonce}`);
|
||||||
|
|
||||||
// Короткая задержка перед следующей попыткой
|
// Короткая задержка перед следующей попыткой
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
@@ -402,24 +405,32 @@ async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce
|
|||||||
const rc = await tx.wait();
|
const rc = await tx.wait();
|
||||||
const deployedAddress = rc.contractAddress || predictedAddress;
|
const deployedAddress = rc.contractAddress || predictedAddress;
|
||||||
|
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleType} deployed at=${deployedAddress}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} ${moduleType} deployed at=${deployedAddress}`);
|
||||||
return { address: deployedAddress, chainId: Number(net.chainId) };
|
return { address: deployedAddress, chainId: chainId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Деплой всех модулей в одной сети
|
// Деплой всех модулей в одной сети
|
||||||
async function deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces, params) {
|
async function deployAllModulesInNetwork(chainId, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces, params) {
|
||||||
const { ethers } = hre;
|
const { ethers } = hre;
|
||||||
|
|
||||||
|
// Получаем RPC URL для данной сети
|
||||||
|
const rpcService = require('../../services/rpcProviderService');
|
||||||
|
const rpcUrl = await rpcService.getRpcUrlByChainId(chainId);
|
||||||
|
if (!rpcUrl) {
|
||||||
|
throw new Error(`RPC URL не найден для chainId ${chainId}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Используем новый менеджер RPC с retry логикой
|
// Используем новый менеджер RPC с retry логикой
|
||||||
const { provider, wallet, network } = await createRPCConnection(rpcUrl, pk, {
|
const { provider, wallet, network } = await createRPCConnection(chainId, pk, {
|
||||||
maxRetries: 3,
|
maxRetries: 3,
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
});
|
});
|
||||||
|
|
||||||
const net = network;
|
const net = network;
|
||||||
|
const chainId = Number(net.chainId);
|
||||||
|
|
||||||
logger.info(`[MODULES_DBG] chainId=${Number(net.chainId)} deploying modules: ${modulesToDeploy.join(', ')}`);
|
logger.info(`[MODULES_DBG] chainId=${chainId} deploying modules: ${modulesToDeploy.join(', ')}`);
|
||||||
|
|
||||||
const results = {};
|
const results = {};
|
||||||
|
|
||||||
@@ -432,14 +443,14 @@ async function deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesTo
|
|||||||
logger.info(`[MODULES_DBG] Деплой модуля ${moduleType} в сети ${net.name || net.chainId}`);
|
logger.info(`[MODULES_DBG] Деплой модуля ${moduleType} в сети ${net.name || net.chainId}`);
|
||||||
|
|
||||||
if (!MODULE_CONFIGS[moduleType]) {
|
if (!MODULE_CONFIGS[moduleType]) {
|
||||||
logger.error(`[MODULES_DBG] chainId=${Number(net.chainId)} Unknown module type: ${moduleType}`);
|
logger.error(`[MODULES_DBG] chainId=${chainId} Unknown module type: ${moduleType}`);
|
||||||
results[moduleType] = { success: false, error: `Unknown module type: ${moduleType}` };
|
results[moduleType] = { success: false, error: `Unknown module type: ${moduleType}` };
|
||||||
logger.error(`[MODULES_DBG] Неизвестный тип модуля: ${moduleType}`);
|
logger.error(`[MODULES_DBG] Неизвестный тип модуля: ${moduleType}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!moduleInit) {
|
if (!moduleInit) {
|
||||||
logger.error(`[MODULES_DBG] chainId=${Number(net.chainId)} No init code for module: ${moduleType}`);
|
logger.error(`[MODULES_DBG] chainId=${chainId} No init code for module: ${moduleType}`);
|
||||||
results[moduleType] = { success: false, error: `No init code for module: ${moduleType}` };
|
results[moduleType] = { success: false, error: `No init code for module: ${moduleType}` };
|
||||||
logger.error(`[MODULES_DBG] Отсутствует код инициализации для модуля: ${moduleType}`);
|
logger.error(`[MODULES_DBG] Отсутствует код инициализации для модуля: ${moduleType}`);
|
||||||
continue;
|
continue;
|
||||||
@@ -458,7 +469,7 @@ async function deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesTo
|
|||||||
|
|
||||||
// Получаем аргументы конструктора для модуля
|
// Получаем аргументы конструктора для модуля
|
||||||
const moduleConfig = MODULE_CONFIGS[moduleType];
|
const moduleConfig = MODULE_CONFIGS[moduleType];
|
||||||
const constructorArgs = moduleConfig.constructorArgs(dleAddress, Number(net.chainId), wallet.address);
|
const constructorArgs = moduleConfig.constructorArgs(dleAddress, chainId, wallet.address);
|
||||||
|
|
||||||
// Ждем 30 секунд перед верификацией, чтобы транзакция получила подтверждения
|
// Ждем 30 секунд перед верификацией, чтобы транзакция получила подтверждения
|
||||||
logger.info(`[MODULES_DBG] Ждем 30 секунд перед верификацией модуля ${moduleType}...`);
|
logger.info(`[MODULES_DBG] Ждем 30 секунд перед верификацией модуля ${moduleType}...`);
|
||||||
@@ -478,7 +489,7 @@ async function deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesTo
|
|||||||
}
|
}
|
||||||
|
|
||||||
const verificationResult = await verifyModuleAfterDeploy(
|
const verificationResult = await verifyModuleAfterDeploy(
|
||||||
Number(net.chainId),
|
chainId,
|
||||||
result.address,
|
result.address,
|
||||||
moduleType,
|
moduleType,
|
||||||
constructorArgs,
|
constructorArgs,
|
||||||
@@ -510,9 +521,9 @@ async function deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesTo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleType} deployment failed:`, error.message);
|
logger.error(`[MODULES_DBG] chainId=${chainId} ${moduleType} deployment failed:`, error.message);
|
||||||
results[moduleType] = {
|
results[moduleType] = {
|
||||||
chainId: Number(net.chainId),
|
chainId: chainId,
|
||||||
success: false,
|
success: false,
|
||||||
error: error.message
|
error: error.message
|
||||||
};
|
};
|
||||||
@@ -521,7 +532,7 @@ async function deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesTo
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
chainId: Number(net.chainId),
|
chainId: chainId,
|
||||||
modules: results
|
modules: results
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -544,6 +555,15 @@ async function deployAllModulesInAllNetworks(networks, pk, salt, dleAddress, mod
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
// 🔧 BEST PRACTICE: Настраиваем NO_PROXY перед деплоем
|
||||||
|
try {
|
||||||
|
const proxyManager = require('../../utils/proxyManager');
|
||||||
|
await proxyManager.initialize();
|
||||||
|
console.log('[MODULES_DBG] ✅ ProxyManager инициализирован');
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[MODULES_DBG] ⚠️ Не удалось инициализировать ProxyManager:', error.message);
|
||||||
|
}
|
||||||
|
|
||||||
const { ethers } = hre;
|
const { ethers } = hre;
|
||||||
|
|
||||||
// Обрабатываем аргументы командной строки и переменные окружения
|
// Обрабатываем аргументы командной строки и переменные окружения
|
||||||
@@ -609,6 +629,7 @@ async function main() {
|
|||||||
|
|
||||||
const pk = params.privateKey || params.private_key || process.env.PRIVATE_KEY;
|
const pk = params.privateKey || params.private_key || process.env.PRIVATE_KEY;
|
||||||
const networks = params.rpcUrls || params.rpc_urls || [];
|
const networks = params.rpcUrls || params.rpc_urls || [];
|
||||||
|
const supportedChainIds = params.supportedChainIds || [];
|
||||||
const dleAddress = params.dleAddress;
|
const dleAddress = params.dleAddress;
|
||||||
const salt = params.CREATE2_SALT || params.create2_salt;
|
const salt = params.CREATE2_SALT || params.create2_salt;
|
||||||
|
|
||||||
@@ -666,7 +687,7 @@ async function main() {
|
|||||||
const ContractFactory = await hre.ethers.getContractFactory(moduleConfig.contractName);
|
const ContractFactory = await hre.ethers.getContractFactory(moduleConfig.contractName);
|
||||||
|
|
||||||
// Получаем аргументы конструктора для первой сети (для расчета init кода)
|
// Получаем аргументы конструктора для первой сети (для расчета init кода)
|
||||||
const firstConnection = await createRPCConnection(networks[0], pk, {
|
const firstConnection = await createRPCConnection(supportedChainIds[0], pk, {
|
||||||
maxRetries: 3,
|
maxRetries: 3,
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
});
|
});
|
||||||
@@ -688,8 +709,8 @@ async function main() {
|
|||||||
|
|
||||||
// Подготовим провайдеры и вычислим общие nonce для каждого модуля
|
// Подготовим провайдеры и вычислим общие nonce для каждого модуля
|
||||||
// Создаем RPC соединения с retry логикой
|
// Создаем RPC соединения с retry логикой
|
||||||
logger.info(`[MODULES_DBG] Создаем RPC соединения для ${networks.length} сетей...`);
|
logger.info(`[MODULES_DBG] Создаем RPC соединения для ${supportedChainIds.length} сетей...`);
|
||||||
const connections = await createMultipleRPCConnections(networks, pk, {
|
const connections = await createMultipleRPCConnections(supportedChainIds, pk, {
|
||||||
maxRetries: 3,
|
maxRetries: 3,
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
});
|
});
|
||||||
@@ -698,7 +719,7 @@ async function main() {
|
|||||||
throw new Error('Не удалось установить ни одного RPC соединения');
|
throw new Error('Не удалось установить ни одного RPC соединения');
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`[MODULES_DBG] ✅ Успешно подключились к ${connections.length}/${networks.length} сетям`);
|
logger.info(`[MODULES_DBG] ✅ Успешно подключились к ${connections.length}/${supportedChainIds.length} сетям`);
|
||||||
|
|
||||||
const nonces = [];
|
const nonces = [];
|
||||||
for (const connection of connections) {
|
for (const connection of connections) {
|
||||||
@@ -718,33 +739,28 @@ async function main() {
|
|||||||
logger.info(`[MODULES_DBG] nonces=${JSON.stringify(nonces)} targetNonces=${JSON.stringify(targetNonces)}`);
|
logger.info(`[MODULES_DBG] nonces=${JSON.stringify(nonces)} targetNonces=${JSON.stringify(targetNonces)}`);
|
||||||
|
|
||||||
// ПАРАЛЛЕЛЬНЫЙ деплой всех модулей во всех сетях одновременно
|
// ПАРАЛЛЕЛЬНЫЙ деплой всех модулей во всех сетях одновременно
|
||||||
logger.info(`[MODULES_DBG] Starting PARALLEL deployment of all modules to ${networks.length} networks`);
|
logger.info(`[MODULES_DBG] Starting PARALLEL deployment of all modules to ${connections.length} networks`);
|
||||||
|
|
||||||
const deploymentPromises = networks.map(async (rpcUrl, networkIndex) => {
|
const deploymentPromises = connections.map(async (connection, networkIndex) => {
|
||||||
logger.info(`[MODULES_DBG] 🚀 Starting deployment to network ${networkIndex + 1}/${networks.length}: ${rpcUrl}`);
|
logger.info(`[MODULES_DBG] 🚀 Starting deployment to network ${networkIndex + 1}/${connections.length}: ${connection.rpcUrl}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Получаем chainId динамически из сети с retry логикой
|
const chainId = Number(connection.network.chainId);
|
||||||
const { provider, network } = await createRPCConnection(rpcUrl, pk, {
|
|
||||||
maxRetries: 3,
|
|
||||||
timeout: 30000
|
|
||||||
});
|
|
||||||
const chainId = Number(network.chainId);
|
|
||||||
|
|
||||||
logger.info(`[MODULES_DBG] 📡 Network ${networkIndex + 1} chainId: ${chainId}`);
|
logger.info(`[MODULES_DBG] 📡 Network ${networkIndex + 1} chainId: ${chainId}`);
|
||||||
|
|
||||||
const result = await deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces, params);
|
const result = await deployAllModulesInNetwork(chainId, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces, params);
|
||||||
logger.info(`[MODULES_DBG] ✅ Network ${networkIndex + 1} (chainId: ${chainId}) deployment SUCCESS`);
|
logger.info(`[MODULES_DBG] ✅ Network ${networkIndex + 1} (chainId: ${chainId}) deployment SUCCESS`);
|
||||||
return { rpcUrl, chainId, ...result };
|
return { rpcUrl: connection.rpcUrl, chainId, ...result };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[MODULES_DBG] ❌ Network ${networkIndex + 1} deployment FAILED:`, error.message);
|
logger.error(`[MODULES_DBG] ❌ Network ${networkIndex + 1} deployment FAILED:`, error.message);
|
||||||
return { rpcUrl, error: error.message };
|
return { rpcUrl: connection.rpcUrl, error: error.message };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ждем завершения всех деплоев
|
// Ждем завершения всех деплоев
|
||||||
const deployResults = await Promise.all(deploymentPromises);
|
const deployResults = await Promise.all(deploymentPromises);
|
||||||
logger.info(`[MODULES_DBG] All ${networks.length} deployments completed`);
|
logger.info(`[MODULES_DBG] All ${connections.length} deployments completed`);
|
||||||
|
|
||||||
// Логируем результаты деплоя для каждой сети
|
// Логируем результаты деплоя для каждой сети
|
||||||
deployResults.forEach((result, index) => {
|
deployResults.forEach((result, index) => {
|
||||||
|
|||||||
@@ -220,12 +220,19 @@ async function verifyDLEAfterDeploy(chainId, contractAddress, constructorArgs, a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit, params, dleConfig, initializer, etherscanKey) {
|
async function deployInNetwork(chainId, pk, initCodeHash, targetDLENonce, dleInit, params, dleConfig, initializer, etherscanKey) {
|
||||||
try {
|
try {
|
||||||
const { ethers } = hre;
|
const { ethers } = hre;
|
||||||
|
|
||||||
|
// Получаем RPC URL для данной сети
|
||||||
|
const rpcService = require('../../services/rpcProviderService');
|
||||||
|
const rpcUrl = await rpcService.getRpcUrlByChainId(chainId);
|
||||||
|
if (!rpcUrl) {
|
||||||
|
throw new Error(`RPC URL не найден для chainId ${chainId}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Используем новый менеджер RPC с retry логикой
|
// Используем новый менеджер RPC с retry логикой
|
||||||
const { provider, wallet, network } = await createRPCConnection(rpcUrl, pk, {
|
const { provider, wallet, network } = await createRPCConnection(chainId, pk, {
|
||||||
maxRetries: 3,
|
maxRetries: 3,
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
});
|
});
|
||||||
@@ -245,27 +252,26 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1) Используем NonceManager для получения актуального nonce
|
// 1) Используем NonceManager для получения актуального nonce
|
||||||
const chainId = Number(net.chainId);
|
|
||||||
let current = await nonceManager.getNonce(wallet.address, rpcUrl, chainId, { timeout: 15000, maxRetries: 5 });
|
let current = await nonceManager.getNonce(wallet.address, rpcUrl, chainId, { timeout: 15000, maxRetries: 5 });
|
||||||
logger.info(`[MULTI_DBG] chainId=${chainId} current nonce=${current} (target was ${targetDLENonce})`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} current nonce=${current} (target was ${targetDLENonce})`);
|
||||||
|
|
||||||
// Если текущий nonce больше целевого, обновляем targetDLENonce
|
// Если текущий nonce больше целевого, обновляем targetDLENonce
|
||||||
if (current > targetDLENonce) {
|
if (current > targetDLENonce) {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} current nonce ${current} > targetDLENonce ${targetDLENonce}, updating target`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} current nonce ${current} > targetDLENonce ${targetDLENonce}, updating target`);
|
||||||
targetDLENonce = current;
|
targetDLENonce = current;
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} updated targetDLENonce to: ${targetDLENonce}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} updated targetDLENonce to: ${targetDLENonce}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если текущий nonce меньше целевого, выравниваем его
|
// Если текущий nonce меньше целевого, выравниваем его
|
||||||
if (current < targetDLENonce) {
|
if (current < targetDLENonce) {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} starting nonce alignment: ${current} -> ${targetDLENonce} (${targetDLENonce - current} transactions needed)`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} starting nonce alignment: ${current} -> ${targetDLENonce} (${targetDLENonce - current} transactions needed)`);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce already aligned: ${current} = ${targetDLENonce}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} nonce already aligned: ${current} = ${targetDLENonce}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Выравниваем nonce если нужно (используем NonceManager)
|
// 2) Выравниваем nonce если нужно (используем NonceManager)
|
||||||
if (current < targetDLENonce) {
|
if (current < targetDLENonce) {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} aligning nonce from ${current} to ${targetDLENonce}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} aligning nonce from ${current} to ${targetDLENonce}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
current = await nonceManager.alignNonceToTarget(
|
current = await nonceManager.alignNonceToTarget(
|
||||||
@@ -277,31 +283,31 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
{ gasLimit: 21000, maxRetries: 5 }
|
{ gasLimit: 21000, maxRetries: 5 }
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce alignment completed, current nonce=${current}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} nonce alignment completed, current nonce=${current}`);
|
||||||
|
|
||||||
// Зарезервируем nonce в NonceManager
|
// Зарезервируем nonce в NonceManager
|
||||||
nonceManager.reserveNonce(wallet.address, chainId, targetDLENonce);
|
nonceManager.reserveNonce(wallet.address, chainId, targetDLENonce);
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} ready for DLE deployment with nonce=${current}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} ready for DLE deployment with nonce=${current}`);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce alignment failed: ${error.message}`);
|
logger.error(`[MULTI_DBG] chainId=${chainId} nonce alignment failed: ${error.message}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce already aligned at ${current}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} nonce already aligned at ${current}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Проверяем баланс перед деплоем
|
// 2) Проверяем баланс перед деплоем
|
||||||
const balance = await provider.getBalance(wallet.address, 'latest');
|
const balance = await provider.getBalance(wallet.address, 'latest');
|
||||||
const balanceEth = ethers.formatEther(balance);
|
const balanceEth = ethers.formatEther(balance);
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} wallet balance: ${balanceEth} ETH`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} wallet balance: ${balanceEth} ETH`);
|
||||||
|
|
||||||
if (balance < ethers.parseEther('0.01')) {
|
if (balance < ethers.parseEther('0.01')) {
|
||||||
throw new Error(`Insufficient balance for deployment on chainId=${Number(net.chainId)}. Current: ${balanceEth} ETH, required: 0.01 ETH minimum`);
|
throw new Error(`Insufficient balance for deployment on chainId=${chainId}. Current: ${balanceEth} ETH, required: 0.01 ETH minimum`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Деплой DLE с актуальным nonce
|
// 3) Деплой DLE с актуальным nonce
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} deploying DLE with current nonce`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} deploying DLE with current nonce`);
|
||||||
|
|
||||||
const feeOverrides = await getFeeOverrides(provider);
|
const feeOverrides = await getFeeOverrides(provider);
|
||||||
let gasLimit;
|
let gasLimit;
|
||||||
@@ -318,7 +324,7 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
const fallbackGas = maxByBalance > 5_000_000n ? 5_000_000n : (maxByBalance < 2_500_000n ? 2_500_000n : maxByBalance);
|
const fallbackGas = maxByBalance > 5_000_000n ? 5_000_000n : (maxByBalance < 2_500_000n ? 2_500_000n : maxByBalance);
|
||||||
gasLimit = est ? (est + est / 5n) : fallbackGas;
|
gasLimit = est ? (est + est / 5n) : fallbackGas;
|
||||||
|
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} estGas=${est?.toString?.()||'null'} effGasPrice=${effPrice?.toString?.()||'0'} maxByBalance=${maxByBalance.toString()} chosenGasLimit=${gasLimit.toString()}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} estGas=${est?.toString?.()||'null'} effGasPrice=${effPrice?.toString?.()||'0'} maxByBalance=${maxByBalance.toString()} chosenGasLimit=${gasLimit.toString()}`);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
gasLimit = 3_000_000n;
|
gasLimit = 3_000_000n;
|
||||||
}
|
}
|
||||||
@@ -328,17 +334,17 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
from: wallet.address,
|
from: wallet.address,
|
||||||
nonce: targetDLENonce
|
nonce: targetDLENonce
|
||||||
});
|
});
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} predicted DLE address=${predictedAddress} (nonce=${targetDLENonce})`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} predicted DLE address=${predictedAddress} (nonce=${targetDLENonce})`);
|
||||||
|
|
||||||
// Проверяем, не развернут ли уже контракт
|
// Проверяем, не развернут ли уже контракт
|
||||||
const existingCode = await provider.getCode(predictedAddress);
|
const existingCode = await provider.getCode(predictedAddress);
|
||||||
if (existingCode && existingCode !== '0x') {
|
if (existingCode && existingCode !== '0x') {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} DLE already exists at predictedAddress, skip deploy`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} DLE already exists at predictedAddress, skip deploy`);
|
||||||
|
|
||||||
// Проверяем и инициализируем логотип для существующего контракта
|
// Проверяем и инициализируем логотип для существующего контракта
|
||||||
if (params.logoURI && params.logoURI !== '') {
|
if (params.logoURI && params.logoURI !== '') {
|
||||||
try {
|
try {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} checking logoURI for existing contract`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} checking logoURI for existing contract`);
|
||||||
|
|
||||||
// Ждем 2 секунды для стабильности соединения
|
// Ждем 2 секунды для стабильности соединения
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||||
@@ -348,19 +354,19 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
|
|
||||||
const currentLogo = await dleContract.logoURI();
|
const currentLogo = await dleContract.logoURI();
|
||||||
if (currentLogo === '' || currentLogo === '0x') {
|
if (currentLogo === '' || currentLogo === '0x') {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} initializing logoURI for existing contract: ${params.logoURI}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} initializing logoURI for existing contract: ${params.logoURI}`);
|
||||||
const logoTx = await dleContract.connect(wallet).initializeLogoURI(params.logoURI, feeOverrides);
|
const logoTx = await dleContract.connect(wallet).initializeLogoURI(params.logoURI, feeOverrides);
|
||||||
await logoTx.wait();
|
await logoTx.wait();
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialized for existing contract`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} logoURI initialized for existing contract`);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI already set: ${currentLogo}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} logoURI already set: ${currentLogo}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialization failed for existing contract: ${error.message}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} logoURI initialization failed for existing contract: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { address: predictedAddress, chainId: Number(net.chainId) };
|
return { address: predictedAddress, chainId: chainId };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Деплоим DLE с retry логикой для обработки race conditions
|
// Деплоим DLE с retry логикой для обработки race conditions
|
||||||
@@ -374,13 +380,13 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
|
|
||||||
// Получаем актуальный nonce прямо перед отправкой транзакции
|
// Получаем актуальный nonce прямо перед отправкой транзакции
|
||||||
const currentNonce = await nonceManager.getNonce(wallet.address, rpcUrl, chainId, { timeout: 15000, maxRetries: 5 });
|
const currentNonce = await nonceManager.getNonce(wallet.address, rpcUrl, chainId, { timeout: 15000, maxRetries: 5 });
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} deploy attempt ${deployAttempts}/${maxDeployAttempts} with current nonce=${currentNonce} (target was ${targetDLENonce})`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} deploy attempt ${deployAttempts}/${maxDeployAttempts} with current nonce=${currentNonce} (target was ${targetDLENonce})`);
|
||||||
|
|
||||||
// Если текущий nonce больше целевого, обновляем targetDLENonce
|
// Если текущий nonce больше целевого, обновляем targetDLENonce
|
||||||
if (currentNonce > targetDLENonce) {
|
if (currentNonce > targetDLENonce) {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} current nonce ${currentNonce} > target nonce ${targetDLENonce}, updating target`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} current nonce ${currentNonce} > target nonce ${targetDLENonce}, updating target`);
|
||||||
targetDLENonce = currentNonce;
|
targetDLENonce = currentNonce;
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} updated targetDLENonce to: ${targetDLENonce}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} updated targetDLENonce to: ${targetDLENonce}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const txData = {
|
const txData = {
|
||||||
@@ -397,25 +403,25 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
// Отмечаем транзакцию как pending в NonceManager
|
// Отмечаем транзакцию как pending в NonceManager
|
||||||
nonceManager.markTransactionPending(wallet.address, chainId, currentNonce, tx.hash);
|
nonceManager.markTransactionPending(wallet.address, chainId, currentNonce, tx.hash);
|
||||||
|
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} deploy successful on attempt ${deployAttempts}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} deploy successful on attempt ${deployAttempts}`);
|
||||||
break; // Успешно отправили, выходим из цикла
|
break; // Успешно отправили, выходим из цикла
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errorMsg = e?.message || e;
|
const errorMsg = e?.message || e;
|
||||||
logger.warn(`[MULTI_DBG] chainId=${Number(net.chainId)} deploy attempt ${deployAttempts} failed: ${errorMsg}`);
|
logger.warn(`[MULTI_DBG] chainId=${chainId} deploy attempt ${deployAttempts} failed: ${errorMsg}`);
|
||||||
|
|
||||||
// Проверяем, является ли это ошибкой nonce
|
// Проверяем, является ли это ошибкой nonce
|
||||||
if (String(errorMsg).toLowerCase().includes('nonce too low') && deployAttempts < maxDeployAttempts) {
|
if (String(errorMsg).toLowerCase().includes('nonce too low') && deployAttempts < maxDeployAttempts) {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce race condition detected, retrying...`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} nonce race condition detected, retrying...`);
|
||||||
|
|
||||||
// Используем NonceManager для обновления nonce
|
// Используем NonceManager для обновления nonce
|
||||||
nonceManager.resetNonce(wallet.address, chainId);
|
nonceManager.resetNonce(wallet.address, chainId);
|
||||||
const currentNonce = await nonceManager.getNonce(wallet.address, rpcUrl, chainId, { timeout: 15000, maxRetries: 5 });
|
const currentNonce = await nonceManager.getNonce(wallet.address, rpcUrl, chainId, { timeout: 15000, maxRetries: 5 });
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} current nonce: ${currentNonce}, target was: ${targetDLENonce}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} current nonce: ${currentNonce}, target was: ${targetDLENonce}`);
|
||||||
|
|
||||||
// Обновляем targetDLENonce на актуальный nonce
|
// Обновляем targetDLENonce на актуальный nonce
|
||||||
targetDLENonce = currentNonce;
|
targetDLENonce = currentNonce;
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} updated targetDLENonce to: ${targetDLENonce}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} updated targetDLENonce to: ${targetDLENonce}`);
|
||||||
|
|
||||||
// Короткая задержка перед следующей попыткой
|
// Короткая задержка перед следующей попыткой
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
@@ -440,19 +446,19 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
|
|
||||||
// Проверяем, что адрес соответствует предсказанному
|
// Проверяем, что адрес соответствует предсказанному
|
||||||
if (deployedAddress !== predictedAddress) {
|
if (deployedAddress !== predictedAddress) {
|
||||||
logger.error(`[MULTI_DBG] chainId=${Number(net.chainId)} ADDRESS MISMATCH! predicted=${predictedAddress} actual=${deployedAddress}`);
|
logger.error(`[MULTI_DBG] chainId=${chainId} ADDRESS MISMATCH! predicted=${predictedAddress} actual=${deployedAddress}`);
|
||||||
throw new Error(`Address mismatch: predicted ${predictedAddress} != actual ${deployedAddress}`);
|
throw new Error(`Address mismatch: predicted ${predictedAddress} != actual ${deployedAddress}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} DLE deployed at=${deployedAddress} ✅`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} DLE deployed at=${deployedAddress} ✅`);
|
||||||
|
|
||||||
// Инициализация логотипа если он указан
|
// Инициализация логотипа если он указан
|
||||||
if (params.logoURI && params.logoURI !== '') {
|
if (params.logoURI && params.logoURI !== '') {
|
||||||
try {
|
try {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} initializing logoURI: ${params.logoURI}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} initializing logoURI: ${params.logoURI}`);
|
||||||
|
|
||||||
// Ждем 5 секунд, чтобы контракт получил подтверждения
|
// Ждем 5 секунд, чтобы контракт получил подтверждения
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} waiting 5 seconds for contract confirmations...`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} waiting 5 seconds for contract confirmations...`);
|
||||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
const DLE = await hre.ethers.getContractFactory('contracts/DLE.sol:DLE');
|
const DLE = await hre.ethers.getContractFactory('contracts/DLE.sol:DLE');
|
||||||
@@ -460,24 +466,24 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
|
|
||||||
// Проверяем текущий логотип перед инициализацией
|
// Проверяем текущий логотип перед инициализацией
|
||||||
const currentLogo = await dleContract.logoURI();
|
const currentLogo = await dleContract.logoURI();
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} current logoURI: ${currentLogo}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} current logoURI: ${currentLogo}`);
|
||||||
|
|
||||||
if (currentLogo === '' || currentLogo === '0x') {
|
if (currentLogo === '' || currentLogo === '0x') {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI is empty, initializing...`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} logoURI is empty, initializing...`);
|
||||||
const logoTx = await dleContract.connect(wallet).initializeLogoURI(params.logoURI, feeOverrides);
|
const logoTx = await dleContract.connect(wallet).initializeLogoURI(params.logoURI, feeOverrides);
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI transaction sent: ${logoTx.hash}`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} logoURI transaction sent: ${logoTx.hash}`);
|
||||||
await logoTx.wait(2); // Ждем 2 подтверждения с таймаутом
|
await logoTx.wait(2); // Ждем 2 подтверждения с таймаутом
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialized successfully`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} logoURI initialized successfully`);
|
||||||
} else {
|
} else {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI already set: ${currentLogo}, skipping initialization`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} logoURI already set: ${currentLogo}, skipping initialization`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialization failed: ${error.message}`);
|
logger.error(`[MULTI_DBG] chainId=${chainId} logoURI initialization failed: ${error.message}`);
|
||||||
logger.error(`[MULTI_DBG] chainId=${Number(net.chainId)} error stack: ${error.stack}`);
|
logger.error(`[MULTI_DBG] chainId=${chainId} error stack: ${error.stack}`);
|
||||||
// Не прерываем деплой из-за ошибки логотипа
|
// Не прерываем деплой из-за ошибки логотипа
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} no logoURI specified, skipping initialization`);
|
logger.info(`[MULTI_DBG] chainId=${chainId} no logoURI specified, skipping initialization`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Автоматическая верификация DLE контракта после успешного деплоя
|
// Автоматическая верификация DLE контракта после успешного деплоя
|
||||||
@@ -573,6 +579,15 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit
|
|||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('[MULTI_DBG] 🚀 ВХОДИМ В ФУНКЦИЮ MAIN!');
|
console.log('[MULTI_DBG] 🚀 ВХОДИМ В ФУНКЦИЮ MAIN!');
|
||||||
|
|
||||||
|
// 🔧 BEST PRACTICE: Настраиваем NO_PROXY перед деплоем
|
||||||
|
try {
|
||||||
|
const proxyManager = require('../../utils/proxyManager');
|
||||||
|
await proxyManager.initialize();
|
||||||
|
console.log('[MULTI_DBG] ✅ ProxyManager инициализирован');
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[MULTI_DBG] ⚠️ Не удалось инициализировать ProxyManager:', error.message);
|
||||||
|
}
|
||||||
const { ethers } = hre;
|
const { ethers } = hre;
|
||||||
console.log('[MULTI_DBG] ✅ ethers получен');
|
console.log('[MULTI_DBG] ✅ ethers получен');
|
||||||
|
|
||||||
@@ -699,8 +714,8 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Подготовим провайдеры и вычислим общий nonce для DLE с retry логикой
|
// Подготовим провайдеры и вычислим общий nonce для DLE с retry логикой
|
||||||
logger.info(`[MULTI_DBG] Создаем RPC соединения для ${networks.length} сетей...`);
|
logger.info(`[MULTI_DBG] Создаем RPC соединения для ${supportedChainIds.length} сетей...`);
|
||||||
const connections = await createMultipleRPCConnections(networks, pk, {
|
const connections = await createMultipleRPCConnections(supportedChainIds, pk, {
|
||||||
maxRetries: 3,
|
maxRetries: 3,
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
});
|
});
|
||||||
@@ -709,7 +724,7 @@ async function main() {
|
|||||||
throw new Error('Не удалось установить ни одного RPC соединения');
|
throw new Error('Не удалось установить ни одного RPC соединения');
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`[MULTI_DBG] ✅ Успешно подключились к ${connections.length}/${networks.length} сетям`);
|
logger.info(`[MULTI_DBG] ✅ Успешно подключились к ${connections.length}/${supportedChainIds.length} сетям`);
|
||||||
|
|
||||||
// Очищаем старые pending транзакции для всех сетей
|
// Очищаем старые pending транзакции для всех сетей
|
||||||
for (const connection of connections) {
|
for (const connection of connections) {
|
||||||
@@ -719,12 +734,13 @@ async function main() {
|
|||||||
|
|
||||||
const nonces = [];
|
const nonces = [];
|
||||||
for (const connection of connections) {
|
for (const connection of connections) {
|
||||||
|
logger.info(`[MULTI_DBG] Получаем nonce для connection: address=${connection.wallet.address}, rpcUrl=${connection.rpcUrl}, chainId=${Number(connection.network.chainId)}`);
|
||||||
const n = await nonceManager.getNonce(connection.wallet.address, connection.rpcUrl, Number(connection.network.chainId));
|
const n = await nonceManager.getNonce(connection.wallet.address, connection.rpcUrl, Number(connection.network.chainId));
|
||||||
nonces.push(n);
|
nonces.push(n);
|
||||||
}
|
}
|
||||||
const targetDLENonce = Math.max(...nonces);
|
const targetDLENonce = Math.max(...nonces);
|
||||||
logger.info(`[MULTI_DBG] nonces=${JSON.stringify(nonces)} targetDLENonce=${targetDLENonce}`);
|
logger.info(`[MULTI_DBG] nonces=${JSON.stringify(nonces)} targetDLENonce=${targetDLENonce}`);
|
||||||
logger.info(`[MULTI_DBG] Starting deployment to ${networks.length} networks:`, networks);
|
logger.info(`[MULTI_DBG] Starting deployment to ${connections.length} networks`);
|
||||||
|
|
||||||
// ПАРАЛЛЕЛЬНЫЙ деплой во всех успешных сетях одновременно
|
// ПАРАЛЛЕЛЬНЫЙ деплой во всех успешных сетях одновременно
|
||||||
console.log(`[MULTI_DBG] 🚀 ДОШЛИ ДО ПАРАЛЛЕЛЬНОГО ДЕПЛОЯ!`);
|
console.log(`[MULTI_DBG] 🚀 ДОШЛИ ДО ПАРАЛЛЕЛЬНОГО ДЕПЛОЯ!`);
|
||||||
@@ -744,7 +760,7 @@ async function main() {
|
|||||||
throw new Error(`InitCode не найден для chainId: ${chainId}`);
|
throw new Error(`InitCode не найден для chainId: ${chainId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const r = await deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, networkInitCode, params, dleConfig, initializer, etherscanKey);
|
const r = await deployInNetwork(chainId, pk, initCodeHash, targetDLENonce, networkInitCode, params, dleConfig, initializer, etherscanKey);
|
||||||
logger.info(`[MULTI_DBG] ✅ Network ${i + 1} (chainId: ${chainId}) deployment SUCCESS: ${r.address}`);
|
logger.info(`[MULTI_DBG] ✅ Network ${i + 1} (chainId: ${chainId}) deployment SUCCESS: ${r.address}`);
|
||||||
return {
|
return {
|
||||||
rpcUrl,
|
rpcUrl,
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ async function main() {
|
|||||||
console.log(`Читаем данные DLE из блокчейна по адресу: ${dleAddress}`);
|
console.log(`Читаем данные DLE из блокчейна по адресу: ${dleAddress}`);
|
||||||
|
|
||||||
// Получаем RPC URL из переменных окружения или используем дефолтный для Sepolia
|
// Получаем RPC URL из переменных окружения или используем дефолтный для Sepolia
|
||||||
const rpcUrl = process.env.RPC_URL || 'https://eth-sepolia.nodereal.io/v1/YOUR_NODEREAL_KEY';
|
// Получаем RPC URL из базы данных
|
||||||
|
const rpcService = require('../../services/rpcProviderService');
|
||||||
|
const rpcUrl = await rpcService.getRpcUrlByChainId(11155111);
|
||||||
|
|
||||||
// Создаем провайдер
|
// Создаем провайдер
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcService.getRpcUrlByChainId(11155111));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Получаем ABI контракта DLE
|
// Получаем ABI контракта DLE
|
||||||
|
|||||||
@@ -11,6 +11,51 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
|
||||||
|
// Функция для настройки NO_PROXY на основе RPC провайдеров из базы данных
|
||||||
|
async function configureNoProxyFromRpcProviders() {
|
||||||
|
try {
|
||||||
|
const rpcService = require('./services/rpcProviderService');
|
||||||
|
const providers = await rpcService.getAllRpcProviders();
|
||||||
|
|
||||||
|
const rpcDomains = providers
|
||||||
|
.map(provider => provider.rpc_url)
|
||||||
|
.filter(url => url && url.startsWith('http'))
|
||||||
|
.map(url => {
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
return urlObj.hostname;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(hostname => hostname)
|
||||||
|
.filter((hostname, index, array) => array.indexOf(hostname) === index); // убираем дубликаты
|
||||||
|
|
||||||
|
if (rpcDomains.length > 0) {
|
||||||
|
const existingNoProxy = process.env.NO_PROXY || '';
|
||||||
|
|
||||||
|
// Добавляем RPC домены к существующему NO_PROXY
|
||||||
|
const newDomains = rpcDomains.filter(domain => !existingNoProxy.includes(domain));
|
||||||
|
|
||||||
|
if (newDomains.length > 0) {
|
||||||
|
process.env.NO_PROXY = existingNoProxy ? `${existingNoProxy},${newDomains.join(',')}` : newDomains.join(',');
|
||||||
|
console.log('[Server] ✅ Добавлены RPC домены в NO_PROXY:', newDomains.join(', '));
|
||||||
|
console.log('[Server] 📋 Обновленный NO_PROXY:', process.env.NO_PROXY);
|
||||||
|
} else {
|
||||||
|
console.log('[Server] ℹ️ Все RPC домены уже в NO_PROXY');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('[Server] ⚠️ Не найдено RPC провайдеров для настройки NO_PROXY');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[Server] ❌ Не удалось загрузить RPC провайдеры для NO_PROXY:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Экспортируем функцию для использования в других модулях
|
||||||
|
module.exports.configureNoProxyFromRpcProviders = configureNoProxyFromRpcProviders;
|
||||||
|
|
||||||
const { app, nonceStore } = require('./app');
|
const { app, nonceStore } = require('./app');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
const { initWSS } = require('./wsHub');
|
const { initWSS } = require('./wsHub');
|
||||||
@@ -32,6 +77,9 @@ initWSS(server);
|
|||||||
async function startServer() {
|
async function startServer() {
|
||||||
await initDbPool();
|
await initDbPool();
|
||||||
|
|
||||||
|
// Настройка NO_PROXY для RPC провайдеров из базы данных
|
||||||
|
await configureNoProxyFromRpcProviders();
|
||||||
|
|
||||||
// Инициализация AI ассистента В ФОНЕ (неблокирующая)
|
// Инициализация AI ассистента В ФОНЕ (неблокирующая)
|
||||||
seedAIAssistantSettings().catch(error => {
|
seedAIAssistantSettings().catch(error => {
|
||||||
console.warn('[Server] Ollama недоступен, AI ассистент будет инициализирован позже:', error.message);
|
console.warn('[Server] Ollama недоступен, AI ассистент будет инициализирован позже:', error.message);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ async function checkAdminRole(address) {
|
|||||||
errorCount++;
|
errorCount++;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcService.getRpcUrlByChainId(chainId));
|
||||||
// Проверяем доступность сети с таймаутом
|
// Проверяем доступность сети с таймаутом
|
||||||
try {
|
try {
|
||||||
const networkCheckPromise = provider.getNetwork();
|
const networkCheckPromise = provider.getNetwork();
|
||||||
|
|||||||
@@ -455,6 +455,7 @@ class AuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Определяет уровень доступа пользователя на основе количества токенов
|
* Определяет уровень доступа пользователя на основе количества токенов
|
||||||
* @param {string} address - Адрес кошелька
|
* @param {string} address - Адрес кошелька
|
||||||
@@ -490,23 +491,35 @@ class AuthService {
|
|||||||
const tokens = tokensResult.rows;
|
const tokens = tokensResult.rows;
|
||||||
|
|
||||||
// Получаем RPC провайдеры
|
// Получаем RPC провайдеры
|
||||||
const rpcProvidersResult = await db.getQuery()(
|
// Убрано - используем rpcService вместо прямого запроса к БД
|
||||||
'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;
|
|
||||||
|
|
||||||
const rpcMap = {};
|
// Используем правильный RPC URL из базы данных
|
||||||
for (const rpc of rpcProviders) {
|
const rpcService = require('./rpcProviderService');
|
||||||
rpcMap[rpc.network_id] = rpc.rpc_url;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем балансы токенов из блокчейна
|
// Получаем балансы токенов из блокчейна
|
||||||
const ERC20_ABI = ['function balanceOf(address owner) view returns (uint256)'];
|
const ERC20_ABI = ['function balanceOf(address owner) view returns (uint256)'];
|
||||||
const tokenBalances = [];
|
const tokenBalances = [];
|
||||||
|
|
||||||
|
// Получаем все RPC провайдеры из базы данных для маппинга
|
||||||
|
const allRpcProviders = await rpcService.getAllRpcProviders();
|
||||||
|
const networkToChainId = {};
|
||||||
|
|
||||||
|
// Создаем маппинг из базы данных
|
||||||
|
for (const provider of allRpcProviders) {
|
||||||
|
if (provider.network_id) {
|
||||||
|
networkToChainId[provider.network_id] = provider.chain_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const token of tokens) {
|
for (const token of tokens) {
|
||||||
const rpcUrl = rpcMap[token.network];
|
// Получаем chain_id из названия сети из базы данных
|
||||||
|
const chainId = networkToChainId[token.network];
|
||||||
|
if (!chainId) {
|
||||||
|
logger.warn(`[getUserAccessLevel] Неизвестная сеть: ${token.network}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpcUrl = await rpcService.getRpcUrlByChainId(chainId);
|
||||||
if (!rpcUrl) continue;
|
if (!rpcUrl) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ class DLEV2Service {
|
|||||||
* @param {Array} allDles - Все DLE
|
* @param {Array} allDles - Все DLE
|
||||||
* @returns {Array} - Сгруппированные DLE
|
* @returns {Array} - Сгруппированные DLE
|
||||||
*/
|
*/
|
||||||
groupMultichainDLEs(allDles) {
|
async groupMultichainDLEs(allDles) {
|
||||||
const groups = new Map();
|
const groups = new Map();
|
||||||
|
|
||||||
for (const dle of allDles) {
|
for (const dle of allDles) {
|
||||||
@@ -588,7 +588,7 @@ class DLEV2Service {
|
|||||||
groups.get(groupKey).networks.push({
|
groups.get(groupKey).networks.push({
|
||||||
chainId: dle.chainId,
|
chainId: dle.chainId,
|
||||||
address: dle.address,
|
address: dle.address,
|
||||||
networkName: this.getRpcUrlForChain(dle.chainId)?.name || `Chain ${dle.chainId}`,
|
networkName: (await this.getRpcUrlForChain(dle.chainId))?.name || `Chain ${dle.chainId}`,
|
||||||
status: dle.status || 'active'
|
status: dle.status || 'active'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -610,16 +610,25 @@ class DLEV2Service {
|
|||||||
* @param {number} chainId - ID сети
|
* @param {number} chainId - ID сети
|
||||||
* @returns {Object|null} - Информация о RPC
|
* @returns {Object|null} - Информация о RPC
|
||||||
*/
|
*/
|
||||||
getRpcUrlForChain(chainId) {
|
async getRpcUrlForChain(chainId) {
|
||||||
const rpcMappings = {
|
try {
|
||||||
1: { name: 'Ethereum Mainnet', url: 'https://mainnet.infura.io/v3/' },
|
// Получаем RPC URL из базы данных
|
||||||
11155111: { name: 'Sepolia Testnet', url: 'https://sepolia.infura.io/v3/' },
|
const rpcService = require('./rpcProviderService');
|
||||||
17000: { name: 'Holesky Testnet', url: 'https://holesky.infura.io/v3/' },
|
const rpcUrl = await rpcService.getRpcUrlByChainId(chainId);
|
||||||
421614: { name: 'Arbitrum Sepolia', url: 'https://sepolia-rollup.arbitrum.io/rpc' },
|
|
||||||
84532: { name: 'Base Sepolia', url: 'https://sepolia.base.org' }
|
|
||||||
};
|
|
||||||
|
|
||||||
return rpcMappings[chainId] || null;
|
if (!rpcUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращаем объект с RPC URL из базы данных
|
||||||
|
return {
|
||||||
|
name: `Chain ${chainId}`,
|
||||||
|
url: rpcUrl
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[DLE V2 Service] Ошибка получения RPC для chain_id ${chainId}:`, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -639,7 +648,7 @@ class DLEV2Service {
|
|||||||
throw new Error(`RPC URL не найден для сети ${chainId}`);
|
throw new Error(`RPC URL не найден для сети ${chainId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await getRpcUrlByChainId(chainId));
|
||||||
const balance = await provider.getBalance(wallet.address);
|
const balance = await provider.getBalance(wallet.address);
|
||||||
|
|
||||||
console.log(`💰 Баланс в сети ${chainId}: ${ethers.formatEther(balance)} ETH`);
|
console.log(`💰 Баланс в сети ${chainId}: ${ethers.formatEther(balance)} ETH`);
|
||||||
|
|||||||
@@ -32,26 +32,52 @@ async function getUserTokenBalances(address) {
|
|||||||
);
|
);
|
||||||
const tokens = tokensResult.rows;
|
const tokens = tokensResult.rows;
|
||||||
|
|
||||||
const rpcProvidersResult = await db.getQuery()(
|
// Убрано - используем rpcService вместо прямого запроса к БД
|
||||||
'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',
|
// Используем правильный RPC URL из базы данных
|
||||||
[encryptionKey]
|
const rpcService = require('./rpcProviderService');
|
||||||
);
|
|
||||||
const rpcProviders = rpcProvidersResult.rows;
|
|
||||||
const rpcMap = {};
|
|
||||||
for (const rpc of rpcProviders) {
|
|
||||||
rpcMap[rpc.network_id] = rpc.rpc_url;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ERC20_ABI = ['function balanceOf(address owner) view returns (uint256)'];
|
const ERC20_ABI = ['function balanceOf(address owner) view returns (uint256)'];
|
||||||
const results = [];
|
const results = [];
|
||||||
|
|
||||||
|
// Получаем все RPC провайдеры из базы данных для маппинга
|
||||||
|
const allRpcProviders = await rpcService.getAllRpcProviders();
|
||||||
|
const networkToChainId = {};
|
||||||
|
|
||||||
|
// Создаем маппинг из базы данных
|
||||||
|
for (const provider of allRpcProviders) {
|
||||||
|
if (provider.network_id) {
|
||||||
|
networkToChainId[provider.network_id] = provider.chain_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const token of tokens) {
|
for (const token of tokens) {
|
||||||
const rpcUrl = rpcMap[token.network];
|
// Получаем chain_id из названия сети из базы данных
|
||||||
if (!rpcUrl) {
|
const chainId = networkToChainId[token.network];
|
||||||
logger.warn(`[tokenBalanceService] RPC URL не найден для сети ${token.network}`);
|
if (!chainId) {
|
||||||
|
logger.warn(`[tokenBalanceService] Неизвестная сеть: ${token.network}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info(`[tokenBalanceService] Ищем RPC для token.network: ${token.network} (chainId: ${chainId})`);
|
||||||
|
const rpcUrl = await rpcService.getRpcUrlByChainId(chainId);
|
||||||
|
if (!rpcUrl) {
|
||||||
|
logger.warn(`[tokenBalanceService] RPC URL не найден для сети ${token.network} (chainId: ${chainId})`);
|
||||||
|
// Пропускаем токен, если нет RPC URL
|
||||||
|
results.push({
|
||||||
|
network: token.network,
|
||||||
|
tokenAddress: token.address,
|
||||||
|
tokenName: token.name,
|
||||||
|
symbol: token.symbol || '',
|
||||||
|
balance: '0',
|
||||||
|
minBalance: token.min_balance,
|
||||||
|
readonlyThreshold: token.readonly_threshold || 1,
|
||||||
|
editorThreshold: token.editor_threshold || 2,
|
||||||
|
error: 'RPC URL не настроен'
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logger.info(`[tokenBalanceService] Найден RPC URL: ${rpcUrl}`);
|
||||||
|
|
||||||
// Создаем провайдер с таймаутом
|
// Создаем провайдер с таймаутом
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl, undefined, {
|
const provider = new ethers.JsonRpcProvider(rpcUrl, undefined, {
|
||||||
polling: false,
|
polling: false,
|
||||||
@@ -84,8 +110,38 @@ async function getUserTokenBalances(address) {
|
|||||||
`[tokenBalanceService] Ошибка получения баланса для ${token.name} (${token.address}) в сети ${token.network}:`,
|
`[tokenBalanceService] Ошибка получения баланса для ${token.name} (${token.address}) в сети ${token.network}:`,
|
||||||
e.message || e
|
e.message || e
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Проверяем тип ошибки для лучшей диагностики
|
||||||
|
const errorMessage = e.message || e.toString();
|
||||||
|
let errorType = 'Неизвестная ошибка';
|
||||||
|
|
||||||
|
if (errorMessage.includes('timeout') || errorMessage.includes('TIMEOUT')) {
|
||||||
|
errorType = 'Таймаут соединения - возможно, нужен VPN';
|
||||||
|
} else if (errorMessage.includes('ECONNREFUSED') || errorMessage.includes('ENOTFOUND')) {
|
||||||
|
errorType = 'Не удается подключиться к RPC провайдеру';
|
||||||
|
} else if (errorMessage.includes('NETWORK_ERROR')) {
|
||||||
|
errorType = 'Ошибка сети - проверьте интернет-соединение';
|
||||||
|
}
|
||||||
|
|
||||||
balance = '0';
|
balance = '0';
|
||||||
|
|
||||||
|
// Добавляем информацию об ошибке в результат
|
||||||
|
results.push({
|
||||||
|
network: token.network,
|
||||||
|
tokenAddress: token.address,
|
||||||
|
tokenName: token.name,
|
||||||
|
symbol: token.symbol || '',
|
||||||
|
balance,
|
||||||
|
minBalance: token.min_balance,
|
||||||
|
readonlyThreshold: token.readonly_threshold || 1,
|
||||||
|
editorThreshold: token.editor_threshold || 2,
|
||||||
|
error: errorType,
|
||||||
|
errorDetails: errorMessage
|
||||||
|
});
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Добавляем успешный результат
|
||||||
results.push({
|
results.push({
|
||||||
network: token.network,
|
network: token.network,
|
||||||
tokenAddress: token.address,
|
tokenAddress: token.address,
|
||||||
@@ -98,7 +154,8 @@ async function getUserTokenBalances(address) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
// Преобразуем в обычный массив для корректной сериализации
|
||||||
|
return JSON.parse(JSON.stringify(results));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { getUserTokenBalances };
|
module.exports = { getUserTokenBalances };
|
||||||
|
|||||||
@@ -93,26 +93,26 @@ async function getBalance(provider, address) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Создает RPC соединение с retry логикой
|
* Создает RPC соединение с retry логикой
|
||||||
* @param {string} rpcUrl - URL RPC
|
* @param {number} chainId - ID цепочки
|
||||||
* @param {string} privateKey - Приватный ключ
|
* @param {string} privateKey - Приватный ключ
|
||||||
* @param {Object} options - Опции соединения
|
* @param {Object} options - Опции соединения
|
||||||
* @returns {Promise<Object>} - {provider, wallet, network}
|
* @returns {Promise<Object>} - {provider, wallet, network}
|
||||||
*/
|
*/
|
||||||
async function createRPCConnection(rpcUrl, privateKey, options = {}) {
|
async function createRPCConnection(chainId, privateKey, options = {}) {
|
||||||
const rpcManager = new RPCConnectionManager();
|
const rpcManager = new RPCConnectionManager();
|
||||||
return await rpcManager.createConnection(rpcUrl, privateKey, options);
|
return await rpcManager.createConnection(chainId, privateKey, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Создает множественные RPC соединения с обработкой ошибок
|
* Создает множественные RPC соединения с обработкой ошибок
|
||||||
* @param {Array} rpcUrls - Массив RPC URL
|
* @param {Array} chainIds - Массив chain ID
|
||||||
* @param {string} privateKey - Приватный ключ
|
* @param {string} privateKey - Приватный ключ
|
||||||
* @param {Object} options - Опции соединения
|
* @param {Object} options - Опции соединения
|
||||||
* @returns {Promise<Array>} - Массив успешных соединений
|
* @returns {Promise<Array>} - Массив успешных соединений
|
||||||
*/
|
*/
|
||||||
async function createMultipleRPCConnections(rpcUrls, privateKey, options = {}) {
|
async function createMultipleRPCConnections(chainIds, privateKey, options = {}) {
|
||||||
const rpcManager = new RPCConnectionManager();
|
const rpcManager = new RPCConnectionManager();
|
||||||
return await rpcManager.createMultipleConnections(rpcUrls, privateKey, options);
|
return await rpcManager.createMultipleConnections(chainIds, privateKey, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendTransactionWithRetry функция удалена - используем RPCConnectionManager напрямую
|
// sendTransactionWithRetry функция удалена - используем RPCConnectionManager напрямую
|
||||||
|
|||||||
87
backend/utils/loadRpcFromDatabase.js
Normal file
87
backend/utils/loadRpcFromDatabase.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* Загрузка RPC URL из базы данных и установка в переменные окружения
|
||||||
|
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Убрано - не нужен для загрузки RPC
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Загружает RPC URL из базы данных и устанавливает их в переменные окружения
|
||||||
|
*/
|
||||||
|
async function loadRpcFromDatabase() {
|
||||||
|
try {
|
||||||
|
console.log('[RPC Loader] Загружаем RPC URL из базы данных...');
|
||||||
|
|
||||||
|
// Получаем все RPC провайдеры из базы данных
|
||||||
|
const rpcService = require('../services/rpcProviderService');
|
||||||
|
const providers = await rpcService.getAllRpcProviders();
|
||||||
|
|
||||||
|
if (providers.length === 0) {
|
||||||
|
console.warn('[RPC Loader] В базе данных нет RPC провайдеров');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[RPC Loader] Найдено ${providers.length} RPC провайдеров в базе данных`);
|
||||||
|
|
||||||
|
// Устанавливаем переменные окружения для каждой сети
|
||||||
|
for (const provider of providers) {
|
||||||
|
const chainId = provider.chain_id;
|
||||||
|
const rpcUrl = provider.rpc_url;
|
||||||
|
|
||||||
|
if (!rpcUrl) {
|
||||||
|
console.warn(`[RPC Loader] RPC URL не найден для chain_id ${chainId}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем переменные окружения в зависимости от chain_id
|
||||||
|
switch (chainId) {
|
||||||
|
case 1: // Ethereum Mainnet
|
||||||
|
process.env.MAINNET_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен MAINNET_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
case 11155111: // Sepolia
|
||||||
|
process.env.SEPOLIA_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен SEPOLIA_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
case 17000: // Holesky
|
||||||
|
process.env.HOLESKY_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен HOLESKY_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
case 421614: // Arbitrum Sepolia
|
||||||
|
process.env.ARBITRUM_SEPOLIA_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен ARBITRUM_SEPOLIA_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
case 84532: // Base Sepolia
|
||||||
|
process.env.BASE_SEPOLIA_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен BASE_SEPOLIA_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
case 42161: // Arbitrum One
|
||||||
|
process.env.ARBITRUM_ONE_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен ARBITRUM_ONE_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
case 8453: // Base
|
||||||
|
process.env.BASE_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен BASE_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
case 137: // Polygon
|
||||||
|
process.env.POLYGON_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен POLYGON_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
case 56: // BSC
|
||||||
|
process.env.BSC_RPC_URL = rpcUrl;
|
||||||
|
console.log(`[RPC Loader] Установлен BSC_RPC_URL: ${rpcUrl}`);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log(`[RPC Loader] Неизвестный chain_id ${chainId}, пропускаем`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[RPC Loader] ✅ RPC URL успешно загружены из базы данных');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[RPC Loader] ❌ Ошибка загрузки RPC URL из базы данных:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { loadRpcFromDatabase };
|
||||||
@@ -23,10 +23,20 @@ class NonceManager {
|
|||||||
async getNonce(address, rpcUrl, chainId, options = {}) {
|
async getNonce(address, rpcUrl, chainId, options = {}) {
|
||||||
const { timeout = 15000, maxRetries = 5 } = options; // Увеличиваем таймаут и попытки
|
const { timeout = 15000, maxRetries = 5 } = options; // Увеличиваем таймаут и попытки
|
||||||
|
|
||||||
|
// КРИТИЧЕСКАЯ ПРОВЕРКА: логируем входящие параметры
|
||||||
|
console.log(`[NonceManager] getNonce вызван с параметрами: address=${address}, rpcUrl=${rpcUrl}, chainId=${chainId}`);
|
||||||
|
|
||||||
|
// КРИТИЧЕСКАЯ ПРОВЕРКА: если rpcUrl содержит 127.0.0.1:8545, это ошибка!
|
||||||
|
if (rpcUrl && rpcUrl.includes('127.0.0.1:8545')) {
|
||||||
|
console.error(`[NonceManager] ❌ КРИТИЧЕСКАЯ ОШИБКА: Получен неправильный rpcUrl: ${rpcUrl} для chainId ${chainId}`);
|
||||||
|
throw new Error(`Получен неправильный rpcUrl: ${rpcUrl} для chainId ${chainId}`);
|
||||||
|
}
|
||||||
|
|
||||||
const cacheKey = `${address}-${chainId}`;
|
const cacheKey = `${address}-${chainId}`;
|
||||||
|
|
||||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||||
try {
|
try {
|
||||||
|
console.log(`[NonceManager] Попытка ${attempt}: создаем JsonRpcProvider с rpcUrl: ${rpcUrl}`);
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl, undefined, {
|
const provider = new ethers.JsonRpcProvider(rpcUrl, undefined, {
|
||||||
polling: false, // Отключаем polling для более быстрого получения nonce
|
polling: false, // Отключаем polling для более быстрого получения nonce
|
||||||
staticNetwork: true
|
staticNetwork: true
|
||||||
@@ -204,8 +214,8 @@ class NonceManager {
|
|||||||
console.warn(`[NonceManager] networkLoader недоступен для chainId ${chainId}, используем fallback: ${error.message}`);
|
console.warn(`[NonceManager] networkLoader недоступен для chainId ${chainId}, используем fallback: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Всегда добавляем fallback RPC для надежности
|
// Всегда добавляем fallback RPC для надежности ИЗ БАЗЫ ДАННЫХ
|
||||||
const fallbackRPCs = this.getFallbackRPCs(chainId);
|
const fallbackRPCs = await this.getFallbackRPCs(chainId);
|
||||||
for (const fallbackRpc of fallbackRPCs) {
|
for (const fallbackRpc of fallbackRPCs) {
|
||||||
if (!rpcUrls.includes(fallbackRpc)) {
|
if (!rpcUrls.includes(fallbackRpc)) {
|
||||||
rpcUrls.push(fallbackRpc);
|
rpcUrls.push(fallbackRpc);
|
||||||
@@ -217,34 +227,31 @@ class NonceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Получить список fallback RPC для сети
|
* Получить список fallback RPC для сети ИЗ БАЗЫ ДАННЫХ
|
||||||
* @param {number} chainId - ID сети
|
* @param {number} chainId - ID сети
|
||||||
* @returns {Array} - Массив RPC URL
|
* @returns {Array} - Массив RPC URL из базы данных
|
||||||
*/
|
*/
|
||||||
getFallbackRPCs(chainId) {
|
async getFallbackRPCs(chainId) {
|
||||||
const fallbackRPCs = {
|
try {
|
||||||
1: [ // Mainnet
|
// Получаем ВСЕ RPC провайдеры для данной сети из базы данных
|
||||||
'https://eth.llamarpc.com',
|
const rpcService = require('../services/rpcProviderService');
|
||||||
'https://rpc.ankr.com/eth',
|
const providers = await rpcService.getAllRpcProviders();
|
||||||
'https://ethereum.publicnode.com'
|
|
||||||
],
|
|
||||||
11155111: [ // Sepolia
|
|
||||||
'https://rpc.sepolia.org',
|
|
||||||
process.env.SEPOLIA_INFURA_URL || 'https://sepolia.infura.io/v3/YOUR_INFURA_KEY'
|
|
||||||
],
|
|
||||||
17000: [ // Holesky
|
|
||||||
'https://ethereum-holesky.publicnode.com',
|
|
||||||
process.env.HOLESKY_INFURA_URL || 'https://holesky.infura.io/v3/YOUR_INFURA_KEY'
|
|
||||||
],
|
|
||||||
421614: [ // Arbitrum Sepolia
|
|
||||||
'https://sepolia-rollup.arbitrum.io/rpc'
|
|
||||||
],
|
|
||||||
84532: [ // Base Sepolia
|
|
||||||
'https://sepolia.base.org'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
return fallbackRPCs[chainId] || [];
|
// Фильтруем по chain_id
|
||||||
|
const networkProviders = providers.filter(p => p.chain_id === chainId);
|
||||||
|
|
||||||
|
if (networkProviders.length === 0) {
|
||||||
|
console.warn(`[NonceManager] Нет RPC провайдеров в базе данных для chain_id: ${chainId}`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращаем только RPC URL из базы данных
|
||||||
|
return networkProviders.map(p => p.rpc_url).filter(url => url);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[NonceManager] Ошибка получения RPC из базы данных для chain_id ${chainId}:`, error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -276,7 +283,7 @@ class NonceManager {
|
|||||||
const cacheKey = `${address}-${chainId}`;
|
const cacheKey = `${address}-${chainId}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
const provider = new ethers.JsonRpcProvider(await rpcService.getRpcUrlByChainId(chainId));
|
||||||
const networkNonce = await provider.getTransactionCount(address, 'pending');
|
const networkNonce = await provider.getTransactionCount(address, 'pending');
|
||||||
|
|
||||||
// Принудительно обновляем кэш
|
// Принудительно обновляем кэш
|
||||||
|
|||||||
105
backend/utils/proxyManager.js
Normal file
105
backend/utils/proxyManager.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* Менеджер прокси настроек для RPC провайдеров
|
||||||
|
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||||
|
*/
|
||||||
|
|
||||||
|
const rpcProviderService = require('../services/rpcProviderService');
|
||||||
|
|
||||||
|
class ProxyManager {
|
||||||
|
constructor() {
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация менеджера прокси
|
||||||
|
*/
|
||||||
|
async initialize() {
|
||||||
|
if (this.initialized) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.configureNoProxyFromRpcProviders();
|
||||||
|
this.initialized = true;
|
||||||
|
console.log('[ProxyManager] ✅ Инициализация завершена');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[ProxyManager] ❌ Ошибка инициализации:', error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Настройка NO_PROXY на основе RPC провайдеров из базы данных
|
||||||
|
*/
|
||||||
|
async configureNoProxyFromRpcProviders() {
|
||||||
|
try {
|
||||||
|
const providers = await rpcProviderService.getAllRpcProviders();
|
||||||
|
|
||||||
|
const rpcDomains = providers
|
||||||
|
.map(provider => provider.rpc_url)
|
||||||
|
.filter(url => url && url.startsWith('http'))
|
||||||
|
.map(url => {
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
return urlObj.hostname;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`[ProxyManager] Неверный URL: ${url}`, e.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(hostname => hostname)
|
||||||
|
.filter((hostname, index, array) => array.indexOf(hostname) === index); // убираем дубликаты
|
||||||
|
|
||||||
|
if (rpcDomains.length > 0) {
|
||||||
|
const existingNoProxy = process.env.NO_PROXY || '';
|
||||||
|
|
||||||
|
// Добавляем RPC домены к существующему NO_PROXY
|
||||||
|
const newDomains = rpcDomains.filter(domain => !existingNoProxy.includes(domain));
|
||||||
|
|
||||||
|
if (newDomains.length > 0) {
|
||||||
|
process.env.NO_PROXY = existingNoProxy ? `${existingNoProxy},${newDomains.join(',')}` : newDomains.join(',');
|
||||||
|
console.log('[ProxyManager] ✅ Добавлены RPC домены в NO_PROXY:', newDomains.join(', '));
|
||||||
|
console.log('[ProxyManager] 📋 Обновленный NO_PROXY:', process.env.NO_PROXY);
|
||||||
|
} else {
|
||||||
|
console.log('[ProxyManager] ℹ️ Все RPC домены уже в NO_PROXY');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('[ProxyManager] ⚠️ Не найдено RPC провайдеров для настройки NO_PROXY');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[ProxyManager] ❌ Не удалось загрузить RPC провайдеры для NO_PROXY:', error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверить текущие настройки прокси
|
||||||
|
*/
|
||||||
|
getProxyStatus() {
|
||||||
|
return {
|
||||||
|
httpProxy: process.env.HTTP_PROXY || null,
|
||||||
|
httpsProxy: process.env.HTTPS_PROXY || null,
|
||||||
|
noProxy: process.env.NO_PROXY || null,
|
||||||
|
initialized: this.initialized
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Принудительно обновить настройки NO_PROXY
|
||||||
|
*/
|
||||||
|
async refresh() {
|
||||||
|
this.initialized = false;
|
||||||
|
await this.initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем singleton
|
||||||
|
const proxyManager = new ProxyManager();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
ProxyManager,
|
||||||
|
proxyManager,
|
||||||
|
// Экспортируем методы для удобства
|
||||||
|
initialize: () => proxyManager.initialize(),
|
||||||
|
configureNoProxyFromRpcProviders: () => proxyManager.configureNoProxyFromRpcProviders(),
|
||||||
|
getProxyStatus: () => proxyManager.getProxyStatus(),
|
||||||
|
refresh: () => proxyManager.refresh()
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
const { ethers } = require('ethers');
|
const { ethers } = require('ethers');
|
||||||
const logger = require('./logger');
|
const logger = require('./logger');
|
||||||
|
const rpcService = require('../services/rpcProviderService');
|
||||||
|
|
||||||
class RPCConnectionManager {
|
class RPCConnectionManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -19,13 +20,26 @@ class RPCConnectionManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Создает RPC соединение с retry логикой
|
* Создает RPC соединение с retry логикой
|
||||||
* @param {string} rpcUrl - URL RPC
|
* @param {number} chainId - ID цепочки
|
||||||
* @param {string} privateKey - Приватный ключ
|
* @param {string} privateKey - Приватный ключ
|
||||||
* @param {Object} options - Опции соединения
|
* @param {Object} options - Опции соединения
|
||||||
* @returns {Promise<Object>} - {provider, wallet, network}
|
* @returns {Promise<Object>} - {provider, wallet, network}
|
||||||
*/
|
*/
|
||||||
async createConnection(rpcUrl, privateKey, options = {}) {
|
async createConnection(chainId, privateKey, options = {}) {
|
||||||
const config = { ...this.retryConfig, ...options };
|
const config = { ...this.retryConfig, ...options };
|
||||||
|
const rpcUrl = await rpcService.getRpcUrlByChainId(chainId);
|
||||||
|
logger.info(`[RPC_MANAGER] Получен RPC URL для chainId ${chainId}: ${rpcUrl}`);
|
||||||
|
|
||||||
|
// КРИТИЧЕСКАЯ ПРОВЕРКА: если rpcUrl содержит 127.0.0.1:8545, это ошибка!
|
||||||
|
if (rpcUrl && rpcUrl.includes('127.0.0.1:8545')) {
|
||||||
|
logger.error(`[RPC_MANAGER] ❌ КРИТИЧЕСКАЯ ОШИБКА: Получен неправильный RPC URL: ${rpcUrl} для chainId ${chainId}`);
|
||||||
|
throw new Error(`Получен неправильный RPC URL: ${rpcUrl} для chainId ${chainId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rpcUrl) {
|
||||||
|
throw new Error(`RPC URL не найден для chainId ${chainId}`);
|
||||||
|
}
|
||||||
|
|
||||||
const connectionKey = `${rpcUrl}_${privateKey}`;
|
const connectionKey = `${rpcUrl}_${privateKey}`;
|
||||||
|
|
||||||
// Проверяем кэш
|
// Проверяем кэш
|
||||||
@@ -33,7 +47,8 @@ class RPCConnectionManager {
|
|||||||
const cached = this.connections.get(connectionKey);
|
const cached = this.connections.get(connectionKey);
|
||||||
if (Date.now() - cached.timestamp < 60000) { // 1 минута кэш
|
if (Date.now() - cached.timestamp < 60000) { // 1 минута кэш
|
||||||
logger.info(`[RPC_MANAGER] Используем кэшированное соединение: ${rpcUrl}`);
|
logger.info(`[RPC_MANAGER] Используем кэшированное соединение: ${rpcUrl}`);
|
||||||
return cached.connection;
|
// Убеждаемся, что кэшированное соединение содержит rpcUrl
|
||||||
|
return { ...cached.connection, rpcUrl };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +71,7 @@ class RPCConnectionManager {
|
|||||||
|
|
||||||
const wallet = new ethers.Wallet(privateKey, provider);
|
const wallet = new ethers.Wallet(privateKey, provider);
|
||||||
|
|
||||||
const connection = { provider, wallet, network };
|
const connection = { provider, wallet, network, rpcUrl };
|
||||||
|
|
||||||
// Кэшируем соединение
|
// Кэшируем соединение
|
||||||
this.connections.set(connectionKey, {
|
this.connections.set(connectionKey, {
|
||||||
@@ -84,21 +99,21 @@ class RPCConnectionManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Создает множественные RPC соединения с обработкой ошибок
|
* Создает множественные RPC соединения с обработкой ошибок
|
||||||
* @param {Array} rpcUrls - Массив RPC URL
|
* @param {Array} chainIds - Массив chain ID
|
||||||
* @param {string} privateKey - Приватный ключ
|
* @param {string} privateKey - Приватный ключ
|
||||||
* @param {Object} options - Опции соединения
|
* @param {Object} options - Опции соединения
|
||||||
* @returns {Promise<Array>} - Массив успешных соединений
|
* @returns {Promise<Array>} - Массив успешных соединений
|
||||||
*/
|
*/
|
||||||
async createMultipleConnections(rpcUrls, privateKey, options = {}) {
|
async createMultipleConnections(chainIds, privateKey, options = {}) {
|
||||||
logger.info(`[RPC_MANAGER] Создаем ${rpcUrls.length} RPC соединений...`);
|
logger.info(`[RPC_MANAGER] Создаем ${chainIds.length} RPC соединений...`);
|
||||||
|
|
||||||
const connectionPromises = rpcUrls.map(async (rpcUrl, index) => {
|
const connectionPromises = chainIds.map(async (chainId, index) => {
|
||||||
try {
|
try {
|
||||||
const connection = await this.createConnection(rpcUrl, privateKey, options);
|
const connection = await this.createConnection(chainId, privateKey, options);
|
||||||
return { index, rpcUrl, ...connection, success: true };
|
return { index, chainId, ...connection, success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[RPC_MANAGER] ❌ Соединение ${index + 1} failed: ${rpcUrl} - ${error.message}`);
|
logger.error(`[RPC_MANAGER] ❌ Соединение ${index + 1} failed: chainId ${chainId} - ${error.message}`);
|
||||||
return { index, rpcUrl, error: error.message, success: false };
|
return { index, chainId, error: error.message, success: false };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,10 +121,10 @@ class RPCConnectionManager {
|
|||||||
const successful = results.filter(r => r.success);
|
const successful = results.filter(r => r.success);
|
||||||
const failed = results.filter(r => !r.success);
|
const failed = results.filter(r => !r.success);
|
||||||
|
|
||||||
logger.info(`[RPC_MANAGER] ✅ Успешных соединений: ${successful.length}/${rpcUrls.length}`);
|
logger.info(`[RPC_MANAGER] ✅ Успешных соединений: ${successful.length}/${chainIds.length}`);
|
||||||
if (failed.length > 0) {
|
if (failed.length > 0) {
|
||||||
logger.warn(`[RPC_MANAGER] ⚠️ Неудачных соединений: ${failed.length}`);
|
logger.warn(`[RPC_MANAGER] ⚠️ Неудачных соединений: ${failed.length}`);
|
||||||
failed.forEach(f => logger.warn(`[RPC_MANAGER] - ${f.rpcUrl}: ${f.error}`));
|
failed.forEach(f => logger.warn(`[RPC_MANAGER] - ChainId ${f.chainId}: ${f.error}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (successful.length === 0) {
|
if (successful.length === 0) {
|
||||||
@@ -183,13 +198,25 @@ class RPCConnectionManager {
|
|||||||
'ENOTFOUND',
|
'ENOTFOUND',
|
||||||
'ETIMEDOUT',
|
'ETIMEDOUT',
|
||||||
'RPC timeout',
|
'RPC timeout',
|
||||||
'Transaction timeout'
|
'Transaction timeout',
|
||||||
|
'ECONNREFUSED',
|
||||||
|
'ENETUNREACH',
|
||||||
|
'EHOSTUNREACH'
|
||||||
];
|
];
|
||||||
|
|
||||||
const errorMessage = error.message.toLowerCase();
|
const errorMessage = error.message.toLowerCase();
|
||||||
return retryableErrors.some(retryableError =>
|
const isRetryable = retryableErrors.some(retryableError =>
|
||||||
errorMessage.includes(retryableError.toLowerCase())
|
errorMessage.includes(retryableError.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Логируем информацию об ошибке для диагностики
|
||||||
|
if (isRetryable) {
|
||||||
|
logger.warn(`[RPC_MANAGER] Повторяемая ошибка: ${error.message}`);
|
||||||
|
} else {
|
||||||
|
logger.error(`[RPC_MANAGER] Неповторяемая ошибка: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isRetryable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNonceWithRetry функция удалена - используем nonceManager.getNonceWithRetry() вместо этого
|
// getNonceWithRetry функция удалена - используем nonceManager.getNonceWithRetry() вместо этого
|
||||||
|
|||||||
@@ -554,34 +554,56 @@ module.exports = {
|
|||||||
// Обработчик запроса балансов токенов
|
// Обработчик запроса балансов токенов
|
||||||
async function handleTokenBalancesRequest(ws, address, userId) {
|
async function handleTokenBalancesRequest(ws, address, userId) {
|
||||||
try {
|
try {
|
||||||
// console.log(`[WebSocket] Запрос балансов для адреса: ${address}`); // Убрано избыточное логирование
|
console.log(`[WebSocket] Запрос балансов для адреса: ${address}`);
|
||||||
|
|
||||||
// Получаем балансы через отдельный сервис без зависимостей от wsHub
|
// Получаем балансы через отдельный сервис без зависимостей от wsHub
|
||||||
const balances = await tokenBalanceService.getUserTokenBalances(address);
|
const balances = await tokenBalanceService.getUserTokenBalances(address);
|
||||||
|
|
||||||
|
console.log(`[WebSocket] Получены балансы для ${address}:`, balances);
|
||||||
|
console.log(`[WebSocket] Количество токенов:`, balances?.length || 0);
|
||||||
|
|
||||||
// Отправляем ответ клиенту
|
// Отправляем ответ клиенту
|
||||||
ws.send(JSON.stringify({
|
const response = {
|
||||||
type: 'token_balances_response',
|
type: 'token_balances_response',
|
||||||
data: {
|
data: {
|
||||||
address: address,
|
address: address,
|
||||||
balances: balances,
|
balances: balances,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
}
|
}
|
||||||
}));
|
};
|
||||||
|
|
||||||
// console.log(`[WebSocket] Отправлены балансы для ${address}:`, balances.length, 'токенов'); // Убрано избыточное логирование
|
console.log(`[WebSocket] Отправляем ответ:`, JSON.stringify(response, null, 2));
|
||||||
|
ws.send(JSON.stringify(response));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[WebSocket] Ошибка при получении балансов:', error);
|
console.error('[WebSocket] Ошибка при получении балансов:', error);
|
||||||
|
|
||||||
|
// Определяем тип ошибки для лучшей диагностики
|
||||||
|
let errorType = 'Неизвестная ошибка';
|
||||||
|
const errorMessage = error.message || error.toString();
|
||||||
|
|
||||||
|
if (errorMessage.includes('timeout') || errorMessage.includes('TIMEOUT')) {
|
||||||
|
errorType = 'Таймаут соединения - возможно, нужен VPN';
|
||||||
|
} else if (errorMessage.includes('ECONNREFUSED') || errorMessage.includes('ENOTFOUND')) {
|
||||||
|
errorType = 'Не удается подключиться к RPC провайдеру';
|
||||||
|
} else if (errorMessage.includes('TLS') || errorMessage.includes('socket disconnected')) {
|
||||||
|
errorType = 'Проблема с TLS соединением - проверьте VPN';
|
||||||
|
} else if (errorMessage.includes('NETWORK_ERROR')) {
|
||||||
|
errorType = 'Ошибка сети - проверьте интернет-соединение';
|
||||||
|
}
|
||||||
|
|
||||||
// Отправляем ошибку клиенту
|
// Отправляем ошибку клиенту
|
||||||
ws.send(JSON.stringify({
|
const errorResponse = {
|
||||||
type: 'token_balances_error',
|
type: 'token_balances_error',
|
||||||
data: {
|
data: {
|
||||||
address: address,
|
address: address,
|
||||||
error: error.message,
|
error: errorType,
|
||||||
|
errorDetails: errorMessage,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
}
|
}
|
||||||
}));
|
};
|
||||||
|
|
||||||
|
console.log('[WebSocket] Отправляем ошибку клиенту:', JSON.stringify(errorResponse, null, 2));
|
||||||
|
ws.send(JSON.stringify(errorResponse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,10 +121,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
|
||||||
<div v-for="(token, index) in tokenBalances" :key="token.tokenAddress ? token.tokenAddress : 'token-' + index" class="token-balance-row">
|
<div v-for="(token, index) in tokenBalances" :key="token.tokenAddress ? token.tokenAddress : 'token-' + index" class="token-balance-row" :class="{ 'token-error': token.error }">
|
||||||
<span class="token-name">{{ token.tokenName }}</span>
|
<span class="token-name">{{ token.tokenName }}</span>
|
||||||
<span class="token-network">{{ token.network }}</span>
|
<span class="token-network">{{ token.network }}</span>
|
||||||
<span class="token-amount">{{ isNaN(Number(token.balance)) ? '—' : Number(token.balance).toLocaleString() }}</span>
|
<span v-if="token.error" class="token-error-message" :title="token.errorDetails">
|
||||||
|
❌ {{ token.error }}
|
||||||
|
</span>
|
||||||
|
<span v-else class="token-amount">{{ isNaN(Number(token.balance)) ? '—' : Number(token.balance).toLocaleString() }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -456,6 +459,22 @@ h3 {
|
|||||||
font-size: var(--font-size-sm);
|
font-size: var(--font-size-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Стили для ошибок токенов */
|
||||||
|
.token-balance-row.token-error {
|
||||||
|
background-color: rgba(255, 0, 0, 0.1);
|
||||||
|
border: 1px solid rgba(255, 0, 0, 0.3);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
padding: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-error-message {
|
||||||
|
color: var(--color-danger);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
font-weight: bold;
|
||||||
|
flex: 1;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
/* Медиа-запросы для адаптивности */
|
/* Медиа-запросы для адаптивности */
|
||||||
@media screen and (min-width: 1200px) {
|
@media screen and (min-width: 1200px) {
|
||||||
.wallet-sidebar {
|
.wallet-sidebar {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
import { ref, onMounted, onUnmounted, provide, inject } from 'vue';
|
import { ref, onMounted, onUnmounted, provide, inject } from 'vue';
|
||||||
import axios from '../api/axios';
|
import axios from '../api/axios';
|
||||||
|
import eventBus from '../utils/eventBus';
|
||||||
|
|
||||||
// === SINGLETON STATE ===
|
// === SINGLETON STATE ===
|
||||||
const isAuthenticated = ref(false);
|
const isAuthenticated = ref(false);
|
||||||
@@ -243,15 +244,14 @@ const updateAuth = async ({
|
|||||||
window.dispatchEvent(new CustomEvent('refresh-application-data'));
|
window.dispatchEvent(new CustomEvent('refresh-application-data'));
|
||||||
}
|
}
|
||||||
|
|
||||||
window.dispatchEvent(new CustomEvent('auth-state-changed', {
|
// Отправляем событие через eventBus (централизованный подход)
|
||||||
detail: {
|
eventBus.emit('auth-state-changed', {
|
||||||
authenticated: isAuthenticated.value,
|
authenticated: isAuthenticated.value,
|
||||||
authType: authType.value,
|
authType: authType.value,
|
||||||
userId: userId.value,
|
userId: userId.value,
|
||||||
address: address.value,
|
address: address.value,
|
||||||
userAccessLevel: userAccessLevel.value
|
userAccessLevel: userAccessLevel.value
|
||||||
}
|
});
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если пользователь только что аутентифицировался или сменил аккаунт,
|
// Если пользователь только что аутентифицировался или сменил аккаунт,
|
||||||
|
|||||||
@@ -51,9 +51,24 @@ export function useTokenBalancesWebSocket() {
|
|||||||
|
|
||||||
// Обработчик ошибки
|
// Обработчик ошибки
|
||||||
const handleTokenBalancesError = (data) => {
|
const handleTokenBalancesError = (data) => {
|
||||||
console.error('[useTokenBalancesWebSocket] Ошибка получения балансов:', data.error);
|
console.error('[useTokenBalancesWebSocket] Ошибка получения балансов:', data);
|
||||||
isLoadingTokens.value = false;
|
isLoadingTokens.value = false;
|
||||||
tokenBalances.value = [];
|
|
||||||
|
// Создаем объект с информацией об ошибке для отображения пользователю
|
||||||
|
const errorInfo = {
|
||||||
|
network: 'unknown',
|
||||||
|
tokenAddress: 'error',
|
||||||
|
tokenName: 'Ошибка получения балансов',
|
||||||
|
symbol: 'ERROR',
|
||||||
|
balance: '0',
|
||||||
|
minBalance: '0',
|
||||||
|
readonlyThreshold: 1,
|
||||||
|
editorThreshold: 1,
|
||||||
|
error: data.error || 'Неизвестная ошибка',
|
||||||
|
errorDetails: data.errorDetails || data.error
|
||||||
|
};
|
||||||
|
|
||||||
|
tokenBalances.value = [errorInfo];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Обработчик обновления балансов
|
// Обработчик обновления балансов
|
||||||
|
|||||||
@@ -906,6 +906,7 @@ import { useAuthContext } from '@/composables/useAuth';
|
|||||||
import { usePermissions } from '@/composables/usePermissions';
|
import { usePermissions } from '@/composables/usePermissions';
|
||||||
import api from '@/api/axios';
|
import api from '@/api/axios';
|
||||||
import DeploymentWizard from '@/components/deployment/DeploymentWizard.vue';
|
import DeploymentWizard from '@/components/deployment/DeploymentWizard.vue';
|
||||||
|
import eventBus from '@/utils/eventBus';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
// Нормализация приватного ключа: убираем пробелы/"0x", посторонние символы,
|
// Нормализация приватного ключа: убираем пробелы/"0x", посторонние символы,
|
||||||
@@ -924,19 +925,10 @@ function normalizePrivateKey(raw) {
|
|||||||
const { address } = useAuthContext();
|
const { address } = useAuthContext();
|
||||||
const { canManageSettings } = usePermissions();
|
const { canManageSettings } = usePermissions();
|
||||||
|
|
||||||
// Подписываемся на централизованные события очистки и обновления данных
|
// Обработчики событий будут определены после функций clearAllData и resetUIState
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener('clear-application-data', () => {
|
|
||||||
console.log('[DleDeployFormView] Clearing DLE deploy data');
|
|
||||||
// Очищаем данные при выходе из системы
|
|
||||||
// DleDeployFormView не нуждается в очистке данных
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('refresh-application-data', () => {
|
// Подписка на события авторизации (как в других файлах проекта)
|
||||||
console.log('[DleDeployFormView] Refreshing DLE deploy data');
|
let unsubscribe = null;
|
||||||
checkAdminTokens(); // Обновляем данные при входе в систему
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Состояние для проверки админских токенов
|
// Состояние для проверки админских токенов
|
||||||
const adminTokenCheck = ref({
|
const adminTokenCheck = ref({
|
||||||
@@ -945,6 +937,28 @@ const adminTokenCheck = ref({
|
|||||||
error: null
|
error: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Обработка события изменения авторизации
|
||||||
|
const handleAuthEvent = (eventData) => {
|
||||||
|
console.log('[DleDeployFormView] Получено событие изменения авторизации:', eventData);
|
||||||
|
|
||||||
|
// Если пользователь отключился, сбрасываем все данные формы
|
||||||
|
if (!eventData.authenticated) {
|
||||||
|
console.log('[DleDeployFormView] User disconnected, clearing form data');
|
||||||
|
clearAllData();
|
||||||
|
resetUIState();
|
||||||
|
} else {
|
||||||
|
// При подключении обновляем проверку токенов
|
||||||
|
checkAdminTokens();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Watcher для отслеживания изменений в правах доступа
|
||||||
|
watch(canManageSettings, (newValue, oldValue) => {
|
||||||
|
console.log('[DleDeployFormView] canManageSettings changed:', { oldValue, newValue });
|
||||||
|
// При изменении прав обновляем локальное состояние
|
||||||
|
adminTokenCheck.value.canManageSettings = newValue;
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
// Основные настройки DLE
|
// Основные настройки DLE
|
||||||
const dleSettings = reactive({
|
const dleSettings = reactive({
|
||||||
// Юрисдикция
|
// Юрисдикция
|
||||||
@@ -1573,12 +1587,17 @@ const clearAllData = () => {
|
|||||||
// Очищаем также поиск адресов и флаги автовыбора
|
// Очищаем также поиск адресов и флаги автовыбора
|
||||||
postalCodeInput.value = '';
|
postalCodeInput.value = '';
|
||||||
searchResults.value = [];
|
searchResults.value = [];
|
||||||
|
isSearchingAddress.value = false;
|
||||||
autoSelectedOktmo.value = false;
|
autoSelectedOktmo.value = false;
|
||||||
lastApiResult.value = null;
|
lastApiResult.value = null;
|
||||||
|
|
||||||
// Сбрасываем выбранные уровни ОКВЭД
|
// Сбрасываем выбранные уровни ОКВЭД
|
||||||
selectedOkvedLevel1.value = '';
|
selectedOkvedLevel1.value = '';
|
||||||
selectedOkvedLevel2.value = '';
|
selectedOkvedLevel2.value = '';
|
||||||
|
selectedOkvedLevel3.value = '';
|
||||||
|
selectedOkvedLevel4.value = '';
|
||||||
|
currentSelectedOkvedCode.value = '';
|
||||||
|
currentSelectedOkvedText.value = '';
|
||||||
|
|
||||||
// Очищаем мульти-чейн состояние
|
// Очищаем мульти-чейн состояние
|
||||||
selectedNetworks.value = [];
|
selectedNetworks.value = [];
|
||||||
@@ -1591,10 +1610,79 @@ const clearAllData = () => {
|
|||||||
Object.keys(keyValidation).forEach(key => delete keyValidation[key]);
|
Object.keys(keyValidation).forEach(key => delete keyValidation[key]);
|
||||||
showUnifiedKey.value = false;
|
showUnifiedKey.value = false;
|
||||||
|
|
||||||
|
// Очищаем настройки деплоя
|
||||||
|
etherscanApiKey.value = '';
|
||||||
|
unifiedScanKeyVisible.value = false;
|
||||||
|
autoVerifyAfterDeploy.value = true;
|
||||||
|
showDeploymentWizard.value = false;
|
||||||
|
deployedDLEAddress.value = '';
|
||||||
|
|
||||||
// Очищаем localStorage
|
// Очищаем localStorage
|
||||||
clearStoredData();
|
clearStoredData();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Сброс состояния UI компонентов
|
||||||
|
const resetUIState = () => {
|
||||||
|
// Сбрасываем состояние загрузки
|
||||||
|
isLoadingCountries.value = false;
|
||||||
|
isLoadingRussianClassifiers.value = false;
|
||||||
|
isLoadingNetworks.value = false;
|
||||||
|
isLoadingOkvedLevel1.value = false;
|
||||||
|
isLoadingOkvedLevel2.value = false;
|
||||||
|
isLoadingOkvedLevel3.value = false;
|
||||||
|
isLoadingOkvedLevel4.value = false;
|
||||||
|
isLoadingKppCodes.value = false;
|
||||||
|
|
||||||
|
// Сбрасываем состояние админских токенов
|
||||||
|
adminTokenCheck.value = {
|
||||||
|
isLoading: false,
|
||||||
|
canManageSettings: false,
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Очищаем файл логотипа
|
||||||
|
logoFile.value = null;
|
||||||
|
logoPreviewUrl.value = '';
|
||||||
|
ensDomain.value = '';
|
||||||
|
ensResolvedUrl.value = '';
|
||||||
|
|
||||||
|
// Сбрасываем состояние видимости ключей
|
||||||
|
showPrivateKey.value = false;
|
||||||
|
showUnifiedKey.value = false;
|
||||||
|
|
||||||
|
console.log('[DleDeployFormView] UI state reset completed');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Обработчики событий для очистки и обновления данных
|
||||||
|
const handleClearApplicationData = () => {
|
||||||
|
console.log('[DleDeployFormView] Clearing DLE deploy data');
|
||||||
|
// Очищаем все данные формы при выходе из системы
|
||||||
|
clearAllData();
|
||||||
|
// Сбрасываем состояние UI
|
||||||
|
resetUIState();
|
||||||
|
};
|
||||||
|
|
||||||
|
// handleRefreshApplicationData будет определен после checkAdminTokens
|
||||||
|
|
||||||
|
// Подписываемся на централизованные события очистки и обновления данных
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('clear-application-data', handleClearApplicationData);
|
||||||
|
window.addEventListener('refresh-application-data', handleRefreshApplicationData);
|
||||||
|
// Подписка на события авторизации
|
||||||
|
unsubscribe = eventBus.on('auth-state-changed', handleAuthEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
// Отписка от события при удалении компонента
|
||||||
|
if (unsubscribe) {
|
||||||
|
unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем слушатели событий window
|
||||||
|
window.removeEventListener('clear-application-data', handleClearApplicationData);
|
||||||
|
window.removeEventListener('refresh-application-data', handleRefreshApplicationData);
|
||||||
|
});
|
||||||
|
|
||||||
// (Старые функции ОКВЭД удалены - заменены каскадной системой)
|
// (Старые функции ОКВЭД удалены - заменены каскадной системой)
|
||||||
|
|
||||||
// Поиск по почтовому индексу (по кнопке)
|
// Поиск по почтовому индексу (по кнопке)
|
||||||
@@ -2382,7 +2470,12 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Watcher для автоматического обновления адреса первого партнера при подключении кошелька
|
// Watcher для автоматического обновления адреса первого партнера при подключении кошелька
|
||||||
watch(address, (newAddress) => {
|
watch(address, (newAddress, oldAddress) => {
|
||||||
|
console.log('[DleDeployFormView] Address changed:', { oldAddress, newAddress });
|
||||||
|
|
||||||
|
// Обновляем состояние при изменении адреса (подключение/отключение кошелька)
|
||||||
|
checkAdminTokens();
|
||||||
|
|
||||||
if (newAddress && dleSettings.partners[0]) {
|
if (newAddress && dleSettings.partners[0]) {
|
||||||
// Подставляем адрес, если поле пустое или пользователь только что подключил кошелек
|
// Подставляем адрес, если поле пустое или пользователь только что подключил кошелек
|
||||||
if (!dleSettings.partners[0].address) {
|
if (!dleSettings.partners[0].address) {
|
||||||
@@ -2394,8 +2487,16 @@ watch(address, (newAddress) => {
|
|||||||
|
|
||||||
// Функция проверки админских токенов
|
// Функция проверки админских токенов
|
||||||
const checkAdminTokens = async () => {
|
const checkAdminTokens = async () => {
|
||||||
|
console.log('[DleDeployFormView] checkAdminTokens called, address:', address.value);
|
||||||
|
|
||||||
|
// Небольшая задержка чтобы дать время useAuth обновить состояние
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
|
||||||
|
// Обновляем локальное состояние на основе текущих прав из usePermissions
|
||||||
|
adminTokenCheck.value.canManageSettings = canManageSettings.value;
|
||||||
|
|
||||||
if (!address.value) {
|
if (!address.value) {
|
||||||
adminTokenCheck.value = { isLoading: false, canManageSettings: false, error: 'Кошелек не подключен' };
|
adminTokenCheck.value = { ...adminTokenCheck.value, isLoading: false, error: 'Кошелек не подключен' };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2405,8 +2506,8 @@ const checkAdminTokens = async () => {
|
|||||||
const response = await api.get(`/dle-v2/check-admin-tokens?address=${address.value}`);
|
const response = await api.get(`/dle-v2/check-admin-tokens?address=${address.value}`);
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
adminTokenCheck.value = { ...adminTokenCheck.value, canManageSettings: response.data.data.userAccessLevel.hasAccess };
|
|
||||||
console.log('Проверка админских токенов:', response.data.data);
|
console.log('Проверка админских токенов:', response.data.data);
|
||||||
|
// Не перезаписываем canManageSettings, так как это управляется usePermissions
|
||||||
} else {
|
} else {
|
||||||
adminTokenCheck.value = { ...adminTokenCheck.value, error: response.data.message || 'Ошибка проверки токенов' };
|
adminTokenCheck.value = { ...adminTokenCheck.value, error: response.data.message || 'Ошибка проверки токенов' };
|
||||||
}
|
}
|
||||||
@@ -2418,6 +2519,12 @@ const checkAdminTokens = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Определяем handleRefreshApplicationData после checkAdminTokens
|
||||||
|
const handleRefreshApplicationData = () => {
|
||||||
|
console.log('[DleDeployFormView] Refreshing DLE deploy data');
|
||||||
|
checkAdminTokens(); // Обновляем данные при входе в систему
|
||||||
|
};
|
||||||
|
|
||||||
// Функции для работы с партнерами
|
// Функции для работы с партнерами
|
||||||
const addPartner = () => {
|
const addPartner = () => {
|
||||||
dleSettings.partners.push({ address: '', amount: 1 });
|
dleSettings.partners.push({ address: '', amount: 1 });
|
||||||
|
|||||||
Reference in New Issue
Block a user