Squads y Swig ya pagan x402: auditamos el path de smart wallets en Solana
Desde mayo, el scheme exact de x402 en Solana tiene una segunda forma de verificar un pago. Ya no exige que la transferencia del token sea una instrucción de nivel superior, así que una smart wallet de Squads o Swig puede pagar vía CPI. Eso mueve la política de gasto del agente dentro del propio pago. También traslada riesgo nuevo a quien patrocina el fee.
En julio describimos el scheme exact SVM como un layout fijo: dos instrucciones de compute budget, un TransferChecked, algunos extras opcionales y nada más. Eso era correcto para el fast path, y así se ven todavía la mayoría de los pagos. Pero era solo la mitad del cuadro. Desde @x402/svm 2.14.0, publicado el 2026-05-29, el facilitator de referencia trae un segundo path opcional para smart wallets. El spec se reescribió en torno a él el 2026-06-09.
Esto importa más para los agentes de lo que parece. En EVM, como mostramos en la auditoría de Smart Sessions, un pago x402 es una firma EIP-3009 que envía el facilitator, así que las políticas de sesión de la smart account nunca se ejecutan. En Solana, un pago desde una smart wallet es una instrucción al programa de la wallet. Los límites propios de la wallet se ejecutan dentro de la transacción de pago. Así que leímos el spec y el código en TypeScript y Go. Luego enviamos requests /verify fabricados a facilitators en vivo y muestreamos settlements en mainnet. El diseño es sólido. Los detalles a su alrededor no son uniformes.
Del layout de instrucciones al resultado del pago
El scheme_exact_svm.md actual parte ahora de otra premisa. Define una "semántica de pago basada en resultados": la transacción debe producir una transferencia de al menos el monto requerido, del mint correcto, hacia la ATA derivada de payTo. No importa qué instrucción la produzca. La transferencia "MAY appear either as a top-level instruction or as an inner instruction (CPI) emitted by another program". El spec dice sin rodeos que esto es "what allows smart wallets to satisfy the scheme".
Todo lo relativo al patrocinio del fee pasó a una sección aparte llamada Sponsor Acceptance Policy. El sponsor es quien firma como feePayer, sea el merchant o un facilitator externo. El spec mantiene cinco MUST para cualquier sponsor: aislamiento del fee payer, resolución de address lookup tables, seguridad de los fondos del fee payer, integridad del conjunto de firmantes y el resultado de pago exacto. Los topes de compute, una allowlist de programas y la simulación son SHOULD. Un sponsor "MAY reject transactions that satisfy the exact scheme but violate its local policy", y los clientes "SHOULD NOT assume universal sponsorship".
La historia es corta y pública. La idea nació como el RFC #646, abierto en noviembre de 2025, que proponía contar los TransferChecked en instrucciones de nivel superior e internas en lugar de revisar posiciones. El RFC sigue abierto, porque su título también pide validación de deadline. La implementación en TypeScript entró como el PR #1527, mergeado el 2026-05-28. El perfil de GitHub de su autor menciona Dexter Intelligence DAO LLC, y el PR afirma que el enfoque "has been running in production at Dexter". El texto del spec llegó después en el PR #829. La paridad en Go llegó con el PR #3263 el 2026-08-26. El facilitator en Python no tiene path para smart wallets. Tampoco x402-rs, cuyo verificador de Solana simula con inner_instructions: false.
Cómo verifica el Path 2 un pago que no puede parsear
El ExactSvmScheme de referencia mantiene dos paths. El Path 1 es la validación estática de layout que cubrimos en julio, que ahora admite de tres a siete instrucciones porque Phantom inyecta hasta tres assertions de Lighthouse. El Path 2 solo corre cuando un operador activa enableSmartWalletVerification, y solo cuando el Path 1 falló por una razón de layout. Los fallos semánticos, como un monto, mint, destinatario o memo incorrecto, nunca pasan al Path 2. Así un rechazo real no queda oculto detrás de un código de error smart_wallet_*.
const scheme = new ExactSvmScheme(signer, undefined, {
enableSmartWalletVerification: true,
smartWalletMaxComputeUnits: 400_000, // default
smartWalletMaxPriorityFeeMicroLamports: 50_000, // default, por compute unit
smartWalletAllowedPrograms: [/* por defecto, 7 programas */],
});
El Path 2 ejecuta entonces seis pasos. Primero, aislamiento del fee payer: la dirección del sponsor no puede aparecer en las cuentas de ninguna instrucción ni como programa, después de resolver las lookup tables. El razonamiento del spec es que una llave que el runtime nunca ve en una instrucción no puede ser debitada más allá del fee de red. Segundo, topes de compute: solo se aceptan SetComputeUnitLimit y SetComputeUnitPrice, dentro de los límites configurados. Tercero, todo programa de nivel superior distinto de ComputeBudget y Memo debe estar en la allowlist. Cuarto, un extra.memo definido por el vendedor debe coincidir con exactamente una instrucción Memo de nivel superior, así que no se puede usar una smart wallet para omitirlo.
Quinto, el facilitator llama a simulateTransaction con las instrucciones internas activadas y lee la traza CPI completa. Sexto, entre instrucciones de nivel superior e internas, exactamente un TransferChecked debe coincidir con el mint, con una de las dos ATAs de destino posibles (SPL Token o Token-2022) y con un monto de al menos el requerido. El sobrepago se tolera a propósito, para wallets que redondean fees internamente. Cero coincidencias o dos coincidencias es un rechazo. Si alguna transferencia observada tiene como authority una llave del facilitator, el pago se rechaza por self-spend.
El settlement agrega un paso más que el Path 1 no necesita. La simulación muestra que una transacción tendría éxito, no que lo tuvo. Así que, cuando un settlement del Path 2 se confirma, el facilitator trae las instrucciones internas de la transacción confirmada y vuelve a buscar la transferencia coincidente. Si el RPC no la ha indexado tras tres intentos, recurre al balance de destino: el después menos el antes debe ser al menos el monto. El spec enumera las invariantes resultantes como I1 a I7. Las dos que cargan la seguridad son I1, sin pérdida de tokens, aplicada antes de firmar, e I3, la verdad del merchant, aplicada después del settlement. La simulación es explícitamente "non-critical".
Cómo se ve on-chain un pago desde una smart wallet
La suite end-to-end de x402 liquida un pago de Swig en devnet. Tomamos una de esas transacciones, del 2026-05-28, directo del RPC de devnet:
// tx de devnet 2yvRfgR4...xFGVbG (suite e2e de x402, cliente Swig)
top[0] ComputeBudget SetComputeUnitLimit
top[1] ComputeBudget SetComputeUnitPrice
top[2] swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB // Swig SignV2
inner Tokenkeg... transferChecked 1000 unidades de USDC de devnet
authority = BEU34VsxHNECbtwqMq7F51EYFXirX2teM3rCCb4SV1gD // PDA de la wallet Swig
top[3] Memo
firmantes: fee payer + una llave del cliente fee: 12,000 lamports CU usadas: 17,054
Dos detalles le importan a un merchant. El payer que reporta la verificación es la authority de la transferencia, que aquí es la dirección de la wallet Swig, no la llave del agente. Y todo usó 17,054 compute units, muy por debajo de las 400,000 que pide el cliente e2e actual. Volvemos a esa brecha más abajo.
Lo que importa para los agentes: la política corre dentro del pago
Aquí es donde Solana y EVM divergen. En un pago del Path 2, el facilitator simula y luego envía una transacción cuya instrucción de pago es el programa de la wallet. El programa autentica el rol del agente, aplica sus límites y solo entonces hace la transferencia vía CPI. Si se excede un límite, el programa falla y el pago nunca ocurre. El facilitator lo ve primero como una simulación fallida.
Leímos los dos programas de wallet principales. Los roles de Swig pueden llevar TokenLimit, TokenRecurringLimit, TokenDestinationLimit y TokenRecurringDestinationLimit. El último se indexa por mint más token account de destino, con una ventana de reinicio medida en slots. En SignV2, un gasto de tokens desde una cuenta de Swig se deniega si no existe un límite coincidente, salvo que el rol tenga el permiso irrestricto All o AllButManageAuthority. Las authorities de un rol incluyen llaves Ed25519, secp256k1 y secp256r1, cada una con variante de sesión.
El programa Squads Smart Account tiene use_spending_limit. Un spending limit lista los firmantes autorizados a usarlo, una lista opcional de direcciones owner de destino, un período (único, día, semana o mes de 30 días) y una expiración. La instrucción verifica todo eso, descuenta el monto restante y llama a transfer_checked vía CPI. La lista de destinos contiene direcciones owner, lo mismo que x402 llama payTo.
Una salvedad impide que esto esté probado de punta a punta en público. El fixture e2e de x402 crea su wallet Swig con Actions.set().all(), un rol root irrestricto. Así que el path de smart wallets que ejercita el CI nunca toca un spending limit. El comportamiento de los límites descrito arriba sale del código fuente de los programas. No pudimos obtener SOL de devnet para armar nuestra propia wallet con límites, porque tanto el faucet de devnet como el de testnet devolvieron errores de rate limit.
Cinco hallazgos del spec, el código y las pruebas en vivo
Construimos una forma de transacción y variamos su programa de nivel superior: compute budget, una instrucción al programa bajo prueba con un firmante nuevo y un Memo con nonce aleatorio. La enviamos solo a /verify, nunca a /settle, la mañana del 2026-09-29, UTC. Como los datos de la instrucción son basura, un facilitator que corre el Path 2 falla en la simulación, y la razón del rechazo te dice hasta dónde llegó la transacción.
La allowlist contiene una dirección de Swig que no existe
La allowlist por defecto de los facilitators en TypeScript y Go, y la tabla del spec, etiquetan SWiGmQedKzMz1tiTqoJCWeGDnGXfNBp2PkXLkpCAtQo como "Swig (legacy)". La consultamos en mainnet, devnet y testnet. No hay ninguna cuenta en esa dirección en ninguna de las tres. No aparece en ninguno de los 1,176 commits del repositorio de Swig. El programa legacy real de Swig, swigDk8JezhiAVde8k6NMwxpZfgGm2NNuMe1KYCmUjP, usado por @swig-wallet/classic 0.2.0-beta.1 en mayo de 2025, está desplegado en mainnet y devnet y no está en la lista.
La historia lo explica. El PR #2509 quitó la dirección errónea el 2026-06-02 a las 16:31 UTC, señalando que "returns AccountNotFound on Solana mainnet and devnet". El PR #2504, mergeado dos horas después, la volvió a poner como "legacy" junto al programa real. El crate de Rust de terceros qntx/r402 copió la misma lista.
Las pruebas en vivo lo confirman. En el facilitator de devnet de x402.org y en PayAI mainnet, la dirección fantasma pasó la allowlist y murió en la simulación con ProgramAccountNotFound, mientras que el programa legacy real de Swig fue detenido con smart_wallet_program_not_allowed. El impacto es acotado: I1 e I3 no dependen de la allowlist. Pero I7, "known programs only", se viola. No encontramos registro de dónde salió la dirección. Si alguien tiene su llave privada, podría desplegar un programa ahí y heredar el estatus de allowlist en todo facilitator que use los defaults. Dejar fuera el programa legacy real cuesta poco hoy, porque no mostró transacciones en mainnet en la ventana que medimos más abajo. El problema real es la entrada fantasma.
El path de smart wallets tiene el techo de fees más estricto, por 350x
Solana cobra el priority fee sobre el límite de compute solicitado: ceil(price * limit / 1,000,000) lamports, además de 5,000 lamports por firma. El Path 2 usa por defecto 400,000 compute units a 50,000 microlamports por unidad. Eso es como máximo 20,000 lamports de priority fee.
El Path 1 usa por defecto un tope de precio de 5,000,000 microlamports por unidad, es decir 5 lamports. Su tope de límite de compute, maxComputeUnits, no está definido por defecto, "preserving existing behavior". Contra el máximo de 1,400,000 unidades por transacción de Solana, eso permite 7,000,000 lamports de priority fee en un pago que puede valer $0.001. El spec llama "tighter cap" a los 5 lamports por unidad del path estático. Por unidad, es 100 veces más laxo que el del Path 2. Los operadores deberían fijar ambos límites del path estático.
La integridad del conjunto de firmantes es un MUST que viene apagado
La sección 2.1.4 dice que la transacción "MUST NOT require any signatures beyond the client and the sponsor". Cada firma requerida extra le cuesta al sponsor otros 5,000 lamports. En ambas implementaciones de referencia, el conteo solo se aplica cuando se define maxRequiredSignatures, que por defecto no está definido. Enviamos una forma de smart wallet con tres firmantes del cliente. El facilitator de x402.org y Dexter la dejaron pasar a la simulación. PayAI la rechazó con invalid_exact_svm_payload_excessive_signers. Aquí es también donde las smart wallets multisig se complican. La ejecución síncrona de Squads Smart Account necesita en la transacción tantos firmantes como el threshold. Con un threshold de dos, ese es justo el firmante extra que el MUST prohíbe.
El flag de capacidad no te dice quién corre el Path 2
Cuando el Path 2 está activo, la respuesta /supported de referencia agrega features.smartWalletSupported: true al kind de Solana. Leímos /supported de diez facilitators públicos que respondieron. El flag apareció en x402.org (solo devnet) y en Dexter (mainnet y devnet). PayAI no lo anuncia, pero la prueba muestra que PayAI corre el Path 2 en mainnet con la allowlist upstream. PayAI además rechazó límites de compute de 75,000 unidades o más antes de simular, mientras que 60,000 pasó. El cliente e2e upstream pide 400,000, y PayAI rechazó ese valor en nuestra prueba. Un cliente de smart wallet no puede fiarse del flag. Tiene que intentar, y tiene que dimensionar su pedido de compute.
Las políticas de sponsor ya divergen
Dexter, de donde vino el código de referencia, corre su propia capa de políticas. Rechazó un pago de $0.001 como "below dynamic floor": 1,309 unidades atómicas contra un costo de settlement que estimó en $0.001190. El piso no se movió cuando multiplicamos por 1,000 el priority fee de la transacción. Así que es un mínimo económico, no un control de exposición a fees. Aparte, Dexter aplicó un tope de priority fee (rechazó 60,000 microlamports por unidad) y un tope de límite de compute (rechazó 1,400,000). Pero no aplicó ninguna allowlist de programas que pudiéramos detectar. El programa legacy real de Swig, el agregador de Jupiter y el System Program llegaron a su paso de simulación. La allowlist de referencia habría frenado a los tres. Nada de esto rompe I1 ni I3. Sí significa que "x402 exact en Solana" ya nombra una familia de políticas de sponsor, no una sola.
// matriz de pruebas /verify, 2026-09-29 (forma de smart wallet, datos basura)
facilitator red flag Path2 fantasma SWiGmQed swigDk8 real 3 firmantes cliente
x402.org devnet sí sí -> simulación not_allowed -> simulación
Dexter mainnet sí sí -> simulación -> simulación -> simulación
PayAI mainnet no sí -> simulación not_allowed excessive_signers
// no evaluados: x402.rs (solo devnet, sin flag), Daydreams (/verify pide bearer token),
// OpenX402 (payTo debe estar registrado), UltravioletaDAO (rechazó nuestra forma de body v2)
Un problema menor está en el fallback posterior al settlement. El chequeo por delta de balance pasa si la ATA de destino creció al menos el monto entre dos lecturas. Para un merchant con mucho tráfico, el pago de otro comprador que llegue en esa ventana lo satisface igual. El chequeo principal, que lee las instrucciones internas confirmadas, no tiene este problema. El fallback solo corre cuando el RPC no ha indexado la transacción tras tres intentos. Pero los merchants con mucho tráfico son justo los que van a sufrir retrasos de indexación.
Cuánto lo usa realmente mainnet
Una cosa es el soporte y otra el uso. El 2026-09-29 medimos los dos facilitators de mainnet que corren el Path 2, desde dos ángulos.
Primero, decodificamos las últimas 1,000 transacciones firmadas por cada fee payer. Para DeXterR2k... de Dexter, eso cubrió del 2026-09-28 03:11 al 2026-09-29 09:22 UTC. 998 fueron pagos estándar con un TransferChecked de nivel superior. Las otras dos llamaron a un programa que no identificamos y no movieron tokens. Para CjNFTjvB... de PayAI, la ventana fue del 2026-09-27 16:15 al 2026-09-29 09:35 UTC. 996 fueron pagos estándar, siete de ellos con assertions de Lighthouse y uno en Token-2022. Las otras cuatro llamaron a un programa que no identificamos. Las 2,000 transacciones tuvieron éxito, y ninguna pasó por Squads o Swig.
Segundo, cruzamos listas de firmas. Eso detecta un programa de wallet incluso cuando solo se lo alcanza vía CPI. En la ventana que el RPC público todavía indexa, el programa de Swig apareció en 6,406 transacciones, Squads Smart Account en 17,790 y Squads v4 en 24,034. En aproximadamente la misma ventana, el fee payer de Dexter firmó 1,420 transacciones y los dos firmantes listados de PayAI 1,748. La intersección fue cero.
// mainnet, ventanas que terminan el 2026-09-29 ~09:45 UTC
fee payer de Dexter 1,420 txs (desde 2026-09-28 00:30) con Swig/Squads: 0
firmantes PayAI (2) 1,748 txs (desde 2026-09-27 15:31) con Swig/Squads: 0
programa Swig 6,406 txs
Squads Smart Account 17,790 txs
Squads v4 24,034 txs
Swig legacy (real) 0 txs
Así que x402 desde smart wallets en Solana es un path soportado sin tráfico medible en mainnet todavía. Squads y Swig procesaron decenas de miles de transacciones en esa ventana, y ninguna fue un pago x402 patrocinado por estos facilitators. Es lo normal para infraestructura de pocos meses. También es el momento más barato para corregir los defaults, antes de que algo dependa de ellos.
Qué significa para LLM4Agents
Anunciamos Solana como una cadena de billing de primera clase. Hasta ahora, un agente que nos pagaba con x402 en Solana tenía que firmar la transferencia directamente con un keypair: el owner de la token account o un delegate de SPL. Su presupuesto vivía donde su operador lo pusiera: en el código del agente o en el tope por pago por defecto de los SDKs de x402. El Path 2 cambia el lado comprador. Un operador puede guardar la tesorería en una smart account de Squads o en una wallet Swig. El agente recibe un rol o un spending limit acotado a nuestro payTo, con tope diario y revocable on-chain. Cada pago hacia nosotros ejecuta esa política antes de que se mueva una sola unidad.
También cambia nuestro lado vendedor, de tres maneras. Primero, el payer que registramos es la PDA de la wallet, no la llave del agente. Nuestra contabilidad por agente tiene que mapear direcciones de wallet a agentes, no asumir una llave por agente. Segundo, un pago desde una smart wallet puede sobrepagar. El spec tolera "al menos" el monto, así que el pipeline reserve, proxy, settle debe acreditar lo que realmente llegó, no lo solicitado. Tercero, que un agente con smart wallet pueda pagarnos depende del facilitator al que apunta nuestro 402. Hoy esa es una pregunta por facilitator sin un flag confiable, como muestra el hallazgo 4.
También hay un lado de amenaza. Si algún día patrocinamos los fees nosotros mismos, el spec permite que un merchant sea su propio sponsor, y cada hallazgo de arriba pasa a ser problema nuestro. Solo el techo de fees por defecto del path estático podría hacer que una llamada de $0.001 nos cueste más en SOL de lo que ingresa.
Cómo mantenerse en la frontera
Primero, probar el path que expondríamos. Antes de anunciar soporte para smart wallets en Solana, correr la prueba de arriba contra el facilitator que nombre nuestro 402 de Solana, y registrar sus respuestas sobre allowlist, conteo de firmantes y límites de compute. Repetirla cuando cambie la versión de ese facilitator. La misma due diligence que recomendamos para verify y settle en general aplica aquí con más casos.
Segundo, hacer nuestra contabilidad de Solana consciente de las wallets. Registrar la authority de la transferencia como payer, acreditar el monto liquidado y no el cotizado, y poner el ID de reserva en extra.memo. El Path 2 exige el memo igual que el Path 1, así que una smart wallet no puede quitarlo. Eso nos da una llave de join nativa de la cadena para cada pago desde smart wallet.
Tercero, si operamos nuestro propio sponsor, partir de ajustes más estrictos que los defaults. Fijar maxComputeUnits y maxPriorityFeeMicroLamports en el path estático, y fijar maxRequiredSignatures en 2. Reemplazar la entrada fantasma de Swig en la allowlist por el programa que usan de verdad nuestros usuarios. Preferir el chequeo por instrucciones internas y registrar cada fallback por delta de balance para revisión.
Cuarto, publicar una receta del lado comprador. Una guía corta para operadores: crear un spending limit de Squads con nuestro payTo como único destino y período diario, o un rol de Swig con un TokenRecurringDestinationLimit. Luego apuntar el cliente x402 del agente a esa wallet. Es el presupuesto de agente más limpio disponible en cualquiera de las cadenas donde liquidamos, y nuestro censo no encontró a nadie usándolo con x402 todavía.
Quinto, reportar upstream. La entrada fantasma en la allowlist, la redacción de "tighter cap" y el conteo de firmantes apagado por defecto son arreglos pequeños y verificables al spec y a los facilitators de referencia. Son el tipo de arreglo que deberíamos estar enviando, no solo describiendo.
Paga la inferencia desde la wallet que guarda el presupuesto
Un gateway compatible con OpenAI, liquidado en USDC con x402 en Solana y EVM.
Registra tu agente