Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Guías de red y protocolo13 min de lectura

Simular llamadas de Substrate con system_dryRun antes de enviarlas

Usa system_dryRun de Substrate para aplicar una llamada contra el estado actual sin confirmarla, analizar el weight o DispatchError devuelto y decidir si firmar y enviar.

TL;DR

system_dryRun es un método JSON-RPC de Substrate que aplica un extrinsic o un mensaje XCM contra la superposición de almacenamiento actual del nodo y devuelve Ok(weight consumido) o Err(un DispatchError con post-info), sin escribir en el estado de la cadena. Es una comprobación segura previa al envío: indica si una llamada tendría éxito y cuánto weight consume, pero no aplica la validez del pool de transacciones, no consume un nonce, no paga una comisión ni emite eventos. Dado que el dry run se evalúa en un bloque y el envío real se ejecuta en un bloque posterior, un dry run exitoso es necesario pero no suficiente para un envío exitoso. Esta guía muestra cómo llamar a system_dryRun con @polkadot/api, analizar el Result codificado en texto, comparar el weight devuelto con una estimación de comisión de payment_queryInfo e interpretar la salida como una decisión de permitir/advertir/denegar.

Qué hace system_dryRun contra el estado actual

system_dryRun es un método JSON-RPC de Substrate que aplica un extrinsic o un mensaje XCM contra la superposición de almacenamiento actual del nodo y devuelve el resultado sin confirmar ningún cambio de estado. El nodo ejecuta la llamada como si se fuera a incluir en un bloque, mide el weight consumido y luego descarta la superposición. No se escribe nada en el estado de la cadena, no se emite ningún evento y no se consume ningún nonce. La referencia JSON-RPC de Substrate de polkadot.js documenta el método y sus variantes en polkadot.js.org/docs/substrate/rpc.

El valor de retorno es un Result codificado en texto. En caso de éxito es Ok(weight), donde weight es el weight consumido por la llamada. En caso de fallo es Err(un DispatchError con post-info), que incluye el error de dispatch y el weight consumido antes de que ocurriera el error. El cliente debe analizar esta codificación de texto en lugar de tratar la respuesta como un objeto JSON simple. Dado que la llamada se aplica contra la superposición de almacenamiento actual del nodo, el resultado refleja el estado en el bloque que el nodo está importando actualmente o el bloque que especifiques con el parámetro opcional at.

Esto convierte a system_dryRun en una forma segura de saber si una llamada tendría éxito y cuánto weight consume antes de gastar un nonce o pagar una comisión. Es la contraparte previa al envío de la lectura del pool de extrinsics, que se trata en Pool de extrinsics de Polkadot y transacciones pendientes.

  • Aplica un extrinsic o un mensaje XCM contra la superposición de almacenamiento actual.
  • Devuelve Ok(weight) en caso de éxito o Err(DispatchError con post-info) en caso de fallo.
  • No escribe en el estado de la cadena, no emite eventos, no consume un nonce ni paga una comisión.
  • Se evalúa en un bloque; el envío real se ejecuta en un bloque posterior.

Formas de argumentos y el hash de bloque opcional at

El argumento de system_dryRun tiene dos formas según la versión del nodo. En una forma se pasa un extrinsic codificado (los bytes del extrinsic firmado o sin firmar). En la otra se pasa una llamada codificada en hexadecimal (los bytes de la llamada sin la envoltura del extrinsic). La referencia JSON-RPC de Substrate de polkadot.js documenta ambas variantes. Dado que la codificación difiere entre versiones de cliente, debes confirmar qué forma espera tu nodo objetivo antes de construir la solicitud. El parámetro opcional at permite fijar el dry run a un hash de bloque específico; si se omite, el nodo usa su cabeza actual.

Cuando fijas at a un hash de bloque, el dry run se evalúa contra el estado en ese bloque. Esto es útil para la reproducibilidad: puedes volver a ejecutar la misma simulación contra el mismo bloque y comparar resultados. También es útil cuando quieres comprobar una llamada contra un estado conocido como bueno en lugar de una cabeza en movimiento. La contrapartida es que un dry run en un bloque antiguo puede no reflejar el estado contra el que se ejecutará el envío real.

Si no estás seguro de qué hash de bloque usar, puedes leer la cabeza actual con chain_getHeader y chain_getBlockHash. El método para leer datos de bloque en un bloque específico se describe en Leer datos de bloques de Polkadot en un bloque específico.

  • Forma 1: bytes de extrinsic codificado.
  • Forma 2: bytes de llamada codificada en hexadecimal.
  • El parámetro opcional at fija el dry run a un hash de bloque específico.
  • La codificación difiere entre versiones de cliente; confírmala antes de construir la solicitud.

En qué se diferencia dryRun de enviar un extrinsic

Enviar un extrinsic y esperar un evento es una operación distinta de un dry run. Un envío real entra en el pool de transacciones, se valida según las reglas del pool, consume un nonce, paga una comisión y emite eventos cuando se incluye en un bloque. Un dry run no hace nada de esto. No aplica la validez del pool de transacciones, no consume un nonce, no paga una comisión y no emite eventos. La referencia JSON-RPC de Substrate de polkadot.js documenta la forma de retorno del método, y la documentación para desarrolladores de Polkadot en docs.polkadot.com cubre extrinsics, weights y semántica de dispatch.

Esto significa que Ok de un dry run es necesario pero no suficiente para un envío real exitoso. Una llamada puede pasar un dry run y aun así fallar en el momento del envío porque el pool la rechaza, porque el nonce está obsoleto, porque no se puede pagar la comisión o porque el estado cambió entre el dry run y la ejecución real. El dry run te informa sobre el resultado del dispatch y el weight, no sobre la admisión en el pool ni el pago de la comisión.

La consecuencia práctica es que debes tratar un dry run como una entrada más en la decisión de envío, no como una garantía. Combínalo con una estimación de comisión y una comprobación de nonce antes de firmar. El flujo de estimación de comisiones se trata en Polkadot payment_queryInfo y estimación de comisiones.

  • Dry run: sin validación del pool, sin nonce, sin comisión, sin eventos.
  • Envío real: validación del pool, consumo de nonce, pago de comisión, eventos.
  • Ok de un dry run es necesario pero no suficiente para un envío exitoso.
  • El estado puede cambiar entre el dry run y la ejecución real.

Usar el weight devuelto para verificar una estimación de comisión

El weight devuelto por un dry run exitoso es el weight que consumiría la llamada. Puedes usarlo para verificar una estimación de comisión de payment_queryInfo. Si la estimación de comisión implica un weight muy diferente del weight del dry run, algo es inconsistente: la estimación puede basarse en una llamada diferente, un bloque diferente o un límite de weight diferente. El método payment_queryInfo devuelve una estimación de comisión derivada del weight y la longitud, y la derivación se describe en Polkadot payment_queryInfo y estimación de comisiones.

También puedes usar el weight del dry run para establecer un límite de weight antes de firmar. Si estableces el límite de weight demasiado bajo, la llamada fallará con un error de overweight aunque el dry run haya tenido éxito. Si lo estableces demasiado alto, puedes pagar de más o alcanzar un límite de bloque. El dry run te da un weight medido para anclar el límite. Ten en cuenta que el weight devuelto por el dry run es el weight consumido en el bloque contra el que simulaste; la ejecución real puede consumir una cantidad diferente si el estado difiere.

Dado que el dry run no paga una comisión, el weight que devuelve no es una comisión. Es una entrada para el cálculo de la comisión. Trátalo como una medición, no como un cargo.

  • Compara el weight del dry run con el weight implícito en payment_queryInfo.
  • Usa el weight del dry run para establecer un límite de weight antes de firmar.
  • Un límite de weight demasiado bajo provoca un fallo por overweight a pesar de un dry run exitoso.
  • El weight del dry run es una medición, no una comisión.

Diferencias en la simulación de XCM y llamadas a contratos

system_dryRun tiene variantes para diferentes tipos de payload. Una variante simula un mensaje XCM y otra simula una llamada a un contrato. La diferencia importa porque la codificación del payload y la superficie de errores difieren. Un dry run de XCM devuelve errores específicos de XCM, mientras que un dry run de llamada a contrato devuelve errores específicos de contrato. La referencia JSON-RPC de Substrate de polkadot.js documenta las variantes. Debes hacer coincidir la variante con el payload que pretendes enviar.

Para XCM, el dry run aplica el mensaje contra el estado actual y devuelve el weight consumido o un error de XCM. Para una llamada a contrato, el dry run aplica la llamada contra el almacenamiento del contrato y devuelve el weight consumido o un error de contrato. En ambos casos el dry run no confirma el estado. El beneficio práctico es el mismo: sabes si el payload tendría éxito y cuánto weight consume antes de enviarlo.

Si trabajas con una cadena que expone estas variantes, consulta los metadatos del nodo o la referencia de polkadot.js para conocer los nombres exactos de los métodos. La codificación difiere entre versiones de cliente, por lo que un payload que funciona en un nodo puede necesitar una codificación diferente en otro.

  • La variante XCM devuelve errores específicos de XCM y weight.
  • La variante de llamada a contrato devuelve errores específicos de contrato y weight.
  • Haz coincidir la variante con el payload que pretendes enviar.
  • La codificación difiere entre versiones de cliente.

Decodificar un DispatchError antes de gastar un nonce

Cuando un dry run falla, devuelve Err(un DispatchError con post-info). El DispatchError indica por qué fallaría la llamada. Decodificarlo antes de enviar te permite corregir la llamada sin gastar un nonce ni pagar una comisión. El flujo de manejo de errores se trata en Decodificar errores de dispatch de extrinsics de Polkadot.

El post-info del error incluye el weight consumido antes de que ocurriera el error. Esto es útil para entender hasta dónde llegó la llamada antes de fallar. No es una comisión, porque el dry run no paga una comisión. Es una medición del trabajo realizado antes del error.

Las variantes comunes de DispatchError incluyen BadOrigin, errores de módulo y otros errores específicos del runtime. El conjunto exacto depende del runtime. Debes decodificar el error contra los metadatos del runtime de la cadena a la que apuntas. Si el error es un error de módulo, el índice de módulo y el índice de error identifican el fallo específico.

  • Err incluye un DispatchError y el weight post-info.
  • Decodifica el error antes de enviar para evitar gastar un nonce.
  • post-info es el weight consumido antes del error, no una comisión.
  • Decodifica contra los metadatos del runtime de la cadena objetivo.

Ejemplo ejecutable en Node.js: dry run y comparación de comisiones

El siguiente ejemplo en Node.js se conecta a un endpoint WebSocket de Polkadot, codifica una llamada con @polkadot/api, llama a system_dryRun en la cabeza actual, analiza el weight resultante y lo compara con una estimación de comisión de payment_queryInfo. Reemplaza el endpoint por el de tu propio proveedor. El ejemplo usa una llamada simple de transferencia de balances; adapta la llamada a tu caso de uso.

El ejemplo asume que tienes @polkadot/api instalado. Usa el método api.rpc.system.dryRun, que envuelve el RPC system_dryRun. El resultado es un Result codificado en texto, por lo que el ejemplo lo analiza con el registry de la api. El ejemplo también llama a payment_queryInfo para obtener una estimación de comisión e imprime ambos para compararlos.

const { ApiPromise, WsProvider } = require('@polkadot/api');

async function main() {
  const provider = new WsProvider('wss://your-polkadot-endpoint');
  const api = await ApiPromise.create({ provider });

  // Build a call: transfer 1 DOT to a recipient.
  const recipient = '15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5';
  const amount = '10000000000'; // 1 DOT in plancks
  const call = api.tx.balances.transferKeepAlive(recipient, amount);

  // Get the current head block hash.
  const head = await api.rpc.chain.getHeader();
  const at = head.hash.toHex();

  // Dry run the call at the current head.
  const dryRunResult = await api.rpc.system.dryRun(call.toHex(), at);
  console.log('dryRun raw:', dryRunResult.toString());

  // Parse the text-encoded Result.
  const parsed = api.registry.createType('Result<Weight, DispatchError>', dryRunResult);
  if (parsed.isOk) {
    const weight = parsed.asOk;
    console.log('dryRun weight:', weight.toString());
  } else {
    const err = parsed.asErr;
    console.log('dryRun error:', err.toString());
  }

  // Get a fee estimate for the same call.
  const info = await api.rpc.payment.queryInfo(call.toHex(), at);
  console.log('payment_queryInfo:', info.toString());

  await api.disconnect();
}

main().catch(console.error);

Ejemplo ejecutable con curl: solicitud system_dryRun sin procesar

Si prefieres llamar al RPC directamente, el siguiente ejemplo con curl envía una solicitud system_dryRun sin procesar. Reemplaza el endpoint y la llamada codificada por tus propios valores. El campo call es la llamada codificada en hexadecimal. El campo at es opcional; si se omite, el nodo usa su cabeza actual.

La respuesta es un objeto JSON-RPC 2.0 cuyo result es un Result codificado en texto. Debes analizar la cadena de resultado para extraer el weight o el DispatchError. La especificación JSON-RPC 2.0 define la envoltura de solicitud y respuesta, y la especificación JSON-RPC de Ethereum es una referencia útil para la misma semántica de envoltura en un ecosistema diferente.

curl -sS -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "system_dryRun",
    "params": [
      "0x...encoded-call...",
      "0x...block-hash..."
    ]
  }' \
  https://your-polkadot-endpoint

Lista de verificación reproducible para decisiones de permitir, advertir o denegar

Usa la siguiente lista de verificación para convertir un dry run en una decisión de envío. La lista es reproducible: ejecútala contra tu propio endpoint y registra los resultados en una tabla. Las columnas de la tabla deben incluir la llamada, el hash de bloque, el resultado del dry run, el weight del dry run, la estimación de payment_queryInfo y la decisión. Rellena la tabla con tus propias mediciones; no te bases en cifras de este artículo.

Las categorías de decisión son permitir, advertir y denegar. Permitir significa que el dry run tuvo éxito y el weight está dentro de tu límite. Advertir significa que el dry run tuvo éxito pero el weight está cerca de tu límite o la estimación de comisión es inconsistente. Denegar significa que el dry run falló con un DispatchError, o que el weight supera tu límite, o que la estimación de comisión no se puede pagar.

Registra el hash de bloque de cada ejecución para poder reproducirla. Si vuelves a ejecutar contra un bloque diferente, el resultado puede diferir. La lista de verificación es un método, no una garantía.

  • Ejecuta system_dryRun en un hash de bloque fijado y registra el resultado.
  • Analiza Ok(weight) o Err(DispatchError con post-info).
  • Compara el weight del dry run con tu límite de weight.
  • Compara el weight del dry run con la estimación de payment_queryInfo.
  • Decide permitir, advertir o denegar según la comparación.
  • Registra el hash de bloque, la llamada, el resultado, el weight, la estimación y la decisión en una tabla.

Limitaciones y contrapartidas de la simulación con dry run

La limitación más importante es que un dry run se evalúa contra el estado en un bloque, mientras que el envío real se ejecuta en un bloque posterior. Las llamadas dependientes del estado aún pueden fallar después de un dry run exitoso. Por ejemplo, una transferencia puede fallar si el saldo del remitente cambia entre el dry run y la ejecución real. Una llamada de gobernanza puede fallar si cambia el estado de la propuesta. El dry run es una instantánea, no una predicción.

Algunos nodos restringen o deshabilitan system_dryRun. Este es un comportamiento documentado que varía según el nodo. Un operador de nodo puede deshabilitar el método por razones de rendimiento, seguridad o política. Si el método no está disponible, no puedes usarlo como comprobación previa al envío. Debes confirmar la disponibilidad contra tu endpoint objetivo antes de depender de él.

La codificación de los argumentos difiere entre versiones de cliente. Un payload que funciona en un nodo puede necesitar una codificación diferente en otro. Debes confirmar la forma esperada antes de construir la solicitud. Por último, el dry run no modela la prioridad del pool de transacciones ni el comportamiento de replace-by-fee. Una llamada que pasa un dry run aún puede ser descartada del pool o reemplazada por una transacción con una comisión más alta. Para el comportamiento del pool, consulta Pool de extrinsics de Polkadot y transacciones pendientes.

  • El dry run se evalúa en un bloque; la ejecución real ocurre después.
  • Las llamadas dependientes del estado aún pueden fallar después de un dry run exitoso.
  • Algunos nodos restringen o deshabilitan system_dryRun; la disponibilidad varía según el nodo.
  • La codificación de los argumentos difiere entre versiones de cliente.
  • El dry run no modela la prioridad del pool ni el replace-by-fee.

Solución de problemas comunes de system_dryRun

Si system_dryRun devuelve un error de método no encontrado, el nodo puede no exponer el método o puede exponerlo con un nombre diferente. Consulta los metadatos del nodo y la referencia JSON-RPC de Substrate de polkadot.js. Si el método está deshabilitado, no puedes usarlo; considera un endpoint diferente o una comprobación previa al envío diferente.

Si el dry run devuelve Err(DispatchError), decodifica el error contra los metadatos del runtime. Las causas comunes incluyen BadOrigin, saldo insuficiente y errores específicos de módulo. El flujo de decodificación se trata en Decodificar errores de dispatch de extrinsics de Polkadot. Si el error es un error de módulo, el índice de módulo y el índice de error identifican el fallo específico.

Si el dry run tiene éxito pero el envío real falla, es probable que el estado haya cambiado entre el dry run y el envío. Vuelve a ejecutar el dry run en la cabeza actual y compara. Si el fallo es un rechazo del pool, comprueba el nonce y la comisión. Si el fallo es un error de overweight, aumenta el límite de weight según el weight del dry run. Si el fallo es un nonce obsoleto, actualiza el nonce y vuelve a firmar.

  • Método no encontrado: consulta los metadatos y la referencia; el método puede estar deshabilitado.
  • DispatchError: decodifícalo contra los metadatos del runtime.
  • El envío real falla tras un dry run exitoso: el estado cambió; vuelve a ejecutar en la cabeza actual.
  • Error de overweight: aumenta el límite de weight según el weight del dry run.
  • Nonce obsoleto: actualízalo y vuelve a firmar.

Próximos pasos para la simulación previa al envío

Para poner system_dryRun en práctica, empieza por confirmar que tu endpoint objetivo expone el método. Puedes usar la Guía RPC de Polkadot (RPC Assistant) para explorar los métodos disponibles. Luego crea un pequeño script que haga un dry run de tu llamada en la cabeza actual e imprima el weight o el DispatchError. Compara el weight con una estimación de payment_queryInfo y registra los resultados en una tabla.

Para un endpoint gestionado, consulta Polkadot y el servicio de API. Para precios, consulta Precios de RPC. Para más guías, consulta el centro de aprendizaje de OnFinality.

A medida que desarrolles tu flujo de trabajo previo al envío, combina el dry run con una estimación de comisión y una comprobación de nonce. El dry run te informa sobre el resultado del dispatch y el weight; la estimación de comisión te informa sobre el coste; la comprobación de nonce te informa sobre la admisión en el pool. Juntos te dan una imagen más completa que cualquier comprobación por sí sola.

  • Confirma que el endpoint expone system_dryRun.
  • Haz un dry run de tu llamada en la cabeza actual y registra el resultado.
  • Compara el weight con payment_queryInfo y registra la comparación.
  • Combina el dry run con una estimación de comisión y una comprobación de nonce.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar