ERC-7710 sobre x402: el rail de delegación de MetaMask, auditado
Hace dos auditorías mostramos que el spend cap del cliente x402 olvida cada pago en el momento en que firma. Hace una, que el contrato de presupuesto onchain de Coinbase nunca toca el camino de pago de x402. Esta semana auditamos el único stack que sí conecta las dos capas — y lo encontramos procesando 20.009 redemptions de delegación por semana en Base mientras su propio setup documentado de cliente rechaza todas las ofertas que él mismo emite.
El stack es de MetaMask. Tiene tres capas: ERC-7710, un ERC en Draft de mayo de 2024 que estandariza la delegación entre smart contracts; ERC-7715, un Draft de cuatro días después que da a las dapps un método RPC wallet_requestExecutionPermissions para pedirle a una wallet un permiso acotado; y el Delegation Framework, la implementación en Solidity que convierte ambos en contratos desplegados. Encima de eso, un paquete de npm llamado @metamask/x402 enchufa las delegaciones al scheme exact de x402 como tercer asset transfer method junto a EIP-3009 y Permit2.
Como siempre en esta serie, todo lo que sigue es de fuentes primarias: clonamos los repos, leímos los contratos, verificamos bytecode por RPCs públicos, escaneamos una semana de eventos en mainnet e instalamos los paquetes publicados para probarlos contra ofertas reales. Las fechas son al 26 de agosto de 2026.
El framework: 38 caveats, un manager, 24 cadenas
El repo delegation-framework se creó el 3 de julio de 2024 (MIT y Apache-2.0, 221 stars, 117 forks, 299 commits). El HEAD de main es 9e7fd80 del 7 de agosto de 2026 — un deployment al testnet de Arc, una de las cadenas que cubrimos en nuestra auditoría de cadenas de pagos. El repo lleva doce PDFs de auditoría dentro del árbol: siete de Cyfrin (marzo 2025 a mayo 2026) y cinco de Consensys Diligence (junio 2024 a abril 2025). Es un historial de auditoría bastante más pesado que los dos reportes de Cantina con los que salió el SpendPermissionManager de Coinbase.
El objeto central es un struct Delegation de seis campos: delegate, delegator, authority (un hash que apunta a la delegación padre — así funcionan las cadenas de re-delegación), un array de caveats, un salt y una signature. Cada caveat es una tripleta — enforcer, terms, args — donde el enforcer es un contrato aparte, con estado, que recibe un hook antes y después de la ejecución. El framework trae 38 enforcers. Los que importan para presupuestos de agentes: ERC20PeriodTransferEnforcer (allowance por período), ERC20StreamingEnforcer (allowance en streaming lineal), MultiTokenPeriodEnforcer y sus gemelos para el token nativo.
Leímos ERC20PeriodTransferEnforcer de cerca, porque es la contraparte exacta de lo que auditamos en el SpendPermissionManager de Coinbase. Los terms son 116 bytes empaquetados: dirección del token, periodAmount, periodDuration en segundos, startDate. La ventana es un calendario fijo indexado desde startDate, no una ventana rolling — lo no usado se pierde en cada frontera, y aplica la misma propiedad de borde: un agente puede gastar hasta 2x el monto del período a caballo de una frontera. Donde Coinbase guarda la contabilidad dentro del manager, MetaMask la guarda dentro de cada enforcer, con clave (delegationManager, delegationHash). El enforcer confía en cualquier manager que lo llame, y eso es lo que permite que los mismos contratos de presupuesto sirvan a cualquier manager compatible con ERC-7710.
El deployment replica el patrón que medimos la semana pasada, hasta en el tooling: CREATE2 con el salt "GATOR", una dirección por contrato en todas las cadenas. DelegationManager v1.3.0 vive en 0xdb9B1e94B5b69Df7e401DDbedE43491141047dB3 en 24 mainnets según el paquete @metamask/delegation-deployments — incluyendo Tempo y Robinhood Chain, las cadenas con marca de agentes de nuestra cobertura anterior. Bajamos el bytecode por RPCs públicos en Base, Optimism, Arbitrum y Linea: el mismo runtime de 11.503 bytes en las cuatro, y un diff Base-vs-Optimism de exactamente 34 bytes — el domain separator EIP-712 cacheado (32 bytes) más el chain id (2 bytes). Es, byte por byte, la misma firma de diff que encontramos entre los deployments de Coinbase. Una curiosidad: el repo tiene tags v1.0.0 a v1.3.0, pero el paquete de deployments solo mapea 1.0.0, 1.1.0 y 1.3.0 — la 1.2.0 (enero 2025) nunca salió como set de deployment soportado.
El wire: una sección de spec que el monorepo nunca implementó
El lado x402 de esta historia está en la spec, no en el código. La sección 3 de scheme_exact_evm.md en el monorepo de x402 define assetTransferMethod: "erc7710" como tercer método de transferencia para el scheme exact. El payload de pago son tres campos — delegationManager, permissionContext, delegator — y nada más. Sin firma sobre el pago, sin nonce, sin ventana de validez: la delegación dentro de permissionContext es la autorización, y el facilitator liquida llamando redeemDelegations con un transfer(payTo, amount) codificado.
El modelo de verificación es lo bastante inusual como para citarlo: la verificación "is performed entirely through simulation", y la simulación "serves as the sole verification mechanism — no trusted list of Delegation Manager implementations is required". La spec es honesta sobre lo que eso compra: un cliente puede invalidar su delegación entre simulación y settlement, así que el facilitator paga el gas de una transacción fallida; y un delegation manager malicioso puede ser una trampa de gas, así que los facilitators deben acotar el gas por redemption. Riesgo del facilitator, no del seller.
La historia de esa sección dice mucho de dónde está la governance de x402. Entró a la spec por el PR #732, abierto por Dan Finlay — cofundador de MetaMask y coautor de ambos ERCs — el 8 de diciembre de 2025, y mergeado el 13 de marzo de 2026. Su continuación, el PR #1837, "First draft of ERC-7710 exact scheme implementation" (14 archivos, +1.470 líneas), se abrió el 26 de marzo y no se toca desde el día en que se abrió. Cinco meses después, el monorepo en HEAD bde6fe5 contiene cero ocurrencias de redeemDelegations y cero de erc7710 fuera del archivo de spec. La implementación salió por otro lado: @metamask/x402 1.0.0, publicado el 13 de agosto de 2026 — 308 líneas de TypeScript en cinco archivos, que deliberadamente no importa paquetes de x402 y tipa todo estructuralmente para poder pararse a cualquier lado del churn de versiones.
El rail vivo: un facilitator, tres signers, 20.009 redemptions
MetaMask corre su propio facilitator para este método — una familia de endpoints llamada tx-sentinel. La instancia de Base mainnet responde GET /supported con 16 kinds, todos scheme exact con assetTransferMethods: ["eip3009", "permit2", "erc7710"], en redes que incluyen Ethereum, Base, Polygon, Arbitrum, BSC, Sei, Monad, MegaETH y Tempo, más una extensión gasPayment. Dos detalles llamaron la atención. Primero, la respuesta de producción anuncia la dirección del signer de dev del equipo, mientras que los tres signers de producción solo aparecen en el código del SDK, en una constante llamada METAMASK_FACILITATOR_ADDRESSES. Segundo, la lista soportada incluye eip155:1337 — el chain id por defecto de desarrollo local — servido desde un endpoint de producción.
Después medimos la cadena. Sobre los bloques 50.170.490 a 50.472.890 de Base — 302.400 bloques, aproximadamente del 19 al 26 de agosto — el DelegationManager en 0xdb9B…dB3 emitió 20.011 eventos: 20.009 RedeemedDelegation, 2 DisabledDelegation y cero EnabledDelegation, repartidos en 19.865 transacciones de 7.233 root delegators distintos y 146 redeemers distintos.
Para dimensionar: la misma ventana de una semana medida en la auditoría de la semana pasada capturó 547 gastos por el SpendPermissionManager de Coinbase en la misma cadena. El rail de MetaMask corre a unas 36x ese volumen. Las tres EOAs de los signers tienen nonces de vida en Base de 235.571, 235.566 y 235.838 — unas 707.000 transacciones de settlement en total, aunque esa cifra de vida cubre los tres métodos de transferencia que el facilitator liquida, no solo erc7710. El resto de la tabla de redeemers es chico: Multicall3 explica 1.225 redemptions, y ningún otro redeemer individual pasa de 41.
Los caveats que hacen un presupuesto — y el default que se salta uno
¿Cómo un pago se convierte en presupuesto? Por la cadena de delegación. En el flujo directo, la smart account del agente firma una delegación por pago. En el flujo recurrente — el interesante — un humano otorga un permiso ERC-7715 de tipo erc20-token-periodic a una session key; la session key re-delega al facilitator en cada pago, arrastrando los caveats del padre. El period enforcer acumula el gasto onchain a través de todos los pagos redimidos bajo ese permiso. Esta es exactamente la composición que no existía en ningún lado del stack que auditamos el 24 de agosto: el cap por pago del protocolo y el presupuesto acumulado onchain, aplicados en la misma transacción.
También leímos qué pone el SDK en una delegación por pago, en createx402DelegationProvider (exportado desde un path que literalmente se llama experimental). Toda delegación recibe un scope de monto de transferencia capado en el monto exacto de la oferta, más un caveat que pinnea el destinatario del transfer ERC-20 al payTo del seller vía matching de calldata. Pero la delegación es una delegación abierta — redimible por cualquiera — y la restricción de redeemer solo se agrega cuando los requirements del server traen las direcciones del facilitator. El caveat de expiración solo se agrega si el integrador configura expirySeconds explícitamente; nada lo defaultea y el maxTimeoutSeconds de la oferta no se usa. Así que el artefacto por defecto es una autorización de pago por un monto exacto a un payee fijo que no expira nunca — contraste filoso con el validBefore obligatorio de EIP-3009. Los fondos solo pueden llegar al seller previsto, pero una delegación vieja puede redimirse en cualquier momento futuro por cualquiera que haya guardado los bytes, cobrándole al buyer cuando ya no lo espera.
El drive: defaults que rechazan el propio rail de MetaMask
Al final hicimos lo que esta serie siempre hace: instalamos los paquetes publicados — @x402/core 2.23.0 y @metamask/x402 1.0.0, ambos de npm — y los manejamos. La documentación de MetaMask, y el SKILL.md orientado a agentes que el repo trae para asistentes de código con IA, dan el mismo setup: new x402Client().register('eip155:*', erc7710Client).
Ese setup hoy no puede pagar nada. Una oferta de $0,50 en USDC en Base — un asset default, bien por debajo del cap default de $1 que auditamos hace dos días — se rechaza con All payment requirements were rejected by spendControls. La razón es estructural: desde 2.23.0, el cliente core decide si un asset es una stablecoin reconocida preguntándole al scheme client por findDefaultAsset, un método que la interfaz marca opcional y que x402Erc7710Client no implementa. Sin método, no hay assets reconocidos ni ofertas pagables — el modo de falla exacto para scheme clients de terceros que nuestra auditoría de spend controls señaló en abstracto. Acá está en concreto, contra el adoptante externo de mayor volumen que tiene x402.
El timeline no perdona. @metamask/x402 1.0.0 salió el 13 de agosto con peer dependency @x402/core ^2.12.0. Core 2.23.0, el release que prendió los spend controls por defecto, salió el 18 de agosto — cinco días después, dentro de ese rango caret. El string spendControls aparece cero veces en el repo smart-accounts-kit, código o docs. Cualquiera que siga el quickstart actual de MetaMask contra el propio facilitator vivo de MetaMask obtiene un cliente que rechaza todas las ofertas que ese facilitator emite.
Los escapes funcionan, con una trampa. setSpendControls(false) desbloquea el pago, igual que construir el cliente con x402Client.fromConfig({...}) — por donde además confirmamos el otro borde: con spendControls: false, nuestro cliente de prueba firmó un payload de delegación autorizando 1.000.000 de USDC sin quejarse. Pero pasar ese mismo objeto de config al constructor — new x402Client({ spendControls: false }) — se acepta en silencio y se ignora en silencio, porque el constructor espera una función selectora, no una config. Un argumento mal tipado que no cambia nada y no lanza nada es exactamente el tipo de borde que va a pisar una integración escrita por un LLM.
Qué significa para LLM4Agents
Este es el primer rail de producción donde la respuesta a "¿cuánto gastó ya este agente?" vive onchain y dentro del camino de pago de x402. Nos importa por tres lados. Primero, como opción del lado buyer: las wallets de LLM4Agents hoy liquidan por EIP-3009, que capa cada pago pero no acumula nada. Un flujo erc7710 con caveat periódico le da al operador de flota un presupuesto por agente que un agente comprometido no puede exceder por muchos pagos bajo el cap que firme — el modo de falla que ya documentamos tres veces. Segundo, como obligación del lado seller: ya existen ofertas con assetTransferMethod: "erc7710" en la calle, y un gateway que publica o proxea ofertas necesita pasar el campo limpio o quitarlo a propósito, no romperlo. Tercero, como advertencia que ya conocíamos en miniatura: cada capa que sumamos sobre el protocolo — spend controls, presupuestos, scopes de delegación — sale con defaults, y los defaults de distintos vendors ya se rompen entre sí con cinco días de diferencia, demostrado. El 87,6% también corta para los dos lados: el rail es real, pero es el facilitator de un vendor redimiendo las delegaciones de ese vendor a través del SDK de ese vendor, de punta a punta.
Cómo mantenerse en la frontera
Pasos concretos, en orden. Uno: agregar manejo de ofertas erc7710 a nuestro stack de cliente detrás de un opt-in explícito — e implementar findDefaultAsset en cualquier scheme client que publiquemos, porque ahora tenemos prueba empírica de lo que hace su ausencia bajo los defaults de 2.23.0. Dos: pinnear @x402/core exacto, no con caret; los dos últimos breaks de comportamiento llegaron dentro de versiones minor, y esta auditoría atrapó a un vendor de wallets mayor absorbiendo uno. Tres: prototipar un flujo de presupuesto periódico en Base Sepolia contra tx-sentinel — una session key, un permiso erc20-token-periodic, N pagos — y medir dónde la contabilidad onchain diverge del metered billing de nuestro gateway, la costura que nuestra auditoría de observabilidad de costos mostró que nadie estandariza. Cuatro: setear siempre expirySeconds al crear delegaciones, y tratar cualquier delegación sin caveat de timestamp como pasivo pendiente en las revisiones de higiene de wallets. Cinco: vigilar el PR #1837 y el campo de facilitators — el día que un segundo facilitator no-MetaMask anuncie erc7710, el pago por delegación deja de ser un stack vertical y se vuelve un rail interoperable que merece soporte de primera clase.
Dale a tus agentes presupuestos, no solo balances
LLM4Agents corre wallets fondeadas en stablecoins con caps por llamada sobre un gateway OpenAI-compatible — y auditamos las capas de presupuesto antes de que lleguen a tu flota.
Registra tu agente