/** * 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/VC-HB3-Accelerator */ import axios from 'axios'; import { ethers } from 'ethers'; import { SiweMessage } from 'siwe'; export const connectWallet = async () => { try { // console.log('Starting wallet connection...'); // Проверяем наличие MetaMask или другого Ethereum провайдера if (!window.ethereum) { // console.error('No Ethereum provider (like MetaMask) detected!'); return { success: false, error: 'Не найден кошелек MetaMask или другой Ethereum провайдер. Пожалуйста, установите расширение MetaMask.', }; } // console.log('MetaMask detected, requesting accounts...'); // Запрашиваем доступ к аккаунтам const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); // console.log('Got accounts:', accounts); if (!accounts || accounts.length === 0) { return { success: false, error: 'Не удалось получить доступ к аккаунтам. Пожалуйста, разрешите доступ в MetaMask.', }; } // Берем первый аккаунт в списке const address = accounts[0]; // Нормализуем адрес (приводим к нижнему регистру для последующих сравнений) const normalizedAddress = ethers.utils.getAddress(address); // console.log('Normalized address:', normalizedAddress); // Запрашиваем nonce с сервера // console.log('Requesting nonce...'); const nonceResponse = await axios.get(`/auth/nonce?address=${normalizedAddress}`); const nonce = nonceResponse.data.nonce; // console.log('Got nonce:', nonce); if (!nonce) { return { success: false, error: 'Не удалось получить nonce от сервера.', }; } // Создаем провайдер Ethers const provider = new ethers.providers.Web3Provider(window.ethereum); const signer = provider.getSigner(); // Создаем сообщение для подписи const domain = window.location.host; const origin = window.location.origin; // Создаем SIWE сообщение const message = new SiweMessage({ domain, address: normalizedAddress, statement: 'Sign in with Ethereum to the app.', uri: origin, version: '1', chainId: 1, // Ethereum mainnet nonce: nonce, issuedAt: new Date().toISOString(), resources: [`${origin}/api/auth/verify`], }); // Получаем строку сообщения для подписи const messageToSign = message.prepareMessage(); // console.log('SIWE message:', messageToSign); // Запрашиваем подпись // console.log('Requesting signature...'); const signature = await signer.signMessage(messageToSign); if (!signature) { return { success: false, error: 'Подпись не была получена. Пожалуйста, подпишите сообщение в MetaMask.', }; } // console.log('Got signature:', signature); // Отправляем верификацию на сервер // console.log('Sending verification request...'); const requestData = { address: normalizedAddress, signature, nonce, issuedAt: new Date().toISOString(), }; // console.log('Request data:', requestData); const verifyResponse = await axios.post('/auth/verify', requestData, { withCredentials: true, }); // Обновляем интерфейс для отображения подключенного состояния document.body.classList.add('wallet-connected'); // Обновляем отображение адреса кошелька в UI const authDisplayEl = document.getElementById('auth-display'); if (authDisplayEl) { const shortAddress = `${normalizedAddress.substring(0, 6)}...${normalizedAddress.substring(normalizedAddress.length - 4)}`; authDisplayEl.innerHTML = `Кошелек: ${shortAddress}`; authDisplayEl.style.display = 'inline-block'; } // Скрываем кнопки авторизации и показываем кнопку выхода const authButtonsEl = document.getElementById('auth-buttons'); const logoutButtonEl = document.getElementById('logout-button'); if (authButtonsEl) authButtonsEl.style.display = 'none'; if (logoutButtonEl) logoutButtonEl.style.display = 'inline-block'; // console.log('Verification response:', verifyResponse.data); if (verifyResponse.data.success) { return { success: true, address: normalizedAddress, userId: verifyResponse.data.userId, }; } else { return { success: false, error: verifyResponse.data.error || 'Ошибка верификации на сервере.', }; } } catch (error) { // console.error('Error connecting wallet:', error); // Формируем понятное сообщение об ошибке let errorMessage = 'Произошла ошибка при подключении кошелька.'; if (error.message && error.message.includes('MetaMask extension not found')) { errorMessage = 'Расширение MetaMask не найдено. Пожалуйста, установите MetaMask и обновите страницу.'; } else if (error.message && error.message.includes('Failed to connect to MetaMask')) { errorMessage = 'Не удалось подключиться к MetaMask. Проверьте, что расширение установлено и активно.'; } else if (error.code === 4001) { errorMessage = 'Вы отклонили запрос на подпись в MetaMask.'; } else if (error.message && error.message.includes('No accounts found')) { errorMessage = 'Аккаунты не найдены. Пожалуйста, разблокируйте MetaMask и попробуйте снова.'; } else if (error.message && error.message.includes('MetaMask not detected')) { errorMessage = 'MetaMask не обнаружен. Пожалуйста, установите расширение MetaMask.'; } else if (error.response && error.response.data && error.response.data.error) { errorMessage = error.response.data.error; } else if (error.message) { errorMessage = error.message; } return { success: false, error: errorMessage, }; } };