← Blog
6 de septiembre, 2026 · 15 min

La wallet de agente que todavía no existe: ERC-6492 en x402

Un agente puede pagar su primer request desde una wallet que nunca fue desplegada. La firma lleva adentro las instrucciones de deployment, y el facilitator las ejecuta camino al settlement.

La especificación del scheme exact de x402 sobre EVM es explícita sobre lo que envía el cliente. El campo del payload se describe como "la firma de 65 bytes de la operación transferWithAuthorization", y el primer paso de verificación del facilitator es comprobar que la firma "es válida y recupera la dirección authorization.from". Las dos frases describen una cuenta externa (EOA). Sesenta y cinco bytes son r, s, v. "Recupera" es ecrecover.

Ahora lee las implementaciones. El repositorio x402-foundation/x402 incluye manejo de ERC-6492 en Go, Python y TypeScript, en el scheme exact y en el scheme batch-settlement. Hay un erc6492.go, un erc6492_deploy.go, un erc6492.py, y archivos de test para cada uno. El 5 de septiembre de 2026 se mergearon dos pull requests que optimizan ese camino todavía más. Nada de eso aparece en specs/schemes/exact/scheme_exact_evm.md.

Esa brecha importa para cualquiera que construya infraestructura de agentes, porque la wallet counterfactual es la forma de cuenta más útil para un agente autónomo, y las reglas que la gobiernan viven en código de implementación y no en un documento normativo.

El wrapper: qué es realmente una firma ERC-6492

ERC-6492, "Signature Validation for Predeploy Contracts", está en estado Final. Lo escribieron Ivo Georgiev y Agustin Aguilar, se creó el 10 de febrero de 2023, y requiere ERC-1271.

El problema que resuelve es acotado. Una cuenta de contrato prueba firmas mediante ERC-1271: el verificador llama isValidSignature(bytes32,bytes) sobre la cuenta y acepta la respuesta si devuelve el magic value 0x1626ba7e. Esa llamada es imposible si la cuenta no tiene código. Pero una cuenta CREATE2 tiene dirección determinista antes del deployment. Puede recibir USDC, aparecer en un explorer, y entregarse a un agente como identidad, todo mientras es una dirección vacía.

ERC-6492 empaqueta la firma ERC-1271 interna junto con las instrucciones necesarias para traer la cuenta a la existencia:

// Formato de cable de ERC-6492
abi.encode((address factory, bytes factoryCalldata, bytes signature)) + magicBytes

// magicBytes = bytes32(uint256(keccak256("erc6492.invalid.signature")) - 1)
0x6492649264926492649264926492649264926492649264926492649264926492

El sufijo termina en 0x92, que no es un valor v válido, así que una firma envuelta nunca puede confundirse con una firma ECDSA empaquetada. x402 lleva la misma constante en su módulo de constantes EVM, con la derivación keccak escrita en un comentario.

El parser en python/x402/mechanisms/evm/erc6492.py es deliberadamente total. parse_erc6492_signature revisa los últimos 32 bytes; si el magic no está, devuelve los bytes originales como inner_signature con factory en cero y calldata vacío. Toda firma que entra al facilitator se convierte en un ERC6492SignatureData, envuelta o no. Dos predicados la clasifican después: is_eoa_signature (65 bytes y factory en cero) y has_deployment_info (factory distinto de cero y calldata no vacío).

Es la misma forma que el initCode de ERC-4337, reubicada. En 4337 las instrucciones de deployment viajan en la UserOperation. En 6492 viajan en la firma, lo que significa que cualquier verificador que acepte bytes puede aceptar una cuenta counterfactual sin saber nada de bundlers ni entry points. Esa portabilidad es la razón por la que las firmas de Base Account incluyen el wrapper ERC-6492 por defecto, para poder verificarse antes de que el contrato de la wallet esté desplegado.

Ruteo por código, no por forma de la firma

El centro de la auditoría es verify_universal_signature. Su docstring enuncia la regla en una línea: el ruteo lo determina code.length, no la forma de la firma. La función siempre emite un eth_getCode contra el payer, y después ramifica.

code = signer.get_code(signer_address)
is_deployed = len(code) > 0
sig_data.code_deployed = is_deployed

if not is_deployed:
    if has_deployment_info(sig_data):
        # ERC-6492 counterfactual — se difiere a simulación/settle
        return (False, sig_data)
    if len(sig_data.inner_signature) == 65:
        return (verify_eoa_signature(hash, sig_data.inner_signature, signer_address), sig_data)
    return (False, sig_data)

# Tiene código (contrato O delegación ERC-7702) — EIP-1271 estricto, sin fallback ECDSA
return (verify_eip1271_signature(signer, signer_address, hash, sig_data.inner_signature), sig_data)

El comentario arriba de ese get_code es el rastro de un bug real de producción, y vale citarlo porque explica por qué la lectura es incondicional: "el viejo fast-path de is_eoa_signature lo salteaba para firmas de 65 bytes, haciendo que la pre-verificación devolviera válido para EOAs 7702 cuyo delegate rechaza ECDSA crudo on-chain". Saltear un round trip de red para firmas que parecían de EOA era correcto hasta que EIP-7702 volvió compatibles los hechos "firma de 65 bytes" y "dirección con código". Auditamos esa colisión cuando apareció por primera vez en el camino de firma de las smart EOAs 7702; el arreglo acá es el mismo principio aplicado una capa más abajo.

La rama counterfactual merece atención por lo que devuelve. La función devuelve (False, sig_data)no válido — y el docstring aclara que esto espeja el (false, sigData, nil) de Go y significa "diferido a simulación/settle". El código después advierte al caller de forma directa: quien chequee valid == True para aceptar un pago tiene que manejar este caso explícitamente, porque el pago no es válido hasta que la simulación confirme que el factory despliega la wallet y que la transferencia funciona.

La trampa — un booleano que significa "no" y un booleano que significa "todavía no" son el mismo booleano. Una implementación de facilitator de terceros que lea solo el primer valor de retorno rechaza toda wallet counterfactual, y lo hace con un error de firma inválida que no le dice nada útil al cliente. El issue de 2025 "Failing Payments with Coinbase Smart Wallet" en el tracker de x402 es exactamente esa clase de falla, de cuando el parsing todavía no existía.

Verify: un solo eth_call que despliega y paga

Como la rama de no desplegado difiere, algo más tiene que decidir. Ese algo es una simulación, y es más interesante que el eth_call habitual.

Cuando la clasificación lleva información de deployment, simulate_eip3009_transfer_result arma dos llamadas y las manda por Multicall3 en 0xcA11bde05977b3631167028862bE2a173976CA11: primero la llamada al factory que viene del wrapper de la firma, después transferWithAuthorization contra el token. Un eth_call, un contexto de ejecución EVM, estado arrastrado de la primera sub-llamada a la segunda. El éxito se define de forma acotada: el código chequea results[1].success, la transferencia, y expone el revert decodificado de la transferencia cuando falla.

Esto espeja lo que hace el propio contrato validador universal de ERC-6492 on-chain, donde desplegar requiere un CALL y no un STATICCALL y la implementación de referencia hace revert para descartar los side effects. x402 obtiene el mismo aislamiento gratis al no enviar nunca la simulación como transacción.

El resultado es un pre-chequeo fuerte. Responde la única pregunta que importa: si este factory corre y después se envía esta autorización, ¿se mueve el dinero? Una wallet cuyo validator set solo existe después del deployment se evalúa en su estado desplegado, no en el vacío.

Settle: dos transacciones, sin atomicidad

El settlement no preserva esa propiedad. El flujo en exact/facilitator.py es:

verify_result, classification = self._verify(payload, requirements,
                                              simulate=self._config.simulate_in_settle)
...
if has_deployment_info(sig_data):
    if not sig_data.code_deployed:
        factory_addr = bytes_to_hex(sig_data.factory)
        if factory_addr.lower() not in allowed:
            return SettleResponse(success=False, error_reason=ERR_FACTORY_NOT_ALLOWED, ...)
        self._deploy_smart_wallet(sig_data)      # tx 1: llamada al factory, espera receipt

tx_hash = execute_transfer_with_authorization(...)  # tx 2: el pago real

Dos transacciones separadas, secuenciadas por una espera de receipt. El helper de Go SendFactoryDeployTransaction es explícito sobre la guarda que sí aplica: espera el receipt y devuelve error si receipt.Status != TxStatusSuccess. Lo que ninguna implementación hace es re-simular después del deploy, y el razonamiento está documentado con detalle en el código: un eth_call aislado emitido justo después de una transacción de deploy real puede competir con la propagación de estado entre nodos RPC balanceados, lo que estaba produciendo rechazos falsos de tipo "inner signature unsupported" para wallets que en realidad estaban bien. El comentario nombra a Coinbase Smart Wallet como ejemplo. En su lugar, el transferWithAuthorization on-chain se trata como el chequeo definitivo.

Es una decisión de ingeniería defendible — un rechazo falso después de un deploy exitoso es peor que un revert honesto — pero cambia la garantía. La simulación probó que deploy-y-después-transferencia funciona de forma atómica. La ejecución hace deploy-y-después-transferencia de forma secuencial, entre dos bloques, con un hueco arbitrario. Todo lo que pueda cambiar en el medio queda fuera de la prueba: el nonce de la autorización puede consumirse en otro lado, el balance del payer puede moverse, validBefore puede vencer.

Hay un segundo hueco, más grande. El flag simulate que pasa settle es self._config.simulate_in_settle, y ese campo por defecto es False. La simulación atómica con Multicall3 corre durante el endpoint /verify por defecto, no durante /settle. En un despliegue típico esas son dos llamadas HTTP separadas, hechas por el resource server en dos momentos distintos. El "único pre-chequeo autoritativo" del que depende el settle puede haber ocurrido segundos o minutos antes, contra otro estado de la cadena. El diseño de los endpoints verify y settle del facilitator asume esa separación en todas partes; el camino counterfactual simplemente hereda más consecuencias de ella.

La exposición es asimétrica pero acotada. Los fondos no se pueden desviar: transferWithAuthorization codifica to y value dentro del mensaje EIP-712 firmado, y el facilitator sigue siendo un broadcaster sin capacidad de alterar ninguno de los dos, que es el punto entero de el primitivo EIP-3009. Lo que sí queda expuesto es el gas. Si el deploy entra y la transferencia revierte, el facilitator pagó el deployment de una smart account y no cobró nada.

El allowlist es todo el modelo de seguridad

Un wrapper ERC-6492 es, estructuralmente, una instrucción que le dice al facilitator que mande una transacción a una dirección elegida por el payer con calldata elegido por el payer, firmada con la clave del facilitator y pagada desde su balance. Dicho así, el riesgo es obvio.

x402 lo controla con un solo campo de configuración. El docstring de la config del scheme es inusualmente directo:

Allowlist de direcciones de contratos factory. Una lista no vacía habilita el deployment de smart wallets ERC-4337 vía EIP-6492. Una lista vacía (el default) niega todas las llamadas de deployment a factories. Los facilitators deben listar explícitamente cada factory en el que confían para prevenir inyección arbitraria de transacciones mediante firmas ERC-6492 controladas por un atacante.

Tres propiedades valen la pena. El default es negar: un facilitator que nunca configure el campo no puede ser inducido a desplegar nada. El chequeo se aplica dos veces, en verify y otra vez en settle, con un comentario que explica por qué: verify no debe aprobar un pago que settle va a rechazar. Y la falla es un código de error propio, eip6492_factory_not_allowed, en lugar de una respuesta genérica de firma inválida, junto a invalid_exact_evm_payload_undeployed_smart_wallet y smart_wallet_deployment_failed. El IsFactoryAllowed de Go repite la misma comparación case-insensitive para que el ruteo sea idéntico entre SDKs.

El allowlist acota qué se puede desplegar. No acota cuántas veces. Un factory de la lista, llamado con calldata que produce una dirección CREATE2 válida y una wallet cuyo validator desplegado después rechaza la firma interna, le cuesta al facilitator un deployment y no produce pago. La simulación es lo que frena esto en la práctica, ya que esa misma wallet fallaría el eth_call atómico — pero solo cuando la simulación efectivamente corrió, y solo si el estado de la cadena no se movió desde entonces. La sección de seguridad del propio ERC-6492 señala la propiedad que evita que esto empeore: como las direcciones derivan de CREATE2, cambiar el calldata del factory cambia la dirección desplegada y por lo tanto rompe la verificación. Un atacante no puede insertar bytecode arbitrario y conservar la misma dirección de payer.

Lo que la matriz de soporte admite

x402 documentó todo esto el 25 de junio de 2026 en una página de compatibilidad de wallets que clasifica a los payers en cinco tipos: EOA simple, smart account desplegada, counterfactual ERC-6492, 7702 con delegate permisivo, y 7702 con delegate estricto. La matriz es honesta sobre dónde falla el caso counterfactual.

Funciona para exact con EIP-3009, y para depósitos ERC-3009 de batch-settlement, en ambos casos solo con un allowlist de factories configurado. No funciona para ningún flujo Permit2: exact vía Permit2, upto, o depósitos Permit2. La razón es estructural: permitWitnessTransferFrom llama isValidSignature sobre el payer en el settlement, y el camino Permit2 nunca despliega la wallet primero, así que la llamada revierte contra una dirección sin código. Permit2 no tiene mecanismo counterfactual. Un agente que use el scheme medido upto necesita una wallet desplegada, sin excepción.

La página también documenta la falla que sobrevive al allowlist. Si una wallet instala su validator de verificación de forma diferida en vez de durante la llamada al factory — la forma que usan algunos setups de ERC-7579 y de session keys de Kernel, que despliegan solo con un root validator — entonces la wallet desplegada puede rechazar la firma interna y la transferencia revierte. La wallet ahora existe, así que un reintento normalmente liquida, porque una wallet desplegada puede producir una firma ERC-1271 estándar. El primer pago es el impuesto.

Un ítem más del propio ERC pertenece a cualquier modelo de amenaza. Las firmas counterfactual pueden validarse en otra red mientras la firma haya sido válida al momento del deployment y la wallet pueda desplegarse allí con la misma dirección de factory y el mismo bytecode. En x402 el pago en sí queda atado por el dominio EIP-712, que fija chainId y el verifyingContract del token, así que una autorización de pago no se replica entre cadenas. Pero la instrucción de deployment dentro del wrapper es agnóstica de la cadena. Un facilitator que opera en muchas redes tiene en la mano una instrucción portable para desplegar una cuenta, controlada únicamente por su propio allowlist.

La optimización que disparó esta auditoría

Los dos pull requests mergeados el 5 de septiembre de 2026 son pequeños y revelan la forma del problema. Ambos eliminan un eth_getCode redundante: verify ahora devuelve su clasificación de firma para que settle reutilice el estado de deployment del payer en vez de preguntarle a la cadena una segunda vez. El changeset explica la restricción que hizo esto seguro y no peligroso: ambas lecturas ocurren dentro de una misma llamada de settle y antes de cualquier transacción de deploy, así que no es la re-lectura post-deploy que compite con la propagación de estado del RPC.

Es el mismo riesgo contra el que guarda el comentario de "no re-simular", apareciendo en otro lugar. Un facilitator que toca la cadena después de haberla cambiado obtiene respuestas inconsistentes de un RPC balanceado. El camino counterfactual es el único flujo de x402 donde el facilitator despliega un contrato a mitad del settlement, y por eso acumula estas reglas.

Qué significa para LLM4Agents

La wallet counterfactual es la forma de cuenta correcta por defecto para un agente autónomo, y este es el mecanismo que la vuelve usable.

Considera la secuencia de onboarding sin ella. Creas un agente, derivas una dirección de smart account, la fondeas con USDC, y después mandas una transacción de deployment — que requiere gas nativo en esa dirección, desde una cuenta que se suponía gasless. El agente no puede pagar su propia existencia con las stablecoins que tiene. Cada workaround (un paymaster, un bundler subsidiado, una recarga manual) agrega una dependencia antes de que el agente haya hecho nada. Recorrimos esa cadena de dependencias en el artículo de account abstraction; ERC-6492 la elimina para el caso específico del primer pago.

Con ella, la secuencia colapsa. Derivas la dirección, la fondeas con USDC, firmas una autorización EIP-3009 envuelta con el calldata del factory, y mandas el request. El facilitator despliega la cuenta y mueve el dinero. El agente nunca tuvo gas nativo y nunca envió una transacción.

Para el gateway de LLM4Agents esto corta en dos direcciones. Del lado buyer es una ganancia directa: un agente registrado en la plataforma puede recibir una dirección a la que se lo puede fondear de inmediato, y su primera inferencia paga es también el primer bloque de existencia de su wallet. En cualquier superficie de facilitator que operemos, es un pasivo que hay que configurar. El default de negar está bien. Activarlo implica nombrar factories específicos, aceptar que pagamos gas de deployment fuera de banda sin ningún campo en el payload de x402 para cobrarlo, y aceptar que una wallet con validator diferido puede quemar ese gas sin producir un pago.

El punto más amplio es dónde viven las reglas. Un resource server que lea solo la especificación implementaría ecrecover contra una firma de 65 bytes y rechazaría toda smart account y toda wallet counterfactual — la mayoría de las wallets de agentes que importan. El comportamiento que decide si un pago funciona está en tres implementaciones de SDK y una página de documentación, no en el texto normativo. Cualquiera que integre x402 solo desde la spec está integrando el protocolo equivocado.

Cómo mantenerse en la frontera

Pasos concretos, en orden.

Primero, tratar el booleano de verify como de tres valores. Todo camino de código que controlemos y consuma una verificación de facilitator tiene que distinguir válido, inválido y diferido. Colapsar "todavía no" en "no" es el bug de integración más frecuente en esta área, y se manifiesta como usuarios de smart wallet a quienes se les dice que su firma está mal formada.

Segundo, decidir el allowlist de factories de forma deliberada. Si operamos una superficie de facilitator, el allowlist debe contener los factories detrás de las wallets que nuestros agentes realmente usan, verificados leyendo el contrato del factory, y nada más. Cada entrada es una autorización permanente a gastar nuestro gas por instrucción de un payer. La lista va en configuración revisada, no en una variable de entorno que cualquiera pueda ampliar.

Tercero, instrumentar el camino counterfactual por separado. Las transacciones de deployment, su costo en gas, y la tasa con la que un deploy es seguido por una transferencia revertida son una métrica distinta del éxito de settlement. Esa proporción es la alerta temprana de la falla del validator diferido, y es el número que nos dice si subsidiar deployments es económicamente sensato a nuestro volumen.

Cuarto, definir simulate_in_settle con honestidad. El default de False es una optimización de latencia que asume que verify corrió hace poco contra un estado comparable. Para payers counterfactual en particular, donde settle va a mandar una transacción de deployment real, volver a correr la simulación atómica en el momento del settle vale el eth_call extra. Medirlo, y después elegir.

Quinto, mantener las wallets counterfactual lejos de los flujos Permit2. El ruteo no es un problema de descubrimiento en runtime; es una precondición. Si la wallet de un agente no tiene código, EIP-3009 es el único asset transfer method que puede funcionar, y cualquier librería cliente que enviemos debería rechazar el camino Permit2 en vez de emitir un pago que revierte on-chain.

Sexto, empujar el comportamiento río arriba. La matriz de compatibilidad es documentación, y la documentación no es contra lo que implementan los demás facilitators. El camino counterfactual — el wrapper, el requisito del allowlist, el estado de verificación diferida, los códigos de error — pertenece a la especificación del scheme exact. Hasta que esté ahí, cada implementación de facilitator es un protocolo ligeramente distinto, y los agentes van a descubrir las diferencias con pagos que fallan.

Paga desde una wallet que todavía no existe

Inferencia compatible con OpenAI, liquidada por llamada en stablecoins.

Registrar un agente