ваше сообщение коммита

This commit is contained in:
2025-09-30 13:17:39 +03:00
parent 084075bf02
commit d344448c40
19 changed files with 772 additions and 928 deletions

View File

@@ -17,9 +17,9 @@ const { ethers } = require('ethers');
const logger = require('../utils/logger');
const { getRpcUrlByChainId } = require('./rpcProviderService');
const deploymentTracker = require('../utils/deploymentTracker');
const etherscanV2 = require('./etherscanV2VerificationService');
// ContractVerificationService удален - используем Hardhat verify
const DeployParamsService = require('./deployParamsService');
const verificationStore = require('./verificationStore');
// verificationStore удален - используем Hardhat verify
/**
* Сервис для управления DLE v2 (Digital Legal Entity)

View File

@@ -1,89 +0,0 @@
/**
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
* All rights reserved.
*
* This software is proprietary and confidential.
* Unauthorized copying, modification, or distribution is prohibited.
*
* For licensing inquiries: info@hb3-accelerator.com
* Website: https://hb3-accelerator.com
* GitHub: https://github.com/HB3-ACCELERATOR
*/
const axios = require('axios');
const logger = require('../utils/logger');
const ETHERSCAN_V2_ENDPOINT = 'https://api.etherscan.io/v2/api';
class EtherscanV2VerificationService {
/**
* Отправить исходники контракта на верификацию (V2)
* Документация: https://docs.etherscan.io/etherscan-v2/contract-verification/multichain-verification
* @param {Object} opts
* @param {number} opts.chainId
* @param {string} opts.contractAddress
* @param {string} opts.contractName - формат "contracts/DLE.sol:DLE"
* @param {string} opts.compilerVersion - например, "v0.8.24+commit.e11b9ed9"
* @param {Object|string} opts.standardJsonInput - стандартный JSON input (рекомендуется)
* @param {string} [opts.constructorArgsHex]
* @param {string} [opts.apiKey]
* @returns {Promise<string>} guid
*/
async submitVerification({ chainId, contractAddress, contractName, compilerVersion, standardJsonInput, constructorArgsHex, apiKey }) {
const key = apiKey || process.env.ETHERSCAN_API_KEY;
if (!key) throw new Error('ETHERSCAN_API_KEY не задан');
if (!chainId) throw new Error('chainId обязателен');
if (!contractAddress) throw new Error('contractAddress обязателен');
if (!contractName) throw new Error('contractName обязателен');
if (!compilerVersion) throw new Error('compilerVersion обязателен');
if (!standardJsonInput) throw new Error('standardJsonInput обязателен');
const payload = new URLSearchParams();
// Согласно V2, chainid должен передаваться в query, а не в теле формы
payload.set('module', 'contract');
payload.set('action', 'verifysourcecode');
payload.set('apikey', key);
payload.set('codeformat', 'solidity-standard-json-input');
payload.set('sourceCode', typeof standardJsonInput === 'string' ? standardJsonInput : JSON.stringify(standardJsonInput));
payload.set('contractaddress', contractAddress);
payload.set('contractname', contractName);
payload.set('compilerversion', compilerVersion);
if (constructorArgsHex) {
const no0x = constructorArgsHex.startsWith('0x') ? constructorArgsHex.slice(2) : constructorArgsHex;
payload.set('constructorArguments', no0x);
}
const url = `${ETHERSCAN_V2_ENDPOINT}?chainid=${encodeURIComponent(String(chainId))}`;
const { data } = await axios.post(url, payload.toString(), {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
logger.info('[EtherscanV2] verifysourcecode response', data);
if (data && data.status === '1' && data.result) return data.result; // guid
throw new Error(data?.result || data?.message || 'Etherscan V2 verifysourcecode error');
}
/**
* Проверить статус верификации по guid
* @param {number} chainId
* @param {string} guid
* @param {string} [apiKey]
* @returns {Promise<{status:string,message:string,result:string}>}
*/
async checkStatus(chainId, guid, apiKey) {
const key = apiKey || process.env.ETHERSCAN_API_KEY;
if (!key) throw new Error('ETHERSCAN_API_KEY не задан');
const params = new URLSearchParams();
params.set('chainid', String(chainId));
params.set('module', 'contract');
params.set('action', 'checkverifystatus');
params.set('guid', guid);
params.set('apikey', key);
const { data } = await axios.get(`${ETHERSCAN_V2_ENDPOINT}?${params.toString()}`);
logger.info('[EtherscanV2] checkverifystatus response', data);
return data;
}
}
module.exports = new EtherscanV2VerificationService();

View File

@@ -10,7 +10,7 @@ const deploymentTracker = require('../utils/deploymentTracker');
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
const etherscanV2 = require('./etherscanV2VerificationService');
// ContractVerificationService удален - используем Hardhat verify
const { getRpcUrlByChainId } = require('./rpcProviderService');
const { ethers } = require('ethers');
// Убираем прямой импорт broadcastDeploymentUpdate - используем только deploymentTracker
@@ -262,6 +262,23 @@ class UnifiedDeploymentService {
// Извлекаем конкретную ошибку из вывода
const errorMessage = stderr || stdout || 'Неизвестная ошибка';
// Создаем объект ошибки для сохранения в БД
const errorResult = {
success: false,
error: `Деплой завершился с ошибкой (код ${code}): ${errorMessage}`,
stdout: stdout,
stderr: stderr
};
// Сохраняем ошибку в БД
this.deployParamsService.updateDeploymentStatus(deploymentId, 'failed', errorResult)
.then(() => {
logger.info(`✅ Результат ошибки сохранен в БД: ${deploymentId}`);
})
.catch(dbError => {
logger.error(`❌ Ошибка сохранения результата ошибки в БД: ${dbError.message}`);
});
// Отправляем WebSocket сообщение об ошибке через deploymentTracker
deploymentTracker.failDeployment(deploymentId, new Error(`Деплой завершился с ошибкой (код ${code}): ${errorMessage}`));

View File

@@ -1,47 +0,0 @@
/**
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
* All rights reserved.
*/
const path = require('path');
const fs = require('fs');
const baseDir = path.join(__dirname, '../contracts-data/verifications');
function ensureDir() {
if (!fs.existsSync(baseDir)) fs.mkdirSync(baseDir, { recursive: true });
}
function getFilePath(address) {
ensureDir();
const key = String(address || '').toLowerCase();
return path.join(baseDir, `${key}.json`);
}
function read(address) {
const fp = getFilePath(address);
if (!fs.existsSync(fp)) return { address: String(address).toLowerCase(), chains: {} };
try {
return JSON.parse(fs.readFileSync(fp, 'utf8'));
} catch {
return { address: String(address).toLowerCase(), chains: {} };
}
}
function write(address, data) {
const fp = getFilePath(address);
fs.writeFileSync(fp, JSON.stringify(data, null, 2));
}
function updateChain(address, chainId, patch) {
const data = read(address);
if (!data.chains) data.chains = {};
const cid = String(chainId);
data.chains[cid] = { ...(data.chains[cid] || {}), ...patch, chainId: Number(chainId), updatedAt: new Date().toISOString() };
write(address, data);
return data;
}
module.exports = { read, write, updateChain };