← Blog
24 de septiembre, 2026 · 16 min

Auditoría de ERC-8183: tres ABIs y 81.095 jobs de agentes

ERC-8183 quiere ser el primitivo de escrow para el comercio entre agentes: un cliente bloquea fondos, un proveedor entrega, un evaluador decide. Leímos los tres textos que lo definen y contamos cada job del contrato en producción del que nació. La lógica del escrow funciona. El estándar todavía no existe como una sola cosa, y la evaluación, su idea central, casi nunca se usa.

Hoy la mayoría de los pagos de agentes son por llamada. Un agente firma una autorización x402, un facilitator la liquida y la respuesta vuelve. Eso funciona cuando el servicio es un único request HTTP. No encaja con trabajo que toma horas, produce un entregable y puede estar mal. ERC-8183, titulado "Agentic Commerce", apunta a ese segundo caso.

Este post hace tres cosas. Compara, función por función, el EIP publicado, su código de referencia inline y el repositorio de referencia. Corre cuatro pruebas en Foundry contra la implementación de referencia. Y lee el estado de los 81.095 jobs de AgenticCommerceV3, el contrato de producción en Base que la discusión de ERC-8183 trata como su predecesor. Todas las mediciones son del 24 de septiembre de 2026.

El job en un párrafo

ERC-8183 es un Draft creado el 25 de febrero de 2026. Se mergeó en el repositorio de ERCs el 5 de marzo con el PR #1581. Tiene cuatro autores, entre ellos Davide Crapis, que también es coautor de ERC-8004. La descripción de una línea de la spec es "Job escrow with evaluator attestation for agent commerce."

Un job tiene tres roles y seis estados. El cliente lo crea y nombra a un evaluador. El proveedor propone un precio. El cliente fondea el escrow en un único ERC-20. El proveedor hace submit de una referencia bytes32 al trabajo. Solo el evaluador puede entonces llamar a complete, que le paga al proveedor, o a reject, que le devuelve el dinero al cliente. Si nadie actúa antes de expiredAt, cualquiera puede llamar a claimRefund y el cliente recupera todo. El camino feliz mínimo se ve así:

// cliente
jobId = createJob(provider, evaluator, expiredAt, "summarise 40 PDFs", hook);
// proveedor
setBudget(jobId, 20_000000, "");                // 20 USDC
// cliente
fund(jobId, 20_000000, "");                     // expectedBudget: anti-front-running
// proveedor
submit(jobId, keccak256(deliverable), "");   // Funded -> Submitted
// evaluador
complete(jobId, reasonHash, "");             // Submitted -> Completed, escrow liberado

Son cinco transacciones de tres partes, más una aprobación del token, para liquidar un job. Un pago x402 exact es una firma y un settlement. La maquinaria extra compra una sola cosa: que alguien distinto del que paga y del que cobra decida si el trabajo se hizo.

Dos piezas opcionales completan el cuadro. Un contrato hook por job recibe callbacks beforeAction y afterAction y puede vetar transiciones. claimRefund no es hookable a propósito, para que un hook malo no pueda atrapar fondos. Y la spec recomienda mapear los resultados de cada job a reputación ERC-8004, con esta división del trabajo: "ACP remains the payment and escrow layer; ERC-8004 is the identity and reputation layer."

Un estándar, tres textos

La página en eips.ethereum.org no cambia desde el 13 de marzo, cuando el PR #1601 le pegó un contrato de referencia completo. Cuatro días después, el repositorio de referencia, erc-8183/base-contracts, empezó a divergir. Su último commit es del 30 de junio. Tiene su propia copia de la spec en eip.md, y esa copia nunca se propuso de vuelta al repositorio de ERCs. El único cambio abierto sobre el texto publicado es el PR #1732, una aclaración de dos líneas sobre anchos de enteros, que espera la revisión de un autor desde el 9 de mayo.

El resultado son tres textos que no coinciden en la función que mueve el dinero. Calculamos los selectores:

fund()                         selector    firma
EIP page, prosa (c/ optParams) 0xd2e13f50  fund(uint256,uint256,bytes)
EIP page, código de referencia 0xe25ba707  fund(uint256,bytes)
base-contracts @ 142e669       0x1f989ec8  fund(uint256,address,uint256,bytes)
AgenticCommerceV3 en Base      0xd2e13f50  fund(uint256,uint256,bytes)

createJob()
EIP page, código de referencia 0x41528812  createJob(address,address,uint256,string,address)
base-contracts @ 142e669       0xbaf3ede2  createJob(address,address,uint48,string,address,uint256)
AgenticCommerceV3 en Base      0x41528812  createJob(address,address,uint256,string,address)

La prosa publicada exige fund(jobId, expectedBudget, optParams?) y dice que SHALL revertir si el budget no coincide "(front-running protection)". El código de referencia de la misma página no tiene expectedBudget. Solo tiene fund(uint256 jobId, bytes optParams). En ese código solo el proveedor puede llamar a setBudget, así que un proveedor puede cambiar el precio entre la decisión del cliente y su transacción, y fund va a tomar el budget que haya, hasta el allowance del cliente. El repositorio agregó el chequeo el 17 de marzo. Después también fijó la dirección del token, porque ahora el budget lleva su propio token.

La capa de eventos también se parte. El repositorio cambió expiredAt a uint48 el 6 de mayo, lo que cambia el topic de JobCreated de 0xb0f0239b… a 0x834a72f1…. Un indexer construido para uno nunca ve los jobs del otro. El PR #1732 pide que los anchos uint256 sean normativos, con el argumento de que la implementación de referencia los usa. Se abrió tres días después de que la implementación de referencia dejara de usarlos.

La página publicada también se contradice sobre los hooks. Su tabla de encoding dice que un hook de setBudget recibe abi.encode(amount, optParams). Su código de referencia envía abi.encode(msg.sender, amount, optParams). Un hook escrito a partir de la tabla lee la dirección del caller como si fuera el monto. La prosa además lista setProvider como hookable, y su ejemplo de hook de subasta depende de eso, pero el setProvider de referencia no llama a ningún hook. El eip.md del repositorio corrige la tabla de encoding y ahora marca setProvider como no hookable. También agrega una nota que admite que el ejemplo de subasta supone un comportamiento que la implementación de referencia no tiene.

Vimos el mismo patrón en ERC-7824, donde el draft y el hub desplegado eran dos protocolos distintos. Aquí es peor, porque la discusión pública apunta al texto desactualizado. En agosto, posts del hilo de Ethereum Magicians, que ya pasa de 400 mensajes, todavía proponían agregar un campo submittedAt al struct Job. El repositorio lo había agregado el 24 de marzo.

Lo que agrega el borrador de trabajo

Evaluado por sí mismo, el borrador del repositorio es el mejor diseño. Sus 76 tests pasan. Los cambios que importan para agentes:

Casi todo esto llegó en mayo y junio. Se mergeó el 30 de junio desde una rama llamada okx/feature/meta-transactions-and-claims (PR #24). Un PR #26 abierto propone hacer hookable todo el ciclo de vida.

El contrato de producción: 81.095 jobs

El contrato que de verdad liquida dinero es AgenticCommerceV3, en 0x238E…32E0 en Base. En el hilo se lo identifica como el despliegue ACP de Virtuals y se lo llama predecesor de ERC-8183. Es un proxy UUPS desplegado el 8 de abril de 2026 (bloque 44.427.013). Su implementación, 0x8e86…77bc, se verificó en Blockscout el 6 de mayo, y el proxy nunca se actualizó. Leímos su configuración directamente:

$ ACP=0x238E541BfefD82238730D00a2208E5497F1832E0
$ cast call $ACP 'jobCounter()(uint256)'     # 81095
$ cast call $ACP 'paymentToken()(address)'   # 0x8335…2913 (USDC)
$ cast call $ACP 'platformFeeBP()(uint256)'  # 500 -> 5% al treasury
$ cast call $ACP 'evaluatorFeeBP()(uint256)' # 500 -> 5% al evaluador, solo en complete
$ cast code 0xe220329659d41b2a9f26e83816b424bdacf62567  # 0x -> el admin es una EOA común

V3 es una cuarta variante. Su fund coincide con la prosa publicada y su createJob con el código publicado. Se aparta de los tres textos en un punto importante: acepta evaluator = address(0). ERC-8183 dice que createJob SHALL revertir en ese caso. En V3, un evaluador cero significa que submit se autocompleta y le paga al proveedor en el acto. El período de gracia es de 15 minutos, no de una hora.

Leímos los 81.095 jobs con Multicall3, 250 llamadas jobs(uint256) por eth_call, entre los bloques 51.725.866 y 51.726.048 (09:11–09:17 UTC). Los mismos datos ya los había indexado MarselSultanov, que publicó los resultados en el post #393 del 19 de agosto, con un dataset reproducible de 62.953 jobs. Para esos mismos IDs nuestra lectura coincide exactamente: 72,50% sin evaluador, 27,48% con el cliente como su propio evaluador y diez jobs, 0,02%, con uno independiente. El cuadro completo hoy:

jobs                        81.095
  Open (nunca fondeados)    57.592   71,02%   todos menos uno vencidos
  Completed                 15.724   19,39%
  Expired                    6.026    7,43%
  Rejected                   1.750    2,16%
  Funded                         3            todos vencidos
evaluador = ninguno         46.052   56,79%
evaluador = cliente         35.006   43,17%
evaluador independiente         37    0,05%   8 direcciones
volumen bruto completado  2.097,64 USDC
  autoevaluado            1.994,62 USDC   95,09%
  independiente               0,97 USDC    0,05%
mediana de job pagado         0,01 USDC   (p90 0,05, máx 10,00)

Cuatro cosas saltan a la vista.

La evaluación no está ocurriendo. En los 18.142 jobs creados después del snapshot forense, la autoevaluación pasó de 27,48% a 97,60% y los jobs sin evaluador cayeron a 2,25%. El mercado pasó de "sin juez" a "el juez soy yo", que da la misma garantía y cuesta una transacción más. En toda la historia, ocho direcciones de evaluadores independientes manejaron 37 jobs y liberaron 97 centavos.

La mayor parte del volumen son dos direcciones pagándose entre sí. 0xe09f…8584 y 0x44cc…6664 completaron 3.950 jobs entre ellas, 2.014 en un sentido y 1.936 en el otro. Cada job fue autoevaluado. Juntas movieron 1.616,84 USDC, el 77,08% de todo el volumen completado, casi todo en jobs de 0,05 USDC con IDs del 60.245 al 70.984. El protocolo no puede distinguir esto de comercio real. Un sistema de reputación que cuente completions también cuenta estos. Sin el par, el resto del mercado completó 11.774 jobs por 480,80 USDC en cinco meses y medio.

La mayoría de los jobs son cascarones vacíos. Un solo cliente, 0x22f7…1491, creó 43.858 jobs (54,08% del total), todos sin evaluador, y 38.831 nunca se fondearon. En total, el 71% de los jobs nunca llegó a Funded.

El balance del escrow no cuadra. El contrato tiene 22,90 USDC. Los únicos escrows vivos son tres jobs Funded, vencidos y autoevaluados, por 3,00 USDC, que cualquiera puede reembolsar hoy. Los otros 19,90 USDC no pertenecen a ningún job vivo. Cada camino del ciclo de vida de V3 mueve exactamente el budget de un job, así que el excedente tiene que haber llegado por transferencia directa. La única función que puede moverlo es emergencyWithdraw, que solo el admin puede llamar, y solo con el contrato en pausa.

El problema del evaluador, medido

El rationale de la spec dice que, una vez hecho el submit, "the client cannot pull funds back unilaterally, so the provider is protected after starting work." La misma spec permite evaluator = client. blockbird señaló el 12 de agosto lo que se sigue de eso. El cliente se nombra evaluador, recibe el entregable en submit, no hace nada, y después del vencimiento cualquiera puede devolverle al cliente el total. Lo reprodujimos contra el repositorio de referencia:

function test_probe1_silentSelfEvaluatorRefundsClient() public {
    (uint256 jobId, uint48 expiry) = _selfEvaluatedSubmittedJob(); // el proveedor hizo submit
    vm.warp(uint256(expiry) + 1 hours);                           // expiredAt + gracia
    vm.prank(thirdParty);
    core.claimRefund(jobId);
    assertEq(usdc.balanceOf(client), BUDGET);                       // reembolso total
    assertEq(usdc.balanceOf(provider), 0);                          // trabajo entregado, impago
}   // [PASS]

El período de gracia no arregla esto. Se agregó para proteger a un evaluador que está revisando de un tercero que corre a llamar claimRefund. No hace nada contra un evaluador que elige el silencio. Solo obliga al cliente a esperar una hora más, o 15 minutos en V3. El issue #1931 hizo explícito el incentivo: complete cuesta gas, reject cuesta gas, y "saying nothing is free and refunds them in full." Se cerró el 6 de septiembre sin cambios en la spec. El 5 de septiembre el hilo les hizo a los autores una pregunta de sí o no sobre separar la ventana de evaluación de expiredAt. Hasta hoy nadie volvió a postear.

El esquema de fees de V3 agrega un segundo sesgo. El 5% del evaluador se paga en complete y nunca en reject. Un evaluador independiente solo gana dinero aprobando. Un cliente autoevaluado que completa recupera su propio 5%, y uno que se queda callado recupera el 100%.

Nada de esto está oculto. La spec dice que "a malicious evaluator can complete or reject arbitrarily" y recomienda reputación o staking. Los datos muestran que la configuración recomendada, un tercero con algo en juego, representa el 0,05% de los jobs reales.

Quién puede mover el escrow

La spec es cuidadosa con los hooks. No pueden bloquear claimRefund, e "implementations MUST NOT allow hooks to modify core escrow state directly." Sobre el operador casi no dice nada. El borrador de trabajo menciona las herramientas de admin solo para pedir que emitan eventos. Tanto el repositorio como V3 le dan al admin más poder sobre los fondos en escrow que el que tiene cualquier hook. Lo verificamos con tres pruebas más:

[PASS] test_probe2_feeChangedBetweenFundAndComplete()   // el admin pone fee de 100% después de fund; el proveedor recibe 0
[PASS] test_probe3_pauseBlocksRefundAndAdminDrains()    // claimRefund revierte en pausa; emergencyWithdraw vacía el escrow
[PASS] test_probe4_repriceBeforeFundReverts()           // expectedBudget frena el re-pricing del proveedor (el fix funciona)

Los fees son globales y se leen al momento del pago, así que el monto neto del proveedor no queda fijo cuando el cliente fondea. claimRefund lleva whenNotPaused, así que el "permissionless safety mechanism" deja de funcionar en cuanto el admin pausa. En pausa, emergencyWithdraw(token, to, amount) puede mandar cualquier monto de cualquier token a cualquier lado, sin contabilidad por job. batchDetachHook puede quitar la política que un cliente adjuntó a un job vivo. El contrato además es actualizable por UUPS por el default admin. En V3 ambos roles de admin están en manos de una sola EOA, 0xe220…2567, desde el 4 de mayo, cuando el deployer se los traspasó y revocó los suyos. No hay multisig ni timelock.

El monto en riesgo en V3 hoy es pequeño: 22,90 USDC. El problema es el diseño. Compáralo con el escrow detrás del scheme auth-capture de x402, un singleton no actualizable cuyos caminos de liberación quedan fijos en el despliegue. Un cliente de ERC-8183 confía en el evaluador que eligió y también en el admin del contrato, al que no eligió.

Dónde encaja x402 y dónde no

Las dos versiones de la spec declaran "x402 compatibility": un agente firma una intención y un facilitator la ejecuta. La versión publicada recomienda un trusted forwarder ERC-2771. El repositorio lo reemplazó por firmas EIP-712 por llamada, y es la decisión correcta, porque ningún forwarder privilegiado entra en la base de confianza. El propio repositorio de x402 tenía cero referencias a ERC-8183 o a AgenticCommerce cuando lo buscamos el 24 de septiembre. Ningún scheme de x402 envuelve un job.

La diferencia de forma importa más que el código que falta. fund toma los tokens con transferFrom. Un agente cuya única habilidad de pago es firmar un transferWithAuthorization EIP-3009 no puede fondear un job con eso. Necesita un permit EIP-2612 (USDC lo soporta) más una FundAuthorization, dos firmas que un relayer envía juntas. La mediana de gas de ejecución del test suite de referencia para el camino feliz de cinco llamadas suma unos 357.000 (createJob 134.647, setBudget 63.574, fund 63.324, submit 25.042, complete 70.600), antes del costo base de cada transacción.

Frente a ese overhead, la mediana de job pagado en V3 fue de un centavo. El mercado usa un escrow de jobs para pagos del tamaño de una llamada a una API, y en el 99,85% de los jobs recientes ninguna parte independiente evalúa nada, que era la razón para usar un escrow. Para ese tráfico, x402 es la mejor herramienta. El claim settlement es el solapamiento interesante. El settlement acumulado y monótono es la misma idea que el scheme upto de x402 y los vouchers firmados, salvo que aquí cada paso es una transacción on-chain en lugar de una firma.

Qué significa para LLM4Agents

ERC-8183 no compite con nuestro camino de pago. Un chat completion se cotiza, se entrega y se liquida en un solo round trip, y x402 encaja exactamente ahí. ERC-8183 está una capa más arriba. Cubre el trabajo que un agente contrata en lugar de llamar: inferencia batch sobre un corpus, un informe de investigación, un fine-tuning. En esa capa los agentes de nuestros clientes van a empezar a contratarse entre sí, y nuestro gateway podría aparecer ahí en tres roles.

Como proveedor, los jobs largos del gateway se podrían vender mediante un escrow de jobs en lugar de crédito prepago. El censo es una advertencia sobre las condiciones. Si el cliente es su propio evaluador y no hay bond, cada job entregado es una opción gratis para el cliente. Hoy el silencio del evaluador es riesgo del proveedor.

Como evaluador, un servicio de LLM-as-judge es un producto natural para un gateway de inferencia, y es el único rol que le falta al mercado. Solo ocho direcciones de evaluadores independientes aparecen alguna vez en un job de V3. El consenso que se está formando en el hilo le encaja bien: reason debería ser un hash de evidencia canónica y recomputable, no un veredicto opaco.

Como consumidor de reputación, ruteamos entre proveedores y vamos a querer señales de confianza. Los resultados de ERC-8183 volcados en ERC-8004 serían una de esas señales. Con los datos de hoy, esa señal es 95% autoevaluada por volumen y 77% un solo par circular. En nuestra auditoría empírica de ERC-8004 mostramos lo fácil que es manipular las escrituras de reputación. Las completions son más baratas de falsificar que las reviews.

Cómo mantenerse en la frontera

Seis pasos, en orden.

1. Mantener la inferencia en x402. Nada de tráfico por llamada pasa por un escrow de jobs. Vamos a usar ERC-8183 solo para trabajo con un entregable, una duración y un valor por encima de un umbral que justifique cinco transacciones.

2. Construir contra el borrador del repositorio, detrás de un chequeo de selectores. ERC8183WithAuthorization es el diseño al que vale la pena apuntar. Antes de interactuar con cualquier contrato desplegado, nuestro adapter va a leer su bytecode buscando los selectores de fund conocidos (0x1f989ec8, 0xd2e13f50, 0xe25ba707) y va a rechazar el que no tiene expectedBudget. Tres textos obligan a detectar, no a suponer.

3. Escribir una política de aceptación de proveedor antes de tomar un job. Rechazar evaluator == client por encima de un budget pequeño. Exigir que expiredAt deje margen para el trabajo más la evaluación. Revisar quién tiene el rol de admin del contrato de escrow y si es una EOA. Leer la configuración de fees, recordando que puede cambiar antes del pago. Donde el contrato soporte claim settlement, presentar un claim por milestone, para que un evaluador callado cueste como máximo un milestone.

4. Prototipar un evaluador con salida recomputable. Empezar con chequeos determinísticos que un LLM pueda orquestar, como validación de schema y ejecución de tests, y agregar rúbricas evaluadas por modelo después. Comprometer un hash de evidencia canónica como reason y publicar la evidencia, para que cualquier tercero pueda recomputar el veredicto. Cobrar un fee fijo que no dependa del resultado, lo contrario del pago-solo-al-completar de V3.

5. Descontar los resultados de ERC-8183 en cualquier score de confianza. Las completions autoevaluadas cuentan cero. Los pares de direcciones que se pagan entre sí se marcan. Solo cuentan las completions de un evaluador independiente con historial, y hoy eso significa que casi nada cuenta.

6. Empujar la spec, no solo el código. El arreglo más rápido para el ecosistema es subir el eip.md del repositorio, para que el hilo de Magicians deje de discutir el texto de marzo. Vamos a comentar en el hilo pidiendo eso, y también una ventana de evaluación separada o un fallback del lado del proveedor cuando vence un job en Submitted, como propuso el issue #1931.

ERC-8183 resuelve bien la mecánica del escrow, y su borrador de trabajo está bien pensado. Lo difícil es el evaluador, y el protocolo no puede proveer uno. Mientras la evaluación independiente no sea más barata que la autoevaluación, el "trustless agent commerce" sobre este primitivo significa un cliente que se paga a sí mismo.

Paga por llamada hoy, escrow cuando el trabajo lo necesite

Un gateway compatible con OpenAI donde cada request se liquida con un pago firmado en stablecoin. Sin cuentas, sin crédito prepago, sin evaluador en quien confiar.

Registrar un agente