Liquidado no es final: qué significa success en x402 en cada cadena
La llamada settle de x402 devuelve un booleano. En Base, con el código de referencia del facilitator y el RPC público de Base, ese booleano volvió en una mediana de 179 milisegundos. En 94 de 100 settlements reales que observamos, volvió antes de que el bloque del pago estuviera sellado. En Solana significa que votó más de dos tercios del stake. En Cardano decide el vendedor. Y el próximo miércoles, Solana reemplaza su consenso.
Un agente paga, un facilitator liquida y el vendedor libera la respuesta. La especificación v2 de x402 describe settle como el paso que "durably commits payment state for the request — establishing finality from the resource server's perspective". Esa frase carga con mucho. La finalidad es una propiedad de la cadena, no de una perspectiva, y cada cadena que x402 soporta tiene su propia escalera, desde "un nodo lo vio" hasta "no se puede deshacer".
Este post hace una pregunta acotada: cuando vuelve success: true, ¿en qué peldaño de esa escalera estás?
Qué leímos y qué corrimos, todo el 2026-10-16. Leímos el repositorio de x402 en el commit faa1c69: la spec v2, los 17 bindings de red de exact, los paquetes TypeScript en 2.28.0 y las interfaces de signer de Go y Python. También viem 2.57.7, x402-rs en e75adda, el FAQ de Flashblocks de Base y un pull request abierto en los docs de Base, el glosario del OP Stack, la página de finalidad de Polygon, los docs RPC de Solana, SIMD-0326 y SIMD-0384, la página de Alpenglow de Anza y la documentación del facilitator de CDP. Corrimos tres probes de solo lectura. El primero pasó el wrapper de signer EVM del propio x402 sobre 100 settlements reales de USDC en Base. El segundo muestreó durante diez minutos los heads latest, safe y finalized de seis cadenas. El tercero midió la tasa de slots de Solana y la vida útil del blockhash.
La versión corta. x402 no le da profundidad a la palabra "success". Solo un binding, Cardano, deja que el vendedor diga cuánta evidencia quiere. La ruta EVM acepta el primer receipt que consigue, y en el RPC público de Base ese receipt es una preconfirmación del sequencer: 94 de los 100 settlements que observamos se resolvieron antes de que su bloque se sellara. Los 100 se mantuvieron. Es un buen disparador para liberar una respuesta de un centavo, y el estado equivocado para registrar como "settled". La ruta de Solana espera confirmed, que es una elección sólida. Pero la ventana de blockhash de 60–90 segundos que documenta su spec es de unos 25 segundos en la práctica. Y el 21 de octubre, la migración a Alpenglow pone a Solana en una ventana de solo votos donde ningún settlement de x402 puede aterrizar.
Lo que promete la spec
La especificación v2 define tres payment flows. El default, authorization, corre verify, después el recurso, después settle y después la respuesta: "funds move only after it completes successfully". upfront corre settle primero, "giving the server finality first". escrow liquida un depósito antes del recurso y el cobro final después.
El SettleResponse lleva success, errorReason, payer, transaction, network, un amount opcional y un objeto extensions opcional. Nada en él dice qué tan final es la transacción. Hay un único error no terminal, settlement_pending: la transacción "was broadcast but its confirmation could not be established". Debe llevar el hash para que quien llama pueda reconciliar on-chain. El contrato de verify y settle lo cubrimos en nuestro análisis del facilitator. La pregunta aquí es qué significa "confirmation" dentro de él.
Las reglas de familia del scheme exact son lo más cercano. Dividen los métodos en dos familias. Los métodos client-submitted, donde el pagador transmite y presenta una prueba, MUST "declare the observable event that makes a payment final and which party owns confirmation-depth policy". Los métodos facilitator-submitted, donde el cliente firma y el facilitator transmite, deben declarar su fee payer, su primitiva de replay, su ventana de validez y su comportamiento ante envíos duplicados. No tienen que declarar finalidad. EVM y SVM, las dos familias por donde pasa la mayor parte del tráfico x402, son facilitator-submitted.
Un binding llena el hueco de todos modos. El binding de Cardano agrega extra.confirmationPolicy.l1Confirmations, un entero de -1 a 20 con default 1. El vendedor lo elige en el 402, y un facilitator puede anunciar el rango que soporta en /supported. Menos uno significa aceptación en mempool. La spec dice que dar acceso con estado de mempool es "strongly discouraged", porque Ouroboros Praos "has probabilistic finality". Es el único binding donde la profundidad de finalidad es un campo en el wire.
Qué espera cada implementación
Si la spec no define la profundidad, la define el código. Esto es lo que espera cada ruta de settle antes de reportar éxito, leído del repositorio en faa1c69 y de x402-rs.
// Settle devuelve success: true cuando...
EVM (TypeScript) primer receipt con status success; viem waitForTransactionReceipt,
confirmations en el default de 1, timeout de 180 s
EVM (Go, Python) la interfaz de signer recibe un hash y nada más; sin perilla de confirmations
EVM (x402-rs) 1 confirmación, timeout de receipt de 30 s
SVM (TypeScript) getSignatureStatuses reporta confirmed o finalized; presupuesto de 30 s
SVM (x402-rs) CommitmentConfig::confirmed()
Starknet ACCEPTED_ON_L2 más un block_number; la spec nombra el riesgo de reorg L2
NEAR estado de tx con wait_until FINAL
Cardano l1Confirmations elegido por el vendedor; default un bloque
Algorand inclusión; spec: sin forks, finalidad instantánea
Concordium finalización ConcordiumBFT (~10 s según la spec)
Stellar getTransaction con status SUCCESS, 15 consultas cada 1 s
XRPL validated: true y tesSUCCESS
La dispersión es grande. NEAR, Concordium, Algorand, Stellar y XRPL liquidan sobre un estado de ledger que sus bindings describen como final. Starknet y Cardano liquidan antes y lo dicen. El binding de Starknet afirma que ACCEPTED_ON_L2 "is a sequencer commitment prior to L1 finality and can, in rare cases, be reverted in an L2 reorg", y que esperar ACCEPTED_ON_L1 elimina el riesgo "at the cost of hours-scale latency". EVM y SVM también liquidan antes, y sus bindings no dicen nada al respecto.
x402-rs agrega una etiqueta confusa. Su config de cadena documenta el flag flashblocks como "whether this chain supports flashblocks (immediate block finality)". En el código, el flag solo cambia la estimación de gas del bloque pending a latest. Los Flashblocks son preconfirmaciones, no finalidad, como deja claro más abajo la propia documentación de Base. El resto de ese código lo cubrimos en nuestra auditoría de x402-rs.
Base: success antes de que exista el bloque
Base produce un bloque cada dos segundos y transmite Flashblocks dentro de cada uno, "200ms incremental block updates". Un Flashblock es una preconfirmación del sequencer. El FAQ de Base dice que "targets a Flashblock reorg rate of < 0.1%", donde un reorg significa que "a Flashblock was streamed as a preconfirmation but wasn't included in the final block". Si las condiciones se vuelven inseguras, "preconfirmations are disabled network-wide and confirmations fall back to standard 2-second blocks".
El mismo FAQ dice que eth_getTransactionReceipt "returns preconfirmed receipts". La página de referencia de Base para ese método todavía dice que los receipts solo existen para transacciones minadas. Un pull request abierto, base/docs#2081 del 6 de octubre, documenta la contradicción. Reporta que mainnet.base.org devolvió un receipt del bloque todavía no sellado, con blockHash en cero, en 4 de 5 intentos.
Ahora conéctalo con x402. El facilitator EVM en TypeScript espera a través de toFacilitatorEvmSigner, que llama a waitForTransactionReceipt de viem con un timeout y sin argumento de confirmations. En viem 2.57.7, cuando confirmations es 1 o menos, el primer receipt que devuelve eth_getTransactionReceipt resuelve la espera de inmediato. La definición de cadena base de viem usa https://mainnet.base.org como RPC por defecto. Y los facilitators de ejemplo de x402 crean sus clientes viem con transport: http() y sin URL. Después settle revisa el status y, para EIP-3009, el evento Transfer en los logs del receipt. Un receipt preconfirmado trae ambos.
Lo medimos. Durante unos tres minutos consultamos el bloque pending de Base en mainnet.base.org. Filtramos las llamadas a transferWithAuthorization de USDC (ambos selectores, 0xe3ee160e y 0xcf092995), la ruta EIP-3009 por la que liquida el scheme exact de x402. Para cada una cuyo bloque todavía no estaba sellado, corrimos la espera con el wrapper de signer del propio x402.
import { toFacilitatorEvmSigner } from '@x402/evm'; // 2.28.0
const signer = toFacilitatorEvmSigner({
address: ZERO_ADDR,
waitForTransactionReceipt: a => publicClient.waitForTransactionReceipt(a),
});
// la misma llamada que hace la ruta de settle exact EVM tras transmitir
const receipt = await signer.waitForTransactionReceipt({ hash });
const sealed = await publicClient.getBlockNumber();
// preconfirmado si receipt.blockNumber > sealed, o si blockHash es todo ceros
Resultados para 100 settlements en los bloques 52,676,318 a 52,676,397:
- Los 100 se resolvieron con
status: "success"en una mediana de 179 ms (p90 326 ms, máximo 393 ms). - En 94, el último bloque sellado seguía por debajo del número de bloque del receipt cuando lo revisamos justo después de que volviera la espera.
- 89 traían un
blockHashtodo en ceros. - Tras el sellado, los 100 estaban en el mismo número de bloque con el mismo status. Cuando el safe head pasó el bloque 52,676,397, los 100 seguían ahí bajo el block hash canónico.
Eran settlements de quien fuera que los enviara; nosotros solo observamos. El resultado no es un reporte de bug. Las preconfirmaciones de Base se mantuvieron en todos los casos que vimos, y la tasa objetivo de reorg está publicada. El punto es la etiqueta. Cuando el código de referencia dice success: true sobre un endpoint con Flashblocks, significa "el sequencer prometió incluir esto", no "esto está en un bloque", y mucho menos "esto es final".
La escalera, medida
Por encima de la preconfirmación, cada cadena tiene más peldaños. El glosario del OP Stack los define para Base. Un bloque unsafe es uno "not derived from the L1 chain". Un bloque safe "can be derived entirely from L1". El finalized head es el bloque más alto derivado de bloques L1 finalizados, "older than two L1 epochs (64 L1 time slots)". La página de finalidad de Polygon dice que desde Heimdall v2 "deterministic finality is now achieved in 2 to 5 seconds", y que determinística significa que "there is no chance of a reorganization once a block is finalized". Los docs RPC de Solana definen confirmed como "a block directly voted on by a supermajority of stake", y finalized como un bloque "finalized with maximum lockout".
Muestreamos la punta de cada cadena contra sus tags safe y finalized 20 veces, cada 30 segundos, entre las 09:03 y las 09:14 UTC del 16 de octubre. Los números de abajo muestran cuánto iba cada tag detrás de la punta: la mediana y luego el rango.
cadena retraso safe retraso finalized
Base 48 s (16–88 s) 17.9 min (14.5–20.1 min)
OP Mainnet 90 s (24–158 s) 19.5 min (15.6–24.3 min)
Arbitrum One 11.3 min (7.4–14.3) 18.8 min (14.9–21.6 min)
Ethereum 10.0 min (6.4–12.6) 16.4 min (12.8–19.0 min)
Polygon PoS (sin tag safe) 3 s (0–5 s)
Solana confirmed 0–1 slots finalized 32–33 slots (~7 s)
Dos cosas resaltan. En Base, la distancia entre el momento en que el código de referencia reporta éxito y el momento en que el pago es final es de unos 18 minutos: unos 0.2 s hasta la preconfirmación, dos segundos hasta un bloque sellado, un minuto hasta safe y el resto hasta finalized. En Polygon la escalera completa dura unos tres segundos, así que esperar finalized no cuesta casi nada. El RPC de Polygon que usamos rechazó el tag safe de plano: "safe block not found".
Solana: un nivel sólido y un reloj más corto
La ruta de settle SVM hace una elección defendible. Consulta getSignatureStatuses y vuelve en cuanto el estado dice confirmed o finalized, con un presupuesto de 30 segundos. En nuestras muestras, confirmed iba 0 a 1 slots detrás de processed, y finalized 32 a 33 slots detrás. El resto de la ruta de settlement SVM lo recorrimos en nuestro post sobre el scheme exact en Solana.
El problema es el reloj. Una transacción de Solana es válida durante 150 bloques después de su blockhash. El binding SVM lo estima en "~60–90 seconds", y la constante del cache de settlements duplicados está comentada igual. El comentario del loop de confirmación asume que "Solana slots are ~400ms". Mainnet ahora es más rápida. Los block times dicen que 100,000 slots tomaron 21,573 segundos, o 215.7 ms por slot. Un conteo directo mostró que la altura de bloque subió 290 en 61 segundos.
Cuando el 402 no trae recentBlockhash, el cliente SVM de x402 busca el suyo con getLatestBlockhash() sin argumento de commitment, y el RPC lo sirve con su default finalized. En cinco probes, el lastValidBlockHeight devuelto quedó 118 bloques por encima de la altura processed actual. Al ritmo de hoy, son unos 25 segundos desde que el cliente lo pide hasta que el pago ya no puede aterrizar.
Con el flow authorization por defecto, esos 25 segundos tienen que cubrir la firma, el request de reintento, verify, el handler completo del recurso y la transmisión del settle. El binding ya lo dice: "Resource servers with long-running handlers SHOULD use payment flow upfront". Con los tiempos de slot actuales, "long-running" empieza cerca de los 20 segundos. Las llamadas LLM con razonamiento largo o salidas largas pueden tardar más que eso. Es la misma ventana entre verify y settle que el two-phase gap, fallando desde el otro extremo: el handler corre, settle falla porque el blockhash expiró y el vendedor hizo el trabajo para nada.
21 de octubre: Solana cambia su definición
Los niveles de commitment de Solana están por significar algo nuevo. SIMD-0326 reemplaza TowerBFT por Alpenglow. Su componente de votación finaliza un bloque tras una ronda de votos del 80% del stake ("fast-finalization"), o tras dos rondas del 60% ("slow-finalization"). La página de Alpenglow de Anza marca testnet y devnet como completados, mainnet-beta para el 10/21 y una finalidad de "150ms".
La migración, especificada en SIMD-0384, es la parte que un operador de x402 tiene que planificar. Un feature gate se activa en un borde de época. Cinco mil slots después llega el migration boundary. Desde ahí, los bloques llevan solo transacciones de voto, y cualquier otra transacción marca el bloque como muerto. En palabras del SIMD: "Core code will stop notifying RPC services of any optimistic confirmation/commitment updates". Después los validadores juntan genesis votes de al menos el 82% del stake. Cuando los tienen, deshacen todo lo posterior al bloque génesis de Alpenglow, le entregan el consenso a Votor y vuelven a "re-enable packing non-vote transactions, enable Alpenglow rooting, and re-enable RPC commitment/confirmation reporting". El objetivo declarado del SIMD es un traspaso "that doesn't rollback TowerBFT confirmed user transactions".
Nuestra aritmética, con getEpochInfo del 16 de octubre: las épocas son de 432,000 slots, y al ritmo actual el borde que cae el 21 de octubre es la época 1064, en el slot 459,648,000. Eso pondría el migration boundary en el slot 459,653,000, con 5,000 slots de TowerBFT en medio, unos 18 minutos a 215 ms. La cuenta del feature nombrada en SIMD-0384, a1penGLz8Vm2QHYB3JPefBiU4BY3Z6JkW2k3Scw5GWP, no existía en mainnet-beta cuando la consultamos. getAccountInfo devolvió null, con la versión de RPC 4.3.0.
Esto es lo que significa para un settle SVM de x402 enviado dentro de la ventana de solo votos. La transacción no puede empaquetarse. El loop de confirmación de 30 segundos del facilitator no verá confirmed, porque el reporte de commitment está pausado. Devuelve settlement_pending con una firma cuyo blockhash va a expirar antes de que vuelvan las transacciones de usuario. Con authorization, el vendedor ya corrió el handler. Con upfront, el comprador recibe un error antes de que se haga trabajo alguno.
Tras el traspaso, se espera que la distancia entre confirmed y finalized, que medimos en 32 slots, se reduzca al tiempo de finalidad de Alpenglow. SIMD-0326 no dice cómo se mapean los niveles de commitment del RPC sobre los certificados de Alpenglow. Vuelve a medir antes de apoyarte en los números nuevos.
Qué debería llevar success
Nada de esto necesita un protocolo nuevo. Necesita que la profundidad se diga en voz alta, en tres lugares.
En la respuesta de settle. SettleResponse ya tiene un objeto extensions. Un facilitator puede reportar el nivel que realmente observó:
{
"success": true,
"transaction": "0x…",
"network": "eip155:8453",
"extensions": {
"confirmation": {
"level": "preconfirmed", // | "unsafe" | "safe" | "finalized" | "confirmed"
"blockNumber": "52676397",
"blockHash": null, // desconocido hasta el sellado
"observedAt": "2026-10-16T09:21:04Z"
}
}
}
En los requirements. El confirmationPolicy de Cardano es una plantilla que ya funciona. Los bindings EVM y SVM podrían aceptar el mismo campo con niveles nativos de cada cadena: preconfirmed, unsafe, safe y finalized en cadenas OP Stack; confirmed y finalized en Solana. El facilitator anunciaría su rango en /supported, como ya lo permite el binding de Cardano.
En los bindings. Las reglas de familia de exact ya exigen que los métodos client-submitted nombren su evento de finalidad y quién es dueño de la política de profundidad. Extender eso a los métodos facilitator-submitted haría que EVM y SVM declaren lo que Starknet y Cardano ya declaran.
La economía es simple. Un vendedor que libera con preconfirmación y contabiliza con finalización tiene en vuelo, en cualquier momento, más o menos su ingreso por minuto multiplicado por 18 minutos en Base, y por tres segundos en Polygon. Esa exposición es aceptable a un centavo por llamada. Aun así hay que conocerla antes de poder aceptarla.
Qué significa para LLM4Agents
Nuestro rail walk-up de x402 liquida USDC en Base a través de infraestructura de facilitator hospedada. No corremos nosotros la espera del receipt, así que la profundidad detrás del success que recibimos la elige el facilitator, no nosotros. La documentación hospedada que leímos, la página de settlement-pending de CDP, describe una transacción cuya "final confirmation" no se observó "within the synchronous settlement window". No dice qué nivel es ese.
Para liberar una respuesta, está bien. Nuestros precios son centavos por llamada, la tasa objetivo de reorg de Flashblocks está por debajo del 0.1% y nada falló en nuestro probe. Esperar un minuto por safe o 18 minutos por finalized volvería inutilizable una API HTTP. Para nuestra contabilidad, no está bien. Una fila de pago que dice "settled" mientras la cadena dice "preconfirmed" es un registro que puede estar mal durante 18 minutos. Hoy nada en la respuesta nos dice en qué estado estamos.
Los agentes enfrentan la misma ambigüedad desde el otro lado. Un agente que busca el hash de transacción de nuestra respuesta de pago en un RPC sin Flashblocks puede no encontrar nada durante uno o dos bloques. Uno que lee el receipt desde un endpoint con Flashblocks verá un block hash todo en ceros. Ninguno de los dos es un pago fallido, y ninguno está documentado en un lugar donde un agente lo buscaría.
Solana y Polygon son donde esto se convierte en decisiones de diseño. Si agregamos un rail walk-up en Solana, una ventana de blockhash de 25 segundos descarta authorization para llamadas LLM largas. Necesitaríamos upfront, o un upto basado en escrow, que es como la documentación de CDP describe upto en Solana: "one onchain escrow deposit and a second onchain settlement". Cualquier rail en Solana también necesita un runbook para el 21 de octubre. Polygon va en sentido contrario: con finalidad determinística en pocos segundos, es la única red donde esperar finalized antes de liberar casi no cuesta nada.
También hay una oportunidad. Nada en el formato de respuesta de x402 le dice hoy a un agente qué tan final es su pago. Un receipt que diga "preconfirmado en el bloque N, finalizado a las 09:39 UTC" es concreto, verificable y barato de producir. Es el tipo de detalle que hace a un rail de pagos auditable por las máquinas que pagan.
Cómo mantenerse en la frontera
1. Registrar el nivel, no solo el hash. Para cada pago walk-up, guardar el número de bloque, el block hash (todo en ceros significa preconfirmado) y el nivel observado al momento del settle. Correr un reconciliador que promueva cada fila a safe y finalized con los tags de la propia cadena. Alertar ante cualquier transacción que desaparezca o cambie de bloque.
2. Separar liberación de contabilización. Seguir liberando respuestas con el success del facilitator. Reconocer ingresos solo en finalized: unos 18 minutos en Base y segundos en Polygon. Reportar el valor en vuelo como un número propio.
3. Poner el nivel en nuestros receipts. Agregar un objeto confirmation, con la forma del ejemplo de arriba, a las extensions de nuestra respuesta de pago y a un header de respuesta. Documentar qué significa un block hash en cero. Los agentes no deberían tener que redescubrir este probe.
4. Llevarlo upstream. Abrir un issue en x402 que proponga un confirmationPolicy para los bindings EVM y SVM inspirado en el de Cardano, más una extension de confirmación en la respuesta de settle. Por separado, proponer corregir el "~60–90 seconds" del binding SVM por una cifra expresada en bloques, con el tiempo medido de hoy al lado.
5. Elegir el payment flow según la duración del handler y la cadena. Antes de lanzar cualquier walk-up en Solana, usar por defecto upfront o upto basado en escrow para llamadas LLM. Si alguna vez se usa authorization ahí, obtener el blockhash en confirmed, entregarlo en el 402 y limitar el handler a unos 20 segundos.
6. Correr un runbook de Alpenglow el 21 de octubre. Vigilar la aparición de la cuenta del feature a1penGLz…5GWP. Desde la activación hasta que vuelva el reporte de commitment, sacar Solana de cualquier arreglo accepts y tratar ahí settlement_pending como algo esperado. Después, volver a medir la tasa de slots, la distancia entre confirmed y finalized y la ventana de blockhash, y actualizar cada timeout que dependa de ellas.
7. Repetir estos probes cada semana. Seguir la tasa de preconfirmación de Flashblocks en nuestra ruta de settle, los retrasos safe y finalized de cada cadena que aceptamos y la tasa de slots de Solana. Cualquiera de ellos puede cambiar lo que significa "success", y ningún cambio así se anunciaría dentro de x402.
Paga por llamada, en stablecoins, sobre una API compatible con OpenAI
Un 402, una firma, más de 345 modelos. Sin suscripción, sin mínimo.
Registra tu agente