Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ReWallet Stellar PoC

Pago Patrocinado (Fee-Bump) en Testnet

Prueba de concepto de onboarding y transferencia de USDC sin que el usuario final necesite XLM, utilizando arquitectura hexagonal, @stellar/stellar-sdk y @sozu/faucet. El mecanismo de gas sponsorship se implementa con TransactionBuilder.buildFeeBumpTransaction, donde un tercero (sponsor) paga la comisión de red.


🎯 Objetivo validado

Se demostró que una wallet A con 0 XLM (fondada solo con USDC vía Sozu Faucet o Sponsored Reserves) puede enviar USDC a una wallet B, y la comisión de red es pagada por una cuenta sponsor mediante fee-bump, no por el usuario final.


🧩 Decisión de arquitectura: ¿Por qué NO-CUSTODIAL para el producto?

Este es el punto de diseño más importante de la PoC. Documentamos aquí la decisión para que quede constancia en el repositorio y pueda guiar la evolución hacia Mainnet.

¿Por qué el producto es NO-CUSTODIAL?

En el producto que se está construyendo (cobro en moneda local, conversión automática a USDC en un futuro on/off ramp), el usuario es el dueño de sus fondos. Por eso:

Criterio Decisión Razón
¿Quién guarda el secret del usuario? El usuario, en su propia wallet Evita que la aplicación sea objetivo de ataques para robar fondos de terceros
¿Quién firma el pago USDC de A → B? El usuario, con su clave privada La aplicación nunca ve ni toca el secreto del usuario
¿Quién paga la comisión de red? El sponsor (fee-bump) El usuario no necesita XLM para operar
¿Quién crea la wallet y la trustline? El sponsor (vía Sponsored Reserves) El usuario nace con cuenta y trustline, pero sin XLM gastable
Responsabilidad legal Baja La aplicación no es custodio de fondos ajenos

¿Por qué NO se eligió el modelo CUSTODIAL?

El modelo custodial (la aplicación guarda el secret del usuario y firma por él) fue descartado para el producto por las siguientes razones:

Riesgo del modelo custodial Impacto en este producto
Robo masivo Si el backend es comprometido, todos los secretos de los usuarios están expuestos y sus fondos pueden ser robados
Responsabilidad legal Ser custodio de fondos ajenos implica regulaciones financieras más estrictas (KYC/AML, licencias de transmisor de dinero)
Pérdida de confianza Los usuarios de Web3 esperan ser dueños de sus claves; una app custodial rompe ese principio
Punto único de fallo Un solo servidor guarda todas las claves; si cae o se filtra, todo el sistema se compromete

¿Por qué el fee-bump permite la transición sin romper la arquitectura?

El fee-bump es la pieza técnica clave que hace posible que el usuario no necesite XLM para operar, sin que la aplicación tenga que ser custodia de sus fondos:

  1. El usuario genera su propia wallet y guarda su secret (no-custodial).
  2. El usuario firma su propia transacción de pago con su clave.
  3. El sponsor (la aplicación) envuelve ese XDR firmado en una fee-bump transaction y paga la comisión de red.

El usuario nunca necesita XLM para pagar comisiones, pero tampoco necesita confiar su clave privada a la aplicación.


🧩 Arquitectura Hexagonal

src/
├── domain/                 # Entidades de negocio (sin dependencias externas)
│   ├── Wallet.ts           # address, secret (en memoria)
│   └── Payment.ts          # from, to, amount, asset
├── application/            # Casos de uso y puertos
│   ├── ports/
│   │   ├── WalletProvider.ts    # Interfaz: createWallet(): Promise<Wallet>
│   │   ├── PaymentSubmitter.ts  # Interfaz: submitWithSponsor(txXdr): Promise<string>
│   │   └── SecretProvider.ts    # Interfaz: getSponsorSecret(): Promise<string>
│   └── use-cases/
│       ├── CreateFundedWallet.ts
│       └── SendSponsoredPayment.ts
├── infrastructure/         # Adaptadores (infraestructura externa)
│   ├── sozu/
│   │   └── SozuWalletProvider.ts   # Ejecuta `npx @sozu/faucet claim` y parsea JSON
│   ├── stellar/
│   │   ├── StellarPaymentSubmitter.ts # buildFeeBumpTransaction + submit a Horizon
│   │   └── StellarSponsoredWalletProvider.ts # Sponsored Reserves (CAP-33/CAP-31)
│   └── secret/
│       ├── EnvSecretProvider.ts   # Lee secreto de variable de entorno (desarrollo)
│       └── EncryptedFileSecretProvider.ts # Lee secreto de archivo cifrado (producción)
└── labstellar/             # Puntos de entrada E2E (scripts de validación en Testnet)
    ├── e2e-usdc-sozu-fee-bump.ts            # Pago USDC con Sozu Faucet + fee-bump
    ├── e2e-xlm-sponsored-reserves-fee-bump.ts  # Pago XLM con Sponsored Reserves + fee-bump
    └── e2e-usdc-sponsored-reserves-fee-bump.ts # Pago USDC con Sponsored Reserves + fee-bump

Regla de oro respetada: El dominio y los casos de uso no importan nada de @stellar/stellar-sdk ni de child_process. Solo dependen de las interfaces (puertos).


🔄 Pollar On-Ramp / Off-Ramp (fiat ↔ USDC)

Se ha integrado Pollar Server API v3 para permitir la conversión entre moneda fiat y cripto (USDC) en Stellar Testnet. La arquitectura sigue siendo NO-CUSTODIAL: el usuario usa su propia wallet Stellar y firma las transacciones con su clave privada.

Flujo On-Ramp (fiat → USDC)

1. Vincular wallet externa a Pollar (PUT /v3/ramps/customers/wallets/STELLAR)
2. Consultar rutas disponibles (GET /v3/ramps/routes?direction=ONRAMP)
3. Agregar trustlines de los activos de las rutas a la wallet del usuario
4. Cotizar on-ramp (POST /v3/ramps/quotes)
5. Crear orden de rampa (POST /v3/ramps/orders)
6. Firmar pierna de rampa (POST /v3/ramps/orders/{id}/legs/{index}/signature)
7. Poll del estado hasta resolución (GET /v3/ramps/orders/{id})

Flujo Off-Ramp (USDC → fiat)

El flujo off-ramp es el espejo del on-ramp en dirección opuesta. Requiere que la wallet del usuario tenga saldo de USDC antes de ejecutar.

Scripts E2E

Script Descripción
npm run e2e:usdc-onramp-pollar On-ramp fiat → USDC vía Pollar (COP/ARS/PEN → USDC en Stellar Testnet)
npm run e2e:usdc-offramp-pollar Off-ramp USDC → fiat vía Pollar (USDC en Stellar → COP/ARS/PEN)

Requisitos previos

  1. Una wallet Stellar existente con su STELLAR_SECRET_KEY
  2. La wallet debe tener XLM para comisiones (fondar con Friendbot: https://friendbot.stellar.org?addr=DIRECCION)
  3. Para off-ramp, la wallet debe tener saldo de USDC

Ejecución

export SPONSOR_SECRET_KEY="SA_..."
export POLAR_SECRET_KEY="sec_testnet_..."
export POLAR_EXTERNAL_USER_ID="mi-usuario-123"
export STELLAR_SECRET_KEY="SA_..."

# On-ramp: 5000 COP → ~4550 USDC (comisión 450 COP)
npm run e2e:usdc-onramp-pollar

# Off-ramp: USDC → fiat (requiere USDC en la wallet)
npm run e2e:usdc-offramp-pollar

Estados de la orden

Estado Descripción
created Orden creada, pendiente de acción
awaiting_payment Esperando pago fiat del usuario
processing En proceso de liquidación
settling_onchain Liquidando en cadena Stellar
completed Completado exitosamente
failed Falló
refunded Reembolsado

Acciones pendientes (pendingAction)

Tipo Qué hacer
SIGNATURE Firmar transacción Stellar con la clave del usuario
FIAT_PAYMENT Usuario debe realizar el pago fiat (pago local)
KYC Usuario debe completar verificación de identidad
COSIGN_APPROVAL Aprobación de co-firma (wallets co-custody)
NONE Sin acción pendiente

Limitaciones actuales (Testnet)

  • Las rutas disponibles en Pollar testnet son: COP/COP (Colombia), ARS/ARS (Argentina), PEN/PEN (Perú) vía HOSTED
  • Monto mínimo para on-ramp COP: 4000 COP, máximo: 40000 COP
  • El KYC es requerido por Pollar para completar la orden; debe hacerse manualmente por el usuario
  • El SDK @pollar/core requiere entorno de navegador (no funciona en Node.js directamente)
  • KYC con Didit: Para activar el KYC, debes configurar tu cuenta de Didit en el dashboard de Pollar (Developers > Identity checks > Verification steps). Pollar requiere que la URL de devolución esté registrada/permitida antes de crear la opción de Didit. Si recibes error "No se pudo crear su opción de Didit", verifica que la URL de devolución esté en la lista de URLs permitidas de tu aplicación en Pollar.

🚩 Estado actual: Esperando aprobación de URL en Pollar

El flujo on-ramp/off-ramp está completamente implementado y listo para ejecutar, pero está bloqueado a la espera de que el soporte de Pollar apruebe la URL de devolución para el webhook de Didit.

Estado:

  • ✅ Código implementado y compilando (tsc --noEmit limpio)
  • ✅ PollarRampProvider con startKyc, getKycStatus, signRampLeg, quoteRamp, createRampOrder, getOrderStatus, linkWallet, getRampRoutes
  • ✅ Scripts E2E listos (e2e-usdc-onramp-pollar, e2e-usdc-offramp-pollar)
  • ✅ Servidor webhook local corriendo en http://0.0.0.0:3000
  • ✅ Tunnel de Cloudflare activo: https://charger-polar-atom-tourist.trycloudflare.com
  • ⏳ Pendiente: Soporte de Pollar apruebe la URL https://charger-polar-atom-tourist.trycloudflare.com como URL de devolución permitida
  • ⏳ Pendiente: Crear opción de Didit en Pollar Dashboard una vez aprobada la URL

Una vez aprobada la URL, ejecutar:

export SPONSOR_SECRET_KEY="SA_TU_SECRET_KEY_DEL_SPONSOR"
export POLAR_SECRET_KEY="sec_testnet_TU_CLAVE_DE_POLLAR"
export POLAR_EXTERNAL_USER_ID="mi-usuario-prueba"
export STELLAR_SECRET_KEY="SA_TU_SECRET_KEY_DE_USUARIO"

npm run e2e:usdc-onramp-pollar

Presentación del KYC: redirect vs iframe

Contexto Opción recomendada Razón
Lab/pruebas (scripts E2E) redirect Simple: el usuario visita una URL en su navegador, completa el KYC y Didit redirige de vuelta a tu webhook
Billetera/app de producción iframe El KYC se muestra embebido dentro de tu aplicación; el usuario nunca sale de tu app

Nota: Para este laboratorio se usa redirect. Si vas a integrar Pollar en una billetera de producción, cambia a iframe para mantener la experiencia de usuario dentro de tu aplicación.

Configuración del webhook de Didit (opcional)

El webhook de Didit es opcional. Tu script E2E ya hace polling cada 5 segundos consultando GET /v3/ramps/kyc/status?corridorId=..., así que funciona con o sin webhook.

Si quieres usar webhook para notificación en tiempo real:

  1. Configura un servidor que reciba POST de Didit (tu servidor local en http://0.0.0.0:3000 expuesto vía localtunnel o ngrok)
  2. En Didit Dashboard, configura el webhook con:
    • URL de endpoint: https://tu-url-publica.com/webhook/didit
    • Medio ambiente: Sandbox
    • Eventos: Verificaciones → status.updated, Verificaciones → data.updated
  3. En Pollar Dashboard, agrega el mismo webhook secret en la configuración de Didit

🛠 Stack Tecnológico

Capa Tecnología Versión
Lenguaje TypeScript (Node.js) —
SDK Stellar @stellar/stellar-sdk ^14.6.1
Fondeo de wallets @sozu/faucet (CLI) ^0.2.0
On/Off-Ramp Pollar Server API v3 https://server.api.pollar.xyz/v3
SDK Pollar (cliente/navegador) @pollar/core ^0.12.0
Red Stellar Testnet Horizon: https://horizon-testnet.stellar.org
Pruebas Vitest ^1.6.0
Ejecución TS ts-node ^10.9.2
Compilador TypeScript ^5.5.0

📦 Instalación

npm install

🔑 Variables de Entorno

Crear un archivo .env o exportar en la terminal antes de ejecutar:

export SPONSOR_SECRET_KEY="SA_TU_SECRET_KEY_DEL_SPONSOR"
export SOZU_FAUCET_URL="https://faucet.sozu.capital"  # opcional
export POLAR_SECRET_KEY="sec_testnet_TU_CLAVE_DE_POLLAR"
export POLAR_PUBLIC_KEY="pub_testnet_TU_CLAVE_PUBLICA_DE_POLLAR"
export POLAR_EXTERNAL_USER_ID="tu-usuario-unico"  # opcional, por defecto 'rewallet-lab-stellar'
export STELLAR_SECRET_KEY="SA_TU_SECRET_KEY_DE_USUARIO"  # requerido para on-ramp/off-ramp
  • SPONSOR_SECRET_KEY: Clave secreta de una cuenta Stellar Testnet con XLM. Esta cuenta paga las comisiones de red (fee-bump). Nunca hardcodear en el repositorio.
  • SOZU_FAUCET_URL: Origen del faucet Sozu (por defecto https://faucet.sozu.capital).
  • POLAR_SECRET_KEY: Clave secreta de Pollar Server API (sec_testnet_* / sec_mainnet_*). Se usa para autenticar llamadas a la API de Pollar con el header x-pollar-api-key.
  • POLAR_PUBLIC_KEY: Clave pública de Pollar (pub_testnet_*). Se usa con el SDK @pollar/core en entornos de navegador.
  • POLAR_EXTERNAL_USER_ID: ID de usuario externo para Pollar (cualquier string único, 1-128 caracteres). Se usa para asociar órdenes de rampa a un usuario específico.
  • STELLAR_SECRET_KEY: Clave secreta de la wallet Stellar del usuario. Requerido para on-ramp/off-ramp con Pollar. La wallet debe existir en Horizon y tener los trustlines necesarios.

⚠️ Los secretos solo se manejan en memoria. Nunca se escriben en disco ni se versionan.


🔐 Persistencia de Secretos (Puerto SecretProvider)

El puerto SecretProvider permite obtener el secreto del sponsor sin que el dominio ni los casos de uso conozcan el mecanismo de almacenamiento. Esto permite cambiar el proveedor de secretos sin modificar la lógica de negocio.

Proveedores disponibles

Adaptador Archivo Cuándo usar
EnvSecretProvider src/infrastructure/secret/EnvSecretProvider.ts Desarrollo rápido. Lee de process.env.
EncryptedFileSecretProvider src/infrastructure/secret/EncryptedFileSecretProvider.ts Producción ligera. Lee de archivo cifrado AES-256-GCM.

Uso en los scripts E2E

Cada script de src/labstellar/ instancia el proveedor que necesite:

// Desarrollo (variable de entorno)
const sponsorSecretProvider = new EnvSecretProvider('SPONSOR_SECRET_KEY');
const submitter = new StellarPaymentSubmitter(sponsorSecretProvider, HORIZON_URL);

// Producción (archivo cifrado)
const sponsorSecretProvider = new EncryptedFileSecretProvider('.sponsor-secret.enc', 'MASTER_SECRET_KEY');
const submitter = new StellarPaymentSubmitter(sponsorSecretProvider, HORIZON_URL);

Crear archivo cifrado (producción)

# 1. Generar una clave maestra de 64 caracteres hexadecimales (256 bits)
MASTER_SECRET_KEY=$(openssl rand -hex 32)

# 2. Crear el archivo cifrado con el secreto del sponsor
MASTER_SECRET_KEY=$MASTER_SECRET_KEY SPONSOR_SECRET_KEY=SA_TU_CLAVE_SPONSOR \
  npx ts-node src/infrastructure/secret/encrypt-secret.ts

# 3. Ejecutar usando la clave maestra
export MASTER_SECRET_KEY=$MASTER_SECRET_KEY
npm run e2e:usdc-fee-bump-sozu

El archivo .sponsor-secret.enc generado NO debe commitearse al repositorio.


▶️ Ejecutar la Validación E2E

Prueba 1: Pago USDC con Sozu Faucet + Fee-Bump

npm run e2e:usdc-fee-bump-sozu

Flujo:

  1. Wallet A se crea vía Sozu Faucet (npx @sozu/faucet claim). Nace con USDC y 0 XLM para el usuario.
  2. Wallet B se genera aleatoriamente, se fonda con Friendbot y se le añade la trustline de USDC.
  3. Se construye y firma una transacción de pago USDC de A → B con la secret de A.
  4. El XDR se envuelve en una fee-bump transaction con buildFeeBumpTransaction, se firma con el sponsor y se envía a Horizon.
  5. Se imprime el hash de la transacción en consola.

Prueba 2: Pago XLM con Sponsored Reserves + Fee-Bump (sin Friendbot)

npm run e2e:xlm-fee-bump-sponsored-reserves

Flujo:

  1. Wallet A se crea vía Sponsored Reserves (CAP-33/CAP-31):
    • beginSponsoringFutureReserves + createAccount (sponsor paga 1 XLM de reserve).
    • changeTrust(USDC) + endSponsoringFutureReserves (sponsor paga 0.5 XLM de reserve). Todo envuelto en fee-bump pagada por el sponsor.
  2. Sponsor envía 2 XLM a A para comisiones disponibles (A nace con 0 XLM gastables).
  3. Wallet B se genera aleatoriamente, se fonda con Friendbot y se le añade la trustline de USDC.
  4. Se construye y firma una transacción de pago XLM nativo de A → B con la secret de A.
  5. El XDR se envuelve en una fee-bump transaction y se envía a Horizon.
  6. Se imprime el hash de la transacción en consola.

Prueba 3: Pago USDC con Sponsored Reserves + Fee-Bump (sin Friendbot)

npm run e2e:usdc-fee-bump-sponsored-reserves

⚠️ Requisito previo: Wallet A debe tener saldo de USDC antes de ejecutar el pago. Usar Sozu Faucet para fundearla:

npx @sozu/faucet claim <DIRECCION_DE_A>

Flujo:

  1. Wallet A se crea vía Sponsored Reserves (CAP-33/CAP-31):
    • beginSponsoringFutureReserves + createAccount (sponsor paga 1 XLM de reserve).
    • changeTrust(USDC) + endSponsoringFutureReserves (sponsor paga 0.5 XLM de reserve). Todo envuelto en fee-bump pagada por el sponsor.
    • A nace con 0 XLM gastables y 0 USDC balance.
  2. Sponsor envía 2 XLM a A para comisiones disponibles.
  3. Fondear A con USDC — Paso manual requerido: usar Sozu Faucet (npx @sozu/faucet claim <DIRECCION_DE_A>) o recibir USDC de otra wallet.
  4. Wallet B se genera aleatoriamente, se fonda con Friendbot y se le añade la trustline de USDC.
  5. Se construye y firma una transacción de pago USDC de A → B con la secret de A.
  6. El XDR se envuelve en una fee-bump transaction y se envía a Horizon.
  7. Se imprime el hash de la transacción en consola.

Nota importante sobre USDC con Sponsored Reserves: Wallet A nace con la trustline USDC activa pero sin balance de USDC. Para hacer un pago en USDC de A → B, primero debe fundearse A con USDC (vía faucet o pago directo), ya que el patrón de sponsorship solo cubre el reserve de la cuenta y el reserve de la trustline, no el saldo del activo.


✅ Transacciones Verificadas en Testnet (Red Real)

Transacción 1: Fee-Bump con Sozu Faucet (pago USDC)

Campo Valor
Hash 8028aabbd79ad15d003b3555a26534019a36772d7bc7d21aa74f194680d8aa7b
Estado ✅ successful: true
Fee pagado por (fee_account) GAWMZWOPC3ESC6YRRGYPWHPKMKRHIFRXBI27NQS6YKTF2JKZHRGUJXJI (sponsor)
Wallet emisora (source_account) GAOYQYFSQDDVWAXZXD4UH7AVQ6G2T2CYHEJCOAI5KSZQU36IWLQL7NWF (wallet A)
Comisión cobrada 200 stroops
Ledger 5620394
Fecha 2026-11-08T08:13:32Z

Transacción 2: Sponsored Reserves (pago XLM nativo)

Campo Valor
Hash 48d42eecfc2797ecf209ae2ebe1300bb2233eb917436b4df1509aed0f4bb8055
Estado ✅ successful: true
Fee pagada por (fee_account) GBK6IQBLLQ6T7ZMHNVTZUVQ6ETISGVZUVWWAP5SJPPBUD66PM4RZFIRI (sponsor)
Wallet emisora (source_account) GBLHMN6HUKTLL72BY7BHL4UYIE2UBV5O3MBWGHFGYD2ICSAAU4ONPFSZ (wallet A)
Comisión cobrada 200 stroops
Ledger 5621655
Fecha 2026-11-08T09:58:37Z

Transacción 3: Sponsored Reserves + Pago USDC

Campo Valor
Hash db0ae64a00f266c433102c3d0d9d5d20874a0a5d15b59d45119d9c36417e2561
Estado ✅ successful: true
Fee pagada por (fee_account) GBK6IQBLLQ6T7ZMHNVTZUVQ6ETISGVZUVWWAP5SJPPBUD66PM4RZFIRI (sponsor)
Wallet emisora (source_account) GA4QPRBYM2PQVECGLUXBGFQUZBQNHIJA4K4OIVKBXVVIETDQFQNDLYEI (wallet A)
Comisión cobrada 200 stroops
Ledger 5621811
Fecha 2026-11-08T10:11:37Z

Transacción 4: Fee-Bump con Sozu Faucet (pago USDC) — Manejo de errores de red verificado

Campo Valor
Hash 7c91e5cc544d36faeb34a13196b60eb6775575ca07ca89f204cac06fa38f53c0
Estado ✅ successful: true
Fee pagada por (fee_account) GBK6IQBLLQ6T7ZMHNVTZUVQ6ETISGVZUVWWAP5SJPPBUD66PM4RZFIRI (sponsor)
Wallet emisora (source_account) GA3E3OYH3VSU2DABKHCXA6R3275BESVC7AR3W7DX4XAC7MGFWWPFRXCH (wallet A)
Comisión cobrada 200 stroops
Ledger 5622025
Fecha 2026-11-08T10:29:27Z

Transacción 5: Fee-Bump con Sozu Faucet (pago USDC) — Persistencia de secretos verificada

Campo Valor
Hash 1352a2f114f268ef1ceaa78e8c43b7b149121b67135fb7c38153b8f0a2092dc9
Estado ✅ successful: true
Fee pagada por (fee_account) GBK6IQBLLQ6T7ZMHNVTZUVQ6ETISGVZUVWWAP5SJPPBUD66PM4RZFIRI (sponsor)
Wallet emisora (source_account) GBSTNFJZ2C2ADVUMHAKFKPV5ICQN2SY4M4WE6ZNR4LQDBGIY6QDF6CYM (wallet A)
Comisión cobrada 200 stroops
Ledger 5622561
Fecha 2026-11-08T11:14:07Z

Transacción 6: Fee-Bump con Sozu Faucet (pago USDC) — Persistencia de secretos vía archivo cifrado verificada

Campo Valor
Hash eae908dd8615c5a1ebe531a02da54d82a07711756247e91b943f568495773515
Estado ✅ successful: true
Fee pagada por (fee_account) GBK6IQBLLQ6T7ZMHNVTZUVQ6ETISGVZUVWWAP5SJPPBUD66PM4RZFIRI (sponsor)
Wallet emisora (source_account) GDVDKBQCHW273IDPEJGJSLASH47YHHLABDG6Y6PL2IQNPVXFWR2UYIML (wallet A)
Comisión cobrada 200 stroops
Ledger 5622657
Fecha 2026-11-08T11:22:07Z

Enlaces de verificación

Evidencia en cadena (respuesta de Horizon)

Transacción 6 (Fee-Bump + Sozu Faucet, con persistencia de secretos vía archivo cifrado AES-256-GCM):

{
  "successful": true,
  "hash": "eae908dd8615c5a1ebe531a02da54d82a07711756247e91b943f568495773515",
  "source_account": "GDVDKBQCHW273IDPEJGJSLASH47YHHLABDG6Y6PL2IQNPVXFWR2UYIML",
  "fee_account": "GBK6IQBLLQ6T7ZMHNVTZUVQ6ETISGVZUVWWAP5SJPPBUD66PM4RZFIRI",
  "fee_charged": "200",
  "max_fee": "200",
  "fee_bump_transaction": true,
  "inner_transaction": {
    "hash": "c459f7f43cc0b06c63c27c001fd48287e0778f64be04bc8bae7daef61178eb68",
    "max_fee": "100"
  }
}

🛡️ Manejo de Errores en SozuWalletProvider

El adaptador SozuWalletProvider implementa reintentos con backoff exponencial para errores recuperables del faucet Sozu:

Errores recuperables (reintenta automáticamente)

Origen Errores Comportamiento
Red (Node.js) ECONNREFUSED, ECONNRESET, ENETUNREACH, ECONNABORTED, ETIMEDOUT, EAI_AGAIN Reintenta con backoff exponencial
HTTP 429 Too Many Requests Reintenta con backoff exponencial
JSON del faucet user_cooldown, global_cooldown, rate_limited Reintenta esperando nextAvailableAt o delay por defecto

Configuración (SozuWalletProviderOptions)

Opción Por defecto Descripción
maxRetries 3 Número máximo de reintentos
baseDelayMs 5000 Retraso base en ms para backoff exponencial

Backoff exponencial

  • Intento 1: inmediatamente
  • Intento 2: 5 segundos
  • Intento 3: 10 segundos
  • Intento 4: 20 segundos
  • Tope máximo: 60 segundos

Si después de agotar los reintentos el error persiste, se lanza el último error capturado.


🧪 Ejecutar Tests

Tests unitarios (sin red, siempre verdes)

npm test

Tests de integración (red real Testnet)

Requiere E2E=true y SPONSOR_SECRET_KEY:

E2E=true npm run test:integration

Estos tests se saltan automáticamente si no se definen las variables de entorno.


📜 Scripts Disponibles

Script Descripción
npm run e2e:usdc-fee-bump-sozu Ejecuta el flujo E2E con Sozu Faucet + fee-bump (pago USDC) contra Testnet y muestra el hash
npm run e2e:xlm-fee-bump-sponsored-reserves Ejecuta el flujo Sponsored Reserves + fee-bump (pago XLM nativo) contra Testnet y muestra el hash
npm run e2e:usdc-fee-bump-sponsored-reserves Ejecuta el flujo Sponsored Reserves + fee-bump (pago USDC) contra Testnet (requiere fondear A con USDC primero)
npm run e2e:usdc-onramp-pollar Ejecuta el flujo on-ramp fiat → USDC vía Pollar Server API v3 (firma + polling)
npm run e2e:usdc-offramp-pollar Ejecuta el flujo off-ramp USDC → fiat vía Pollar Server API v3 (firma + polling)
npm run e2e:usdc-onramp-pollar-sdk Script de prueba con SDK @pollar/core (requiere navegador, no funciona en Node.js)
npm test Ejecuta tests unitarios (sin red)
npm run test:integration Ejecuta tests de integración contra Testnet (requiere E2E=true)
npm run lint Verifica tipos con tsc --noEmit
npm run build Compila TypeScript a dist/

📌 Criterios de Aceptación Cumplidos

  • tsc --noEmit compila sin errores
  • npm test pasa sin errores (tests unitarios, integración skippeada sin red)
  • npm run e2e:usdc-fee-bump-sozu finaliza con hash de transacción exitoso (verificado en Testnet)
  • npm run e2e:xlm-fee-bump-sponsored-reserves finaliza con hash de transacción exitoso (verificado en Testnet)
  • npm run e2e:usdc-fee-bump-sponsored-reserves compila y está listo para ejecutar (requiere fondear A con USDC)
  • npm run e2e:usdc-onramp-pollar compila y ejecuta flujo on-ramp completo (vinculación, trustlines, quote, orden, firma Stellar)
  • npm run e2e:usdc-offramp-pollar compila y ejecuta flujo off-ramp completo (requiere USDC en wallet)
  • Las transacciones fee-bump aparecen como exitosas en Stellar Expert Testnet
  • La comisión fue pagada por la cuenta del sponsor, no por la wallet A (fee-bump)
  • La wallet A tuvo 0 XLM para el usuario final (fondada solo con USDC o Sponsored Reserves)
  • Arquitectura hexagonal: dominio y casos de uso sin dependencias de SDK ni child_process
  • Secretos solo en memoria, ninguno hardcodeado
  • Manejo de errores de red con reintentos y backoff exponencial en SozuWalletProvider
  • Persistencia de secretos mediante puerto SecretProvider (variable de entorno y archivo cifrado)
  • Integración con Pollar Server API v3: autenticación x-pollar-api-key, linkWallet, getRampRoutes, quoteRamp, createRampOrder, signRampLeg, getOrderStatus
  • Arquitectura NO-CUSTODIAL respetada: el usuario firma con su propia clave Stellar

🚀 Próximos Pasos (Desarrollo Progresivo)

Esta PoC valida el mecanismo principal de fee-bump, sponsored reserves y on/off-ramp con Pollar. Para llevar a producción o extender:

  1. Persistencia de secretos del sponsor: Ya implementada vía SecretProvider (variable de entorno y archivo cifrado). Para producción, usar AWS Secrets Manager, HashiCorp Vault, o similar.
  2. Completar KYC en Pollar: El on-ramp se queda en estado KYC (verificación de identidad). El usuario debe completar el KYC manualmente vía la URL que Pollar proporciona. Para producción, integrar este flujo en el frontend.
  3. SDK @pollar/core en navegador: Para producción frontend, usar el SDK de Pollar con login(), setUpRampWallet(), signRampLeg() y pollRampOrder() en un entorno de navegador (no funciona en Node.js debido a ORIGIN_NOT_ALLOWED).
  4. Off-ramp con USDC real: Una vez completado el on-ramp y recibido USDC en la wallet del usuario, ejecutar el off-ramp para convertir USDC → fiat.

📄 Licencia

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages