I was exploring typegen for my node's custom types and came across these two concepts in the acala.js codebase in the type-definitions package (their use of a "types-bundle.json"), and something similar in the polkadot-js types-known package (their use of a "magic number" and version arrays/overrides). So far I've followed the polkadot-js docs for type gen and these haven't really come up. What I tend to do is pull the metadata, then run the typegen based on a single JSON file that specifies the custom types on the chain with the specified directory structure (I still get type errors but I feel like an understanding of the proper process would help mitigate this).

It seems there is some best practice related to this process for handling multiple chains and exposing the types in one package, and I'd like to know how that works and what the different steps/files involved are.

1 Answer 1


First the good news - for any chain using metadata v14, specifying custom types are not needed. The metadata itself supplies all the types required. This means that when a call is defined in the metadata, the metadata itself specifies the exact type.

For older versions of the metadata, the reality was different - for each call, the metadata only supplied the type name. This meant that there needed to be a mapping to understand that something called Balance is actually a u128 type and should be encoded and decoded that way.

On to your question which relates to chains that have not upgraded to v14 as of yet or chains where there is a requirement to query older blocks. For these, you need to supply a mapping of the type names to the actual type it relates to, for instance in your chain you could have Balance -> u64.

Taking this example, in Substrate chains are upgradable, so at some point you could decide to change the Balance type to a u128 (with the required migrations). At this point, instead of injecting the Balance -> u64 mapping, you would inject Balance -> u128 as a type mapping. So types can and do change through the lifecycle of the runtime.

In this case we now have an issue - assuming you used Balance -> u64 up to runtime version 100 and 101 and above used Balance -> u128, you will have issues querying blocks before runtime 101, i.e. those will try to decode with the new types.

To get around this and define types for specific runtime version ranges, the a types bundle entry has 2 parts -

  • the version range, i.e. first runtime version and last runtime version
  • the actual types that needs to be applied to this version specified by the min/max range

A very good example of this is the Kusama types bundle that tracks the chain and all types from the first runtime version up to the present (where no overrides are applied due to metadata v14 being in operation)

Pulling it all together, for the Balance example above, your types bundle would look something like the following -

    // for runtime 0 to 100 
    minmax: [0, 100],
    types: {
      Balance: 'u64'
    // runtime 101 upgrades to the new Balance
    minmax: [101, 200],
    types: {
      Balance: 'u128'
    // runtime 201 swapped to metadata v14, nothing needed
    minmax: [201, undefined],
    types: {}

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.