El pool de transacciones de un nodo Substrate no es un mempool de EVM: cada extrínseco se valida contra una validez proporcionada por el runtime, por lo que puede estar ready, future, in-block o descartado por un nonce obsoleto, una etiqueta prohibida o una era de mortalidad expirada. author_pendingExtrinsics devuelve los extrínsecos actualmente en el pool como hex codificado en SCALE sin un orden garantizado, por lo que debes decodificarlos contra los metadatos del runtime en lugar de leerlos como direcciones. system_accountNextIndex devuelve el siguiente nonce que la cuenta debería usar, lo que refleja lo que el pool ha encolado más lo que ya está incluido, mientras que el nonce on-chain de System.Account refleja solo lo incluido. Comparar ambos, y releer el pool en cada sondeo, es la forma más económica de separar un extrínseco que genuinamente espera de uno que fue rechazado o descartado. Este artículo documenta la ruta de lectura, las tres firmas de fallo y un bucle de diagnóstico acotado que puedes ejecutar contra tu propio endpoint.
Por qué el pool de transacciones de Substrate no es un mempool de EVM
En una cadena EVM basada en cuentas, una transacción normalmente entra en un mempool y espera su inclusión; el pool es en gran medida un área de retención ordenada por comisión. Substrate funciona de manera diferente. Cuando se envía un extrínseco, el nodo pide al runtime que lo valide, y el runtime devuelve un veredicto de validez que incluye una prioridad, un conjunto de etiquetas y una ventana de longevidad (era). El pool entonces clasifica el extrínseco como ready, future, in-block, o lo descarta por completo. Esto significa que un extrínseco puede ser rechazado en el límite del pool por razones que no tienen nada que ver con el precio del gas.
La consecuencia práctica es que quien solo consulta el hash de su propio envío está ciego a la mayor parte de lo que ocurre. El extrínseco puede estar en la cola future detrás de un hueco de nonce, puede haber sido descartado porque su era de mortalidad expiró, o puede haber sido prohibido porque un extrínseco hermano con la misma etiqueta falló. Ninguno de esos estados es visible solo con la respuesta del envío. La descripción autorizada de este modelo está en la documentación del formato de transacción de Substrate, que define los campos de nonce, mortalidad y validez sobre los que razona el pool.
Leer el pool directamente, en lugar de inferir a partir de tu propio envío, es por tanto la postura de diagnóstico correcta. La lectura del pool te dice qué tiene actualmente el nodo; el índice de cuenta te dice qué nonce espera el nodo a continuación. Juntos responden a la pregunta que los tutoriales de envío nunca responden: ¿mi extrínseco está esperando, incluido o desapareció?
- Ready: válido ahora y elegible para inclusión en el siguiente bloque.
- Future: válido solo después de que se incluya un nonce anterior, por lo que espera detrás de un hueco.
- In-block: ya seleccionado en un bloque que se está construyendo o recién importado.
- Dropped: eliminado porque era inválido, obsoleto o prohibido por un hermano fallido.
Leer el pool con author_pendingExtrinsics
La lectura principal es author_pendingExtrinsics, documentada en la referencia JSON-RPC de polkadot.js para los métodos author. Devuelve los extrínsecos actualmente en el pool como un array de cadenas hex codificadas en SCALE. No devuelve direcciones, nonces ni datos de llamada legibles por humanos, y no se garantiza que el orden del array refleje el orden de inclusión. Trata la respuesta como bytes opacos que deben decodificarse contra los metadatos del runtime de la cadena exacta que estás consultando.
Debido a que la codificación es SCALE, la decodificación requiere los metadatos de la cadena, por lo que esta lectura se combina naturalmente con un paso de decodificación. Si necesitas convertir el hex en un firmante, un nonce y una llamada, el artículo complementario sobre decodificación de errores de envío de extrínsecos de Polkadot cubre la ruta de decodificación en detalle. Para las lecturas del pool específicamente, los campos que te interesan son la cuenta firmante, el nonce y la era, porque esos tres impulsan todos los diagnósticos a continuación.
Una llamada curl mínima es suficiente para confirmar que el método es accesible en tu endpoint antes de construir algo sobre él. La respuesta es un objeto de resultado JSON-RPC 2.0 cuyo result es un array de cadenas hex; un array vacío significa que el pool está actualmente vacío, lo cual es un estado válido y común en una cadena tranquila.
curl -s https://your-polkadot-endpoint.example \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "author_pendingExtrinsics",
"params": []
}'system_accountNextIndex y el nonce on-chain
La segunda lectura es system_accountNextIndex, que toma una dirección SS58 y devuelve el siguiente nonce que esa cuenta debería usar. Su semántica es el quid de todo el diagnóstico. El nonce on-chain, leído del almacenamiento System.Account, refleja lo que se ha incluido en un bloque. system_accountNextIndex refleja lo que el pool ha encolado más lo que ya está incluido. Mientras una transacción está pendiente, estos dos valores difieren legítimamente, y esa diferencia no es un error.
Esta es la forma más económica de distinguir una transacción atascada de una que nunca se envió. Si accountNextIndex es mayor que el nonce on-chain, el nodo cree que tiene trabajo encolado para esa cuenta. Si los dos son iguales, el nodo no tiene nada encolado, lo que significa que tu extrínseco nunca fue aceptado o ya fue descartado. Puedes leer el nonce on-chain a través de la ruta de almacenamiento descrita en cambios de almacenamiento de Polkadot state_queryStorageAt, o a través de cualquier cliente que exponga System.Account.
Una sutileza que vale la pena interiorizar: accountNextIndex es una vista consciente del pool, por lo que puede retroceder si se descarta un extrínseco encolado. No lo almacenes en caché y asumas monotonicidad. Vuelve a leerlo en cada sondeo junto con el pool, y registra ambos valores con una marca de tiempo para que puedas ver la transición cuando ocurra un descarte.
curl -s https://your-polkadot-endpoint.example \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "system_accountNextIndex",
"params": ["15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5"]
}'Las tres firmas de fallo y cómo desambiguarlas
Una vez que tienes el contenido del pool y el índice de cuenta, tres firmas cubren casi todos los informes de extrínsecos atascados. La primera es un extrínseco que genuinamente espera: aparece en author_pendingExtrinsics, y su nonce decodificado es igual al nonce on-chain, lo que significa que es el siguiente en la fila y simplemente aún no se ha incluido. Esto es normal y se resuelve por sí solo a menos que la cadena esté estancada, lo cual puedes verificar con monitoreo de salud y estado de sincronización del sistema Polkadot.
La segunda es un extrínseco rechazado o descartado: está ausente del pool y ausente de la cadena. Esta es la firma que más confunde a los llamadores, porque la llamada de envío puede haber devuelto éxito. El extrínseco fue aceptado en el pool y luego eliminado, con mayor frecuencia porque su era de mortalidad expiró o porque un hermano con la misma etiqueta falló y la etiqueta fue prohibida. La tercera es un fallo de despacho: el extrínseco está incluido en un bloque pero su evento muestra un fallo, lo cual es un problema de ejecución del runtime más que un problema del pool, y pertenece a la ruta de decodificación de errores de despacho.
La desambiguación es mecánica una vez que tienes las tres lecturas. Presencia en el pool más igualdad de nonce significa esperar. Ausencia tanto del pool como de la cadena significa reenviar con una era fresca después de verificar el nonce. Inclusión con un evento fallido significa decodificar el error de despacho y corregir la llamada.
- Waiting: en el pool, nonce decodificado igual al nonce on-chain.
- Dropped o rejected: ausente del pool, ausente de la cadena, accountNextIndex puede haber revertido.
- Dispatch failure: incluido en un bloque, evento de fallo presente, ver el artículo de errores de despacho.
- Nonce gap: en el pool como future, nonce decodificado mayor que el nonce on-chain más uno.
Mortalidad, la etiqueta obsoleta y la desaparición silenciosa
La mortalidad es la razón habitual por la que un extrínseco encolado durante mucho tiempo desaparece silenciosamente. Cada extrínseco lleva una era que limita cuántos bloques permanece válido. Cuando esa ventana pasa, el pool descarta el extrínseco, y como el descarte no es una respuesta de error a tu envío original, nada te lo indica. El extrínseco simplemente deja de aparecer en author_pendingExtrinsics, y accountNextIndex revierte hacia el nonce on-chain.
La documentación del formato de transacción de Substrate define la era y el modelo de validez que produce este comportamiento. La conclusión operativa es que un extrínseco pendiente de larga vida no es un estado estable; tiene una fecha límite. Si estás construyendo un bucle de reintento, la era es el reloj que debes respetar, y la lectura del pool es cómo observas que pasa la fecha límite. Un extrínseco mortal que ha estado pendiente durante muchos bloques es candidato a expirar, no a tener paciencia.
La etiqueta obsoleta está relacionada pero es distinta: se aplica cuando un extrínseco ya no es válido en el momento en que el pool lo revalida, lo que puede ocurrir por razones más allá de la expiración de la era, incluido un nonce que desde entonces ha sido consumido por otra ruta. En cualquier caso, lo observable es lo mismo: el extrínseco sale del pool sin una inclusión correspondiente.
Un bucle de diagnóstico acotado con una tabla de resultados
El patrón fiable es un bucle acotado que registra una pequeña tupla por extrínseco y vuelve a leer tanto el pool como el índice de cuenta en cada sondeo. Registra el hash del extrínseco, el nonce decodificado, la marca de tiempo de primera aparición, el accountNextIndex actual y el nonce on-chain. Detente después de un número fijo de sondeos o un presupuesto fijo de tiempo real para que el bucle no pueda ejecutarse indefinidamente contra una cadena estancada.
Ejecuta esto contra tu propio endpoint y completa la tabla con tus propias observaciones. No confíes en números de ningún artículo, incluido este; el comportamiento del pool es específico de la cadena y de la carga. La tabla es un instrumento de medición, no un benchmark.
- Columnas de la tabla de resultados: etiqueta de sondeo, marca de tiempo, tamaño del pool, hash del extrínseco, nonce decodificado, accountNextIndex, nonce on-chain, veredicto.
- Valores de veredicto: waiting, future, dropped, included-ok, included-failed.
- Completa la tabla desde tu propio endpoint; trata cualquier número publicado como meramente ilustrativo.
const ENDPOINT = 'https://your-polkadot-endpoint.example';
const ADDRESS = '15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5';
async function rpc(method, params) {
const res = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
});
const json = await res.json();
if (json.error) throw new Error(JSON.stringify(json.error));
return json.result;
}
async function poll(tag) {
const [pool, nextIndex] = await Promise.all([
rpc('author_pendingExtrinsics', []),
rpc('system_accountNextIndex', [ADDRESS])
]);
console.log(JSON.stringify({
tag,
at: new Date().toISOString(),
poolSize: pool.length,
accountNextIndex: nextIndex
}));
}
(async () => {
for (let i = 0; i < 10; i++) {
await poll('poll-' + i);
await new Promise(r => setTimeout(r, 6000));
}
})();Decodificar entradas del pool contra los metadatos del runtime
author_pendingExtrinsics devuelve hex, y el hex solo tiene sentido una vez decodificado contra los metadatos de la cadena que consultaste. Un error común es intentar analizar el hex como una dirección o como un objeto JSON; no es ninguna de las dos cosas. La decodificación produce el firmante, el nonce, la era, la propina y la llamada, y son el nonce y la era los que impulsan los diagnósticos anteriores.
Si ya estás decodificando errores de envío, tienes la mayor parte de la maquinaria. El artículo decodificación de errores de envío de extrínsecos de Polkadot recorre la decodificación basada en metadatos, y el mismo enfoque se aplica a las entradas del pool. La única diferencia es que las entradas del pool no están firmadas desde tu perspectiva hasta que decodificas el firmante, mientras que los errores de envío llegan en el contexto de un envío que ya hiciste.
Una nota operativa: los metadatos cambian entre actualizaciones del runtime. Un decodificador fijado a una versión antigua de metadatos puede leer mal los campos después de una actualización. Fija tu decodificación a los metadatos devueltos por state_getMetadata en el momento de la lectura, o actualízalos según un calendario.
Combinar lecturas del pool con contexto de comisión y peso
El estado del pool y la estimación de comisiones son preocupaciones separadas, pero interactúan cuando un extrínseco está encolado como future detrás de un hueco. Si estás decidiendo si aumentar la propina o reenviar, el contexto de peso a comisión importa, y el artículo estimación de peso a comisión de Polkadot con payment_queryInfo cubre cómo poner precio a una llamada antes del envío. Las lecturas del pool te dicen si la llamada está esperando; payment_queryInfo te dice cuánto costará reemplazarla.
No confundas las dos. Una propina alta no mueve un extrínseco encolado como future más allá de un hueco de nonce; el hueco debe llenarse primero. La lectura del pool es lo que revela el hueco, y el índice de cuenta es lo que lo confirma. El contexto de comisión solo se vuelve relevante una vez que el extrínseco está ready y estás compitiendo por la inclusión.
Limitaciones y compensaciones de las lecturas del pool
Las lecturas del pool son puntuales y locales al nodo. author_pendingExtrinsics refleja el pool del nodo específico que consultaste, no una vista de toda la red. Dos nodos detrás del mismo balanceador de carga pueden devolver contenidos de pool diferentes, y un nodo que está sincronizando puede devolver un pool que no refleja el head actual de la cadena. Este es el comportamiento documentado del método, no un defecto, pero significa que no debes tratar una sola lectura como autoritativa para la red.
El método tampoco devuelve garantía de orden ni metadatos sobre por qué un extrínseco está en el pool. No puedes saber solo por la respuesta si una entrada está ready o future; eso lo infieres de la comparación de nonces. Y como el pool está limitado por la configuración del nodo, un nodo ocupado puede desalojar entradas bajo presión, lo cual es otra ruta hacia la desaparición silenciosa que desde fuera se ve idéntica a la expiración por mortalidad.
Finalmente, el comportamiento del proveedor varía. Algunos endpoints gestionados restringen o limitan la tasa de los métodos author_*, y algunos exponen solo un subconjunto. Trata la disponibilidad de author_pendingExtrinsics como documentada pero dependiente del proveedor, y verifícala contra tu propio endpoint antes de construir una dependencia sobre ella.
- Local al nodo, no a toda la red: dos endpoints pueden discrepar.
- Sin garantía de orden y sin etiqueta ready/future en la respuesta.
- El desalojo del pool bajo presión es indistinguible de la expiración sin contexto adicional.
- La disponibilidad y los límites de tasa de author_* varían según el proveedor.
Solución de problemas comunes de lectura del pool
Si author_pendingExtrinsics devuelve un error en lugar de un array, es probable que el método no esté disponible o esté restringido en ese endpoint. Confirma el nombre del método y los parámetros contra la referencia JSON-RPC de polkadot.js, luego prueba contra un endpoint diferente. Si el array está vacío pero esperabas entradas, verifica si el nodo está completamente sincronizado; un nodo que está sincronizando puede no tener el estado del pool que esperas, lo cual se cubre en monitoreo de salud y estado de sincronización del sistema Polkadot.
Si accountNextIndex y el nonce on-chain son iguales pero crees que un extrínseco está pendiente, el extrínseco no está en el pool. Vuelve a verificar la era y el nonce con el que firmaste. Un nonce por debajo del nonce on-chain es un nonce obsoleto y será rechazado en el límite del pool. Un nonce muy por encima del nonce on-chain crea un hueco y estaciona el extrínseco en la cola future.
Si el pool contiene entradas que no puedes decodificar, es probable que tus metadatos estén obsoletos o sean de la cadena equivocada. Actualiza los metadatos y vuelve a intentarlo. Si la decodificación tiene éxito pero el nonce parece incorrecto, verifica que estás leyendo el campo del firmante y no un desplazamiento dentro de los datos de la llamada.
Próximos pasos para el monitoreo en producción
El siguiente paso natural es convertir el bucle acotado en una señal monitoreada. Emite la tupla (hash del extrínseco, nonce, primera aparición, accountNextIndex, nonce on-chain) a tu sistema de métricas, y alerta sobre la transición de waiting a dropped en lugar de sobre el tamaño absoluto del pool. El tamaño del pool por sí solo es ruidoso; la transición es el evento accionable.
Para la selección de endpoints y la disponibilidad de métodos, comienza desde la página de endpoints RPC de Polkadot (RPC Assistant) y la página de la red Polkadot. Si estás dimensionando esto para tráfico de producción, las páginas de precios de RPC y servicio de API describen cómo se estructura el acceso gestionado. Más manuales de infraestructura como este viven en el centro de aprendizaje de OnFinality.
Una postura de producción razonable es: sondear el pool y el índice de cuenta a un intervalo fijo, decodificar contra metadatos recién obtenidos, clasificar cada extrínseco rastreado en una de las tres firmas, y enrutar los fallos de despacho a la ruta de decodificación. Eso cubre por completo el lado de lectura del pool, y deja solo el lado de envío y comisiones a los artículos complementarios.