5

I would like to inspect transactions (extrinsics) in a given block to potentially:

  • Find different types of transactions and their parameters
  • Look through all transactions included in the block and act on them based on type, sender etc.

So far I am able to query the block using subtx and see all transactions in raw form. I do not know how can I decode them to Rust types and act on them:

#[subxt::subxt(runtime_metadata_path = "../specs/kusama.spec.scale")]
pub mod kusama_client {}

...

    let api = OnlineClient::<PolkadotConfig>::from_url(rpc_url)
        .await.unwrap();

    let block = api.rpc().block(None).await.unwrap().unwrap();

    for tx in block.block.extrinsics {
        println!("tx: {:?}", tx);

        // TODO: Decode transactions and act on them based on types etc...
        // ex. ImOnline etc...
    }

I know that the transactions are SCALE encoded, but I do not know what to decode them to Rust types, I do not even know what Rust types to use (I assume those generated from metadata, but how do I find appropriate type to decode to?).

I would also like to do the similar thing with other construct such as offences:

   let offences_addr = kusama_client::storage().offences().reports_by_kind_index(offence_kind);

    let offences = api
        .storage()
        .at(None)
        .await.unwrap()
        .fetch(&offences_addr)
        .await.unwrap()
        .unwrap();

     // TODO: decode and inspect offences

I would appreciate any pointers in the right direction, or examples if such exists.

2
  • As a side note, where does the code generated from metadata (#[subxt::subxt(runtime_metadata_path = "...") end up? Is it possible to configure IDE to go to the definition etc?
    – Szymig
    Apr 6, 2023 at 5:21
  • #[subxt::subxt(runtime_metadata_path = "...")] doesn't generate code anywhere (ie like other macros, you can use cargo expand to see the generated output, but it's generated during compilation. You can use the CLI tool to generate code instead if preferred (ie run subxt codegen after cargo install subxt-cli).
    – jsdw
    Apr 11, 2023 at 9:34

4 Answers 4

4

Also, Subxt doesn't currently have a means to decode transactions, but you can obtain the bytes for each transaction back currently eg:

let extrinsics = api.blocks().at(hash).await?.body().await?.extrinsics();
for ext in extrinsics {
    let bytes = ext.bytes();
    // decode bytes
}

I've updated https://github.com/paritytech/subxt/issues/622 to go into more detail about what sort of interface Subxt could add here to this ext value.

While Subxt doesn't support it at the moment, you can manually look into how to decode the extrinsic bytes. Some pointers for this:

.. You can see why it's something that we should add to Subxt in any case, as there are a number of steps to it :)

2

Subxt currently exposes only the bytes of the extrinsic to users. We plan on adding support for decoding to a concrete type generated by the subxt macro.

In the meanwhile, polkadot-introspector here is decoding manually the extrinsics.

For example, polkadot-introspector is interested only in finding the ParaInherent event.

Steps to decode the ParaInherent events:

  1. Obtain the extrinsic data (example)

Extrinsics are encoded in memory as:

  • Compact u32: Length of the extrinsic
  • first byte: abbbbbbb (a = 0 for unsigned, 1 for signed, b = version)
  • signature
  • extrinsic data

Note: this makes the assumption that the ParaInherent is unsigned, please adjust this per your needs.

  1. Decode the extrinsic date to RuntimeCall generated by subxt macro (may be similar to kusama_client::runtime_types::kusama_runtime::RuntimeCall for your use case)
  2. Match and inspect data (ie ParaInherent data - similar to here)
1

For anyone that stumbles upon this now, it has been implemented in subtx since posting the question in https://github.com/paritytech/subxt/pull/929.

Example code:

use subxt::OnlineClient;
use subxt::PolkadotConfig;

#[tokio::main]
async fn main() {
    let rpc_url = "wss://kusama-rpc.polkadot.io:443/";

    let api = OnlineClient::<PolkadotConfig>::from_url(rpc_url)
        .await.unwrap();
    let block = api.blocks().at_latest().await.unwrap();

    for extrinsic in block.body().await.unwrap().extrinsics().iter() {
        let extrinsic = extrinsic.expect("error getting extrinsic");
        println!("Extrinsic {}::{}", extrinsic.pallet_name().unwrap(), extrinsic.variant_name().unwrap());
        // Do whatever you want with the extrinsic...
    }
}

Tested with subxt version ^0.31.

0

You can decode extrinsic's and parse blocks with Libuptest: https://uptest-sc.github.io/code/decode.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.