Submit the XCM from Your Parachain

### Submit the XCM from Your Parachain

To activate auto-renewal, you must submit an XCM from your parachain to the Coretime chain using Root origin. This can be done through the sudo pallet (if available) or your parachain's governance system.

!!! note "XCM Version"
    The Coretime chain uses XCM v5. When constructing messages in Polkadot.js Apps, select **V5** for the XCM version. Key changes in v5 include:

    - Type renames: `Location` (previously `MultiLocation`), `Asset`/`Assets` (previously `MultiAsset`/`MultiAssets`)
    - New fee instruction: `PayFees` replaces `BuyExecution`

The XCM needs to execute these operations:

1. Withdraw DOT from your parachain's sovereign account on the Coretime chain.
2. Pay fees for execution using the new `PayFees` instruction.
3. Execute the auto-renewal extrinsic.
4. Refund surplus fees and deposit remaining assets back to the sovereign account.

Here's how to submit this XCM using parachain `2000` as an example:

!!! note "Root Origin Required"
    The following example uses the `sudo` pallet for simplicity. Production parachains typically don't have sudo enabled and instead submit this XCM through their governance system (e.g., an OpenGov referendum, a council motion, or a democracy proposal).

1. In [Polkadot.js Apps](https://polkadot.js.org/apps/#/explorer), connect to your parachain, navigate to the **Developer** dropdown and select the **Extrinsics** option.

2. Create a `sudo.sudo` extrinsic that executes `polkadotXcm.send`:
    1. Use the `sudo.sudo` extrinsic to execute the following call as Root (or wrap in a governance proposal for production chains).
    2. Select the **polkadotXcm** pallet.
    3. Choose the **send** extrinsic.
    4. Set the **dest** parameter to target the Coretime chain using XCM v5 `Location` format:
        - **parents**: `1` (go up to relay chain)
        - **interior**: `X1` with `Parachain: 1005`

    ![](/images/parachains/runtime-maintenance/coretime-renewal/coretime-renewal-5.webp)

3. Construct the XCM message using v5 instructions and submit it:

    1. Add a **WithdrawAsset** instruction with the DOT amount to withdraw (specified as `Assets` in v5).
    2. Add a **PayFees** instruction to pay for execution fees:
        - **asset**: The asset to use for fees (must be a subset of the withdrawn assets)
    3. Add a **Transact** instruction with the following parameters:
        - **originKind**: Use `SovereignAccount`
        - **fallbackMaxWeight**: Use the weight calculated previously (specify both `refTime` and `proofSize`)
        - **call**: Use the encoded call data generated before
    4. Add a **RefundSurplus** instruction to return unused fees from the fees register to the holding register.
    5. Add a **DepositAsset** instruction to return remaining funds:
        - **assets**: Use `Wild::All` to deposit all remaining assets
        - **beneficiary**: Your parachain's sovereign account `Location`
    6. Click the **Submit Transaction** button.

    ![](/images/parachains/runtime-maintenance/coretime-renewal/coretime-renewal-6.webp)

After successful execution, your parachain should have auto-renewal enabled. To verify this, check the events emitted in the Coretime chain. You should see a confirmation event named `broker.AutoRenewalEnabled`, which includes two parameters:

- **core**: The core currently assigned to your task, in this example, `48`.
- **task**: The task for which auto-renewal was enabled, in this example, `2000`.

You can find this event in the list of recent events. It should look similar to the following:

![](/images/parachains/runtime-maintenance/coretime-renewal/coretime-renewal-7.webp)


Where to Go Next

## Where to Go Next

-   Guide __Unlock Your Parachain__

    ---

    Learn how to unlock your parachain's potential by migrating from a legacy lease to on-demand or bulk coretime.

    [:octicons-arrow-right-24: Get Started](/parachains/runtime-maintenance/unlock-parachains/)


Introduction

## Introduction

Upgrading the runtime of your Polkadot SDK-based blockchain is a fundamental feature that allows you to add new functionality, fix bugs, or improve performance without requiring a hard fork. Runtime upgrades are performed by submitting a special extrinsic that replaces the existing on-chain WebAssembly (Wasm) runtime code. This process is trustless, transparent, and can be executed either through governance or using sudo, depending on your chain's configuration.

This tutorial guides you through preparing, submitting, and verifying a runtime upgrade for your parachain or standalone Polkadot SDK-based chain. You add a new dispatchable function to your custom pallet, bump the runtime version, build the new Wasm binary, and submit the upgrade through Polkadot.js Apps.


Prerequisites

## Prerequisites

Complete the following before you begin:

- Complete the [Install Polkadot SDK](/parachains/install-polkadot-sdk/) guide.
- Set up and run the parachain template by following the [Set Up the Parachain Template](/parachains/launch-a-parachain/set-up-the-parachain-template/) tutorial.
- Create and integrate a custom pallet by following the [Create a Custom Pallet](/parachains/customize-runtime/pallet-development/create-a-pallet/) tutorial.
- Ensure access to [Polkadot.js Apps](https://polkadot.js.org/apps/#/explorer) connected to your local node.


Start Your Chain

## Start Your Chain

If you don't already have the parachain template running, start the Omni Node in development mode:

```bash
polkadot-omni-node --chain ./chain_spec.json --dev
```

Once the node is running and producing blocks, open [Polkadot.js Apps](https://polkadot.js.org/apps/#/explorer?rpc=ws://127.0.0.1:9944) and connect it to your local node at `ws://127.0.0.1:9944`.

Verify your chain is operational. You should see `parachain-template-runtime/1` in the top left corner of the Polkadot.js Apps header, indicating the chain is running with spec version `1`:

![Initial runtime version showing spec version 1 in the Polkadot.js Apps header](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-01.webp)

Keep this chain running in the background.


Add a New Feature

## Add a New Feature

Extend your existing custom pallet by adding a new dispatchable function to reset the counter to zero. This provides a meaningful upgrade that demonstrates new functionality.

Open your custom pallet's `lib.rs` file and add the following function inside the `#[pallet::call]` block, after the existing dispatchables:

```rust title="custom-pallet/src/lib.rs"
/// Reset the counter to zero.
///
/// The dispatch origin of this call must be _Root_.
///
/// Emits `CounterValueSet` event when successful.
#[pallet::call_index(3)]
#[pallet::weight(0)]
pub fn reset_counter(origin: OriginFor<T>) -> DispatchResult {
	ensure_root(origin)?;
	<CounterValue<T>>::put(0u32);
	Self::deposit_event(Event::CounterValueSet { new_value: 0 });
	Ok(())
}
```

The `reset_counter` function is a Root-only operation that sets the counter value back to zero, regardless of its current state. This is useful for administrative purposes. Unlike the existing `increment` and `decrement` functions that any signed user can call, this reset function requires Root privileges, making it a controlled administrative action.

Ensure that your runtime compiles correctly:

```bash
cargo build --release
```


Bump the Runtime Version

## Bump the Runtime Version

Before building the final Wasm binary, you must increment the `spec_version` in the runtime. This tells the chain's executor that the new runtime contains changes and should replace the current one.

Open `runtime/src/lib.rs` and find the `VERSION` constant. The current version looks like this:

```rust title="runtime/src/lib.rs"
#[sp_version::runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
	spec_name: alloc::borrow::Cow::Borrowed("parachain-template-runtime"),
	impl_name: alloc::borrow::Cow::Borrowed("parachain-template-runtime"),
	authoring_version: 1,
	spec_version: 1,
	impl_version: 0,
	apis: apis::RUNTIME_API_VERSIONS,
	transaction_version: 1,
	system_version: 1,
};
```

Change `spec_version` from `1` to `2`:

```rust title="runtime/src/lib.rs" hl_lines="6"
#[sp_version::runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
	spec_name: alloc::borrow::Cow::Borrowed("parachain-template-runtime"),
	impl_name: alloc::borrow::Cow::Borrowed("parachain-template-runtime"),
	authoring_version: 1,
	spec_version: 2,
	impl_version: 0,
	apis: apis::RUNTIME_API_VERSIONS,
	transaction_version: 1,
	system_version: 1,
};
```

!!! warning
    Forgetting to bump `spec_version` is a common mistake. If you submit a runtime upgrade without incrementing this value, the chain will not recognize it as a new runtime, and the upgrade will have no effect.


Build the New Runtime

## Build the New Runtime

With the new feature added and the version bumped, build the runtime:

```bash
cargo build --release
```

After compilation, verify the Wasm binaries were generated:

  ls -la target/release/wbuild/parachain-template-runtime/

  parachain_template_runtime.wasm
  parachain_template_runtime.compact.wasm
  parachain_template_runtime.compact.compressed.wasm

In the next section, you'll use the `.compact.compressed.wasm` file for the upgrade, as it's the smallest and most efficient format.


Submit the Runtime Upgrade

## Submit the Runtime Upgrade

Now submit the new runtime to the chain using the Sudo pallet through Polkadot.js Apps.

1. Open [Polkadot.js Apps](https://polkadot.js.org/apps/) and connect to your node:

    1. Click on **Developer**.
    2. Select **Extrinsics** from the dropdown.

    ![Navigate to Developer then Extrinsics](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-02.webp)

2. Prepare the sudo call:

    1. Select the **sudo** pallet.
    2. Select the **sudo(call)** extrinsic from the list.

    ![Select the sudo pallet and sudo(call) extrinsic](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-03.webp)

3. In the sudo call:

    1. Select the **system** pallet.
    2. Select the **setCode(code)** extrinsic from the list.

    ![Select system pallet and setCode extrinsic](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-04.webp)

4. Upload the Wasm binary for the `code` parameter:

    1. Click **file upload**.
    2. Select the `parachain_template_runtime.compact.compressed.wasm` file from `target/release/wbuild/parachain-template-runtime/`.

    ![Upload the Wasm file](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-05.webp)

5. Click **Submit Transaction** and sign the transaction with the sudo key.

    ![Sign and submit the transaction](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-06.webp)

!!! info
    On production chains, runtime upgrades are submitted through governance rather than Sudo. The process involves a referendum where token holders vote to approve the upgrade.

After the transaction is included in a block, the runtime upgrade takes effect immediately.


Verify the Upgrade

## Verify the Upgrade


Check Runtime Version

### Check Runtime Version

To confirm the upgrade was successful, navigate to the **Developer** dropdown and select **Chain State**. The runtime version in the header changes to `parachain-template-runtime/2`. Additionally, the recent events show `parachainSystem.ValidationFunctionApplied`, confirming the upgrade was applied:

![Explorer showing updated runtime version and upgrade events](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-07.webp)

Query the on-chain runtime version:

1. Query the last runtime upgrade:

    1. Select the **system** pallet.
    2. Select **lastRuntimeUpgrade()** from the query dropdown.

    ![Select system pallet and lastRuntimeUpgrade query](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-08.webp)

2. Click the **+** button to execute the query.

    ![Click the plus button to query](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-09.webp)

3. The result should show `specVersion: 2`, confirming the upgrade was applied.

    ![Verify specVersion is 2](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-10.webp)


Test New Functionality

### Test New Functionality

Navigate to **Developer** > **Extrinsics** and select your **customPallet**. The new `resetCounter` function appears alongside the existing `increment`, `decrement`, and `setCounterValue` functions.

![Custom pallet showing the new resetCounter function](/images/parachains/runtime-maintenance/runtime-upgrades/runtime-upgrade-11.webp)

Test the new functionality:

1. Increment the counter using the existing `increment` function.
2. Use the new `resetCounter` function (requires sudo/root privileges).
3. Verify the counter value is reset to `0`.


Where to Go Next

## Where to Go Next

-   Guide __Storage Migrations__

    ---

    Learn how to write and manage storage migrations when runtime upgrades change how data is stored on-chain.

    [:octicons-arrow-right-24: Get Started](/parachains/runtime-maintenance/storage-migrations/)

-   Tutorial __Create a Custom Pallet__

    ---

    Learn how to build a custom pallet from scratch to add new functionality to your Polkadot SDK-based runtime.

    [:octicons-arrow-right-24: Get Started](/parachains/customize-runtime/pallet-development/create-a-pallet/)


Introduction

## Introduction

Storage migrations are a crucial part of the runtime upgrade process. They allow you to update the [storage items](https://paritytech.github.io/polkadot-sdk/master/frame_support/pallet_macros/attr.storage.html) of your blockchain, adapting to changes in the runtime. Whenever you change the encoding or data types used to represent data in storage, you'll need to provide a storage migration to ensure the runtime can correctly interpret the existing stored values in the new runtime state.

Storage migrations must be executed precisely during the runtime upgrade process to ensure data consistency and prevent [runtime panics](https://doc.rust-lang.org/std/macro.panic.html). The migration code needs to run as follows:

- After the new runtime is deployed.
- Before any other code from the new runtime executes.
- Before any [`on_initialize`](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.Hooks.html#method.on_initialize) hooks run.
- Before any transactions are processed.

This timing is critical because the new runtime expects data to be in the updated format. Any attempt to decode the old data format without proper migration could result in runtime panics or undefined behavior.


Storage Migration Scenarios

## Storage Migration Scenarios

A storage migration is necessary whenever a runtime upgrade changes the storage layout or the encoding/interpretation of existing data. Even if the underlying data type appears to still "fit" the new storage representation, a migration may be required if the interpretation of the stored values has changed.

Storage migrations ensure data consistency and prevent corruption during runtime upgrades. Below are common scenarios categorized by their impact on storage and migration requirements:

- Migration required:
    - Reordering or mutating fields of an existing data type to change the encoded/decoded data representation.
    - Removal of a pallet or storage item warrants cleaning up storage via a migration to avoid state bloat.

- Migration not required:
    - Adding a new storage item would not require any migration since no existing data needs transformation.
    - Adding or removing an extrinsic introduces no new interpretation of preexisting data, so no migration is required.

The following are some common scenarios where a storage migration is needed:

- **Changing data types**: Changing the underlying data type requires a migration to convert the existing values.

    ```rust
    #[pallet::storage]
    pub type FooValue = StorageValue<_, Foo>;
    // old
    pub struct Foo(u32)
    // new
    pub struct Foo(u64)
    ```

- **Changing data representation**: Modifying the representation of the stored data, even if the size appears unchanged, requires a migration to ensure the runtime can correctly interpret the existing values.

    ```rust
    #[pallet::storage]
    pub type FooValue = StorageValue<_, Foo>;
    // old
    pub struct Foo(u32)
    // new
    pub struct Foo(i32)
    // or
    pub struct Foo(u16, u16)
    ```

- **Extending an enum**: Adding new variants to an enum requires a migration if you reorder existing variants, insert new variants between existing ones, or change the data type of existing variants. No migration is required when adding new variants at the end of the enum.

    ```rust
    #[pallet::storage]
    pub type FooValue = StorageValue<_, Foo>;
    // old
    pub enum Foo { A(u32), B(u32) }
    // new (New variant added at the end. No migration required)
    pub enum Foo { A(u32), B(u32), C(u128) }
    // new (Reordered variants. Requires migration)
    pub enum Foo { A(u32), C(u128), B(u32) }
    ```

- **Changing the storage key**: Modifying the storage key, even if the underlying data type remains the same, requires a migration to ensure the runtime can locate the correct stored values.

    ```rust
    #[pallet::storage]
    pub type FooValue = StorageValue<_, u32>;
    // new
    #[pallet::storage]
    pub type BarValue = StorageValue<_, u32>;
    ```

!!!warning
    In general, any change to the storage layout or data encoding used in your runtime requires careful consideration of the need for a storage migration. Overlooking a necessary migration can lead to undefined behavior or data loss during a runtime upgrade.


Implement Storage Migrations

## Implement Storage Migrations

The [`OnRuntimeUpgrade`](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.OnRuntimeUpgrade.html) trait provides the foundation for implementing storage migrations in your runtime. Here's a detailed look at its essential functions:

```rust
pub trait OnRuntimeUpgrade {
    fn on_runtime_upgrade() -> Weight { ... }
    fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, TryRuntimeError> { ... }
    fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> { ... }
    fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> { ... }
}
```


Core Migration Function

### Core Migration Function

The [`on_runtime_upgrade`](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.Hooks.html#method.on_runtime_upgrade) function executes when the FRAME Executive pallet detects a runtime upgrade. Important considerations when using this function include:

- It runs before any pallet's `on_initialize` hooks.
- Critical storage items (like [`block_number`](https://paritytech.github.io/polkadot-sdk/master/frame_system/pallet/struct.Pallet.html#method.block_number)) may not be set.
- Execution is mandatory and must be completed.
- Careful weight calculation is required to prevent bricking the chain.

When implementing the migration logic, your code must handle several vital responsibilities. A migration implementation must do the following to operate correctly:

- Read existing storage values in their original format.
- Transform data to match the new format.
- Write updated values back to storage.
- Calculate and return consumed weight.


Migration Testing Hooks

### Migration Testing Hooks

The `OnRuntimeUpgrade` trait provides some functions designed specifically for testing migrations. These functions never execute on-chain but are essential for validating migration behavior in test environments. The migration test hooks are as follows:

- **[`try_on_runtime_upgrade`](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.OnRuntimeUpgrade.html#method.try_on_runtime_upgrade)**: This function serves as the primary orchestrator for testing the complete migration process. It coordinates the execution flow from `pre-upgrade` checks through the actual migration to `post-upgrade` verification. Handling the entire migration sequence ensures that storage modifications occur correctly and in the proper order. Preserving this sequence is particularly valuable when testing multiple dependent migrations, where the execution order matters.

- **[`pre_upgrade`](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.Hooks.html#method.pre_upgrade)**: Before a runtime upgrade begins, the `pre_upgrade` function performs preliminary checks and captures the current state. It returns encoded state data that can be used for `post-upgrade` verification. This function must never modify storage: it should only read and verify the existing state. The data it returns includes critical state values that should remain consistent or transform predictably during migration.

- **[`post_upgrade`](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.Hooks.html#method.post_upgrade)**: After the migration completes, `post_upgrade` validates its success. It receives the state data captured by `pre_upgrade` to verify that the migration was executed correctly. This function checks for storage consistency and ensures all data transformations are completed as expected. Like `pre_upgrade`, it operates exclusively in testing environments and should not modify storage.


Migration Structure

### Migration Structure

There are two approaches to implementing storage migrations. The first method involves directly implementing `OnRuntimeUpgrade` on structs. This approach requires manually checking the on-chain storage version against the new [`StorageVersion`](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/struct.StorageVersion.html) and executing the transformation logic only when the check passes. This version verification prevents multiple executions of the migration during subsequent runtime upgrades.

The recommended approach is to implement [`UncheckedOnRuntimeUpgrade`](https://paritytech.github.io/polkadot-sdk/master/frame_support/traits/trait.UncheckedOnRuntimeUpgrade.html) and wrap it with [`VersionedMigration`](https://paritytech.github.io/polkadot-sdk/master/frame_support/migrations/struct.VersionedMigration.html). `VersionedMigration` implements `OnRuntimeUpgrade` and handles storage version management automatically, following best practices and reducing potential errors.

`VersionedMigration` requires five type parameters:

- **`From`**: The source version for the upgrade.
- **`To`**: The target version for the upgrade.
- **`Inner`**: The `UncheckedOnRuntimeUpgrade` implementation.
- **`Pallet`**: The pallet being upgraded.
- **`Weight`**: The runtime's [`RuntimeDbWeight`](https://paritytech.github.io/polkadot-sdk/master/frame_support/weights/struct.RuntimeDbWeight.html) implementation.

Examine the following migration example that transforms a simple `StorageValue` storing a `u32` into a more complex structure that tracks both current and previous values using the `CurrentAndPreviousValue` struct:

- Old `StorageValue` format:

    ```rust
    #[pallet::storage]
    pub type Value<T: Config> = StorageValue<_, u32>;
    ```

- New `StorageValue` format:

    ```rust
    #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, scale_info::TypeInfo, MaxEncodedLen)]
    pub struct CurrentAndPreviousValue {
    	/// The most recently set value.
    	pub current: u32,
    	/// The previous value, if one existed.
    	pub previous: Option<u32>,
    }
    #[pallet::storage]
    	pub type Value<T: Config> = StorageValue<_, CurrentAndPreviousValue>;
    ```

- Migration:

    ```rust
    use frame_support::{
    	storage_alias,
    	traits::{Get, UncheckedOnRuntimeUpgrade},
    };

    #[cfg(feature = "try-runtime")]
    use alloc::vec::Vec;

    /// Collection of storage item formats from the previous storage version.
    ///
    /// Required so we can read values in the v0 storage format during the migration.
    mod v0 {
    	use super::*;

    	/// V0 type for [`crate::Value`].
    	#[storage_alias]
    	pub type Value<T: crate::Config> = StorageValue<crate::Pallet<T>, u32>;
    }

    /// Implements [`UncheckedOnRuntimeUpgrade`], migrating the state of this pallet from V0 to V1.
    ///
    /// In V0 of the template [`crate::Value`] is just a `u32`. In V1, it has been upgraded to
    /// contain the struct [`crate::CurrentAndPreviousValue`].
    ///
    /// In this migration, update the on-chain storage for the pallet to reflect the new storage
    /// layout.
    pub struct InnerMigrateV0ToV1<T: crate::Config>(core::marker::PhantomData<T>);

    impl<T: crate::Config> UncheckedOnRuntimeUpgrade for InnerMigrateV0ToV1<T> {
    	/// Return the existing [`crate::Value`] so we can check that it was correctly set in
    	/// `InnerMigrateV0ToV1::post_upgrade`.
    	#[cfg(feature = "try-runtime")]
    	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
    		use codec::Encode;

    		// Access the old value using the `storage_alias` type
    		let old_value = v0::Value::<T>::get();
    		// Return it as an encoded `Vec<u8>`
    		Ok(old_value.encode())
    	}

    	/// Migrate the storage from V0 to V1.
    	///
    	/// - If the value doesn't exist, there is nothing to do.
    	/// - If the value exists, it is read and then written back to storage inside a
    	///   [`crate::CurrentAndPreviousValue`].
    	fn on_runtime_upgrade() -> frame_support::weights::Weight {
    		// Read the old value from storage
    		if let Some(old_value) = v0::Value::<T>::take();
    			crate::Value::<T>::put(new);
    			// One read + write for taking the old value, and one write for setting the new value
    			T::DbWeight::get().reads_writes(1, 2)
    		} else {
    			// No writes since there was no old value, just one read for checking
    			T::DbWeight::get().reads(1)
    		}
    	}

    	/// Verifies the storage was migrated correctly.
    	///
    	/// - If there was no old value, the new value should not be set.
    	/// - If there was an old value, the new value should be a [`crate::CurrentAndPreviousValue`].
    	#[cfg(feature = "try-runtime")]
    	fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
    		use codec::Decode;
    		use frame_support::ensure;

    		let maybe_old_value = Option::<u32>::decode(&mut &state[..]).map_err(|_| {
    			sp_runtime::TryRuntimeError::Other("Failed to decode old value from storage")
    		})?;

    		match maybe_old_value {
    			Some(old_value) => {
    				let expected_new_value =
    					crate::CurrentAndPreviousValue { current: old_value, previous: None };
    				let actual_new_value = crate::Value::<T>::get();

    				ensure!(actual_new_value.is_some(), "New value not set");
    				ensure!(
    					actual_new_value == Some(expected_new_value),
    					"New value not set correctly"
    				);
    			},
    			None => {
    				ensure!(crate::Value::<T>::get().is_none(), "New value unexpectedly set");
    			},
    		};
    		Ok(())
    	}
    }

    /// [`UncheckedOnRuntimeUpgrade`] implementation [`InnerMigrateV0ToV1`] wrapped in a
    /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), which ensures that:
    /// - The migration only runs once when the on-chain storage version is 0
    /// - The on-chain storage version is updated to `1` after the migration executes
    /// - Reads/Writes from checking/settings the on-chain storage version are accounted for
    pub type MigrateV0ToV1<T> = frame_support::migrations::VersionedMigration<
    ```


Migration Organization

### Migration Organization

Best practices recommend organizing migrations in a separate module within your pallet. Here's the recommended file structure:

```plain
my-pallet/
├── src/
│   ├── lib.rs       # Main pallet implementation
│   └── migrations/  # All migration-related code
│       ├── mod.rs   # Migrations module definition
│       ├── v1.rs    # V0 -> V1 migration
│       └── v2.rs    # V1 -> V2 migration
└── Cargo.toml
```

This structure provides several benefits:

- Separates migration logic from core pallet functionality.
- Makes migrations easier to test and maintain.
- Provides explicit versioning of storage changes.
- Simplifies the addition of future migrations.


Scheduling Migrations

### Scheduling Migrations

To execute migrations during a runtime upgrade, you must configure them in your runtime's Executive pallet. Add your migrations in `runtime/src/lib.rs`:

```rust
/// Tuple of migrations (structs that implement `OnRuntimeUpgrade`)
type Migrations = (
    pallet_my_pallet::migrations::v1::Migration,
    // More migrations can be added here
);
pub type Executive = frame_executive::Executive<
    Runtime,
    Block,
    frame_system::ChainContext<Runtime>,
    Runtime,
    AllPalletsWithSystem,
    Migrations, // Include migrations here
>;
```


Single-Block Migrations

## Single-Block Migrations

Single-block migrations execute their logic within one block immediately following a runtime upgrade. They run as part of the runtime upgrade process through the `OnRuntimeUpgrade` trait implementation and must be completed before any other runtime logic executes.

While single-block migrations are straightforward to implement and provide immediate data transformation, they carry significant risks. The most critical consideration is that they must complete within one block's weight limits. This is especially crucial for parachains, where exceeding block weight limits will brick the chain.

Use single-block migrations only when you can guarantee:

- The migration has a bounded execution time.
- Weight calculations are thoroughly tested.
- Total weight will never exceed block limits.

For a complete implementation example of a single-block migration, refer to the [single-block migration example]( https://paritytech.github.io/polkadot-sdk/master/pallet_example_single_block_migrations/index.html) in the Polkadot SDK documentation.


Multi Block Migrations

## Multi Block Migrations

Multi-block migrations distribute the migration workload across multiple blocks, providing a safer approach for production environments. The migration state is tracked in storage, allowing the process to pause and resume across blocks.

This approach is essential for production networks and parachains as the risk of exceeding block weight limits is eliminated. Multi-block migrations can safely handle large storage collections, unbounded data structures, and complex nested data types where weight consumption might be unpredictable.

Multi-block migrations are ideal when dealing with:

- Large-scale storage migrations.
- Unbounded storage items or collections.
- Complex data structures with uncertain weight costs.

The primary trade-off is increased implementation complexity, as you must manage the migration state and handle partial completion scenarios. However, multi-block migrations' significant safety benefits and operational reliability are typically worth the increased complexity.

For a complete implementation example of multi-block migrations, refer to the [official example](https://github.com/paritytech/polkadot-sdk/tree/polkadot-stable2603/substrate/frame/examples/multi-block-migrations) in the Polkadot SDK.


Introduction

## Introduction

Parachain locks are a critical security mechanism in the Polkadot ecosystem designed to maintain decentralization during the parachain lifecycle. These locks prevent potential centralization risks that could emerge during the early stages of parachain operation.

The locking system follows strict, well-defined conditions that distribute control across multiple authorities:

- Relay chain governance has the authority to lock any parachain.
- A parachain can lock its own lock.
- Parachain managers have permission to lock the parachain.
- Parachains are locked automatically when they successfully produce their first block.

Similarly, unlocking a parachain follows controlled procedures:

- Relay chain governance retains the authority to unlock any parachain.
- A parachain can unlock its own lock.

This document guides you through checking a parachain's lock status and safely executing the unlock procedure from a parachain using [XCM (Cross-Consensus Messaging)](/parachains/interoperability/get-started/).


Check If the Parachain Is Locked

## Check If the Parachain Is Locked

Before unlocking a parachain, you should verify its current lock status. This can be done through the Polkadot.js interface:

1. In [Polkadot.js Apps](https://polkadot.js.org/apps/#/explorer), connect to the relay chain, navigate to the **Developer** dropdown and select the **Chain State** option.

2. Query the parachain locked status:
    1. Select **`registrar`**.
    2. Choose the **`paras`** option.
    3. Input the parachain ID you want to check as a parameter (e.g. `2006`).
    4. Click the **+** button to execute the query.
    5. Check the status of the parachain lock.
        - **`manager`**: The account that has placed a deposit for registering this parachain.
        - **`deposit`**: The amount reserved by the `manager` account for the registration.
        - **`locked`**: Whether the parachain registration should be locked from being controlled by the manager.

    ![](/images/parachains/runtime-maintenance/unlock-parachains/unlock-parachain-1.webp)


How to Unlock a Parachain

## How to Unlock a Parachain

Unlocking a parachain requires sending an XCM (Cross-Consensus Message) to the relay chain from the parachain itself, sending a message with Root origin, or this can be accomplished through the relay chain's governance mechanism, executing a root call.

If sending an XCM, the parachain origin must have proper authorization, typically from either the parachain's sudo pallet (if enabled) or its governance system.

This guide demonstrates the unlocking process using a parachain with the sudo pallet. For parachains using governance-based authorization instead, the process will require adjustments to how the XCM is sent.


Prepare the Unlock Call

### Prepare the Unlock Call

Before sending the XCM, you need to construct the relay chain call that will be executed. Follow these steps to prepare the `registrar.removeLock` extrinsic:

1. In [Polkadot.js Apps](https://polkadot.js.org/apps/#/explorer), connect to the relay chain, navigate to the **Developer** dropdown and select the **Extrinsics** option.

2. Build the `registrar.removeLock` extrinsic:

    1. Select the **registrar** pallet.
    2. Choose the **removeLock** extrinsic.
    3. Fill in the parachain ID parameter (e.g., `2006`).
    4. Copy the **encoded call data**.

    ![](/images/parachains/runtime-maintenance/unlock-parachains/unlock-parachain-2.webp)

    To ensure your encoded call data is correct, check this [example](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fdot-rpc.stakeworld.io#/extrinsics/decode/0x4604d6070000) of a decoded `removeLock` call for parachain 2006. Your encoded data should follow the same pattern.

3. Determine the transaction weight required for executing the call. You can estimate this by executing the `transactionPaymentCallApi.queryCallInfo` runtime call with the encoded call data previously obtained:

    ![](/images/parachains/runtime-maintenance/unlock-parachains/unlock-parachain-3.webp)

    This weight information is crucial for properly configuring your XCM message's execution parameters in the next steps.


Fund the Sovereign Account

### Fund the Sovereign Account

For a successful XCM execution, the [sovereign account](https://github.com/polkadot-fellows/xcm-format/blob/10726875bd3016c5e528c85ed6e82415e4b847d7/README.md?plain=1#L50) of your parachain on the relay chain must have sufficient funds to cover transaction fees. The sovereign account is a deterministic address derived from your parachain ID.

You can identify your parachain's sovereign account using either of these methods:

=== "Runtime API"

    Execute the `locationToAccountApi.convertLocation` runtime API call to convert your parachain's location into its sovereign account address on the relay chain.

    ![](/images/parachains/runtime-maintenance/unlock-parachains/unlock-parachain-7.webp)

=== "Substrate Utilities"

    Use the **"Para ID" to Address** section in [Substrate Utilities](https://www.shawntabrizi.com/substrate-js-utilities/) with the **Child** option selected.

=== "Manual Calculation"

    1. Identify the appropriate prefix:

        - For parent/child chains use the prefix `0x70617261` (which decodes to `b"para"`).

    2. Encode your parachain ID as a u32 [SCALE](/reference/parachains/data-encoding/#data-types) value:

        - For parachain 2006, this would be `d6070000`.

    3. Combine the prefix with the encoded ID to form the sovereign account address:

        - **Hex**: `0x70617261d6070000000000000000000000000000000000000000000000000000`
        - **SS58 format**: `5Ec4AhPW97z4ZyYkd3mYkJrSeZWcwVv4wiANES2QrJi1x17F`

You can transfer funds to this account from any account on the relay chain using a standard transfer. To calculate the amount needed, refer to the [XCM Payment API](https://paritytech.github.io/polkadot-sdk/master/xcm_runtime_apis/fees/trait.XcmPaymentApi.html). The calculation will depend on the XCM built in the next step.


Craft and Submit the XCM

### Craft and Submit the XCM

With the call data prepared and the sovereign account funded, you can now construct and send the XCM from your parachain to the relay chain. The XCM will need to perform several operations in sequence:

1. Withdraw DOT from your parachain's sovereign account.
2. Buy execution to pay for transaction fees.
3. Execute the `registrar.removeLock` extrinsic.
4. Return any unused funds to your sovereign account.

Here's how to submit this XCM using Astar (Parachain 2006) as an example:

1. In [Polkadot.js Apps](https://polkadot.js.org/apps/#/explorer), connect to the parachain, navigate to the **Developer** dropdown and select the **Extrinsics** option.

2. Create a `sudo.sudo` extrinsic that executes `polkadotXcm.send`:

    1. Use the `sudo.sudo` extrinsic to execute the following call as Root.
    2. Select the **polkadotXcm** pallet.
    3. Choose the **send** extrinsic.
    4. Set the **dest** parameter as the relay chain.

    ![](/images/parachains/runtime-maintenance/unlock-parachains/unlock-parachain-4.webp)

3. Construct the XCM and submit it:

    1. Add a **WithdrawAsset** instruction.
    2. Add a **BuyExecution** instruction.
        - **fees**:
            - **id**: The asset location to use for the fee payment. In this example, the relay chain native asset is used.
            - **fun**: Select `Fungible` and use the same amount you withdrew from the sovereign account in the previous step.
        - **weightLimit**: Use `Unlimited`.
    3. Add a **Transact** instruction with the following parameters:
        - **originKind**: Use `Native`.
        - **requireWeightAtMost**: Use the weight calculated previously.
        - **call**: Use the encoded call data generated before.
    4. Add a **RefundSurplus** instruction.
    5. Add a **DepositAsset** instruction to send the remaining funds to the parachain sovereign account.
    6. Click the **Submit Transaction** button.

    ![](/images/parachains/runtime-maintenance/unlock-parachains/unlock-parachain-5.webp)

    If the amount withdrawn in the first instruction is exactly the amount needed to pay the transaction fees, instructions 4 and 5 can be omitted.

    To validate your XCM, examine the following reference [extrinsic](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fastar.public.curie.radiumblock.co%2Fws#/extrinsics/decode/0x63003300040100041400040000000700e40b5402130000000700e40b540200060042d3c91800184604d6070000140d0100000100591f) showing the proper instruction sequence and parameter formatting. Following this structure will help ensure successful execution of your message.

After submitting the transaction, wait for it to be finalized and then verify that your parachain has been successfully unlocked by following the steps described in the [Check if the Parachain is Locked](#check-if-the-parachain-is-locked) section. If the parachain shows as unlocked, your operation has been successful. If it still appears locked, verify that your XCM transaction was processed correctly and consider troubleshooting the XCM built.

![](/images/parachains/runtime-maintenance/unlock-parachains/unlock-parachain-6.webp)


Introduction

## Introduction

[Chopsticks](https://github.com/AcalaNetwork/chopsticks/), developed by the [Acala Foundation](https://github.com/AcalaNetwork), is a versatile tool tailored for developers working on Polkadot SDK-based blockchains. With Chopsticks, you can fork live chains locally, replay blocks to analyze extrinsics, and simulate complex scenarios like XCM interactions all without deploying to a live network.

This guide walks you through installing Chopsticks and provides information on configuring a local blockchain fork. By streamlining testing and experimentation, Chopsticks empowers developers to innovate and accelerate their blockchain projects within the Polkadot ecosystem.

For additional support and information, please reach out through [GitHub Issues](https://github.com/AcalaNetwork/chopsticks/issues).

!!! warning
    Chopsticks uses [Smoldot](https://github.com/smol-dot/smoldot) light client, which only supports the native Polkadot SDK API. Consequently, a Chopsticks-based fork doesn't support Ethereum JSON-RPC calls, meaning you cannot use it to fork your chain and connect Metamask.


Prerequisites

## Prerequisites

Before you begin, ensure you have the following installed:

- [Node.js](https://nodejs.org/en/).
- A package manager such as [npm](https://www.npmjs.com/), which should be installed with Node.js by default, or [Yarn](https://yarnpkg.com/).


Install Chopsticks

## Install Chopsticks

You can install Chopsticks globally or locally in your project. Choose the option that best fits your development workflow. This documentation explains the features of Chopsticks version `1.3.0`. Make sure you're using the correct version to match these instructions.


Global Installation

### Global Installation

To install Chopsticks globally, allowing you to use it across multiple projects, run:

```bash
npm i -g @acala-network/chopsticks@1.3.0
```

Now, you should be able to run the `chopsticks` command from your terminal.


Local Installation

### Local Installation

To use Chopsticks in a specific project, first create a new directory and initialize a Node.js project:

```bash
mkdir my-chopsticks-project
cd my-chopsticks-project
npm init -y
```

Then, install Chopsticks as a local dependency:

```bash
npm i @acala-network/chopsticks@1.3.0
```

Finally, you can run Chopsticks using the `npx` command. To see all available options and commands, run it with the `--help` flag:

```bash
npx @acala-network/chopsticks --help
```


Configure Chopsticks

## Configure Chopsticks

To run Chopsticks, you need to configure some parameters. This can be set either through using a configuration file or the command line interface (CLI). The parameters that can be configured are as follows:

- **`genesis`**: The link to a parachain's raw genesis file to build the fork from, instead of an endpoint.
- **`timestamp`**: Timestamp of the block to fork from.
- **`endpoint`**: The endpoint of the parachain to fork.
- **`block`**: Use to specify at which block hash or number to replay the fork.
- **`wasm-override`**: Path of the Wasm to use as the parachain runtime, instead of an endpoint's runtime.
- **`db`**: Path to the name of the file that stores or will store the parachain's database.
- **`config`**: Path or URL of the config file.
- **`port`**: The port to expose an endpoint on.
- **`build-block-mode`**: How blocks should be built in the fork: batch, manual, instant.
- **`import-storage`**: A pre-defined JSON/YAML storage path to override in the parachain's storage.
- **`allow-unresolved-imports`**: Whether to allow Wasm unresolved imports when using a Wasm to build the parachain.
- **`html`**: Include to generate storage diff preview between blocks.
- **`mock-signature-host`**: Mock signature host so that any signature starts with `0xdeadbeef` and filled by `0xcd` is considered valid.


Configuration File

### Configuration File

The Chopsticks source repository includes a collection of [YAML](https://yaml.org/) files that can be used to set up various Polkadot SDK chains locally. You can download these configuration files from the [repository's `configs` folder](https://github.com/AcalaNetwork/chopsticks/tree/master/configs).

An example of a configuration file for Polkadot is as follows:

```yaml
endpoint:
  - wss://rpc.ibp.network/polkadot
  - wss://polkadot-rpc.dwellir.com
mock-signature-host: true
block: ${env.POLKADOT_BLOCK_NUMBER}
db: ./db.sqlite
runtime-log-level: 5

import-storage:
  System:
    Account:
      - - - 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
        - providers: 1
          data:
            free: '10000000000000000000'
  ParasDisputes:
    $removePrefix: ['disputes'] # those can makes block building super slow
```

The configuration file allows you to modify the storage of the forked network by rewriting the pallet, state component and value that you want to change. For example, Polkadot's file rewrites Alice's `system.Account` storage so that the free balance is set to `10000000000000000000`.


CLI Flags

### CLI Flags

Alternatively, all settings (except for genesis and timestamp) can be configured via command-line flags, providing a comprehensive method to set up the environment.


WebSocket Commands

## WebSocket Commands

Chopstick's internal WebSocket server has special endpoints that allow the manipulation of the local Polkadot SDK chain.

These are the methods that can be invoked and their parameters:

- **dev_newBlock** (newBlockParams): Generates one or more new blocks.

    === "Parameters"

        - **`newBlockParams` ++"NewBlockParams"++**: The parameters to build the new block with. Where the `NewBlockParams` interface includes the following properties.

            - **`count` ++"number"++**: The number of blocks to build.
            - **`dmp` ++"{ msg: string, sentAt: number }[]"++**: The downward messages to include in the block.
            - **`hrmp` ++"Record<string | number, { data: string, sentAt: number }[]>"++**: The horizontal messages to include in the block.
            - **`to` ++"number"++**: The block number to build to.
            - **`transactions` ++"string[]"++**: The transactions to include in the block.
            - **`ump` ++"Record<number, string[]>"++**: The upward messages to include in the block.
            - **`unsafeBlockHeight` ++"number"++**: Build block using a specific block height (unsafe).

    === "Example"

        ```js
        import { ApiPromise, WsProvider } from '@polkadot/api';

        async function main());
          await api.isReady;
          await api.rpc('dev_newBlock', { count: 1 });
        }

        main();
        ```

- **dev_setBlockBuildMode** (buildBlockMode): Sets block build mode.

    === "Parameter"

        - **`buildBlockMode` ++"BuildBlockMode"++**: The build mode. Can be any of the following modes:

            ```ts
            export enum BuildBlockMode {
              Batch = 'Batch', /** One block per batch (default) */
              Instant = 'Instant', /** One block per transaction */
              Manual = 'Manual', /** Only build when triggered */
            }
            ```

    === "Example"

        ```js
        import { ApiPromise, WsProvider } from '@polkadot/api';

        async function main());
          await api.isReady;
          await api.rpc('dev_setBlockBuildMode', 'Instant');
        }

        main();
        ```

- **dev_setHead** (hashOrNumber): Sets the head of the blockchain to a specific hash or number.

    === "Parameter"

        - **`hashOrNumber` ++"string | number"++**: The block hash or number to set as head.

    === "Example"

        ```js
        import { ApiPromise, WsProvider } from '@polkadot/api';

        async function main());
          await api.isReady;
          await api.rpc('dev_setHead', 500);
        }

        main();
        ```

- **dev_setRuntimeLogLevel** (runtimeLogLevel): Sets the runtime log level.

    === "Parameter"

        - **`runtimeLogLevel` ++"number"++**: The runtime log level to set.

    === "Example"

        ```js
        import { ApiPromise, WsProvider } from '@polkadot/api';

        async function main());
          await api.isReady;
          await api.rpc('dev_setRuntimeLogLevel', 1);
        }

        main();
        ```

- **dev_setStorage** (values, blockHash): Creates or overwrites the value of any storage.

    === "Parameters"

        - **`values` ++"object"++**: JSON object resembling the path to a storage value.
        - **`blockHash` ++"string"++**: The block hash to set the storage value.

    === "Example"

        ```js
        import { ApiPromise, WsProvider } from '@polkadot/api';

        import { Keyring } from '@polkadot/keyring';
        async function main());
          await api.isReady;
          const keyring = new Keyring({ type: 'ed25519' });
          const bob = keyring.addFromUri('//Bob');
          const storage = {
            System: {
              Account: [[[bob.address], { data: { free: 100000 }, nonce: 1 }]],
            },
          };
          await api.rpc('dev_setStorage', storage);
        }

        main();
        ```

- **dev_timeTravel** (date): Sets the timestamp of the block to a specific date".

    === "Parameter"

        - **`date` ++"string"++**: Timestamp or date string to set. All future blocks will be sequentially created after this point in time.

    === "Example"

        ```js
        import { ApiPromise, WsProvider } from '@polkadot/api';

        async function main());
          await api.isReady;
          await api.rpc('dev_timeTravel', '2030-08-15T00:00:00');
        }

        main();
        ```


Where to Go Next

## Where to Go Next

-   External __Chopsticks Documentation__

    ---

    For reference documentation on the methods exposed by Chopsticks, see the official Chopsticks documentation.

    [:octicons-arrow-right-24: Get Started](https://acalanetwork.github.io/chopsticks/docs/)


Introduction

## Introduction

[Zombienet](https://github.com/paritytech/zombienet) is a testing framework designed for Polkadot SDK-based blockchain networks. It enables developers to efficiently deploy and test ephemeral blockchain environments using a simple CLI.

This tutorial walks you through spawning a local parachain test network using Zombienet's native provider. You will build a custom parachain binary from the [Polkadot SDK parachain template](https://github.com/paritytech/polkadot-sdk-parachain-template), download the relay chain binaries, configure a network with two relay chain validators and a parachain collator, and verify that the network produces blocks.

For detailed information about Zombienet installation methods, providers, CLI commands, and configuration options, refer to the [Zombienet reference page](/reference/tools/zombienet/).


Prerequisites

## Prerequisites

Before starting, ensure you have the following installed:

- [Rust](https://rust-lang.org/tools/install/) and Cargo
- The `wasm32-unknown-unknown` target. Add it with:

    ```bash
    rustup target add wasm32-unknown-unknown
    ```

- [Zombienet](/reference/tools/zombienet/#install-zombienet) - see the reference page for installation instructions
- [Git](https://git-scm.com/downloads)


Set Up the Parachain Template

## Set Up the Parachain Template

Clone and build the Polkadot SDK parachain template:

1. Clone the repository:

    ```bash
    git clone --branch v0.0.5 \
      https://github.com/paritytech/polkadot-sdk-parachain-template \
    && cd polkadot-sdk-parachain-template
    ```

2. Build the parachain node binary:

    ```bash
    cargo build --release
    ```

    This compiles the `parachain-template-node` binary to `target/release/`.

3. Add the binary to your PATH:

    ```bash
    export PATH=$PATH:$(pwd)/target/release
    ```


Download Relay Chain Binaries

## Download Relay Chain Binaries

You need the Polkadot relay chain binaries (`polkadot`, `polkadot-prepare-worker`, and `polkadot-execute-worker`) to run the relay chain validators.

!!! note
    When using the parachain template v0.0.5, ensure you use a compatible relay chain binary. The recommended version is `polkadot-stable2512-2`, which you can download from the [Polkadot SDK releases](https://github.com/paritytech/polkadot-sdk/releases/tag/polkadot-stable2512-2).

Download the binaries for your platform and make them executable:

```bash
mkdir -p bin
curl -L -o bin/polkadot \
  https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot
curl -L -o bin/polkadot-prepare-worker \
  https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-prepare-worker
curl -L -o bin/polkadot-execute-worker \
  https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/polkadot-execute-worker
chmod +x bin/polkadot bin/polkadot-prepare-worker bin/polkadot-execute-worker
```

Alternatively, you can use Zombienet's setup command:

```bash
zombienet setup polkadot polkadot-parachain
```


Generate a Paseo Local Chain Spec

## Generate a Paseo Local Chain Spec

To run a local Paseo-based relay chain, you need to generate a chain spec using `chain-spec-builder` and the Paseo runtime. This creates a local testnet chain spec with development accounts (Alice, Bob) as validators.

1. Download `chain-spec-builder` from the [Polkadot SDK releases](https://github.com/paritytech/polkadot-sdk/releases/tag/polkadot-stable2512-2):

    ```bash
    curl -L -o bin/chain-spec-builder \
      https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-stable2512-2/chain-spec-builder
    chmod +x bin/chain-spec-builder
    ```

2. Download the Paseo runtime Wasm from the [Paseo runtimes releases](https://github.com/paseo-network/runtimes/releases):

    ```bash
    curl -L -o bin/paseo_runtime.compressed.wasm \
      https://github.com/paseo-network/runtimes/releases/download/INSERT_PASEO_RUNTIME_VERSION/paseo_runtime.compressed.wasm
    ```

3. Generate the local testnet chain spec:

    ```bash
    bin/chain-spec-builder -c configs/paseo-local.json \
      create -r bin/paseo_runtime.compressed.wasm named-preset local_testnet
    ```

    This creates a `configs/paseo-local.json` chain spec with the `local_testnet` preset, which includes Alice and Bob as validators.


Configure the Network

## Configure the Network

Create a `network.toml` file that defines the network topology. This configuration sets up a relay chain with two validators and a parachain with one collator, using the Paseo local chain spec generated in the previous step:

```toml title="network.toml"
[settings]
timeout = 1000
provider = "native"

[relaychain]
chain_spec_path = "./configs/paseo-local.json"
default_command = "./bin/polkadot"

[[relaychain.nodes]]
name = "alice"
validator = true
rpc_port = 9944

[[relaychain.nodes]]
name = "bob"
validator = true
rpc_port = 9945

[[parachains]]
id = 1000

[[parachains.collators]]
name = "collator01"
command = "./polkadot-sdk-parachain-template/target/release/parachain-template-node"
rpc_port = 9988
```

For a full reference of all available configuration options, see the [Zombienet configuration reference](/reference/tools/zombienet/#settings).


Spawn the Network

## Spawn the Network

Launch the network using Zombienet:

```bash
zombienet spawn network.toml --provider native
```

Zombienet will:

1. Generate chain specifications.
2. Start relay chain validators (Alice and Bob).
3. Register and start the parachain collator.
4. Display connection endpoints and logs.

Wait for the output to confirm the network has launched. You'll see RPC endpoints for each node.


Verify the Network

## Verify the Network

Once the network is running, verify that the relay chain is producing blocks by querying the RPC endpoint:

```bash
curl -s -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"chain_getHeader","params":[],"id":1}' \
  http://127.0.0.1:9944
```

The response should include a `number` field showing the current block number, confirming the relay chain is producing blocks.

You can also check the parachain collator:

```bash
curl -s -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"chain_getHeader","params":[],"id":1}' \
  http://127.0.0.1:9988
```

To stop the network, press **Ctrl + C** in the terminal where Zombienet is running.


Where to Go Next

## Where to Go Next

-   __Zombienet Reference__

    ---

    Explore the full Zombienet configuration reference, including all settings, relay chain, parachain, and collator options.

    [:octicons-arrow-right-24: Zombienet Reference](/reference/tools/zombienet/)

-  External __Zombienet Support__

    ---

    [Parity Technologies](https://www.parity.io/) has designed and developed this framework, now maintained by the Zombienet team.

    For further support and information, refer to the following contact points:

    [:octicons-arrow-right-24: Zombienet repository](https://github.com/paritytech/zombienet)

    [:octicons-arrow-right-24: Element public channel](https://matrix.to/#/!FWyuEyNvIFygLnWNMh:parity.io?via=parity.io&via=matrix.org&via=web3.foundation)


Introduction

## Introduction

Chat is the Polkadot App's in-App messaging surface — and the canonical example of a feature that composes two Polkadot infrastructure layers. The [Statement Store](/reference/apps/infrastructure/statement-store/) carries the real-time signaling (who is online, who said what, when); the [Bulletin Chain](/reference/apps/infrastructure/bulletin-chain/) stores the encrypted message content that has to outlive the gossip TTL.

A Product can participate in Chat in two ways:

- **Bot**: Your Product registers as a bot in a chat room and publishes and receives typed messages on behalf of the user.
- **Custom message renderer**: Your Product responds to a _reverse subscription_ the App opens against it, providing custom UI for message types you define. The App calls `product_chat_custom_message_render_subscribe` and your Product returns rendered output for each message it owns.

!!! warning "Provisional"
    The internal architecture of Chat in the App (the local message queue, the bot registry, the proof-routing for outgoing statements) is still being finalized. Architecture diagrams and lifecycle specifics will be added once they are confirmed. The narrative below covers the developer-observable surface only.


Room and Bot Registration

## Room and Bot Registration

Two registration concepts gate participation:

- **Room registration**: Identifies a logical conversation. Rooms can be DM-style (two users), simple group chats, or feature-specific (a Product registers a room to deliver messages to its users).
- **Bot registration**: Identifies a Product participating in a room. A Product that wants to publish messages on the user's behalf registers as a bot inside the room.

A `host_chat_create_simple_group` host call (introduced in TrUAPI v0.2) creates a simple group chat from a Product. Full lifecycle specifics — invitation flow, membership semantics, leave behavior — are documented in the TrUAPI Chat method group once that reference lands.


Message Types

## Message Types

Chat uses a typed message system. The canonical types are:

- **`Text`**: Plain-text messages
- **`RichText`**: Formatted messages, such as links, mentions, and basic formatting
- **`Actions`**: Actionable messages users can interact with directly in-App
- **`File`**: File attachments backed by Bulletin Chain content addresses
- **`Reaction`**: Reactions attached to an existing message
- **`Custom`**: Product-defined message types rendered by the originating Product via custom rendering


Custom Message Rendering

## Custom Message Rendering

Custom rendering inverts the usual Host→Product call direction. When the App needs to display a custom message type your Product defined, it opens a subscription against your Product and asks it to render. The host call is `product_chat_custom_message_render_subscribe`. Your Product receives the message payload, produces UI output, and returns it for the App to display in the chat thread.

This is how Chat stays extensible without baking every possible message type into the App itself.


Message Queueing

## Message Queueing

Messages a Product sends before it has registered as a bot in a room are queued automatically. The Product does not need to track registration state to avoid losing messages. Submit the outgoing chat message on the user's behalf, and the queue resolves once registration completes.


## Composition: Statement Store + Bulletin Chain

Chat is the model composition for Product developers building real-time-with-durable-content features. It pairs two infrastructure layers documented separately in this reference:

- **Statement Store**: Carries signaling, such as presence, typing indicators, message-arrival notifications, and room-membership events. Statements are gossiped, allowance-gated, and short-lived. For mechanics, see [Publish and Subscribe to Off-Chain Data](/apps/build/pub-sub-off-chain-data/).
- **Bulletin Chain**: Stores the encrypted message content readers fetch by CID after they see the statement that points to it. See [Store Data on Chain](/apps/build/store-data-on-chain/).

The composition is general. Many Products with chat-like or activity-feed semantics will use the same split: Statement Store for the live signal, Bulletin Chain for content that needs to survive longer than a session.


Where to Go Next

## Where to Go Next

- Guide **Exchange Ephemeral Messages**

    ---

    The underlying Statement Store layer that Chat is built on — useful when you need pub/sub semantics without the full Chat surface.

    [:octicons-arrow-right-24: Get Started](/apps/build/pub-sub-off-chain-data/)


Introduction

## Introduction

Coinage is the Polkadot App's peer-to-peer payment feature. From a user perspective, it is a way to send and receive funds App-to-App with the privacy and personhood properties of the underlying `pallet-coinage` primitive. From a Product developer perspective, Coinage is one of two payment surfaces a Product can route through:

- **Coinage**: Personhood-gated, peer-to-peer payments where the App is the originating UI on both sides.
- **Standard `Balances` transfer surface**: General transfers and merchant-style flows.

!!! info "Underlying asset: HOLLAR before pUSD"
    Coinage is backed by a stablecoin on Asset Hub (configured via `UnderlyingAssetId`). The configured asset is HOLLAR, and the system is designed to migrate to pUSD when pUSD lands. The asset is an implementation detail of the recycler, onboard, and offboard layer, not a gate on the developer-facing surface. The narrative below is stable across the HOLLAR-to-pUSD transition.

!!! warning "Provisional"
    The App's send/receive UX details and the recommended Product-side integration pattern are still being finalized by Parity. This page documents the conceptual model; per-flow specifics will be added once the surface is confirmed.


Conceptual Model

## Conceptual Model

Three properties differentiate Coinage from a plain `Balances.transfer_keep_alive`:

- **Personhood-aware**: Coinage payments can be gated on PoP, sending to "any real person on the network" rather than to a specific account address. The recipient's address need not be known to the sender.
- **Privacy-preserving by default**: Coinage routes through the alias and Ring-VRF surface where appropriate, so the on-chain trail does not link the sender's account to the recipient's account in the same way a public transfer does.
- **App-native UX**: The send and receive flows are rendered in the Polkadot App on both sides, with the user approving the transaction on their phone.

For the pallet-level storage model, privacy primitives, and dispatch surface, see the [pallet-coinage reference](/reference/apps/infrastructure/pop/pallet-coinage/).


Where to Go Next

## Where to Go Next

- Learn **Proof of Personhood**

    ---

    The personhood surface Coinage gates on, including Ring-VRF aliases and the Full and Lite tier model.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-app/pop/)


Introduction

## Introduction

The Polkadot App is the mobile wallet that holds every Polkadot Product user's private key. It runs on the user's phone and is the only place in the [Polkadot Triangle](/reference/glossary/#triangle) where the key lives — [Polkadot Desktop](/reference/apps/hosts/polkadot-desktop/) and [Polkadot Web](/reference/apps/hosts/polkadot-web/) never touch it.

From a Product developer's perspective, the App plays three roles no other Host plays:

- **It holds the user's private key**: The key lives on the device, in the App's on-device storage. No other Host and no Product ever sees the key.
- **It signs every transaction**: Every signing request that originates from a Product — whether the Product runs inside Desktop or Web — is routed back to the App for the user to approve on their phone.
- **It runs Proof of Personhood**: [Proof of Personhood](pop/) is the privacy-preserving "real human" check that unlocks alias-gated features. The verification happens inside the App; the Product never sees who the user is.

The App also exposes two groups of features that Products integrate against:

- **Host-level features**: [Chat](chat/) is the messaging surface that pairs the [Statement Store](/reference/apps/infrastructure/statement-store/) and [Bulletin Chain](/reference/apps/infrastructure/bulletin-chain/). [Sign In with Polkadot](sign-in/) is the cross-Host authentication handshake initiated by Desktop or Web.
- **Payment-side features**: [Coinage](coinage/) is the peer-to-peer payment flow. [Pocket](pocket/) is the App-side recipient counterpart to Desktop's Pocket send flow.


How It Works

## How It Works

From a Product developer's perspective, three things matter about how the App runs:

- **Session pairing**: Pairing establishes a session, not a copy of the key. When the user pairs the App with Polkadot Desktop, the App returns a derived _session public key_ (see [Install Desktop and Pair](/apps/get-started/)). Desktop stores that public key so it can identify the user and construct per-Product sub-accounts. The private key never leaves the phone.
- **Asynchronous signing**: Signing is asynchronous because it happens on a separate device. When a Product calls `signAndSubmit`, Desktop renders a signing modal locally, but the actual signature happens on the user's phone after they approve in the App. Build UI that tolerates the round-trip; see the [Sign and Submit Transactions](/apps/build/sign-and-submit/) guide for the patterns.
- **PoP verification**: The App is the only place PoP runs. A Product can gate features on Proof of Personhood (alias-based or general personhood), but the verification flow itself happens in the App. The Product never sees the underlying biometric or the user's identity record, only the alias or alias-proof it requested through TrUAPI.


Where the App Fits in the SDK Surface

## Where the App Fits in the SDK Surface

The App is both a consumer and a producer of [TrUAPI](/reference/apps/protocol/truapi/) methods:

- The App consumes Host API methods for chain interaction and storage when its own features (Chat, Coinage, PoP) need to talk to the People Chain, Statement Store, or Bulletin Chain.
- The App produces the signing primitive every other Host's `signAndSubmit` ultimately resolves against. Methods Products call through the SDK — like `createProof` and `getAnonymousAlias` — complete in the App on the user's phone.


Where to Go Next

## Where to Go Next

- Learn **Chat**

    ---

    The in-App messaging surface: room/bot registration, the typed-message system, and how Chat composes the Statement Store with the Bulletin Chain.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-app/chat/)

- Learn **Proof of Personhood**

    ---

    Where the App's PoP verification happens, what Ring-VRF aliases are, and the PoP Full vs PoP Lite tier model.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-app/pop/)

- Learn **Sign In with Polkadot**

    ---

    The Host-level handshake the App resolves when Desktop or Web wants to authenticate a session against the paired identity.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-app/sign-in/)

- Guide **Accounts and Signing**

    ---

    The how-to guide for wiring a Product up to a paired account and submitting signed transactions through the SDK.

    [:octicons-arrow-right-24: Get Started](/apps/build/sign-and-submit/)


Introduction

## Introduction

Pocket is Polkadot Desktop's send flow, a feature that lets a Desktop user package a payment or asset transfer and route it to a recipient identified by their App identity. This page documents the App-side counterpart, covering how the Polkadot App receives a Pocket transfer initiated from Desktop and the user-visible flow inside the App.

The Desktop sender-side view is documented at [Pocket Send Flow in Polkadot Desktop](/reference/apps/hosts/polkadot-desktop/pocket/). This page is the receiver's view.

!!! warning "Provisional"
    The recipient-side flow inside the App is still being defined. The exact handshake between Desktop's Pocket dispatch and the App's incoming-Pocket UI, the notification surface, and the accept and decline mechanics are still being finalized. This page captures the conceptual model only; the detailed flow and any sequence diagram will be added once they are confirmed.


Conceptual Model

## Conceptual Model

From the recipient's perspective:

- A Desktop user (the sender) initiates a Pocket transfer addressed to the recipient's paired App identity.
- The transfer arrives at the App as a pending incoming item, surfaced to the recipient in the App's UI.
- The recipient reviews and either accepts or declines the transfer.
- On accept, the transfer settles to the recipient's account. On decline (or timeout), it does not.

The distinguishing property of Pocket relative to a plain `Balances.transfer_keep_alive` is the accept step. A standard transfer settles unilaterally, so the recipient cannot reject it. Pocket's two-sided flow gives the recipient a chance to refuse, which matters when the transfer is contextually meaningful (an in-band payment between users who know each other) rather than a balance change.

The Product-developer surface that bridges to Pocket is part of the open question above, including whether a Product can initiate a Pocket transfer programmatically, or only the Desktop UI can. Once resolved, the integration pattern will be documented here.


Where to Go Next

## Where to Go Next

- Reference **Payment Method Group**

    ---

    The TrUAPI method group covering `Balances.transfer_keep_alive` and the Pocket peer-to-peer payment flow available to Products.

    [:octicons-arrow-right-24: View Reference](/reference/apps/protocol/truapi/payment/)


Introduction

## Introduction

Proof of Personhood (PoP) is Polkadot's privacy-preserving "real human" check. The Polkadot App is the only place PoP verification happens. When a user completes PoP, they do it on their phone, in the App, once. From then on, a single proof unlocks the developer-facing surface area that gates on personhood, including the TestNet faucet, short `.dot` names, and any alias-gated feature inside a Polkadot Product.

The App's role is narrow but load-bearing. It runs the verification flow, registers the result on the People Chain, and produces Ring-VRF aliases on demand for Products that ask for one through the Host API. Products see the aliases, not the underlying identity.

!!! warning "Provisional"
    The exact PoP verification flow inside the App (the video or biometric interaction, the on-device key-derivation path, peer-attestation cadence for ongoing membership) is still being finalized. Lifecycle diagrams and the precise list of pallets a PoP submission touches will be added once they are confirmed.


Ring-VRF Aliases

## Ring-VRF Aliases

A Ring-VRF alias is a context-specific pseudonym derived from the user's PoP-anchored identity. Aliases have three properties Product developers depend on:

- **Anchored in personhood**: An alias only exists for users who have completed PoP. A valid Ring-VRF proof over an alias is also a valid proof of personhood for the user behind it.
- **Scoped to a context**: The default context is the requesting Product's `.dot` domain. The same user produces a consistent alias each time they return to your Product, but a different alias for any other Product.
- **Unlinkable across domains**: Two Products cannot correlate that they share a user unless the user explicitly grants a cross-domain alias.

The App is the side of the system that holds the alias-producing material. A Product's call to `app.wallet.getAnonymousAlias()` resolves to the App on the user's phone, which signs the alias request.


PoP Tiers: Full vs. Lite

## PoP Tiers: Full vs. Lite

The People Chain recognizes two personhood tiers, both registered in the App:

- **PoP Full**: Cryptographically proven personhood. The user completes the full verification flow in the App: a biometric scan plus ongoing peer-attestation via recurring online sessions. The user's key joins the active membership ring on the People Chain. PoP Full holders can generate zero-knowledge proofs that they are a real person without revealing which one.
- **PoP Lite**: Third-party attestation. An attester authorized by governance submits an on-chain attestation that an account belongs to a real user. The account is then registered against a separate `lite-people` ring on the People Chain alongside a communication identifier and a username. Lite supply is bounded by governance, which is the spam-resistance mechanism. Lite holders can produce lite-ring Ring-VRF proofs but do not yet hold membership in the full personhood ring.

Lite is the on-ramp; Full is the destination. The dotNS registrar uses tier to gate which name lengths a user can register for free. See [dotNS PopRules Pricing](/reference/apps/infrastructure/dotns/poprules-pricing/) for the full PopRules tier table.


What This Unlocks for a Product

## What This Unlocks for a Product

A Product running inside Polkadot Desktop can reach the App's PoP surface in two ways via `@parity/product-sdk`:

- **Alias-gated features**: Call `getAnonymousAlias()` for the user's per-Product alias, then `createProof(challenge)` to prove control of that alias against a challenge. Gate features on the proof rather than on the user's account address.
- **On-chain personhood gates**: Dispatch calls under the `under_alias` runtime origin against PoP pallets (e.g. `pallet-people`). The runtime verifies the underlying Ring-VRF proof and the called pallet's view of the caller is the alias, not the underlying account.


Where to Go Next

## Where to Go Next

- Guide **Verify Your Identity**

    ---

    The setup step where the developer completes PoP, plus the PopRules tier table.

    [:octicons-arrow-right-24: Get Started](/reference/apps/infrastructure/dotns/poprules-pricing/)


Introduction

## Introduction

Sign In with Polkadot is the handshake that lets a user authenticate to [Polkadot Desktop](/reference/apps/hosts/polkadot-desktop/) or [Polkadot Web](/reference/apps/hosts/polkadot-web/) using just their paired Polkadot App on their phone — no password, no extension, no copied keys. Desktop or Web initiates the handshake, the user approves on their phone, and the session is established.

This feature lives in the App's reference because the App is the side that _resolves_ the handshake, while Desktop or Web initiates it and the App signs.

!!! info "Products do not invoke this directly"
    For a Product running inside Polkadot Desktop, sign-in is complete before your code loads. The user paired their App with Desktop during set-up, and Desktop established a session at that point. Sign In with Polkadot is the host-level primitive sitting underneath your Product's session, not a function you call. The Product-side surfaces you reach for instead are `getAnonymousAlias`, `createProof`, and `under_alias`.


How the Handshake Works

## How the Handshake Works

The handshake uses the Statement Store as its rendezvous and the paired session as its trust anchor:

1. The Host (Desktop or Web) posts a sign-in request statement to the Statement Store, addressed to the paired App.
2. The App, subscribed to its own inbox, picks up the request from gossip.
3. The user sees a sign-in prompt in the App and approves or rejects on their phone.
4. On approval, the App returns a signed payload back over the encrypted channel.
5. The Host validates the payload and binds the session.

The handshake is one-time per session; subsequent Product calls inside the session reuse the binding rather than re-prompting the user.

!!! warning "Provisional"
    The exact wire format of the request and response statements, the topic-filter scoping at the Statement Store boundary, the timeout/rejection failure modes, and any per-session session-key derivation steps are still being finalized. This page documents the developer-observable shape only; the protocol-level details will be added once they are confirmed.


## Where Sign In Differs from Per-Transaction Signing

Both Sign In and `signAndSubmit` route to the App and require user approval, but they serve different jobs:

- **Sign In**: Establishes session identity. It runs once when the user lands in a Host, and reruns if the session expires. It tells the Host who the user is so the Host can construct per-Product sub-accounts and surface Product UI.
- **`signAndSubmit`**: Signs a _specific transaction_ with a Product's account. Every transaction requires its own approval in the App; there is no "auto-sign for the rest of this session" mode. This is intentional. See the [Sign and Submit Transactions](/apps/build/sign-and-submit/) reference for the rationale.

A Product that wants to gate a feature on "is this the real user paired with this session?" can rely on the session being authenticated already and instead reach for alias-based or `under_alias` patterns. Re-running Sign In is not the right primitive for that.


Where to Go Next

## Where to Go Next

- Guide **Accounts and Signing**

    ---

    How `signAndSubmit` works for per-transaction signing: the surface a Product actually calls, with the round-trip-to-phone pattern.

    [:octicons-arrow-right-24: Get Started](/apps/build/sign-and-submit/)

- Guide **Install and Pair**

    ---

    The set-up step that establishes the initial paired session between Desktop and the App, before Sign In with Polkadot can resolve.

    [:octicons-arrow-right-24: Get Started](/apps/get-started/)


Introduction

## Introduction

Polkadot Desktop is the workstation app where developers and users actually run Polkadot Products. Think of it as a specialized browser — but instead of typing a URL, the user types a `.dot` name, and instead of rendering arbitrary web pages, Desktop fetches a published Product bundle from decentralized cloud storage and runs it inside a sandbox where signing, storage, and outbound network requests are all mediated by the Host.

Desktop never holds the user's private key. The key lives on the paired [Polkadot App](/reference/apps/hosts/polkadot-app/) on the user's phone. Desktop holds only a derived session public key, enough to identify the user and construct per-Product accounts. Every signing operation routes back to the App for user approval.

This section documents Desktop's developer-facing surface: how a Product runs inside it, how signing works end to end, how the permissions model enforces sandbox boundaries, and how Desktop exposes [Statement Store](/reference/apps/hosts/polkadot-desktop/statement-store/), [Preimage](/reference/apps/hosts/polkadot-desktop/preimage/), and [Pocket](/reference/apps/hosts/polkadot-desktop/pocket/) features to Products and users.


How It Works

## How It Works

From a Product developer's perspective, three properties define how Desktop behaves:

- **`.dot`-name addressing**: A published Product is addressed by its `.dot` name. Desktop resolves the name through [dotNS](/reference/apps/infrastructure/dotns/), fetches the published bundle, and loads it in the sandbox. During development, Desktop's address bar accepts `localhost:PORT` as a whitelisted bypass so you can iterate against a local dev server. See [Set Up Your Project](/apps/build/#set-up-your-project).
- **Sandboxed runtime**: A Product runs inside a sandboxed container, not an arbitrary browser tab. It cannot make outbound network requests, access local storage, or sign transactions except through the Host API. See [Permissions](/reference/apps/hosts/polkadot-desktop/permissions/).
- **Mediated signing**: Every transaction a Product submits, whether dispatched through the Product SDK or directly through TrUAPI, goes through Desktop's signing modal and on to the paired App. There is no path for a Product to sign without user approval. See [Signing](/reference/apps/hosts/polkadot-desktop/signing/).


Where Desktop Fits in the SDK Surface

## Where Desktop Fits in the SDK Surface

Desktop both consumes and produces the [TrUAPI](/reference/apps/protocol/truapi/) surface:

- It consumes Host API methods from the App (signing, PoP proofs) and from infrastructure layers (Bulletin Chain, Statement Store, dotNS) when its own features (Pocket, Preimage, Statement Store mediation) need to talk to those layers.
- It produces the surface Products call into: chain interaction, local storage, account management, signing, statement submission, preimage submission, and so on. The Product SDK (`@parity/product-sdk` and friends) is a typed wrapper over that surface.


Where to Go Next

## Where to Go Next

- Learn **Signing**

    ---

    The full mediated-signing flow: how a `signAndSubmit` call travels from a Product through Desktop to the paired App and back, including the `ChainSubmit` permission model and the timeout or rejection failure modes you need to handle.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/signing/)

- Learn **Permissions**

    ---

    What a Product can do inside the Desktop sandbox is gated by declared permissions. Reference for the permission types, the manifest declaration model, and how denial is surfaced.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/permissions/)

- Learn **Statement Store via Host API**

    ---

    How a Product publishes to and subscribes from the Statement Store through Desktop's mediated surface.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/statement-store/)

- Learn **Preimage**

    ---

    Preimage submission through Desktop, including the Statement-vs-Preimage distinction for off-chain content.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/preimage/)

- Learn **Pocket**

    ---

    Desktop's peer-to-peer send flow. Pair this with the [App-side recipient view](/reference/apps/hosts/polkadot-app/pocket/) for the full round trip.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/pocket/)


Introduction

## Introduction

A Polkadot Product runs inside a Host-governed sandbox; it cannot reach the network, the chain, the user's microphone, or any other resource without an explicit permission. Permissions are declared in your Product's manifest and enforced by Polkadot Desktop at the Host API boundary. This page is the reference for what each permission means, how the declaration works, and what happens when the user denies a permission.


Capability Types

## Capability Types

!!! warning "Provisional"
    The exact permission taxonomy on the active Desktop build is still being finalized. The capabilities below are the ones a Product developer encounters in this surface; the complete reference (every permission name, its scope, and its enforcement point) will be added once the taxonomy is confirmed.

The three permissions most Products will encounter:

- **`Microphone`**: Grants the Product audio-input access. Standard browser-style media permission.
- **`ExternalRequest`**: Grants the Product the ability to make outbound HTTP requests to URLs that match a declared pattern. The pattern is part of the declaration, not requested at runtime. Your manifest says "my Product needs to talk to `https://api.example.com/v1/*`", and Desktop blocks any request the Product makes to URLs outside that pattern, even if the user granted the permission. Pattern scoping is the boundary, not user consent.
- **`ChainSubmit`**: Grants the Product the ability to request a transaction signing. Despite the name, this does not authorize Desktop to sign for the user; every individual transaction still requires a per-request approval on the paired App. See [Signing](/reference/apps/hosts/polkadot-desktop/signing/) for the full model.


Declaring Permissions

## Declaring Permissions

Permissions are declared in your Product's manifest. The declaration is static: your Product cannot dynamically request new permission types at runtime. If a future feature needs a permission you did not declare, you publish a new bundle with an updated manifest and the user re-approves on first open of the new version.

On the first time a user opens your Product, Desktop presents the declared permissions as prompts. The user grants or denies each one. Their decision is persisted; subsequent opens of the same Product do not re-prompt unless the manifest changes.


Denial UX

## Denial UX

When a permission is denied:

- **Capability response**: The capability returns `PermissionDenied` every time the Product attempts to use it. Your Product code should handle this error explicitly: show the user why the feature cannot proceed and what they can do about it.
- **Desktop prompt**: Desktop surfaces an "Enable access in App Settings" prompt alongside your Product's error UI, pointing the user to the settings screen where they can flip the permission back on.

A Product that crashes on `PermissionDenied` (instead of falling through to a graceful "this feature needs X, enable it in Settings" state) is failing this contract. The denial path is part of the normal control flow, not an exception.


Where to Go Next

## Where to Go Next

- Learn **Signing**

    ---

    The signing model that the `ChainSubmit` permission gates: what the permission grants and what it does not grant.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/signing/)


Introduction

## Introduction

Pocket is Polkadot Desktop's peer-to-peer send flow. From the sender's side, Pocket is a UI in Desktop that lets a user package a payment or asset transfer addressed to another real person. The recipient is identified by their personhood identity in Polkadot's network, not necessarily by their account address. The receiver sees the incoming transfer in their paired Polkadot App and can accept or decline.

This page is the sender's view. The App-side receiver view is documented at [Pocket Recipient](/reference/apps/hosts/polkadot-app/pocket/).

!!! warning "Provisional"
    Pocket's exact developer surface is still being finalized, including whether a Product can initiate a Pocket transfer programmatically, the supported asset types and amounts, and the cross-chain routing details. This page captures the conceptual model and the user-visible flow only; the API-level reference and any sequence diagram will be added once the surface is confirmed.


Conceptual Model

## Conceptual Model

Three properties distinguish Pocket from a plain `Balances.transfer_keep_alive`:

- **Personhood-aware addressing**: Instead of (or alongside) an SS58 account address, the sender can address a transfer to a recipient identified by their personhood. The sender does not need to know the recipient's account up front. The recipient resolves to the right address inside their App.
- **Two-sided confirmation**: A standard transfer settles unilaterally, so the recipient cannot refuse it. A Pocket transfer arrives at the recipient's App as a pending item; the recipient explicitly accepts or declines. On accept, it settles; on decline (or timeout), it does not.
- **Split UX**: The sender UX lives in Desktop, and the receiver UX lives in the App. The two sides of the flow are deliberately on different devices: the sender packages the transfer at a desk, the receiver approves on their phone with the same per-transaction approval model that gates every signed action.


What the User Sees

## What the User Sees

From the sender's side in Desktop:

1. The sender opens Pocket from Desktop's UI and selects a recipient (by personhood identifier, contact name, or address).
2. The sender enters the amount and any optional memo.
3. The sender confirms in Desktop, which triggers the standard signing modal and routes a per-transaction approval to the sender's own paired App.
4. After the sender approves on their App, Desktop dispatches the Pocket transfer.
5. The recipient sees an incoming Pocket item in their App and either accepts or declines (see [Pocket Recipient](/reference/apps/hosts/polkadot-app/pocket/)).

For everyday merchant-style payments where the payer is responsive in a browser or a Product, and the merchant wants the funds settled, use the standard `Balances.transfer_keep_alive` flow. Pocket is the right tool when the recipient identity matters more than the recipient address, and when accept-decline semantics fit the interaction.


Where to Go Next

## Where to Go Next

- Learn **Pocket Recipient (App side)**

    ---

    The other half of the round trip: what a Pocket transfer looks like to the recipient, including the accept and decline mechanics.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-app/pocket/)


Preimage Submission via Polkadot Desktop

Introduction

## Introduction

A preimage is the original content of a hash that has been referenced on-chain. The classic example: a governance referendum points at the hash of a proposal, and the preimage is the proposal's actual bytes. The chain stores only the hash to keep block size down; the bytes have to be made available off-chain before the referenced operation can execute. Polkadot Desktop exposes a preimage submission surface that Products can call through the Host API.

This page is the reference for that surface. The [Bulletin Chain reference](/reference/apps/infrastructure/bulletin-chain/) documents the storage layer that preimage submission often interacts with.


What the Preimage Surface Does

## What the Preimage Surface Does

!!! warning "Provisional"
    The exact set of host calls Desktop exposes for preimage submission, the supported size limits, and the lifecycle hooks for preimage availability are still being finalized. This page documents the conceptual model; the current method-group frame is in the [Preimage TrUAPI reference](/reference/apps/protocol/truapi/preimage/).

Conceptually, the preimage surface lets a Product:

- **Submit a preimage**: Submit off-chain content for an on-chain operation that will reference it by hash. The Host signs the submission with the user's account, subject to the usual `ChainSubmit` permission and per-transaction approval on the paired App.
- **Provide hosting**: Host the preimage so it is reachable when the on-chain operation executes. In practice, this often means writing the bytes to the Bulletin Chain and recording the resulting CID, but the exact storage path is part of the surface still being finalized.


Statement vs Preimage

## Statement vs Preimage

Statements and preimages are off-chain content addressed by hash, and both surfaces look superficially similar: your Product submits some bytes, gets a hash back, and the on-chain world refers to that hash later. The two are not interchangeable, though, and choosing the wrong one is a real failure mode.

| Property               | Statement (Statement Store)                                                             | Preimage                                                                                |
| :--------------------: | :-------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------: |
| What it is for         | Real-time signaling between users: chat, presence, typing indicators, multiplayer state | Off-chain content that an on-chain operation must dereference: governance proposals, scheduled call payloads |
| Lifetime               | Short TTL (default 30 seconds, gossip-distributed)                                      | Lives until the referencing on-chain operation has completed (or until explicitly cleaned up)                |
| Availability guarantee | Best-effort gossip; can be missed entirely if no peer was listening on the topic        | Must be available for the referencing operation to execute; storage is durable                               |
| Validation gate        | Allowance-gated by `pallet-statement-store` (`max_count` and `max_size` per account)    | Gated by the same permission and signing requirements as any other on-chain submission                       |
| When to use            | Anything users observe in real time and accept losing if the network dropped it         | Anything an on-chain operation needs to read back later                                                      |

A useful rule of thumb: if your Product would still be correct after the bytes expire from network gossip, it is a statement. If the bytes need to be there in 30 minutes for a referendum to execute, it is a preimage.


Where to Go Next

## Where to Go Next

- Learn **Statement Store via Host API**

    ---

    The other half of the Statement-vs-Preimage decision: when your content belongs on the gossip-distributed signaling layer instead of as an on-chain-referenced preimage.

    [:octicons-arrow-right-24: Reference](/reference/apps/hosts/polkadot-desktop/statement-store/)

- Guide **Store Data on Chain**

    ---

    The Product-side how-to for Bulletin Chain storage, which the preimage surface often delegates to underneath.

    [:octicons-arrow-right-24: Get Started](/apps/build/store-data-on-chain/)


Introduction

## Introduction

Polkadot Desktop is the mediator in the signing model; the Polkadot App on the user's phone is the signer. Desktop never has the private key, and Desktop cannot sign anything on its own. Every transaction a Product submits, no matter how small, completes only after the user explicitly approves it on their phone.

This page is the reference for how that mediation actually works. The Product-side how-to lives at [Sign and Submit Transactions](/apps/build/sign-and-submit/); this page documents the part of the flow Desktop owns.


The Three-Actor Model

## The Three-Actor Model

A signing operation always involves three actors:

- **Product**: Builds the transaction and calls `signAndSubmit` through `@parity/product-sdk` or directly through TrUAPI.
- **Polkadot Desktop**: Receives the encoded payload, renders the signing modal showing the user what they are about to sign, and forwards the request to the paired App.
- **Polkadot App**: Receives the request, prompts the user on their phone, signs on approval, and returns the signature.

```mermaid
sequenceDiagram
    participant Product
    participant Desktop as Polkadot Desktop
    participant App as Polkadot App
    Product->>Desktop: signAndSubmit(payload)
    Desktop->>Desktop: render signing modal
    Desktop->>App: signing request (via paired session)
    App->>App: user approves or rejects
    App->>Desktop: signature or rejection
    Desktop->>Desktop: broadcast signed extrinsic
    Desktop->>Product: result
```

Your Product code awaits a single promise for this entire round trip.


## `ChainSubmit`: Ability to Request ≠ Auto-Sign

The most common point of confusion in the signing model is what the `ChainSubmit` permission actually grants. It does not authorize Desktop to sign on the user's behalf. It authorizes your Product to _request_ that a signing prompt be presented to the user.

The distinction matters because it determines what UI you need to build:

- Without `ChainSubmit`, your Product cannot even ask. Desktop blocks the dispatch at the Host API boundary and returns `PermissionDenied`. The user has to enable the permission in App Settings before your Product can request signing.
- With `ChainSubmit`, your Product can ask, and every individual transaction triggers a fresh per-request prompt on the paired App that the user must approve. There is no "auto-sign for the rest of this session" mode. Two consecutive transactions produce two prompts; ten produce ten.

This is intentional. The key only ever leaves the App as a signature over a specific payload the user reviewed, never as a blanket consent.


What the User Sees

## What the User Sees

When your Product calls `signAndSubmit`, Desktop drives the following sequence:

1. Desktop displays a signing modal. The modal shows the network name, transaction type (for example, Balances Transfer), the call arguments, and an estimated fee. A **More details** toggle reveals the encoded call data for advanced users.
2. The user clicks **Continue to Sign**. Desktop transitions to a waiting screen with the message **Open the Polkadot App on your device, then tap Sign or Reject** and a countdown timer indicating the remaining session validity.
3. The user approves or rejects on the App. The App returns the signature or a rejection.
4. Desktop receives the result. If approved, Desktop broadcasts the signed extrinsic and returns the outcome to your Product. If rejected, or if the timer reached zero before the user responded, Desktop returns an error.