Arquitectura
Esta página describe la arquitectura objetivo de la pila no custodial de OrcaRail y, para cada componente, la ruta de código heredada a la que sustituye.
Diagrama del sistema
Componentes
PaymentLinkFactory (EVM)
Uno por cadena. Direcciones CREATE2 deterministas derivadas de un hash de 32 bytes del UUID del enlace de pago. Expone:
computeAddress(bytes32 linkId) view returns (address): dirección previa al despliegue para la UI y la API.deployAndPay(bytes32 linkId, Split split, address token, uint256 amount): despliegue diferido y reenvío en una sola transacción.isDeployed(bytes32 linkId) view returns (bool).
Actualizable tras un proxy UUPS propiedad del multisig Safe 3 de 5, con un timelock de 48 horas. Los receptores por enlace no son actualizables.
Sustituye: la rama EVM de generateAddressForNetwork en api/src/payments/wallet.service.ts (derivación HD y cálculo de la dirección contrafactual de la smart account de Alchemy).
PaymentLinkReceiver (EVM)
Clon mínimo EIP-1167, uno por enlace de pago. Acepta moneda nativa y ERC-20 (SafeERC20 para tokens no estándar como USDT). Emite Paid(linkId, payer, token, amount). Llama a FeeSplitter.split() de forma atómica.
Sustituye: el paso de barrido de SmartAccountWalletService.drainSmartAccountWallet en api/src/withdrawals/services/smart-account-wallet.service.ts.
FeeSplitter (EVM)
Lógica de reparto sin estado. Recibe una estructura Split en puntos básicos y un importe, y lo distribuye entre las direcciones del comercio, la plataforma, el referido y la comisión de puente. El resto va al comercio (sin polvo).
Sustituye: los cálculos de reparto en TypeScript de drainSmartAccountWallet y buildNativeDrainCalls / la rama ERC-20 equivalente del mismo servicio.
SubscriptionHub (EVM)
Uno por cadena. Guarda una estructura Subscription por cada bytes32 id. charge(id) es pública y no requiere permisos, y aplica:
block.timestamp >= nextChargeAtlastChargedAtmonótonoamountCharged + amount <= cap- comprobaciones de allowance y saldo ERC-20
Emite Charged, ChargeSkipped(reason), Canceled y SubscriptionCreated.
Sustituye: la extracción custodial en api/src/scheduler/processors/subscription-auto-charge.processor.ts, que hoy usa una smart account controlada por OrcaRail.
Programa payment_link (Solana)
Programa Anchor con una PDA por enlace derivada de [b"pl", link_id]. Instrucciones: initialize, pay, refund. Reparte mediante CPI.
Sustituye: la rama Solana de generateAddressForNetwork en api/src/payments/wallet.service.ts (derivación ed25519-hd-key) y la ruta de barrido de SOL en api/src/withdrawals/services/solana-withdrawal-transfer.service.ts.
Programa subscription (Solana)
Programa Anchor que usa Approve / Revoke de SPL como primitiva de autoridad delegada. charge(id) replica la semántica de SubscriptionHub.charge(id) en EVM.
Sustituye: la rama SOL de subscription-auto-charge.processor.ts y la extracción custodial mediante cuentas de token controladas por OrcaRail.
Multisig BTC 2 de 2 + reembolso prefirmado
Dirección taproot por enlace con una clave pública del comercio y otra de la plataforma. La PSBT de reembolso prefirmada se guarda off-chain en el momento del depósito. La plataforma no puede mover los fondos de forma unilateral.
Sustituye: la hot wallet unilateral de api/src/withdrawals/services/bitcoin-hot-wallet.service.ts. La ruta de barrido de api/src/withdrawals/services/bitcoin-withdrawal-transfer.service.ts se mantiene, pero firma mediante multisig en lugar de con una clave unilateral.
Indexador de eventos
Un servicio nuevo, payments/indexer/*, se suscribe a:
- Los eventos EVM
Paid,SubscriptionCreated,Charged,ChargeSkippedyCanceled(watchContractEventde viem, con Alchemy como principal y un RPC público como respaldo). - Los logs de los programas de Solana (webhooks de Helius o streams de Triton).
Inserta o actualiza filas de PaymentTransaction y SubscriptionAutoCharge y despacha los eventos de webhook existentes en api/src/webhooks/webhooks.service.ts.
Sustituye: el sondeo de saldos de depósito y los callbacks de barrido completado que hoy disparan los webhooks.
Flujos de datos
Pago único (EVM)
Cobro de suscripción (EVM)
Reembolso
EVM: la EOA keeper del comercio o el multisig llama a PaymentLinkReceiver.refund(txId). Solana: el firmante invoca la instrucción refund. BTC: el comercio difunde la PSBT de reembolso prefirmada.
Liquidación entre cadenas
Se sigue usando Relay (consulta api/src/withdrawals/services/bridge), pero el disparador es on-chain en lugar de custodial: el receptor llama al ejecutor de Relay en la misma transacción, o el keeper lo hace inmediatamente al recibir el evento Paid.
Qué sustituye en el código
Lista condensada para ingenieros. Se sustituye o se evita en las fases 1–4 y se elimina en la Fase 7.
| Archivo / ruta heredada | Sustituto |
|---|---|
api/src/payments/wallet.service.ts: derivación HD, smart account de Alchemy | computeAddress de la factory + findProgramAddressSync de Solana |
api/src/withdrawals/services/smart-account-wallet.service.ts: vaciado / transferencia | Reparto atómico en PaymentLinkReceiver.pay |
api/src/withdrawals/services/solana-withdrawal-transfer.service.ts: barrido de SOL | Instrucción pay del programa payment_link |
api/src/scheduler/processors/subscription-auto-charge.processor.ts: extracción custodial | SubscriptionHub.charge + charge de Anchor |
api/src/withdrawals/services/bitcoin-hot-wallet.service.ts: clave BTC unilateral | Multisig 2 de 2 por enlace con firmante del comercio |
Tabla hd_wallet_seed + WALLET_ENCRYPTION_KEY | Eliminados para EVM/SOL; archivados en la Fase 7 |