622 lines
22 KiB
JavaScript
622 lines
22 KiB
JavaScript
|
|
// ============================================
|
|||
|
|
// WEB3 CONTRACT MANAGER
|
|||
|
|
// ============================================
|
|||
|
|
// Управление смарт-контрактом токенов доступа
|
|||
|
|
// Интеграция с MetaMask и другими Web3 провайдерами
|
|||
|
|
// ============================================
|
|||
|
|
|
|||
|
|
class Web3ContractManager {
|
|||
|
|
constructor() {
|
|||
|
|
this.contract = null;
|
|||
|
|
this.web3 = null;
|
|||
|
|
this.contractAddress = null;
|
|||
|
|
this.contractABI = null;
|
|||
|
|
this.isInitialized = false;
|
|||
|
|
|
|||
|
|
// Адреса контрактов для разных сетей
|
|||
|
|
this.CONTRACT_ADDRESSES = {
|
|||
|
|
// Mainnet
|
|||
|
|
'0x1': '0x0000000000000000000000000000000000000000', // Заменить на реальный адрес
|
|||
|
|
// Ropsten (тестовая сеть)
|
|||
|
|
'0x3': '0x0000000000000000000000000000000000000000', // Заменить на реальный адрес
|
|||
|
|
// Goerli (тестовая сеть)
|
|||
|
|
'0x5': '0x0000000000000000000000000000000000000000', // Заменить на реальный адрес
|
|||
|
|
// Sepolia (тестовая сеть)
|
|||
|
|
'0xaa36a7': '0x0000000000000000000000000000000000000000' // Заменить на реальный адрес
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ABI контракта (упрощенная версия)
|
|||
|
|
this.CONTRACT_ABI = [
|
|||
|
|
// События
|
|||
|
|
{
|
|||
|
|
"anonymous": false,
|
|||
|
|
"inputs": [
|
|||
|
|
{"indexed": true, "name": "tokenId", "type": "uint256"},
|
|||
|
|
{"indexed": true, "name": "owner", "type": "address"},
|
|||
|
|
{"indexed": false, "name": "tokenType", "type": "uint8"},
|
|||
|
|
{"indexed": false, "name": "expiryDate", "type": "uint256"}
|
|||
|
|
],
|
|||
|
|
"name": "TokenMinted",
|
|||
|
|
"type": "event"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"anonymous": false,
|
|||
|
|
"inputs": [
|
|||
|
|
{"indexed": true, "name": "tokenId", "type": "uint256"},
|
|||
|
|
{"indexed": true, "name": "owner", "type": "address"}
|
|||
|
|
],
|
|||
|
|
"name": "TokenExpired",
|
|||
|
|
"type": "event"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"anonymous": false,
|
|||
|
|
"inputs": [
|
|||
|
|
{"indexed": true, "name": "tokenId", "type": "uint256"},
|
|||
|
|
{"indexed": false, "name": "newExpiryDate", "type": "uint256"}
|
|||
|
|
],
|
|||
|
|
"name": "TokenRenewed",
|
|||
|
|
"type": "event"
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// Функции чтения
|
|||
|
|
{
|
|||
|
|
"inputs": [{"name": "tokenId", "type": "uint256"}],
|
|||
|
|
"name": "isTokenValid",
|
|||
|
|
"outputs": [{"name": "", "type": "bool"}],
|
|||
|
|
"stateMutability": "view",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [{"name": "tokenId", "type": "uint256"}],
|
|||
|
|
"name": "getTokenInfo",
|
|||
|
|
"outputs": [
|
|||
|
|
{"name": "tokenId", "type": "uint256"},
|
|||
|
|
{"name": "owner", "type": "address"},
|
|||
|
|
{"name": "expiryDate", "type": "uint256"},
|
|||
|
|
{"name": "tokenType", "type": "uint8"},
|
|||
|
|
{"name": "isActive", "type": "bool"},
|
|||
|
|
{"name": "createdAt", "type": "uint256"},
|
|||
|
|
{"name": "metadata", "type": "string"}
|
|||
|
|
],
|
|||
|
|
"stateMutability": "view",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [{"name": "user", "type": "address"}],
|
|||
|
|
"name": "getUserTokens",
|
|||
|
|
"outputs": [{"name": "", "type": "uint256[]"}],
|
|||
|
|
"stateMutability": "view",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [{"name": "user", "type": "address"}],
|
|||
|
|
"name": "getActiveUserTokens",
|
|||
|
|
"outputs": [{"name": "", "type": "uint256[]"}],
|
|||
|
|
"stateMutability": "view",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [{"name": "user", "type": "address"}],
|
|||
|
|
"name": "hasActiveToken",
|
|||
|
|
"outputs": [{"name": "", "type": "bool"}],
|
|||
|
|
"stateMutability": "view",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [],
|
|||
|
|
"name": "monthlyPrice",
|
|||
|
|
"outputs": [{"name": "", "type": "uint256"}],
|
|||
|
|
"stateMutability": "view",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [],
|
|||
|
|
"name": "yearlyPrice",
|
|||
|
|
"outputs": [{"name": "", "type": "uint256"}],
|
|||
|
|
"stateMutability": "view",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [],
|
|||
|
|
"name": "getStats",
|
|||
|
|
"outputs": [
|
|||
|
|
{"name": "totalTokens", "type": "uint256"},
|
|||
|
|
{"name": "activeTokens", "type": "uint256"},
|
|||
|
|
{"name": "monthlyTokens", "type": "uint256"},
|
|||
|
|
{"name": "yearlyTokens", "type": "uint256"}
|
|||
|
|
],
|
|||
|
|
"stateMutability": "view",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// Функции записи
|
|||
|
|
{
|
|||
|
|
"inputs": [],
|
|||
|
|
"name": "purchaseMonthlyToken",
|
|||
|
|
"outputs": [],
|
|||
|
|
"stateMutability": "payable",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [],
|
|||
|
|
"name": "purchaseYearlyToken",
|
|||
|
|
"outputs": [],
|
|||
|
|
"stateMutability": "payable",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [{"name": "tokenId", "type": "uint256"}],
|
|||
|
|
"name": "deactivateToken",
|
|||
|
|
"outputs": [],
|
|||
|
|
"stateMutability": "nonpayable",
|
|||
|
|
"type": "function"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"inputs": [{"name": "tokenId", "type": "uint256"}],
|
|||
|
|
"name": "renewToken",
|
|||
|
|
"outputs": [],
|
|||
|
|
"stateMutability": "payable",
|
|||
|
|
"type": "function"
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
this.initialize();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Инициализация Web3 и контракта
|
|||
|
|
async initialize() {
|
|||
|
|
try {
|
|||
|
|
// Проверяем поддержку Web3
|
|||
|
|
if (typeof window.ethereum !== 'undefined') {
|
|||
|
|
console.log('✅ Web3 detected');
|
|||
|
|
|
|||
|
|
// Создаем Web3 экземпляр
|
|||
|
|
this.web3 = new Web3(window.ethereum);
|
|||
|
|
|
|||
|
|
// Получаем текущую сеть
|
|||
|
|
const chainId = await this.getCurrentChainId();
|
|||
|
|
console.log('🔗 Current chain ID:', chainId);
|
|||
|
|
|
|||
|
|
// Получаем адрес контракта для текущей сети
|
|||
|
|
this.contractAddress = this.CONTRACT_ADDRESSES[chainId];
|
|||
|
|
|
|||
|
|
if (!this.contractAddress || this.contractAddress === '0x0000000000000000000000000000000000000000') {
|
|||
|
|
console.warn('⚠️ Contract not deployed on current network:', chainId);
|
|||
|
|
this.showContractNotDeployedMessage(chainId);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создаем экземпляр контракта
|
|||
|
|
this.contract = new this.web3.eth.Contract(
|
|||
|
|
this.CONTRACT_ABI,
|
|||
|
|
this.contractAddress
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
console.log('📋 Contract initialized:', this.contractAddress);
|
|||
|
|
this.isInitialized = true;
|
|||
|
|
|
|||
|
|
} else {
|
|||
|
|
throw new Error('Web3 not detected');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Web3ContractManager initialization failed:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение текущего Chain ID
|
|||
|
|
async getCurrentChainId() {
|
|||
|
|
try {
|
|||
|
|
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
|
|||
|
|
return chainId;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to get chain ID:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверка валидности токена
|
|||
|
|
async isTokenValid(tokenId) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.isTokenValid(tokenId).call();
|
|||
|
|
console.log('🔍 Token validation result:', { tokenId, isValid: result });
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Token validation failed:', error);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение информации о токене
|
|||
|
|
async getTokenInfo(tokenId) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.getTokenInfo(tokenId).call();
|
|||
|
|
|
|||
|
|
// Преобразуем результат в удобный формат
|
|||
|
|
const tokenInfo = {
|
|||
|
|
tokenId: result.tokenId,
|
|||
|
|
owner: result.owner,
|
|||
|
|
expiryDate: parseInt(result.expiryDate),
|
|||
|
|
tokenType: parseInt(result.tokenType),
|
|||
|
|
isActive: result.isActive,
|
|||
|
|
createdAt: parseInt(result.createdAt),
|
|||
|
|
metadata: result.metadata
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
console.log('📋 Token info retrieved:', tokenInfo);
|
|||
|
|
return tokenInfo;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to get token info:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение токенов пользователя
|
|||
|
|
async getUserTokens(userAddress) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.getUserTokens(userAddress).call();
|
|||
|
|
const tokenIds = result.map(id => parseInt(id));
|
|||
|
|
|
|||
|
|
console.log('👤 User tokens retrieved:', { user: userAddress, tokens: tokenIds });
|
|||
|
|
return tokenIds;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to get user tokens:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение активных токенов пользователя
|
|||
|
|
async getActiveUserTokens(userAddress) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.getActiveUserTokens(userAddress).call();
|
|||
|
|
const tokenIds = result.map(id => parseInt(id));
|
|||
|
|
|
|||
|
|
console.log('✅ Active user tokens retrieved:', { user: userAddress, activeTokens: tokenIds });
|
|||
|
|
return tokenIds;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to get active user tokens:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверка наличия активного токена у пользователя
|
|||
|
|
async hasActiveToken(userAddress) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.hasActiveToken(userAddress).call();
|
|||
|
|
console.log('🔍 Active token check:', { user: userAddress, hasActive: result });
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to check active token:', error);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение цен токенов
|
|||
|
|
async getTokenPrices() {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const [monthlyPrice, yearlyPrice] = await Promise.all([
|
|||
|
|
this.contract.methods.monthlyPrice().call(),
|
|||
|
|
this.contract.methods.yearlyPrice().call()
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
const prices = {
|
|||
|
|
monthly: this.web3.utils.fromWei(monthlyPrice, 'ether'),
|
|||
|
|
yearly: this.web3.utils.fromWei(yearlyPrice, 'ether'),
|
|||
|
|
monthlyWei: monthlyPrice,
|
|||
|
|
yearlyWei: yearlyPrice
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
console.log('💰 Token prices retrieved:', prices);
|
|||
|
|
return prices;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to get token prices:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение статистики контракта
|
|||
|
|
async getContractStats() {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.getStats().call();
|
|||
|
|
|
|||
|
|
const stats = {
|
|||
|
|
totalTokens: parseInt(result.totalTokens),
|
|||
|
|
activeTokens: parseInt(result.activeTokens),
|
|||
|
|
monthlyTokens: parseInt(result.monthlyTokens),
|
|||
|
|
yearlyTokens: parseInt(result.yearlyTokens)
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
console.log('📊 Contract stats retrieved:', stats);
|
|||
|
|
return stats;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to get contract stats:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Покупка месячного токена
|
|||
|
|
async purchaseMonthlyToken(price) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const accounts = await this.web3.eth.getAccounts();
|
|||
|
|
const userAddress = accounts[0];
|
|||
|
|
|
|||
|
|
console.log('🛒 Purchasing monthly token:', { user: userAddress, price: price });
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.purchaseMonthlyToken().send({
|
|||
|
|
from: userAddress,
|
|||
|
|
value: price
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ Monthly token purchased:', result);
|
|||
|
|
return result;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Monthly token purchase failed:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Покупка годового токена
|
|||
|
|
async purchaseYearlyToken(price) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const accounts = await this.web3.eth.getAccounts();
|
|||
|
|
const userAddress = accounts[0];
|
|||
|
|
|
|||
|
|
console.log('🛒 Purchasing yearly token:', { user: userAddress, price: price });
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.purchaseYearlyToken().send({
|
|||
|
|
from: userAddress,
|
|||
|
|
value: price
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ Yearly token purchased:', result);
|
|||
|
|
return result;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Yearly token purchase failed:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Деактивация токена
|
|||
|
|
async deactivateToken(tokenId) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const accounts = await this.web3.eth.getAccounts();
|
|||
|
|
const userAddress = accounts[0];
|
|||
|
|
|
|||
|
|
console.log('🚫 Deactivating token:', { tokenId, user: userAddress });
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.deactivateToken(tokenId).send({
|
|||
|
|
from: userAddress
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ Token deactivated:', result);
|
|||
|
|
return result;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Token deactivation failed:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Продление токена
|
|||
|
|
async renewToken(tokenId, price) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const accounts = await this.web3.eth.getAccounts();
|
|||
|
|
const userAddress = accounts[0];
|
|||
|
|
|
|||
|
|
console.log('🔄 Renewing token:', { tokenId, user: userAddress, price: price });
|
|||
|
|
|
|||
|
|
const result = await this.contract.methods.renewToken(tokenId).send({
|
|||
|
|
from: userAddress,
|
|||
|
|
value: price
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('✅ Token renewed:', result);
|
|||
|
|
return result;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Token renewal failed:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение событий о создании токенов
|
|||
|
|
async getTokenMintedEvents(fromBlock = 0, toBlock = 'latest') {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const events = await this.contract.getPastEvents('TokenMinted', {
|
|||
|
|
fromBlock: fromBlock,
|
|||
|
|
toBlock: toBlock
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('📝 Token minted events retrieved:', events.length);
|
|||
|
|
return events;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to get token minted events:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение событий о продлении токенов
|
|||
|
|
async getTokenRenewedEvents(fromBlock = 0, toBlock = 'latest') {
|
|||
|
|
try {
|
|||
|
|
if (!this.isInitialized || !this.contract) {
|
|||
|
|
throw new Error('Contract not initialized');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const events = await this.contract.getPastEvents('TokenRenewed', {
|
|||
|
|
fromBlock: fromBlock,
|
|||
|
|
toBlock: toBlock
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('🔄 Token renewed events retrieved:', events.length);
|
|||
|
|
return events;
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to get token renewed events:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверка поддержки сети
|
|||
|
|
isNetworkSupported(chainId) {
|
|||
|
|
return this.CONTRACT_ADDRESSES.hasOwnProperty(chainId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение поддерживаемых сетей
|
|||
|
|
getSupportedNetworks() {
|
|||
|
|
return Object.keys(this.CONTRACT_ADDRESSES).map(chainId => ({
|
|||
|
|
chainId: chainId,
|
|||
|
|
name: this.getNetworkName(chainId),
|
|||
|
|
contractAddress: this.CONTRACT_ADDRESSES[chainId]
|
|||
|
|
}));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение названия сети
|
|||
|
|
getNetworkName(chainId) {
|
|||
|
|
const networkNames = {
|
|||
|
|
'0x1': 'Ethereum Mainnet',
|
|||
|
|
'0x3': 'Ropsten Testnet',
|
|||
|
|
'0x5': 'Goerli Testnet',
|
|||
|
|
'0xaa36a7': 'Sepolia Testnet'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return networkNames[chainId] || 'Unknown Network';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Переключение на поддерживаемую сеть
|
|||
|
|
async switchToNetwork(chainId) {
|
|||
|
|
try {
|
|||
|
|
if (!this.isNetworkSupported(chainId)) {
|
|||
|
|
throw new Error(`Network ${chainId} is not supported`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await window.ethereum.request({
|
|||
|
|
method: 'wallet_switchEthereumChain',
|
|||
|
|
params: [{ chainId: chainId }]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('🔄 Switched to network:', chainId);
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to switch network:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Добавление новой сети
|
|||
|
|
async addNetwork(chainId, networkName, rpcUrl, blockExplorerUrl) {
|
|||
|
|
try {
|
|||
|
|
await window.ethereum.request({
|
|||
|
|
method: 'wallet_addEthereumChain',
|
|||
|
|
params: [{
|
|||
|
|
chainId: chainId,
|
|||
|
|
chainName: networkName,
|
|||
|
|
rpcUrls: [rpcUrl],
|
|||
|
|
blockExplorerUrls: [blockExplorerUrl],
|
|||
|
|
nativeCurrency: {
|
|||
|
|
name: 'Ether',
|
|||
|
|
symbol: 'ETH',
|
|||
|
|
decimals: 18
|
|||
|
|
}
|
|||
|
|
}]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('➕ Network added:', { chainId, name: networkName });
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('❌ Failed to add network:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Показ сообщений пользователю
|
|||
|
|
showContractNotDeployedMessage(chainId) {
|
|||
|
|
const message = `Smart contract not deployed on network ${chainId}. Please switch to a supported network or deploy the contract.`;
|
|||
|
|
console.warn('⚠️', message);
|
|||
|
|
// Здесь можно добавить UI уведомление
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получение статуса инициализации
|
|||
|
|
getInitializationStatus() {
|
|||
|
|
return {
|
|||
|
|
isInitialized: this.isInitialized,
|
|||
|
|
web3: !!this.web3,
|
|||
|
|
contract: !!this.contract,
|
|||
|
|
contractAddress: this.contractAddress,
|
|||
|
|
currentChainId: this.web3 ? this.web3.currentProvider.chainId : null
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Очистка ресурсов
|
|||
|
|
destroy() {
|
|||
|
|
this.contract = null;
|
|||
|
|
this.web3 = null;
|
|||
|
|
this.contractAddress = null;
|
|||
|
|
this.isInitialized = false;
|
|||
|
|
|
|||
|
|
console.log('🗑️ Web3ContractManager destroyed');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export { Web3ContractManager };
|