Skip to content

Adding an External Module

Introduction

Developers building on top of the Templates offered by Tanssi might want to add some external modules/dependencies into their runtime to expand certain functionality.

The Tanssi repository and the templates take all the dependencies from a fork of the official Polkadot SDK repository. This fork is maintained by the Tanssi engineering team, which usually contributes actively to Substrate development by fixing issues and enhancing functionalities, and, as a result, the fork repository frequently stays temporarily ahead of the official one.

A double reference issue may arise when adding an external dependency, such as a pallet from a third party. This happens if a Tanssi module references a dependency from the Polkadot SDK fork repository, and the third party references the same dependency from the official Polkadot SDK repository. To solve this issue, the references to the dependencies must be unified.

Solving Dependencies Conflicts with Diener

To efficiently handle the dependencies and their origins, you can check out the tool diener.

If the diener executable file, the cloned Polkadot SDK repository, and your Tanssi fork are located in the same folder, step into the Tanssi fork folder and execute the following command:

../diener patch --crates-to-patch ../polkadot-sdk \
    --target https://github.com/paritytech/polkadot-sdk \
    --point-to-git https://github.com/moondance-labs/polkadot-sdk \
    --point-to-git-branch tanssi-polkadot-v1.3.0

This command applies the changes to the Cargo.toml file, patching the dependencies, and solving the double reference issues.

You can visit the diener documentation to learn more about the tool and other extra functions it offers.

Example of the Double Reference Issue

To illustrate the situation, the following steps add a demo external module to a custom runtime based on the baseline Tanssi appchain template. One way to follow this tutorial is to clone the Tanssi Github repository, which will act as the root repository of the project.

This tutorial will generate a multiple reference compile-time error. Finally, the steps will show you how to fix the compile error by patching the dependencies with the tool diener, the runtime will compile successfully and work as intended.

Add a Third-Party Dependency

Similarly to what is described in the built-in module article, adding a third-party module requires the following steps:

  1. Declare the dependency in the root Cargo.toml file
  2. Make the standard features available to the compiler
  3. Configure and add the module to the runtime

Should the third-party module reference any dependency already referenced from a distinct source or version, compilation will fail.

The following diagram shows how two different references to the same dependency are being included in the runtime, causing the compilation to fail:

Double reference

To resolve this issue, it will be necessary to apply a patch so that the references for the dependency are unified:

Patched reference

Declaring the Dependency

The first step to reproduce the double reference issue is to declare the dependency in the Cargo.toml file located in the repository's root folder, under the section [dependencies]. For this example, a simple toggle module is used.

This toggle module, built for testing and educational purposes, adds basic logic to the runtime, allowing users to switch a state between true and false.

[dependencies]
...
pallet-toggle = { 
    git = "https://github.com/papermoonio/pallet-toggle.git", 
    default-features = false 
}
...

Make the Standard Features Available to the Compiler

Having declared the module in the workspace Cargo.toml file, the dependency can now be added to the specific template Cargo.toml file, which, for this example that uses the Tanssi GitHub repo, is located in the folder container-chains/templates/simple/runtime.

[dependencies]
...
pallet-toggle = { workspace = true }
...

In the same Cargo.toml file, add the following features.

[features]
default = [
    "std",
]
std = [
    ...,
    "pallet-toggle/std",
   ...
]
...
runtime-benchmarks = [
    ...,
    "pallet-toggle/runtime-benchmarks",
]

try-runtime = [
    ...,
    "pallet-toggle/try-runtime",
]

Configure and Add the Module to the Runtime

Next, add the following snippet to the lib.rs file inside the runtime folder. This configures the module and adds the module within the construct_runtime! macro.

...
impl pallet_template::Config for Runtime {
    type RuntimeEvent = RuntimeEvent;
    type WeightInfo = pallet_template::weights::SubstrateWeight<Runtime>;
}

construct_runtime!(
    pub enum Runtime
    {
        ...
        ...
        Toggle: pallet_toggle,
    }
);

Compile Runtime

After completing the preceding steps, the module is declared a dependency in the project, configured, and added to the runtime.

Compile the template using the following command:

cargo build -p container-chain-template-simple-node --release

The terminal output will display an error, similar to the following, caused by different modules referencing different versions of the same dependency:

error: failed to select a version for `syn`.

Patch Dependencies

Finally, executing the diener command will add a patch section to your workspace Cargo.toml file, overriding the dependencies and unifying origins and versions.

This is what the execution in the terminal looks like:

../diener patch --crates-to-patch ../polkadot-sdk \
--target https://github.com/paritytech/polkadot-sdk \
--point-to-git https://github.com/moondance-labs/polkadot-sdk \
--point-to-git-branch tanssi-polkadot-v1.3.0

[2024-01-10T23:26:27Z INFO diener] Running diener v0.4.7
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bridge-runtime-common.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bp-header-chain'
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'bp-runtime'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural-tools'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'frame-support-procedural-tools-derive'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-core-hashing'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-api'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-api-proc-macro' .
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-core'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-debug-derive'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-externalities'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-std'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-storage'.
[2024-01-10T23:26:42Z INFO diener::patch] Adding patch for 'sp-runtime-interface'.

As shown in the terminal output, diener adds a patch for the dependencies, creating a patch section in your toml overriding their origin:

[patch."https://github.com/paritytech/polkadot-sdk"]
bridge-runtime-common = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
bp-header-chain = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
bp-runtime = { git = "https://github.com/moondance-labs/polkadot-sdk" , branch = "tanssi-polkadot-v1.3.0" }
...

Finally, compiling will succeed, and the module will be built into your runtime.

Last update: May 10, 2024
| Created: December 1, 2023