Saltar al contenido principal

Fase 3: despliegue en varias cadenas EVM y redundancia de keepers

· 6 min de lectura
OrcaRail
Crypto payment rails for web2 apps

20 de abril de 2026: la Fase 3 toma la superficie de contratos de la Fase 1 y la Fase 2 y la lleva a todas las cadenas EVM que admitimos. Además añade un segundo keeper, para que una caída de NestJS no pueda saltarse un cobro de suscripción.

En resumen​

  • Desplegar PaymentLinkFactory, la implementación de PaymentLinkReceiver, FeeSplitter y SubscriptionHub en Ethereum mainnet, Arbitrum, Optimism y Polygon.
  • El mismo esquema de salt de CREATE2 en todas partes: la dirección de la factoría es la misma en todas las cadenas (limpio para la documentación, para las integraciones y para computeAddress desde el cliente).
  • Registrar Chainlink Automation (preferido) y/o Gelato Web3 Functions como segundo keeper que llama al mismo charge(ids[]).
  • Publicar SLO: 99,5 % de los cobros en los 10 minutos siguientes al vencimiento, medido.

Qué cambia en esta fase​

Varios keepers llamando al mismo charge() público es la garantía de disponibilidad. Ninguno tiene custodia; cualquiera de ellos puede salvar un cobro vencido.

Plan de despliegue​

Orden de despliegue (una cadena por semana, con canario por cadena):

  1. Ethereum mainnet (chainId 1)
  2. Arbitrum One (42161)
  3. Optimism (10)
  4. Polygon PoS (137)

Las direcciones de cada cadena se publican en /docs/non-custodial/contract-reference. La clave de despliegue es la misma multisig Safe 3 de 5 definida en la Fase 0, redesplegada en cada cadena desde la misma ceremonia.

Las cuatro cadenas comparten el mismo feature flag useOnChainReceiver, definido por chainId dentro de api/src/payments/wallet.service.ts. Los comercios lo activan cadena por cadena; el valor por defecto pasa a activado cuando la semana canaria sale limpia.

Estrategia de gas para charge()​

Las transacciones charge() del keeper necesitan gas. Reutilizamos la infraestructura de paymaster existente en api/src/withdrawals/services/gas-strategy:

  • Con paymaster de Alchemy (Base, Arbitrum, Optimism, Polygon): el keeper llama a SubscriptionHub.charge() como UserOperation con patrocinio de la política. Coste de gas cero para la EOA del keeper.
  • Ethereum mainnet (sin paymaster): el keeper paga con su saldo de gas en caliente. Lo recarga a diario la wallet de la plataforma y se monitoriza con una alerta de Datadog por saldo bajo.
  • Gas subvencionado por el comercio (opcional): los comercios pueden definir una gas_refund_address y el keeper cobra el reembolso de ahí, siguiendo el patrón actual de soporte/comisión. Fuera del alcance del despliegue inicial.

El segundo keeper es una apuesta por la redundancia, no un sustituto. Funciona junto al programador de NestJS.

  • Registrar un Upkeep por cada SubscriptionHub.
  • checkUpkeep() devuelve performData = abi.encode(ids[]) para todos los id con block.timestamp >= nextChargeAt (leídos de una función view).
  • performUpkeep(performData) llama a charge(ids) en lote.
  • Financiado con LINK desde la tesorería de la plataforma; saldo monitorizado.

Gelato Web3 Functions (alternativa)​

  • Una Web3 Function en JS consulta el hub, encuentra los ID vencidos y envía una transacción charge(ids[]).
  • El precio se paga a posteriori desde 1Balance de Gelato.

Cualquiera de los dos basta por sí solo; elegiremos según la experiencia operativa durante la Fase 3. Ejecutar ambos no es problema: charge() es idempotente dentro de un periodo gracias a lastChargedAt.

SLO y monitorización​

Objetivos de SLO publicados:

MétricaObjetivo
charge() en los 10 minutos siguientes a nextChargeAt99,5 %
charge() en la hora siguiente a nextChargeAt99,9 %
Saldo de gas en caliente de la EOA del keeper sobre el umbral de alerta99,99 %
Eventos indexados en los 2 minutos siguientes a la finalidad del bloque99,5 %

La monitorización se conecta a la configuración de Sentry existente documentada en la skill de flujo de trabajo de Sentry del repositorio. Alertas:

  • Reverts de charge() por hora > umbral → PagerDuty
  • Saldo nativo de la EOA del keeper por debajo de N gwei × gasLimit × 100 → PagerDuty
  • Upkeep de Chainlink/Gelato en mal estado → aviso
  • Retraso del indexador de eventos Paid > 5 min → aviso

Obsolescencias​

Igual que en las Fases 1 y 2, pero en más cadenas. Todavía no se elimina nada; la eliminación espera a la Fase 7.

Plan de migración​

  • Enlaces de pago nuevos: usan receptores en contrato en cualquier cadena con el flag activado.
  • Suscripciones nuevas: usan SubscriptionHub en cualquier cadena con el flag activado.
  • Clientes existentes: migración voluntaria en la renovación, igual que en la Fase 2.
  • Liquidación entre cadenas (Relay): superficie sin cambios; el keeper sigue llamando a los ejecutores de Relay en api/src/withdrawals/services/bridge, solo que desde una EOA sin custodia.

Riesgos y mitigaciones​

  • Direcciones distintas entre cadenas. Mitigación: desplegador CREATE2 determinista (por ejemplo, 0x4e59b44847b379578588920cA78FbF26c0B4956C) con un hash de init-code fijo; las factorías comparten dirección en las cuatro cadenas.
  • Límites de la política de paymaster por cadena. Mitigación: monitorización por cadena y alertas de cuota de la política al 70 % de uso.
  • Cambios frecuentes en el registro de upkeeps de Chainlink. Mitigación: un upkeep por cadena; la lista de ID se lee on-chain, así que dar de alta comercios no requiere cambios en la configuración de Chainlink.
  • Cobro doble entre keepers. Mitigación: lastChargedAt monótono más ChargeSkipped para duplicados; ambos keepers son idempotentes por diseño.
  • Picos de gas en mainnet. Mitigación: ajuste del tamaño de lote por cadena; un techo de precio de gas por encima del cual charge() se aplaza un bloque.

Lista de estado​

  • Despliegues completados en Ethereum, Arbitrum, Optimism y Polygon
  • Direcciones de factoría + hub publicadas en la referencia de contratos
  • Políticas de paymaster validadas por cadena
  • Upkeep de Chainlink Automation activo en al menos 2 cadenas
  • Panel de SLO en Datadog
  • Runbook para caídas del keeper (charge() manual desde la CLI del comercio)
  • Comercios canarios desplegados por cadena

Qué sigue​

Solana es la otra gran cadena que debemos hacer no custodial: Fase 4.


Documentación de referencia: Estado de la Fase 3 · Estrategia de keepers · Referencia de contratos