document.addEventListener('DOMContentLoaded', async () => { console.log("Web3 button script loaded"); const connectWalletButton = document.getElementById('connectWallet'); const loginWithWalletButton = document.getElementById('loginWithWalletButton'); const walletButtonText = document.getElementById('walletButtonText'); let currentWalletAddress = null; // Verificar estado al cargar la página await checkWalletConnection(); // Agrega un listener para detectar cambios de wallet if (typeof window.ethereum !== 'undefined') { // Función centralizada para manejar eventos de MetaMask async function handleEthereumEvent(eventType, data) { try { if (eventType === 'accountsChanged') { const accounts = data; if (accounts.length > 0) { currentWalletAddress = accounts[0]; console.log('Wallet cambiada:', currentWalletAddress); } else { console.log('No hay wallets conectadas.'); setButtonState('Conectar Wallet', 'conectar'); return; } } if (eventType === 'chainChanged') { const chainId = data; console.log('Red cambiada:', chainId); if (chainId !== '0x89') { // Verificar si no está en Polygon console.log('No estás en la red Polygon.'); await showUniversalSwal({ title: 'Red Incorrecta', text: 'Por favor, cambia a la red Polygon para continuar.', icon: 'warning', confirmText: 'Cambiar a Polygon', }); // Intentar cambiar automáticamente a Polygon try { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x89' }], // Mainnet Polygon }); console.log('Cambio a la red Polygon exitoso.'); } catch (switchError) { console.error('Error al cambiar de red:', switchError.message); await showUniversalSwal({ title: 'Error', text: 'No se pudo cambiar automáticamente a la red Polygon. Por favor, cambia manualmente en MetaMask.', icon: 'error', }); return; } } } // Actualizar estado del usuario y la wallet después de un cambio const userStatus = await fetchUserStatus(); const walletData = await checkWalletRegistration(); console.log('Estado del usuario:', userStatus); console.log('Datos de la wallet:', walletData); // Actualiza los botones dinámicamente updateDynamicButtonsQuick(userStatus, walletData); } catch (error) { console.error(`Error manejando evento ${eventType}:`, error.message); await showUniversalSwal({ title: 'Error', text: `Hubo un problema manejando el evento ${eventType}. Por favor, intenta nuevamente.`, icon: 'error', }); } } // Listener para cambios en las cuentas window.ethereum.on('accountsChanged', (accounts) => { handleEthereumEvent('accountsChanged', accounts); }); // Listener para cambios en la red window.ethereum.on('chainChanged', (chainId) => { handleEthereumEvent('chainChanged', chainId); }); } else { console.error('MetaMask no está instalado.'); } // Función para verificar la conexión de la wallet al cargar la página async function checkWalletConnection() { if (typeof window.ethereum !== 'undefined') { try { // Verificar cuentas conectadas const accounts = await window.ethereum.request({ method: 'eth_accounts' }); if (accounts.length > 0) { const account = accounts[0]; // Validar formato de la dirección if (!/^0x[a-fA-F0-9]{40}$/.test(account)) { console.error('Dirección de wallet inválida:', account); await showUniversalSwal({ icon: 'error', title: 'Error de Conexión', text: 'Se detectó una wallet con un formato inválido. Por favor, intenta reconectar.', }); return false; } currentWalletAddress = account; console.log('Wallet conectada:', currentWalletAddress); // Verificar si está en la red de Polygon const chainId = await window.ethereum.request({ method: 'eth_chainId' }); if (chainId !== '0x89') { // 0x89 es el Chain ID de Polygon Mainnet console.log('No estás en la red Polygon.'); await showUniversalSwal({ title: 'Red Incorrecta', text: 'Por favor, cambia a la red Polygon para continuar.', icon: 'warning', confirmText: 'Cambiar a Polygon', }); // Intentar cambiar automáticamente a Polygon try { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x89' }], // Mainnet Polygon }); console.log('Cambio a la red Polygon exitoso.'); } catch (switchError) { console.error('Error al cambiar de red:', switchError.message); await showUniversalSwal({ title: 'Error', text: 'No se pudo cambiar automáticamente a la red Polygon. Por favor, cambia manualmente en MetaMask.', icon: 'error', }); return false; // Detener si no se pudo cambiar de red } } // Estado del usuario y wallet después de conectar const userStatus = await fetchUserStatus(); const walletData = await checkWalletRegistration(); console.log(userStatus); console.log(walletData); if (!userStatus || !walletData) { console.error("Datos inválidos recibidos:", { userStatus, walletData }); connectWalletButton.innerHTML = 'Error de conexión'; return false; } // Actualizar botones dinámicamente updateDynamicButtonsQuick(userStatus, walletData); return true; } else { console.log('No hay wallet conectada.'); setButtonState('Conectar Wallet', 'conectar'); return false; } } catch (error) { console.error('Error verificando conexión de wallet:', error.message); setButtonState('Conectar Wallet', 'conectar'); return false; } } else { console.log('MetaMask no está instalado.'); setButtonState('Instalar MetaMask', 'instalar'); return false; } } // Función para conectar wallet async function connectWallet() { if (typeof window.ethereum !== 'undefined') { try { // Establecer estado visual de conexión inmediata setButtonState('Conectando...', null); // Solicitar cuentas conectadas const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); if (accounts.length > 0) { currentWalletAddress = accounts[0]; // Validar formato de la dirección de la wallet if (!/^0x[a-fA-F0-9]{40}$/.test(currentWalletAddress)) { console.error('Dirección de wallet inválida:', currentWalletAddress); await showUniversalSwal({ title: 'Error de Conexión', text: 'La dirección de la wallet es inválida. Por favor, intenta reconectar.', icon: 'error', }); return; } console.log('Wallet conectada:', currentWalletAddress); // Obtener el nonce desde el servidor const nonceResponse = await fetch('/api/get-nonce', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, }, body: JSON.stringify({ walletAddress: currentWalletAddress }), }); const { nonce, success } = await nonceResponse.json(); if (!success) { await showUniversalSwal({ title: 'Error', text: 'No se pudo obtener el nonce del servidor.', icon: 'error', }); return; } // Firmar el nonce const signature = await ethereum.request({ method: 'personal_sign', params: [nonce, currentWalletAddress], }); console.log('Firma generada:', signature); // Dividir la firma en sus componentes const r = signature.slice(0, 66); const s = '0x' + signature.slice(66, 130); const recoveryParam = parseInt(signature.slice(130, 132), 16) - 27; // Verificar la firma en el backend const verifyResponse = await fetch('/api/verify-signature', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, }, body: JSON.stringify({ walletAddress: currentWalletAddress, signature: { r, s, recoveryParam }, }), }); const verifyResult = await verifyResponse.json(); if (verifyResult.success) { const linked = await validateAndLinkWallet(); if (linked) { await showUniversalSwal({ title: 'Éxito', text: 'Tu wallet ha sido vinculada correctamente.', icon: 'success', }); // Actualizar estado y botones const userStatus = await fetchUserStatus(); const walletData = await checkWalletRegistration(); updateDynamicButtonsQuick(userStatus, walletData); } } else { await showUniversalSwal({ title: 'Error', text: verifyResult.message || 'No se pudo verificar la firma.', icon: 'error', }); } } else { console.log('No hay cuentas conectadas.'); await showUniversalSwal({ title: 'Conexión Fallida', text: 'No se detectaron wallets conectadas. Por favor, conecta tu wallet.', icon: 'info', }); } } catch (error) { console.error('Error conectando wallet:', error.message); await showUniversalSwal({ title: 'Error', text: `No se pudo conectar la wallet: ${error.message}`, icon: 'error', }); } } else { await showUniversalSwal({ title: 'MetaMask no instalado', text: 'Por favor, instala MetaMask para continuar.', icon: 'info', }); window.open('https://metamask.io/download.html', '_blank'); } } // Función para obtener el estado del usuario async function fetchUserStatus() { const response = await fetch('/api/user-status', { method: 'GET', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), }, credentials: 'same-origin', }); return await response.json(); } // Función para verificar si la wallet está registrada async function checkWalletRegistration() { const response = await fetch('/api/check-wallet-registration', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), }, body: JSON.stringify({ walletAddress: currentWalletAddress }), }); return await response.json(); } // Función para actualizar dinámicamente los botones según el estado function updateDynamicButtonsQuick(userStatus, walletData) { // Limpiar eventos previos del botón para evitar múltiples asignaciones connectWalletButton.onclick = null; if (userStatus.loggedIn && walletData.registered && walletData.userId === userStatus.userId) { // Usuario logueado y wallet vinculada setButtonState('Desconectar Wallet', 'desconectar'); } else if (userStatus.loggedIn && !walletData.registered) { // Usuario logueado pero wallet no vinculada setButtonState('Conectar Wallet', 'conectar'); } else { // Usuario no logueado o wallet desconectada setButtonState('Continuar con ', 'conectar'); } } function setButtonState(label, action) { connectWalletButton.innerHTML = `
${label}
`; connectWalletButton.onclick = async () => { if (action === 'desconectar') { const userStatus = await fetchUserStatus(); // Verificar estado del usuario if (userStatus.loggedIn) { await disconnectWallet(); } else { // Wallet vinculada await showUniversalSwal({ icon: 'info', title: 'Sin sesion', text: 'No tienes una sesion iniciada.', }); } } else if (action === 'conectar') { const userStatus = await fetchUserStatus(); // Verificar estado del usuario console.log("aca1"); if (!userStatus.loggedIn) { console.log("aca2"); const connected = await checkWalletConnection() if (connected) { console.log("aca3"); const walletData = await checkWalletRegistration(); console.log("adata wallet:"+walletData); // Si no está logueado, mostrar modal de registro if (walletData && walletData.registered) { console.log("aca4"); await loginWithWallet(); } else { console.log("aca5"); connectData = await showUniversalSwal({ title: 'Wallet Conectada', text: 'Tu wallet está conectada, pero no existe en nuestros usuarios. Por favor, regístrate o inicia sesión.', icon: 'info', showCancelButton: true, confirmText: 'Registrar', cancelText: 'Iniciar Sesión', }); } } else { console.log("aca6"); // Intentar nuevamente validar y vincular const regist = await validateAndLinkWallet(); // Asegurarse de validar y vincular if (regist) { await showUniversalSwal({ icon: 'success', title: 'Registro Exitoso', text: 'Bienvenido, tu cuenta ha sido creada exitosamente.', }); // Obtener estado actualizado y actualizar botones const userStatus = await fetchUserStatus(); const walletData = await checkWalletRegistration(); updateDynamicButtonsQuick(userStatus, walletData); window.location.href = '/dashboard'; // Redirigir al login } } if (connectData.isConfirmed) { // Intentar nuevamente validar y vincular const regist = await validateAndLinkWallet(); // Asegurarse de validar y vincular if (regist) { await showUniversalSwal({ icon: 'success', title: 'Registro Exitoso', text: 'Bienvenido, tu cuenta ha sido creada exitosamente.', }); // Obtener estado actualizado y actualizar botones const userStatus = await fetchUserStatus(); const walletData = await checkWalletRegistration(); updateDynamicButtonsQuick(userStatus, walletData); window.location.href = '/dashboard'; // Redirigir al login } } else { window.location.href = '/login'; // Redirigir al login } return; } else { const walletData = await checkWalletRegistration(); if (!walletData.registered) { // Wallet no vinculada await connectWallet(); } else { // Wallet vinculada await showUniversalSwal({ icon: 'info', title: 'Wallet vinculada', text: 'Esta wallet ya está vinculada a tu cuenta.', }); const userStatus = await fetchUserStatus(); const walletData = await checkWalletRegistration(); updateDynamicButtonsQuick(userStatus, walletData); } } } else if (action === 'instalar') { // Redirigir a MetaMask await showUniversalSwal({ icon: 'info', title: 'MetaMask no instalado', text: 'Por favor, instala MetaMask para continuar.', }); window.open('https://metamask.app.link/dapp/dex.vanellix.com', '_blank'); } }; } async function disconnectWallet() { const confirmation = await showUniversalSwal({ title: '¿Estás seguro?', text: 'Esto eliminará la vinculación de tu wallet con tu cuenta.', icon: 'warning', showCancelButton: true, confirmButtonText: 'Sí, desconectar', cancelButtonText: 'Cancelar', }); if (confirmation.isConfirmed) { try { // Actualizamos la wallet seleccionada antes de proceder const connected = await checkWalletConnection(); if (!connected) { await showUniversalSwal({ icon: 'error', title: 'Error', text: 'No se detectó una wallet conectada. Por favor, selecciona una wallet antes de continuar.', }); return; } // Proceder con la desconexión const response = await fetch('/api/disconnect-wallet', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), }, body: JSON.stringify({ walletAddress: currentWalletAddress }), }); const result = await response.json(); if (result.success) { await showUniversalSwal({ icon: 'success', title: 'Wallet desconectada', text: 'Tu wallet se ha desvinculado exitosamente.', }); // Verificar nuevamente la wallet seleccionada después de la desconexión const reconnected = await checkWalletConnection(); if (reconnected) { console.log('Wallet actualizada tras la desconexión:', currentWalletAddress); // Actualizar botones dinámicamente con el nuevo estado const userStatus = await fetchUserStatus(); const walletData = await checkWalletRegistration(); updateDynamicButtonsQuick(userStatus, walletData); } else { console.log('No se detecta ninguna wallet tras la desconexión.'); setButtonState('Conectar Wallet', 'conectar'); } } else { await showUniversalSwal({ icon: 'error', title: 'Error', text: result.message || 'No se pudo desconectar la wallet.', }); } } catch (error) { console.error('Error desconectando wallet:', error.message); await showUniversalSwal({ icon: 'error', title: 'Error', text: 'Hubo un problema al desconectar tu wallet.', }); } } } async function validateAndLinkWallet() { try { // Verificar el estado del usuario logueado const userStatus = await fetchUserStatus(); if (userStatus.loggedIn) { // Usuario autenticado const walletData = await checkWalletRegistration(); if (walletData.registered && walletData.userId !== userStatus.userId) { // Wallet vinculada a otra cuenta await showUniversalSwal({ icon: 'error', title: 'Wallet ya registrada', text: 'Esta wallet ya está vinculada a otra cuenta. No puedes usarla aquí.', }); updateDynamicButtonsQuick(userStatus, walletData); return false; // Indicar que no se pudo vincular } if (!walletData.registered) { // Wallet no está registrada, vincularla al usuario actual const connectResponse = await fetch('/api/connect-wallet', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), }, body: JSON.stringify({ walletAddress: currentWalletAddress }), }); const connectResult = await connectResponse.json(); if (!connectResult.success) { await showUniversalSwal({ icon: 'error', title: 'Error', text: connectResult.message || 'No se pudo vincular la wallet.', }); updateDynamicButtonsQuick(userStatus, walletData); return false; // Indicar que no se pudo vincular } } // Actualizar botones dinámicamente const updatedUserStatus = await fetchUserStatus(); const updatedWalletData = await checkWalletRegistration(); updateDynamicButtonsQuick(updatedUserStatus, updatedWalletData); return true; // Indicar éxito } else { try { // Usuario no logueado: Verificar si la wallet está conectada const accounts = await window.ethereum.request({ method: 'eth_accounts' }); if (accounts.length === 0) { // La wallet no está conectada await showUniversalSwal({ icon: 'info', title: 'Conectar Wallet', text: 'Por favor, conecta tu wallet antes de continuar.', confirmButtonText: 'Conectar Wallet', }).then(async () => { // Solicitar conexión de wallet await connectWallet(); }); return false; } currentWalletAddress = accounts[0]; // Guardar la wallet conectada // Verificar si la wallet ya está registrada const walletData = await checkWalletRegistration(); console.log(walletData); if (walletData.registered) { // Si la wallet está registrada, dirigir al inicio de sesión console.log('Wallet registrada. Redirigiendo al login...'); await loginWithWallet(); return; // Detener el flujo después de redirigir al login } // Wallet no registrada, continuar con el registro const registrationData = await showUniversalSwal({ title: 'Registro Requerido', html: `
`, focusConfirm: false, preConfirm: () => { const name = document.getElementById('swal-name').value; const email = document.getElementById('swal-email').value; const password = document.getElementById('swal-password').value; const passwordConfirmation = document.getElementById('swal-password-confirmation').value; if (password !== passwordConfirmation) { Swal.showValidationMessage('Las contraseñas no coinciden'); return false; } if (!name || !email || !password) { Swal.showValidationMessage('Por favor, completa todos los campos'); return false; } return { name, email, password, password_confirmation: passwordConfirmation, walletAddress: currentWalletAddress }; }, didRender: () => { document.getElementById('loginButton').addEventListener('click', () => { Swal.close(); // Cierra el modal window.location.href = '/login'; // Redirige al login }); }, confirmText: 'Registrar', showLoaderOnConfirm: true, allowOutsideClick: () => !Swal.isLoading(), }); if (!registrationData.isConfirmed) { setButtonState('Conectar Wallet', 'conectar'); return false; // Indicar que no se completó el registro } // Registrar al usuario con wallet const response = await fetch('/api/register-with-wallet', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), }, body: JSON.stringify(registrationData.value), }); const result = await response.json(); if (!result.success) { await showUniversalSwal({ icon: 'error', title: 'Error', text: result.message || 'Hubo un problema al registrar la cuenta.', }); return false; // Indicar que no se pudo registrar } // Actualizar botones dinámicamente const updatedUserStatus = await fetchUserStatus(); const updatedWalletData = await checkWalletRegistration(); updateDynamicButtonsQuick(updatedUserStatus, updatedWalletData); return true; // Indicar éxito } catch (error) { console.error('Error validando o registrando wallet:', error.message); await showUniversalSwal({ icon: 'error', title: 'Error', text: 'Hubo un problema validando o registrando tu wallet.', }); return false; // Indicar que hubo un error } } } catch (error) { console.error('Error validando o vinculando wallet:', error.message); await showUniversalSwal({ icon: 'error', title: 'Error', text: 'Hubo un problema validando tu wallet.', }); updateDynamicButtonsQuick({ loggedIn: true }, { registered: false }); return false; // Indicar que hubo un error } } // Función para login con wallet async function loginWithWallet() { // Obtener el nonce desde el backend const nonceResponse = await fetch('/api/get-nonce', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, }, body: JSON.stringify({ walletAddress: currentWalletAddress }), }); const nonceData = await nonceResponse.json(); if (!nonceData.success) { await showUniversalSwal({ icon: 'error', title: 'Error', text: 'No se pudo obtener el nonce del servidor. Por favor, inténtalo nuevamente.', }); return; } const nonce = nonceData.nonce; // Firmar el nonce con la wallet const signature = await ethereum.request({ method: 'personal_sign', params: [nonce, currentWalletAddress], }); // Dividir la firma en sus componentes const r = signature.slice(0, 66); // Los primeros 66 caracteres (32 bytes + '0x') const s = '0x' + signature.slice(66, 130); // Los siguientes 32 bytes const recoveryParam = parseInt(signature.slice(130, 132), 16) - 27; // Último byte // Mostrar formulario para ingresar la clave const { value: password } = await showUniversalSwal({ title: 'Iniciar Sesión', text: 'Ingresa tu clave para continuar.', input: 'password', inputLabel: 'Clave', inputPlaceholder: 'Escribe tu clave', inputAttributes: { maxlength: 20, autocapitalize: 'off', autocorrect: 'off', }, showCancelButton: true, confirmButtonText: 'Iniciar Sesión', cancelButtonText: 'Cancelar', }); if (!password) { await showUniversalSwal({ icon: 'error', title: 'Clave requerida', text: 'Debes ingresar una clave para continuar.', }); return; } // Enviar solicitud al backend try { const response = await fetch('/api/login-with-wallet', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, }, body: JSON.stringify({ walletAddress: currentWalletAddress, password: password, signature: { r, s, recoveryParam }, // Enviar la firma completa }), }); const result = await response.json(); if (result.success) { await showUniversalSwal({ icon: 'success', title: 'Inicio de sesión exitoso', text: '¡Bienvenido de nuevo!', }).then(() => { window.location.href = '/dashboard'; // Redirigir al dashboard }); } else { await showUniversalSwal({ icon: 'error', title: 'Error', text: result.message || 'No se pudo iniciar sesión.', }); } } catch (error) { console.error('Error iniciando sesión con wallet:', error.message); await showUniversalSwal({ icon: 'error', title: 'Error', text: 'Hubo un problema al intentar iniciar sesión. Por favor, inténtalo nuevamente.', }); } } async function showUniversalSwal({ title = '', text = '', html = '', input = null, inputLabel = '', inputPlaceholder = '', inputAttributes = {}, confirmText = 'Aceptar', cancelText = 'Cancelar', showCancelButton = false, allowOutsideClick = true, preConfirm = null, didRender = null, icon = null, }) { try { const { value, isConfirmed } = await Swal.fire({ title: title, // Título del modal text: text, // Texto del modal html: html, // Código HTML personalizado input: input, // Tipo de input, por ejemplo: text, password, email inputLabel: inputLabel, // Etiqueta del input inputPlaceholder: inputPlaceholder, // Placeholder del input inputAttributes: inputAttributes, // Atributos adicionales del input icon: icon, // Ícono del modal (success, error, info, warning, question) background: 'rgba(0, 0, 0, 0.86)', // Fondo negro del modal color: 'rgb(209 213 219)', // Texto gris claro confirmButtonColor: 'rgb(37 99 235)', // Azul para el botón de confirmación cancelButtonColor: 'rgb(67 56 202)', // Púrpura para el botón de cancelar buttonsStyling: false, // Deshabilitar estilos por defecto customClass: { confirmButton: 'px-8 py-3 text-md text-blue-100 bg-blue-600 rounded-lg hover:text-white hover:bg-purple-700', cancelButton: 'px-8 py-3 text-md text-gray-100 border border-white rounded-lg hover:bg-white hover:text-gray-900', }, didOpen: () => { const popup = Swal.getPopup(); popup.style.border = '2px solid rgb(37, 99, 235)'; // Borde azul popup.style.borderRadius = '0.5rem'; // Esquinas redondeadas }, confirmButtonText: confirmText, // Texto del botón de confirmación cancelButtonText: cancelText, // Texto del botón de cancelar showCancelButton: showCancelButton, // Mostrar el botón de cancelar allowOutsideClick: allowOutsideClick, // Permitir clics fuera del modal preConfirm: preConfirm, // Función previa a la confirmación didRender: didRender, // Función ejecutada tras renderizar el modal }); return { value, isConfirmed }; } catch (error) { console.error('Error mostrando el Swal:', error.message); return { value: null, isConfirmed: false }; } } });