diff --git a/CreditContract/contracts/score/src/lib.rs b/CreditContract/contracts/score/src/lib.rs index ea3b40d..2987858 100644 --- a/CreditContract/contracts/score/src/lib.rs +++ b/CreditContract/contracts/score/src/lib.rs @@ -34,7 +34,7 @@ pub enum Error { MetaInvalida = 11, } -// ─── Storage Keys ───────────────────────────────────────────────────────────── +// Storage Keys #[contracttype] pub enum DataKey { @@ -56,7 +56,7 @@ pub enum DataKey { UsdcToken, } -// ─── Constantes del modelo de negocio ───────────────────────────────────────── +// Constantes del modelo de negocio /// Depósito mínimo: 2 USDC (en stroops, 7 decimales Stellar). const MIN_DEPOSIT: i128 = 20_000_000; @@ -71,7 +71,7 @@ const PRESTAMO_MAX_MESES: u32 = 24; /// 1 USDC = 10_000_000 stroops. const STROOP: i128 = 10_000_000; -// ─── Contrato ───────────────────────────────────────────────────────────────── +// Contrato #[contract] pub struct MananaSeguroContract; diff --git a/CreditContract/contracts/score/src/test.rs b/CreditContract/contracts/score/src/test.rs index 5517abe..4814e4e 100644 --- a/CreditContract/contracts/score/src/test.rs +++ b/CreditContract/contracts/score/src/test.rs @@ -64,7 +64,7 @@ fn test_depositar_minimo() { #[test] fn test_depositar_bajo_minimo() { let (_env, cliente, _admin, usuario, _usdc) = setup(); - // $1 USDC = 10_000_000 stroops — debe fallar con MontoBajoMinimo + // $1 USDC = 10_000_000 stroops, debe fallar con MontoBajoMinimo let res = cliente.try_depositar(&usuario, &10_000_000, &20); assert_eq!(res, Err(Ok(Error::MontoBajoMinimo))); } @@ -72,11 +72,11 @@ fn test_depositar_bajo_minimo() { #[test] fn test_depositar_anios_bloqueo_invalidos() { let (_env, cliente, _admin, usuario, _usdc) = setup(); - // Bloqueo 0 años — debe fallar con AniosBloqueoInvalidos + // Bloqueo 0 años, debe fallar con AniosBloqueoInvalidos let res_cero = cliente.try_depositar(&usuario, &20_000_000, &0); assert_eq!(res_cero, Err(Ok(Error::AniosBloqueoInvalidos))); - // Bloqueo 41 años — debe fallar con AniosBloqueoInvalidos + // Bloqueo 41 años, debe fallar con AniosBloqueoInvalidos let res_excede = cliente.try_depositar(&usuario, &20_000_000, &41); assert_eq!(res_excede, Err(Ok(Error::AniosBloqueoInvalidos))); } @@ -113,7 +113,7 @@ fn test_retirar_meta_alcanzada() { let primer_deposito = 100_000_000i128; cliente.depositar(&usuario, &primer_deposito, &1); - // Meta = 1_000_000_000 — depositar más para alcanzarla + // Meta = 1_000_000_000, depositar más para alcanzarla let usdc_admin = token::StellarAssetClient::new(&env, &usdc); usdc_admin.mint(&usuario, &2_000_000_000); @@ -185,7 +185,7 @@ fn test_autoprestamo_excede_limite() { let (_env, cliente, _admin, usuario, _usdc) = setup(); cliente.depositar(&usuario, &100_000_000, &20); - // Solicitar 40% — debe fallar con ExcedeLimitePrestamo + // Solicitar 40%, debe fallar con ExcedeLimitePrestamo let res = cliente.try_solicitar_prestamo(&usuario, &40_000_000); assert_eq!(res, Err(Ok(Error::ExcedeLimitePrestamo))); } @@ -223,7 +223,7 @@ fn test_no_retirar_con_prestamo_activo() { l.timestamp += 365 * 24 * 3600 + 1; }); - // Intentar retirar con préstamo activo — debe fallar con PrestamoPendiente + // Intentar retirar con préstamo activo, debe fallar con PrestamoPendiente let res = cliente.try_retirar(&usuario); assert_eq!(res, Err(Ok(Error::PrestamoPendiente))); } diff --git a/CreditContract/retiro_chain/contracts/retiro_chain/src/test.rs b/CreditContract/retiro_chain/contracts/retiro_chain/src/test.rs index f43e62d..f521894 100644 --- a/CreditContract/retiro_chain/contracts/retiro_chain/src/test.rs +++ b/CreditContract/retiro_chain/contracts/retiro_chain/src/test.rs @@ -10,7 +10,7 @@ fn deploy(env: &Env) -> RetiroChainClient<'_> { RetiroChainClient::new(env, &contract_id) } -// ─── Core tests ────────────────────────────────────────────────────────────── +// Core tests // inicializar computes ver_retiro = now + anos * seconds_per_year #[test] @@ -79,7 +79,7 @@ fn test_puede_retirar_true_after_date() { assert!(client.puede_retirar()); } -// ─── Edge cases ────────────────────────────────────────────────────────────── +// Edge cases // anos = 0 → retirement date equals the current timestamp // the >= check means puede_retirar is immediately true @@ -149,7 +149,7 @@ fn test_ver_balance_sin_inicializar() { assert_eq!(client.ver_balance(), 0); } -// depositar also works before inicializar — unwrap_or(0) seeds the missing balance key +// depositar also works before inicializar , unwrap_or(0) seeds the missing balance key #[test] fn test_depositar_sin_inicializar() { let env = Env::default(); @@ -160,7 +160,7 @@ fn test_depositar_sin_inicializar() { } // re-calling inicializar overwrites the retirement date because there is no -// re-initialization guard — this test documents that behaviour so it is explicit +// re-initialization guard , this test documents that behaviour so it is explicit #[test] fn test_inicializar_sobrescribe_retiro() { let env = Env::default(); diff --git a/CreditRoot/src/components/ErrorBoundary.jsx b/CreditRoot/src/components/ErrorBoundary.jsx index 1a58a3d..b17973e 100644 --- a/CreditRoot/src/components/ErrorBoundary.jsx +++ b/CreditRoot/src/components/ErrorBoundary.jsx @@ -1,19 +1,19 @@ // src/components/ErrorBoundary.jsx -// +// // Error Boundary global de la app. -// +// // React requiere que los Error Boundaries sean componentes de clase: los // hooks (useState/useEffect) NO pueden capturar errores de renderizado de los // hijos. Por eso este componente es una clase y no una función. -// +// // Captura cualquier error que se lance durante el render / lifecycle de los // componentes hijos (p. ej. DepositFlow o WithdrawalFlow cuando el polling // contra /api/etherfuse/order-status devuelve una forma inesperada) y muestra // una pantalla de respaldo amigable en lugar de dejar la app en blanco. -// +// // Props: -// children — árbol a proteger -// onReset — (opcional) callback extra al pulsar "Reintentar" +// children , árbol a proteger +// onReset , (opcional) callback extra al pulsar "Reintentar" import { Component, Fragment } from 'react' import { Link } from 'react-router-dom' @@ -38,7 +38,7 @@ export class ErrorBoundary extends Component { console.error('[ErrorBoundary] Componente capturado:', error, errorInfo) // TODO(Sentry): reportar a Sentry cuando esté configurado, p. ej. - // Sentry.captureException(error, { extra: { componentStack: errorInfo?.componentStack } }) + // Sentry.captureException(error, { extra: { componentStack: errorInfo?.componentStack } }) this.setState({ errorInfo }) } diff --git a/CreditRoot/src/data/retirementContent.js b/CreditRoot/src/data/retirementContent.js index 74196d7..5ace0ce 100644 --- a/CreditRoot/src/data/retirementContent.js +++ b/CreditRoot/src/data/retirementContent.js @@ -1,4 +1,4 @@ -// ─── Mañana Seguro — Constantes del modelo de negocio ─────────────────────── +// Mañana Seguro , Constantes del modelo de negocio export const MANANA_SEGURO_RATES = { cetesRate: 6.5, // tasa bruta actual de Banxico (CETES 28 días) @@ -16,7 +16,7 @@ export const MANANA_SEGURO_RATES = { constancyMinDeposit: 20, } -// Incentivos cada 5 años — máximo 7% +// Incentivos cada 5 años , máximo 7% export const INCENTIVE_SCENARIOS = [ { key: 'solo_fidelidad', label: 'Solo fidelidad', pct: 5, description: 'Mantienes tu ahorro sin retirar' }, { key: 'fidelidad_constancia', label: 'Fidelidad + constancia ($20/mes)', pct: 7, description: '+$20 USDC mensuales mínimo' }, @@ -31,7 +31,7 @@ export const plannerDefaults = { } // NOTA: retirementStats.value se actualiza dinámicamente en HomeScreen -// usando useEtherfuseRate() — este valor es solo el fallback inicial +// usando useEtherfuseRate() , este valor es solo el fallback inicial export const retirementStats = [ { label: 'Mexicanos sin pensión', value: '32M', caption: 'Trabajadores informales sin acceso al sistema tradicional.', tone: 'accent' }, { label: 'Rendimiento vía Etherfuse', value: '~5.5%', caption: 'APY en USDC que recibe el usuario. Respaldado por CETES.', tone: 'brand' }, diff --git a/CreditRoot/src/features/access/components/ConnectAccountCard.jsx b/CreditRoot/src/features/access/components/ConnectAccountCard.jsx index 3862635..5878e67 100644 --- a/CreditRoot/src/features/access/components/ConnectAccountCard.jsx +++ b/CreditRoot/src/features/access/components/ConnectAccountCard.jsx @@ -1,7 +1,7 @@ // src/features/access/components/ConnectAccountCard.jsx -// +// // Muestra la información del usuario autenticado con Google. -// Ya no pide conectar Freighter — el modelo es custodial via Supabase. +// Ya no pide conectar Freighter , el modelo es custodial via Supabase. import { useTranslation } from 'react-i18next' @@ -35,7 +35,7 @@ export function ConnectAccountCard({ usuario }) { ) } - // Usuario autenticado — mostrar sus datos + // Usuario autenticado , mostrar sus datos const panels = [ { label: t('connectCard.panelEstado'), diff --git a/CreditRoot/src/features/dashboard/components/AutoloanCard.jsx b/CreditRoot/src/features/dashboard/components/AutoloanCard.jsx index 5f77c03..0e403c7 100644 --- a/CreditRoot/src/features/dashboard/components/AutoloanCard.jsx +++ b/CreditRoot/src/features/dashboard/components/AutoloanCard.jsx @@ -48,7 +48,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) { setFase('form') } } catch { - /* contrato sin datos aún — mostramos formulario */ + /* contrato sin datos aún, mostramos formulario */ setFase('form') } } @@ -103,7 +103,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) { setRequested(Math.max(10, Math.min(250, Math.floor(maxLoan)))) } - // ── FASE: Cargando ──────────────────────────────────────────────────────────── + // FASE: Cargando if (fase === 'cargando') return (
@@ -116,7 +116,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) { return (
- {/* ── Header ── */} + {/* Header */}
@@ -136,7 +136,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) {
- {/* ── Sin saldo bloqueado ── */} + {/* Sin saldo bloqueado */} {lockedBalance === 0 && fase === 'form' && (
@@ -148,7 +148,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) {
)} - {/* ── FASE: Formulario ── */} + {/* FASE: Formulario */} {fase === 'form' && lockedBalance > 0 && (
@@ -266,7 +266,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) {
)} - {/* ── FASE: Confirmando ── */} + {/* FASE: Confirmando */} {fase === 'confirmando' && (
@@ -294,7 +294,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) {
)} - {/* ── FASE: Procesando ── */} + {/* FASE: Procesando */} {fase === 'procesando' && (
@@ -304,7 +304,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) {
)} - {/* ── FASE: Error ── */} + {/* FASE: Error */} {fase === 'error' && (
@@ -318,7 +318,7 @@ export function AutoloanCard({ lockedBalance = 0, walletAddress = null }) {
)} - {/* ── FASE: Activo ── */} + {/* FASE: Activo */} {fase === 'activo' && (
diff --git a/CreditRoot/src/features/dashboard/components/ContributionHistory.jsx b/CreditRoot/src/features/dashboard/components/ContributionHistory.jsx index 8e19b06..c82ae4c 100644 --- a/CreditRoot/src/features/dashboard/components/ContributionHistory.jsx +++ b/CreditRoot/src/features/dashboard/components/ContributionHistory.jsx @@ -1,6 +1,6 @@ // src/features/dashboard/components/ContributionHistory.jsx -// -// Historial de aportaciones — lee órdenes reales de Supabase +// +// Historial de aportaciones , lee órdenes reales de Supabase // Ya no usa Stellar/Soroban ni localStorage import { useState, useEffect } from 'react' @@ -40,7 +40,7 @@ export function ContributionHistory() { return (
- {/* ── Resumen ── */} + {/* Resumen */}
@@ -83,7 +83,7 @@ export function ContributionHistory() {
- {/* ── Lista de órdenes ── */} + {/* Lista de órdenes */}
diff --git a/CreditRoot/src/features/dashboard/components/RetirementSnapshot.jsx b/CreditRoot/src/features/dashboard/components/RetirementSnapshot.jsx index 21fb531..d78e7de 100644 --- a/CreditRoot/src/features/dashboard/components/RetirementSnapshot.jsx +++ b/CreditRoot/src/features/dashboard/components/RetirementSnapshot.jsx @@ -1,11 +1,11 @@ // src/features/dashboard/components/RetirementSnapshot.jsx -// -// ─── ISO 25010 ─────────────────────────────────────────────────────────────── -// Seguridad: Lee usuario del localStorage — nunca expone datos ajenos. +// +// ISO 25010 +// Seguridad: Lee usuario del localStorage , nunca expone datos ajenos. // Fiabilidad: Manejo de errores en cada fetch. Estados de carga explícitos. // Mantenibilidad: Lógica de metas separada en GoalCard/GoalSetup. // Usabilidad: Primera vez → configurador de meta. Regresa → dashboard completo. -// ───────────────────────────────────────────────────────────────────────────── +// import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -28,7 +28,7 @@ import { GoalSetup as GoalSetupComponent } from '../../goals/components/GoalSetu export function RetirementSnapshot() { const { t } = useTranslation() - // ── Estado de datos ─────────────────────────────────────────────────────── + // Estado de datos const [lockedBalance, setLockedBalance] = useState(0) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) @@ -36,24 +36,24 @@ export function RetirementSnapshot() { const [activeTab, setActiveTab] = useState('resumen') const [showDeposit, setShowDeposit] = useState(false) - // ── Estado de metas ─────────────────────────────────────────────────────── + // Estado de metas const [metas, setMetas] = useState([]) // todas las metas del usuario const [metaSeleccionada, setMetaSeleccionada] = useState(null) const metaIniciadaRef = useRef(false) // evita loop infinito const [metaEditando, setMetaEditando] = useState(null) const [loadingMetas, setLoadingMetas] = useState(true) - // ── Hooks de tasas ──────────────────────────────────────────────────────── + // Hooks de tasas const { cetesRate, userRate, platformRate } = useEtherfuseRate() - // ── Contador animado de rendimiento ────────────────────────────────────── + // Contador animado de rendimiento const { isGrowing, yieldTodayMxn: yieldTodayMxnAnimado, displayBalance: saldoAnimado, } = useYieldCounterDirectMxn(lockedBalance, userRate, lockedBalance > 0) - // ── Tabs ────────────────────────────────────────────────────────────────── + // Tabs const tabs = [ { key: 'resumen', label: t('snapshot.tabs.resumen') }, { key: 'historial', label: t('snapshot.tabs.historial') }, @@ -77,7 +77,7 @@ export function RetirementSnapshot() { setTimeout(() => { document.getElementById(`tab-${tabs[newIndex].key}`)?.focus() }, 0) } - // ── Cargar órdenes completadas ──────────────────────────────────────────── + // Cargar órdenes completadas const cargarDatos = useCallback(async () => { setSinSesionError(false) try { @@ -102,7 +102,7 @@ export function RetirementSnapshot() { } }, [t]) - // ── Cargar metas del usuario ────────────────────────────────────────────── + // Cargar metas del usuario const cargarMetas = useCallback(async () => { try { const usuarioGuardado = JSON.parse(localStorage.getItem('ms_usuario') || 'null') @@ -114,7 +114,7 @@ export function RetirementSnapshot() { const metasData = data.metas ?? [] setMetas(metasData) - // Seleccionar la meta principal solo la primera vez — usar ref para + // Seleccionar la meta principal solo la primera vez , usar ref para // evitar que metaSeleccionada sea dependencia y cause loop infinito if (metasData.length > 0 && !metaIniciadaRef.current) { metaIniciadaRef.current = true @@ -122,31 +122,31 @@ export function RetirementSnapshot() { setMetaSeleccionada(principal) } } catch { - // fallo silencioso — las metas son opcionales en el render + // fallo silencioso , las metas son opcionales en el render } finally { setLoadingMetas(false) } - }, []) // sin dependencias — metaIniciadaRef es estable + }, []) // sin dependencias, metaIniciadaRef es estable useEffect(() => { cargarDatos() cargarMetas() }, [cargarDatos, cargarMetas]) - // ── Cuando se crea una nueva meta ───────────────────────────────────────── + // Cuando se crea una nueva meta function handleMetaCreada(nuevaMeta) { setMetas(prev => [...prev, nuevaMeta]) setMetaSeleccionada(nuevaMeta) } - // ── Cuando se edita una meta ────────────────────────────────────────────── + // Cuando se edita una meta function handleMetaEditada(metaActualizada) { setMetas(prev => prev.map(m => m.id === metaActualizada.id ? metaActualizada : m)) if (metaSeleccionada?.id === metaActualizada.id) setMetaSeleccionada(metaActualizada) setMetaEditando(null) } - // ── Cuando se elimina una meta ──────────────────────────────────────────── + // Cuando se elimina una meta async function handleEliminarMeta(meta) { const usuario = JSON.parse(localStorage.getItem('ms_usuario') || 'null') if (!usuario?.id) return @@ -168,7 +168,7 @@ export function RetirementSnapshot() { } catch { /* fallo silencioso */ } } - // ── Cálculos de la meta seleccionada ───────────────────────────────────── + // Cálculos de la meta seleccionada const metaMxn = metaSeleccionada?.monto_objetivo_mxn ?? 10000 const lockedBalanceMxn = lockedBalance // ya en MXN desde Supabase const proyeccion20Mxn = lockedBalance * Math.pow(1 + userRate / 100, 20) @@ -191,7 +191,7 @@ export function RetirementSnapshot() { return (
- {/* ── Header ── */} + {/* Header */}
@@ -208,15 +208,15 @@ export function RetirementSnapshot() {
- {/* ── PRIMERA VEZ: Configurador de meta ── */} + {/* PRIMERA VEZ: Configurador de meta */} {primeraVez && usuario && ( )} - {/* ── CON METAS: Botón depositar + row de metas ── */} + {/* CON METAS: Botón depositar + row de metas */} {tieneMetas && ( <> - {/* Botón depositar — arriba de todo cuando ya hay meta */} + {/* Botón depositar , arriba de todo cuando ya hay meta */} {!showDeposit && (
- {/* ── Panel: Resumen ── */} + {/* Panel: Resumen */} {activeTab === 'resumen' && (
@@ -440,14 +440,14 @@ export function RetirementSnapshot() {
)} - {/* ── Panel: Historial ── */} + {/* Panel: Historial */} {activeTab === 'historial' && (
)} - {/* ── Panel: Ciclos ── */} + {/* Panel: Ciclos */} {activeTab === 'ciclos' && (
@@ -494,28 +494,28 @@ export function RetirementSnapshot() {
)} - {/* ── Panel: Préstamo ── */} + {/* Panel: Préstamo */} {activeTab === 'prestamo' && (
)} - {/* ── Panel: Referidos ── */} + {/* Panel: Referidos */} {activeTab === 'referidos' && (
)} - {/* ── Panel: Carlos ── */} + {/* Panel: Carlos */} {activeTab === 'carlos' && (
)} - {/* ── Panel: Ingresos ── */} + {/* Panel: Ingresos */} {activeTab === 'ingresos' && (
diff --git a/CreditRoot/src/features/deposit/components/DepositFlow.jsx b/CreditRoot/src/features/deposit/components/DepositFlow.jsx index 4bbc1c6..6c6362c 100644 --- a/CreditRoot/src/features/deposit/components/DepositFlow.jsx +++ b/CreditRoot/src/features/deposit/components/DepositFlow.jsx @@ -1,13 +1,13 @@ // src/features/deposit/components/DepositFlow.jsx -// +// // Flujo de depósito SPEI → CETES via Etherfuse -// Usa las Netlify functions del backend — sin Freighter, sin etherfuseRamp.js -// +// Usa las Netlify functions del backend , sin Freighter, sin etherfuseRamp.js +// // Props: -// usuarioId — ID del usuario en Supabase -// kycStatus — estado KYC del usuario ('pending' | 'approved') -// onComplete — callback cuando el depósito se confirma -// onClose — callback para cerrar +// usuarioId , ID del usuario en Supabase +// kycStatus , estado KYC del usuario ('pending' | 'approved') +// onComplete , callback cuando el depósito se confirma +// onClose , callback para cerrar import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -50,7 +50,7 @@ function DepositSkeleton() { export function DepositFlow({ usuarioId, kycStatus, onComplete, onClose }) { const { t } = useTranslation() - // ── Debug: forzar un crash para probar el ErrorBoundary ─────────────────── + // Debug: forzar un crash para probar el ErrorBoundary // Visita la app con ?crash=1 en la URL para lanzar un error durante el render // y verificar que el ErrorBoundary global muestra el fallback en vez de la // pantalla en blanco. @@ -70,7 +70,7 @@ export function DepositFlow({ usuarioId, kycStatus, onComplete, onClose }) { const [fetchingKyc, setFetchingKyc] = useState(false) const [error, setError] = useState(null) - // ── Polling del estado de la orden ──────────────────────────────────────── + // Polling del estado de la orden useEffect(() => { if (!order?.orderId || orderStatus === 'completed') return @@ -92,7 +92,7 @@ export function DepositFlow({ usuarioId, kycStatus, onComplete, onClose }) { return () => clearInterval(interval) }, [order, orderStatus, onComplete]) - // ── KYC: abrir Etherfuse ────────────────────────────────────────────────── + // KYC: abrir Etherfuse async function handleStartKyc() { setFetchingKyc(true) setError(null) @@ -114,7 +114,7 @@ export function DepositFlow({ usuarioId, kycStatus, onComplete, onClose }) { } } - // ── Depósito: crear orden ───────────────────────────────────────────────── + // Depósito: crear orden async function handleDepositar() { setLoading(true) setError(null) @@ -147,7 +147,7 @@ export function DepositFlow({ usuarioId, kycStatus, onComplete, onClose }) { return (
- {/* ── KYC ── */} + {/* KYC */} {step === STEPS.KYC && ( <>
@@ -176,7 +176,7 @@ export function DepositFlow({ usuarioId, kycStatus, onComplete, onClose }) { )} - {/* ── Monto ── */} + {/* Monto */} {step === STEPS.AMOUNT && ( <>
@@ -232,7 +232,7 @@ export function DepositFlow({ usuarioId, kycStatus, onComplete, onClose }) { )} - {/* ── CLABE ── */} + {/* CLABE */} {step === STEPS.CLABE && order && ( <>
@@ -270,7 +270,7 @@ export function DepositFlow({ usuarioId, kycStatus, onComplete, onClose }) { )} - {/* ── Done ── */} + {/* Done */} {step === STEPS.DONE && (
diff --git a/CreditRoot/src/features/goals/components/GoalCard.jsx b/CreditRoot/src/features/goals/components/GoalCard.jsx index 0e333c2..9614cdd 100644 --- a/CreditRoot/src/features/goals/components/GoalCard.jsx +++ b/CreditRoot/src/features/goals/components/GoalCard.jsx @@ -1,6 +1,6 @@ // src/features/goals/components/GoalCard.jsx -// -// Tarjeta de meta de ahorro — seleccionable. +// +// Tarjeta de meta de ahorro , seleccionable. // Muestra nombre, progreso, ahorro mensual y meta objetivo. import { useState } from 'react' @@ -94,7 +94,7 @@ export function GoalCard({ meta, saldoMxn = 0, seleccionada = false, onSeleccion ) } -// ─── Modal de edición ───────────────────────────────────────────────────────── +// Modal de edición export function GoalEditModal({ meta, usuarioId, onGuardado, onCerrar }) { const { t } = useTranslation() @@ -186,7 +186,7 @@ export function GoalEditModal({ meta, usuarioId, onGuardado, onCerrar }) { ) } -// ─── Botón para agregar nueva meta ──────────────────────────────────────────── +// Botón para agregar nueva meta export function AddGoalButton({ usuarioId, onMetaCreada }) { const { t } = useTranslation() diff --git a/CreditRoot/src/features/goals/components/GoalSetup.jsx b/CreditRoot/src/features/goals/components/GoalSetup.jsx index f20ce46..a6a64e9 100644 --- a/CreditRoot/src/features/goals/components/GoalSetup.jsx +++ b/CreditRoot/src/features/goals/components/GoalSetup.jsx @@ -1,8 +1,8 @@ // src/features/goals/components/GoalSetup.jsx -// +// // Componente de configuración de primera meta. // Se muestra cuando el usuario no tiene ninguna meta creada. -// Simplificado — solo ahorro mensual y años al retiro. +// Simplificado , solo ahorro mensual y años al retiro. import { useState } from 'react' import { useTranslation } from 'react-i18next' @@ -83,7 +83,7 @@ export function GoalSetup({ usuario, onMetaCreada }) {
- {/* Lado izquierdo — configuración */} + {/* Lado izquierdo , configuración */}
{/* Ahorro mensual */} @@ -153,7 +153,7 @@ export function GoalSetup({ usuario, onMetaCreada }) {
- {/* Lado derecho — proyección */} + {/* Lado derecho , proyección */}

{t('goalSetup.totalAcumularas')}

diff --git a/CreditRoot/src/features/planner/components/ContributionPlanner.jsx b/CreditRoot/src/features/planner/components/ContributionPlanner.jsx index 2942afa..d7bb04b 100644 --- a/CreditRoot/src/features/planner/components/ContributionPlanner.jsx +++ b/CreditRoot/src/features/planner/components/ContributionPlanner.jsx @@ -1,6 +1,6 @@ // src/features/planner/components/ContributionPlanner.jsx -// Configurador de meta de ahorro — integrado en el Dashboard -// Ya no usa Freighter ni Stellar — el depósito real se hace desde DepositFlow +// Configurador de meta de ahorro , integrado en el Dashboard +// Ya no usa Freighter ni Stellar , el depósito real se hace desde DepositFlow import { useState } from 'react' import { useTranslation } from 'react-i18next' @@ -72,7 +72,7 @@ export function ContributionPlanner() { return (
- {/* ── Configurador ── */} + {/* Configurador */}
@@ -185,7 +185,7 @@ export function ContributionPlanner() {
- {/* ── Proyección ── */} + {/* Proyección */}
@@ -222,7 +222,7 @@ export function ContributionPlanner() {
- {/* ── Ciclos ── */} + {/* Ciclos */} {showCycles && cycles.length > 0 && (
@@ -261,7 +261,7 @@ export function ContributionPlanner() {
)} - {/* ── Info — sin bloquear con Freighter ── */} + {/* Info , sin bloquear con Freighter */}
💡 diff --git a/CreditRoot/src/features/referrals/components/ReferralModule.jsx b/CreditRoot/src/features/referrals/components/ReferralModule.jsx index 79c5ee0..38f8bcb 100644 --- a/CreditRoot/src/features/referrals/components/ReferralModule.jsx +++ b/CreditRoot/src/features/referrals/components/ReferralModule.jsx @@ -61,7 +61,7 @@ export function ReferralModule({ userName = 'Usuario', walletAddress = null }) { return (
- {/* ── Tier actual ── */} + {/* Tier actual */}
@@ -124,7 +124,7 @@ export function ReferralModule({ userName = 'Usuario', walletAddress = null }) {
- {/* ── Link de referido ── */} + {/* Link de referido */}
{t('referrals.tuLink')}
@@ -149,7 +149,7 @@ export function ReferralModule({ userName = 'Usuario', walletAddress = null }) {

- {/* ── Invitar por correo ── */} + {/* Invitar por correo */}
{t('referrals.invitarCorreo')}
@@ -172,7 +172,7 @@ export function ReferralModule({ userName = 'Usuario', walletAddress = null }) { )}
- {/* ── Lista de referidos ── */} + {/* Lista de referidos */} {referrals.length > 0 && (
{t('referrals.misReferidos')}
@@ -228,7 +228,7 @@ export function ReferralModule({ userName = 'Usuario', walletAddress = null }) { ) } -// ─── Helpers ───────────────────────────────────────────────────────────────── +// Helpers function generateCode(seed) { let hash = 0 for (let i = 0; i < seed.length; i++) { diff --git a/CreditRoot/src/features/simulator/components/CarlosSimulator.jsx b/CreditRoot/src/features/simulator/components/CarlosSimulator.jsx index f779e8a..78b2bbc 100644 --- a/CreditRoot/src/features/simulator/components/CarlosSimulator.jsx +++ b/CreditRoot/src/features/simulator/components/CarlosSimulator.jsx @@ -47,7 +47,7 @@ export function CarlosSimulator() { return (
- {/* ── Header stepper ── */} + {/* Header stepper */}
🛵 @@ -81,7 +81,7 @@ export function CarlosSimulator() {
- {/* ── Parámetros ── */} + {/* Parámetros */}
{t('carlos.personaliza')}
@@ -119,7 +119,7 @@ export function CarlosSimulator() {
- {/* ── Step 0: Perfil ── */} + {/* Step 0: Perfil */} {step === 0 && (
{t('carlos.perfilTitulo')}
@@ -145,7 +145,7 @@ export function CarlosSimulator() {
)} - {/* ── Step 1: Ciclos ── */} + {/* Step 1: Ciclos */} {step === 1 && (
@@ -189,7 +189,7 @@ export function CarlosSimulator() {
)} - {/* ── Step 2: Emergencia ── */} + {/* Step 2: Emergencia */} {step === 2 && (
@@ -259,7 +259,7 @@ export function CarlosSimulator() {
)} - {/* ── Step 3: Resultado ── */} + {/* Step 3: Resultado */} {step === 3 && (
@@ -329,7 +329,7 @@ export function CarlosSimulator() { ) } -// ─── Helpers ───────────────────────────────────────────────────────────────── +// Helpers function estimateSaldoMes(mensual, meses, annualRate) { const monthlyRate = annualRate / 100 / 12 let balance = 0 diff --git a/CreditRoot/src/features/withdrawal/components/WithdrawalFlow.jsx b/CreditRoot/src/features/withdrawal/components/WithdrawalFlow.jsx index 01c42c9..bb634fc 100644 --- a/CreditRoot/src/features/withdrawal/components/WithdrawalFlow.jsx +++ b/CreditRoot/src/features/withdrawal/components/WithdrawalFlow.jsx @@ -1,5 +1,5 @@ // src/features/withdrawal/components/WithdrawalFlow.jsx -// Flujo de retiro — lee datos de Supabase, sin Freighter/Stellar +// Flujo de retiro , lee datos de Supabase, sin Freighter/Stellar // El retiro real se habilitará cuando Etherfuse lance su API de retiros import { useState, useEffect, useCallback } from 'react' @@ -105,7 +105,7 @@ export function WithdrawalFlow({ meta = 175000 }) { return (
- {/* ── Verificando ── */} + {/* Verificando */} {fase === 'verificando' && (
)} - {/* ── Meta no alcanzada ── */} + {/* Meta no alcanzada */} {fase === 'no_alcanzada' && (
@@ -216,7 +216,7 @@ export function WithdrawalFlow({ meta = 175000 }) {
)} - {/* ── Meta alcanzada ── */} + {/* Meta alcanzada */} {fase === 'alcanzada' && (
@@ -266,7 +266,7 @@ export function WithdrawalFlow({ meta = 175000 }) {
)} - {/* ── Error ── */} + {/* Error */} {fase === 'error' && (
diff --git a/CreditRoot/src/hooks/useEtherfuseRate.js b/CreditRoot/src/hooks/useEtherfuseRate.js index 5ac2cae..711595e 100644 --- a/CreditRoot/src/hooks/useEtherfuseRate.js +++ b/CreditRoot/src/hooks/useEtherfuseRate.js @@ -20,7 +20,7 @@ export function useEtherfuseRate() { async function fetchRate() { try { - // Llama al proxy local de Vite — el SDK corre en Node, sin CORS + // Llama al proxy local de Vite , el SDK corre en Node, sin CORS const res = await fetch('/api/cetes-rate') if (!res.ok) throw new Error(`HTTP ${res.status}`) const { rate } = await res.json() @@ -81,7 +81,7 @@ function saveToCache(rate) { try { localStorage.setItem(CACHE_KEY, JSON.stringify({ rate, ts: Date.now() })) } catch { - // fallo silencioso — el cache es opcional + // fallo silencioso , el cache es opcional } } diff --git a/CreditRoot/src/hooks/useExchangeRate.js b/CreditRoot/src/hooks/useExchangeRate.js index 510bec5..4b9c51c 100644 --- a/CreditRoot/src/hooks/useExchangeRate.js +++ b/CreditRoot/src/hooks/useExchangeRate.js @@ -1,6 +1,6 @@ // src/hooks/useExchangeRate.js // Tipo de cambio USD/MXN en tiempo real desde Banxico -// Caché de 4 horas — Banxico actualiza el FIX una vez al día hábil +// Caché de 4 horas , Banxico actualiza el FIX una vez al día hábil import { useEffect, useState } from 'react' diff --git a/CreditRoot/src/hooks/useYieldCounter.js b/CreditRoot/src/hooks/useYieldCounter.js index ae26d87..3a0f5f8 100644 --- a/CreditRoot/src/hooks/useYieldCounter.js +++ b/CreditRoot/src/hooks/useYieldCounter.js @@ -5,7 +5,7 @@ const TICK_MS = 100 export function useYieldCounterDirectMxn(realBalance, annualYieldRate, running = true) { // El tiempo vive en state. Cada tick lo actualiza desde el interval (async, permitido). - // El render solo lee este valor — puro. + // El render solo lee este valor , puro. const [currentTime, setCurrentTime] = useState(() => Date.now()) useEffect(() => { @@ -14,7 +14,7 @@ export function useYieldCounterDirectMxn(realBalance, annualYieldRate, running = return () => clearInterval(intervalId) }, [running, realBalance]) - // ── Caso pausado ── + // Caso pausado if (!running || realBalance <= 0) { return { isGrowing: false, @@ -23,7 +23,7 @@ export function useYieldCounterDirectMxn(realBalance, annualYieldRate, running = } } - // ── Cálculo derivado (puro: solo depende de currentTime + props) ── + // Cálculo derivado (puro: solo depende de currentTime + props) const ratePerMs = (annualYieldRate || 0) / 100 / (365 * 24 * 60 * 60 * 1000) const now = new Date(currentTime) const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime() diff --git a/CreditRoot/src/lib/etherfuseRamp.js b/CreditRoot/src/lib/etherfuseRamp.js index e1fb163..4e30bdf 100644 --- a/CreditRoot/src/lib/etherfuseRamp.js +++ b/CreditRoot/src/lib/etherfuseRamp.js @@ -2,7 +2,7 @@ const RAMP_PROXY = '/api/etherfuse-ramp' -// Helper interno +// Helper interno async function rampFetch(action, method = 'GET', body = null) { const url = `${RAMP_PROXY}?action=${action}` const options = { method, headers: { 'Content-Type': 'application/json' } } @@ -18,13 +18,13 @@ async function rampFetch(action, method = 'GET', body = null) { return data } -// Activos disponibles en Stellar +// Activos disponibles en Stellar // Retorna la lista de tokens que Etherfuse puede rampar (MXNe, CETES, USDC, etc.) export async function getAvailableAssets() { return rampFetch('assets', 'GET') } -// KYC: obtener URL hosted de Etherfuse +// KYC: obtener URL hosted de Etherfuse // El usuario completa su KYC en la página de Etherfuse, luego regresa a tu app // Llama esto ANTES de permitirle depositar a un usuario nuevo export async function getKycUrl(walletAddress, email) { @@ -32,47 +32,47 @@ export async function getKycUrl(walletAddress, email) { // Retorna: { url: "https://devnet.etherfuse.com/onboarding/..." } } -// KYC: verificar estado ─ +// KYC: verificar estado // Statuses: 'not_started' | 'proposed' | 'approved' | 'rejected' export async function getKycStatus(customerId, walletAddress) { return rampFetch(`kyc-status&customerId=${customerId}&walletAddress=${walletAddress}`, 'GET') } -// Cotizar depósito MXN → token en Stellar +// Cotizar depósito MXN → token en Stellar // amountMxn: número (ej. 500) // targetAsset: string en formato CODE:ISSUER (ej. "USDC:GBBD47IF6...") -// o el identifier que viene de getAvailableAssets() +// o el identifier que viene de getAvailableAssets() // Retorna: { quoteId, sourceAmount, targetAmount, feeBps, feeAmount, expiresAt } export async function getDepositQuote({ walletAddress, amountMxn, targetAsset, customerId }) { return rampFetch('quote', 'POST', { walletAddress, amountMxn, targetAsset, customerId }) } -// Crear orden y obtener CLABE SPEI +// Crear orden y obtener CLABE SPEI // Retorna la CLABE única a la que el usuario debe mandar el SPEI -// depositClabe: "646180XXXXXXXXXX" — CLABE STP de Etherfuse para este usuario +// depositClabe: "646180XXXXXXXXXX" , CLABE STP de Etherfuse para este usuario // IMPORTANTE: el usuario debe mandar EXACTAMENTE el monto del quote, ni un peso más ni menos export async function createDepositOrder({ quoteId, bankAccountId, cryptoWalletId }) { return rampFetch('order', 'POST', { quoteId, bankAccountId, cryptoWalletId }) // Retorna: // { - // orderId: string, - // depositClabe: "646180XXXXXXXXXX", - // depositBankName: "STP", - // depositAccountHolder: "Etherfuse MX", - // statusPage: "https://devnet.etherfuse.com/ramp/order/...", - // status: "created" + // orderId: string, + // depositClabe: "646180XXXXXXXXXX", + // depositBankName: "STP", + // depositAccountHolder: "Etherfuse MX", + // statusPage: "https://devnet.etherfuse.com/ramp/order/...", + // status: "created" // } } -// Verificar estado de una orden ─ +// Verificar estado de una orden // Statuses: 'created' | 'funded' | 'completed' // Cuando status === 'completed' y la wallet es nueva, viene: -// stellarClaimTransaction: XDR sin firmar — el usuario lo firma con Freighter +// stellarClaimTransaction: XDR sin firmar , el usuario lo firma con Freighter export async function getOrderStatus(orderId) { return rampFetch(`order-status&orderId=${orderId}`, 'GET') } -// Flujo completo: polling hasta que la orden se complete +// Flujo completo: polling hasta que la orden se complete // Llama a esto después de mostrarle la CLABE al usuario // onStatusChange(status) se llama cada vez que cambia el estado export async function waitForOrderCompletion(orderId, onStatusChange, timeoutMs = 30 * 60 * 1000) { diff --git a/CreditRoot/src/lib/stellar.js b/CreditRoot/src/lib/stellar.js index e0196ce..8ecd9f7 100644 --- a/CreditRoot/src/lib/stellar.js +++ b/CreditRoot/src/lib/stellar.js @@ -15,7 +15,7 @@ export const USDC_ASSET = new StellarSdk.Asset( // 1 USDC = 10_000_000 stroops (7 decimales Stellar) export const STROOP = 10_000_000 -// ─── Helper: construir, simular y retornar tx ───────────────────────────────── +// Helper: construir, simular y retornar tx async function buildAndSimulate(account, operations) { const tx = new StellarSdk.TransactionBuilder(account, { fee: StellarSdk.BASE_FEE, @@ -32,13 +32,13 @@ async function buildAndSimulate(account, operations) { return StellarSdk.rpc.assembleTransaction(built, sim).build() } -// ─── Balances de la wallet ──────────────────────────────────────────────────── +// Balances de la wallet export async function getBalances(publicKey) { const account = await server.loadAccount(publicKey) return account.balances } -// ─── Depositar USDC y bloquear ──────────────────────────────────────────────── +// Depositar USDC y bloquear export async function lockFunds(sourcePublicKey, amountUSDC, aniosBloqueo = 20) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(sourcePublicKey) @@ -54,7 +54,7 @@ export async function lockFunds(sourcePublicKey, amountUSDC, aniosBloqueo = 20) ]) } -// ─── Ver saldo bloqueado en el contrato ─────────────────────────────────────── +// Ver saldo bloqueado en el contrato export async function verBalanceContrato(publicKey) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(publicKey) @@ -72,7 +72,7 @@ export async function verBalanceContrato(publicKey) { return Number(raw) / STROOP } -// ─── Ver fecha de retiro ────────────────────────────────────────────────────── +// Ver fecha de retiro export async function verFechaRetiro(publicKey) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(publicKey) @@ -94,7 +94,7 @@ export async function verFechaRetiro(publicKey) { }) } -// ─── Ver meta de retiro ─────────────────────────────────────────────────────── +// Ver meta de retiro export async function verMeta(publicKey) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(publicKey) @@ -111,7 +111,7 @@ export async function verMeta(publicKey) { return Number(raw) / STROOP } -// ─── Ver número de depósitos ────────────────────────────────────────────────── +// Ver número de depósitos export async function verDepositos(publicKey) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(publicKey) @@ -127,7 +127,7 @@ export async function verDepositos(publicKey) { return Number(StellarSdk.scValToNative(sim.result?.retval)) } -// ─── Retirar fondos al llegar la meta ──────────────────────────────────────── +// Retirar fondos al llegar la meta export async function retirarFondos(publicKey) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(publicKey) @@ -140,7 +140,7 @@ export async function retirarFondos(publicKey) { ]) } -// ─── Solicitar autopréstamo ─────────────────────────────────────────────────── +// Solicitar autopréstamo export async function solicitarPrestamo(publicKey, amountUSDC) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(publicKey) @@ -155,7 +155,7 @@ export async function solicitarPrestamo(publicKey, amountUSDC) { ]) } -// ─── Pagar cuota del autopréstamo ───────────────────────────────────────────── +// Pagar cuota del autopréstamo export async function pagarPrestamo(publicKey) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(publicKey) @@ -168,7 +168,7 @@ export async function pagarPrestamo(publicKey) { ]) } -// ─── Ver estado del autopréstamo ────────────────────────────────────────────── +// Ver estado del autopréstamo export async function verPrestamo(publicKey) { const contract = new StellarSdk.Contract(CONTRACT_ID) const account = await rpc.getAccount(publicKey) @@ -188,7 +188,7 @@ export async function verPrestamo(publicKey) { } } -// ─── Enviar transacción firmada ─────────────────────────────────────────────── +// Enviar transacción firmada export async function enviarTransaccion(signedXdr) { const tx = StellarSdk.TransactionBuilder.fromXDR(signedXdr, networkPassphrase) const result = await server.submitTransaction(tx) diff --git a/CreditRoot/src/screens/AuthScreen.jsx b/CreditRoot/src/screens/AuthScreen.jsx index a8a729a..9137c9f 100644 --- a/CreditRoot/src/screens/AuthScreen.jsx +++ b/CreditRoot/src/screens/AuthScreen.jsx @@ -19,7 +19,7 @@ export function AuthScreen({ onAuth, onVolver }) { const [googleListo, setGoogleListo] = useState(false) const googleBtnRef = useRef(null) - // ── Callback de Google — cuando el usuario selecciona su cuenta ──────────── + // Callback de Google , cuando el usuario selecciona su cuenta const handleCredentialResponse = useCallback(async (response) => { if (!response.credential) { setError(t('auth.errorSinCredencial')) @@ -44,7 +44,7 @@ export function AuthScreen({ onAuth, onVolver }) { } }, [onAuth, t]) - // ── Inicializar SDK de Google ─────────────────────────────────────────────── + // Inicializar SDK de Google const inicializarGoogle = useCallback(() => { if (!window.google?.accounts) return window.google.accounts.id.initialize({ @@ -67,7 +67,7 @@ export function AuthScreen({ onAuth, onVolver }) { setGoogleListo(true) }, [handleCredentialResponse]) - // ── Cargar SDK de Google ─────────────────────────────────────────────────── + // Cargar SDK de Google useEffect(() => { if (!GOOGLE_CLIENT_ID) { setError(t('auth.errorConfig')) @@ -86,7 +86,7 @@ export function AuthScreen({ onAuth, onVolver }) { document.head.appendChild(script) }, [inicializarGoogle, t]) - // ── Freighter: conectar wallet ───────────────────────────────────────────── + // Freighter: conectar wallet async function handleConectarFreighter() { setLoading(true) setError(null) @@ -160,7 +160,7 @@ export function AuthScreen({ onAuth, onVolver }) {
- {/* ── Paso inicio ── */} + {/* Paso inicio */} {paso === 'inicio' && (
@@ -222,7 +222,7 @@ export function AuthScreen({ onAuth, onVolver }) {
- {/* Freighter — avanzado */} + {/* Freighter , avanzado */}
)} - {/* ── Paso freighter ── */} + {/* Paso freighter */} {paso === 'freighter' && (
@@ -287,7 +287,7 @@ export function AuthScreen({ onAuth, onVolver }) {
)} - {/* ── Paso nombre (Freighter) ── */} + {/* Paso nombre (Freighter) */} {paso === 'nombre' && (
diff --git a/CreditRoot/src/screens/LandingScreen.jsx b/CreditRoot/src/screens/LandingScreen.jsx index 8eeed55..824c756 100644 --- a/CreditRoot/src/screens/LandingScreen.jsx +++ b/CreditRoot/src/screens/LandingScreen.jsx @@ -13,14 +13,14 @@ export function LandingScreen({ onLogin, onRegister }) { const apy = userRate > 0 ? userRate.toFixed(2) : '—' - // Puntos del hero — todos via i18n para que el cambio de idioma funcione + // Puntos del hero , todos via i18n para que el cambio de idioma funcione const puntos = [ t('landing.puntos.apy', { apy }), t('landing.puntos.spei'), t('landing.puntos.prestamo'), ] - // Bancos — via i18n + // Bancos , via i18n const bancos = t('landing.bancos', { returnObjects: true }) return ( diff --git a/CreditRoot/src/utils/projections.js b/CreditRoot/src/utils/projections.js index 60ac34d..53e3650 100644 --- a/CreditRoot/src/utils/projections.js +++ b/CreditRoot/src/utils/projections.js @@ -1,6 +1,6 @@ import { MANANA_SEGURO_RATES, INCENTIVE_SCENARIOS } from '../data/retirementContent' -// ─── Proyección base con tasa del usuario (4.7%) ───────────────────────────── +// Proyección base con tasa del usuario (4.7%) export function calculateRetirementProjection({ monthlyDepositUsd, yearsToRetirement, @@ -33,7 +33,7 @@ export function calculateRetirementProjection({ } } -// ─── Simulación por ciclos de 5 años (lógica exacta del doc) ───────────────── +// Simulación por ciclos de 5 años (lógica exacta del doc) export function calculateCycles(monthlyDepositUsd, totalYears, userRate, incentivePct) { const cycles = Math.floor(totalYears / 5) let balance = 0 @@ -90,7 +90,7 @@ function calculateTotalIncentives(monthlyDeposit, years, rate, incentivePct) { return cycles.reduce((sum, c) => sum + c.incentiveAmount, 0) } -// ─── Simulador de autopréstamo ──────────────────────────────────────────────── +// Simulador de autopréstamo export function calculateLoan(lockedBalance, requestedAmount) { const maxLoan = lockedBalance * MANANA_SEGURO_RATES.loanMaxPct const amount = Math.min(requestedAmount, maxLoan) @@ -132,7 +132,7 @@ export function calculateLoan(lockedBalance, requestedAmount) { } } -// ─── Ingresos de la plataforma con Carlos ──────────────────────────────────── +// Ingresos de la plataforma con Carlos export function calculatePlatformRevenue(monthlyDeposit, years, platformRate = 1.0) { const monthlyRate = platformRate / 100 / 12 let balance = 0