diff --git a/.github/workflows/build-publish-images.yml b/.github/workflows/build-publish-images.yml
index 874b5d37469cde33f2b92f8cec0a4d61c57b7ffb..deb3b3df5ff2f6d0c13e038cb6291e958a9c879a 100644
--- a/.github/workflows/build-publish-images.yml
+++ b/.github/workflows/build-publish-images.yml
@@ -53,7 +53,7 @@ jobs:
       - name: pack artifacts
         run: |
           mkdir -p ./artifacts
-          VERSION="${{ needs.preflight.outputs.SOURCE_REF_NAME }}" # will be tag or branch name
+          VERSION="${{ needs.preflight.outputs.SOURCE_REF_SLUG }}" # will be tag or branch name
           mv ./target/testnet/polkadot ./artifacts/.
           mv ./target/testnet/polkadot-prepare-worker ./artifacts/.
           mv ./target/testnet/polkadot-execute-worker ./artifacts/.
@@ -62,7 +62,7 @@ jobs:
           sha256sum polkadot | tee polkadot.sha256
           shasum -c polkadot.sha256
           cd ../
-          EXTRATAG="${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}"
+          EXTRATAG="${{ needs.preflight.outputs.SOURCE_REF_SLUG }}-${COMMIT_SHA}"
           echo "Polkadot version = ${VERSION} (EXTRATAG = ${EXTRATAG})"
           echo -n ${VERSION} > ./artifacts/VERSION
           echo -n ${EXTRATAG} > ./artifacts/EXTRATAG
@@ -77,7 +77,7 @@ jobs:
       - name: upload artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
           path: artifacts.tar
           retention-days: 1
 
@@ -103,7 +103,7 @@ jobs:
           mkdir -p ./artifacts
           mv ./target/release/polkadot-parachain ./artifacts/.
           echo "___The VERSION is either a tag name or the curent branch if triggered not by a tag___"
-          echo ${{ needs.preflight.outputs.SOURCE_REF_NAME }} | tee ./artifacts/VERSION
+          echo ${{ needs.preflight.outputs.SOURCE_REF_SLUG }} | tee ./artifacts/VERSION
 
       - name: tar
         run: tar -cvf artifacts.tar artifacts
@@ -111,7 +111,7 @@ jobs:
       - name: upload artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
           path: artifacts.tar
           retention-days: 1
 
@@ -147,7 +147,7 @@ jobs:
       - name: upload artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
           path: artifacts.tar
           retention-days: 1
 
@@ -172,8 +172,8 @@ jobs:
           mkdir -p ./artifacts
           mv ./target/testnet/adder-collator ./artifacts/.
           mv ./target/testnet/undying-collator ./artifacts/.
-          echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}" > ./artifacts/VERSION
-          echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG
+          echo -n "${{ needs.preflight.outputs.SOURCE_REF_SLUG }}" > ./artifacts/VERSION
+          echo -n "${{ needs.preflight.outputs.SOURCE_REF_SLUG }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG
           echo "adder-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))"
           echo "undying-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))"
           cp -r ./docker/* ./artifacts
@@ -184,7 +184,7 @@ jobs:
       - name: upload artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
           path: artifacts.tar
           retention-days: 1
 
@@ -209,8 +209,8 @@ jobs:
           mv ./target/testnet/malus ./artifacts/.
           mv ./target/testnet/polkadot-execute-worker ./artifacts/.
           mv ./target/testnet/polkadot-prepare-worker ./artifacts/.
-          echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}" > ./artifacts/VERSION
-          echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG
+          echo -n "${{ needs.preflight.outputs.SOURCE_REF_SLUG }}" > ./artifacts/VERSION
+          echo -n "${{ needs.preflight.outputs.SOURCE_REF_SLUG }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG
           echo "polkadot-test-malus = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))"
           cp -r ./docker/* ./artifacts
 
@@ -220,7 +220,7 @@ jobs:
       - name: upload artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
           path: artifacts.tar
           retention-days: 1
 
@@ -246,6 +246,7 @@ jobs:
           WASM_BUILD_NO_COLOR=1 forklift cargo build --locked --release -p staging-node-cli
           ls -la target/release/
       - name: pack artifacts
+        shell: bash
         run: |
           mv target/release/substrate-node ./artifacts/substrate/substrate
           echo -n "Substrate version = "
@@ -264,7 +265,7 @@ jobs:
       - name: upload artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
           path: artifacts.tar
           retention-days: 1
 
@@ -294,7 +295,7 @@ jobs:
       - name: upload artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
           path: artifacts.tar
           retention-days: 1
 
@@ -313,7 +314,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: build-test-parachain-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: build-test-parachain-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
 
       - name: tar
         run: tar -xvf artifacts.tar
@@ -337,7 +338,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
 
       - name: tar
         run: tar -xvf artifacts.tar
@@ -361,7 +362,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: build-test-collators-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: build-test-collators-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
 
       - name: tar
         run: tar -xvf artifacts.tar
@@ -385,7 +386,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: build-malus-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: build-malus-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
 
       - name: tar
         run: tar -xvf artifacts.tar
@@ -409,7 +410,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: build-linux-substrate-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: build-linux-substrate-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
 
       - name: tar
         run: tar -xvf artifacts.tar
@@ -441,7 +442,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
       - name: tar
         run: |
           tar -xvf artifacts.tar
@@ -449,7 +450,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
       - name: tar
         run: |
           tar -xvf artifacts.tar
@@ -457,7 +458,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: prepare-bridges-zombienet-artifacts-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: prepare-bridges-zombienet-artifacts-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
       - name: tar
         run: |
           tar -xvf artifacts.tar
@@ -482,7 +483,7 @@ jobs:
 
       - uses: actions/download-artifact@v4.1.8
         with:
-          name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+          name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
 
       - name: tar
         run: tar -xvf artifacts.tar
diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml
index 0da3e54ef60b88420a0bb9762bc9fff2e88be4af..43c70d6abc78b0aca6fe0738617632a9b33b809a 100644
--- a/.github/workflows/check-semver.yml
+++ b/.github/workflows/check-semver.yml
@@ -81,7 +81,7 @@ jobs:
       - name: install parity-publish
         if: ${{ !contains(github.event.pull_request.labels.*.name, 'R0-silent') }}
         # Set the target dir to cache the build.
-        run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.10.3 --locked -q
+        run: CARGO_TARGET_DIR=./target/ cargo install parity-publish@0.10.4 --locked -q
 
       - name: check semver
         if: ${{ !contains(github.event.pull_request.labels.*.name, 'R0-silent') }}
diff --git a/.github/workflows/checks-quick.yml b/.github/workflows/checks-quick.yml
index 4c26b85a6303c88ba49d7c9d5be173403887d7f6..1a8813833deff8cccb412d832a38de470e0dc933 100644
--- a/.github/workflows/checks-quick.yml
+++ b/.github/workflows/checks-quick.yml
@@ -138,7 +138,7 @@ jobs:
           # Fixes "detected dubious ownership" error in the ci
           git config --global --add safe.directory '*'
           python3 scripts/generate-umbrella.py --sdk . --version 0.1.0
-          cargo +nightly fmt --all
+          cargo +nightly fmt -p polkadot-sdk
 
           if [ -n "$(git status --porcelain)" ]; then
             cat <<EOF
diff --git a/.github/workflows/publish-check-compile.yml b/.github/workflows/publish-check-compile.yml
index ce1b2cb231d0bbfa9497559a1c0dbe8a2dedb783..f20909106a82098d79ab8e47b03bf5b4076b9380 100644
--- a/.github/workflows/publish-check-compile.yml
+++ b/.github/workflows/publish-check-compile.yml
@@ -26,12 +26,14 @@ jobs:
       - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7
 
       - name: Rust Cache
-        uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5
+        uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7
         with:
           cache-on-failure: true
 
       - name: install parity-publish
-        run: cargo install parity-publish@0.10.3 --locked -q
+        run: |
+          rustup override set 1.82.0
+          cargo install parity-publish@0.10.4 --locked -q
 
       - name: parity-publish update plan
         run: parity-publish --color always plan --skip-check --prdoc prdoc/
diff --git a/.github/workflows/publish-check-crates.yml b/.github/workflows/publish-check-crates.yml
index 3150cb9dd40508a214280f716ba161c5420bfc92..c1b13243ba193c7c141782dc263c577c5f0ec159 100644
--- a/.github/workflows/publish-check-crates.yml
+++ b/.github/workflows/publish-check-crates.yml
@@ -19,12 +19,12 @@ jobs:
       - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7
 
       - name: Rust Cache
-        uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5
+        uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7
         with:
           cache-on-failure: true
 
       - name: install parity-publish
-        run: cargo install parity-publish@0.10.3 --locked -q
+        run: cargo install parity-publish@0.10.4 --locked -q
 
       - name: parity-publish check
         run: parity-publish --color always check --allow-unpublished
diff --git a/.github/workflows/publish-claim-crates.yml b/.github/workflows/publish-claim-crates.yml
index a6efc8a5599e4171cd93c5bdb287d2b5a8514787..804baf9ff06cfc4c32c19fbe3a70be81c8f07b46 100644
--- a/.github/workflows/publish-claim-crates.yml
+++ b/.github/workflows/publish-claim-crates.yml
@@ -13,12 +13,12 @@ jobs:
       - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7
 
       - name: Rust Cache
-        uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5
+        uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7
         with:
           cache-on-failure: true
 
       - name: install parity-publish
-        run: cargo install parity-publish@0.10.3 --locked -q
+        run: cargo install parity-publish@0.10.4 --locked -q
 
       - name: parity-publish claim
         env:
diff --git a/.github/workflows/zombienet-reusable-preflight.yml b/.github/workflows/zombienet-reusable-preflight.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8e938567d8118ad6d68eb91108281fd20e72b26f
--- /dev/null
+++ b/.github/workflows/zombienet-reusable-preflight.yml
@@ -0,0 +1,145 @@
+# Reusable workflow to set various useful variables
+# and to perform checks and generate conditions for other workflows.
+# Currently it checks if any Rust (build-related) file is changed
+# and if the current (caller) workflow file is changed.
+# Example:
+#
+# jobs:
+#   preflight:
+#     uses: ./.github/workflows/reusable-preflight.yml
+#   some-job:
+#     needs: changes
+#     if: ${{ needs.preflight.outputs.changes_rust }}
+#  .......
+
+name: Zombienet Preflight
+
+on:
+  workflow_call:
+    # Map the workflow outputs to job outputs
+    outputs:
+      changes_substrate:
+        value: ${{ jobs.preflight.outputs.changes_substrate }}
+
+      ZOMBIENET_IMAGE:
+        value: ${{ jobs.preflight.outputs.ZOMBIENET_IMAGE }}
+        description: "ZOMBIENET CI image"
+
+      ZOMBIENET_RUNNER:
+        value: ${{ jobs.preflight.outputs.ZOMBIENET_RUNNER }}
+        description: |
+          Main runner for zombienet tests.
+
+      DOCKER_IMAGES_VERSION:
+        value: ${{ jobs.preflight.outputs.DOCKER_IMAGES_VERSION }}
+        description: |
+          Version for temp docker images.
+
+      # Global vars (from global preflight)
+      SOURCE_REF_SLUG:
+        value: ${{ jobs.global_preflight.outputs.SOURCE_REF_SLUG }}
+
+      # Zombie vars
+      PUSHGATEWAY_URL:
+        value: ${{ jobs.preflight.outputs.PUSHGATEWAY_URL }}
+        description: "Gateway (url) to push metrics related to test."
+      DEBUG:
+        value: ${{ jobs.preflight.outputs.DEBUG }}
+        description: "Debug value to zombienet v1 tests."
+      ZOMBIE_PROVIDER:
+        value: ${{ jobs.preflight.outputs.ZOMBIE_PROVIDER }}
+        description: "Provider to use in zombienet-sdk tests."
+      RUST_LOG:
+        value: ${{ jobs.preflight.outputs.RUST_LOG }}
+        description: "Log value to use in zombinet-sdk tests."
+      RUN_IN_CI:
+        value: ${{ jobs.preflight.outputs.RUN_IN_CI }}
+        description: "Internal flag to make zombienet aware of the env."
+
+      KUBERNETES_CPU_REQUEST:
+        value: ${{ jobs.preflight.outputs.KUBERNETES_CPU_REQUEST }}
+        description: "Base cpu (request) for pod runner."
+
+      KUBERNETES_MEMORY_REQUEST:
+        value: ${{ jobs.preflight.outputs.KUBERNETES_MEMORY_REQUEST }}
+        description: "Base memory (request) for pod runner."
+
+jobs:
+  global_preflight:
+    uses: ./.github/workflows/reusable-preflight.yml
+
+  #
+  #
+  #
+  preflight:
+    runs-on: ubuntu-latest
+    outputs:
+      changes_substrate: ${{ steps.set_changes.outputs.substrate_any_changed || steps.set_changes.outputs.currentWorkflow_any_changed }}
+
+      ZOMBIENET_IMAGE: ${{ steps.set_vars.outputs.ZOMBIENET_IMAGE }}
+      ZOMBIENET_RUNNER: ${{ steps.set_vars.outputs.ZOMBIENET_RUNNER }}
+
+      DOCKER_IMAGES_VERSION: ${{ steps.set_images_version.outputs.ZOMBIENET_RUNNER }}
+
+      # common vars
+      PUSHGATEWAY_URL: ${{ steps.set_vars.outputs.PUSHGATEWAY_URL }}
+      DEBUG: ${{ steps.set_vars.outputs.DEBUG }}
+      ZOMBIE_PROVIDER: ${{ steps.set_vars.outputs.ZOMBIE_PROVIDER }}
+      RUST_LOG: ${{ steps.set_vars.outputs.RUST_LOG }}
+      RUN_IN_CI: ${{ steps.set_vars.outputs.RUN_IN_CI }}
+      KUBERNETES_CPU_REQUEST: ${{ steps.set_vars.outputs.KUBERNETES_CPU_REQUEST }}
+      KUBERNETES_MEMORY_REQUEST: ${{ steps.set_vars.outputs.KUBERNETES_MEMORY_REQUEST }}
+
+    steps:
+
+      - uses: actions/checkout@v4
+
+      #
+      # Set changes
+      #
+      - name: Current file
+        id: current_file
+        shell: bash
+        run: |
+          echo "currentWorkflowFile=$(echo ${{ github.workflow_ref }} | sed -nE "s/.*(\.github\/workflows\/[a-zA-Z0-9_-]*\.y[a]?ml)@refs.*/\1/p")" >> $GITHUB_OUTPUT
+          echo "currentActionDir=$(echo ${{ github.action_path }} | sed -nE "s/.*(\.github\/actions\/[a-zA-Z0-9_-]*)/\1/p")" >> $GITHUB_OUTPUT
+
+      - name: Set changes
+        id: set_changes
+        uses: tj-actions/changed-files@v45
+        with:
+          files_yaml: |
+            substrate:
+              - 'substrate/**/*'
+            currentWorkflow:
+              - '${{ steps.current_file.outputs.currentWorkflowFile }}'
+              - '.github/workflows/zombienet-reusable-preflight.yml'
+              - '.github/zombienet-env'
+
+
+      #
+      # Set environment vars (including runner/image)
+      #
+      - name: Set vars
+        id: set_vars
+        shell: bash
+        run: cat .github/env >> $GITHUB_OUTPUT
+
+
+      #
+      #
+      #
+      - name: Set docker images version
+        id: set_images_version
+        shell: bash
+        run: |
+          export BRANCH_NAME=${{ github.head_ref || github.ref_name }}
+          export DOCKER_IMAGES_VERSION=${BRANCH_NAME/\//-}
+          if [[ ${{ github.event_name }} == "merge_group" ]]; then export DOCKER_IMAGES_VERSION="${GITHUB_SHA::8}"; fi
+          echo "DOCKER_IMAGES_VERSION=${DOCKER_IMAGES_VERSION}" >> $GITHUB_OUTPUT
+
+      - name: log
+        shell: bash
+        run: |
+          echo "workflow file: ${{ steps.current_file.outputs.currentWorkflowFile }}"
+          echo "Modified: ${{ steps.set_changes.outputs.modified_keys }}"
\ No newline at end of file
diff --git a/.github/workflows/zombienet_substrate.yml b/.github/workflows/zombienet_substrate.yml
new file mode 100644
index 0000000000000000000000000000000000000000..823679d67d5c0adc204a2fc15360fdab561a4c17
--- /dev/null
+++ b/.github/workflows/zombienet_substrate.yml
@@ -0,0 +1,45 @@
+name: Zombienet Substrate
+
+on:
+  workflow_run:
+    workflows: [Build and push images]
+    types: [completed]
+  merge_group:
+  workflow_dispatch:
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  preflight:
+    uses: ./.github/workflows/zombienet-reusable-preflight.yml
+
+  zombienet-substrate-0000-block-building:
+    needs: [preflight]
+    # only run if we have changes in ./substrate directory and the build workflow already finish with success status.
+    if: ${{ needs.preflight.outputs.changes_substrate && github.event.workflow_run.conclusion == 'success' }}
+    runs-on: ${{ needs.preflight.outputs.ZOMBIENET_RUNNER }}
+    timeout-minutes: 60
+    container:
+      image: ${{ needs.preflight.outputs.ZOMBIENET_IMAGE }}
+    env:
+      FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR: 1
+      LOCAL_DIR: "./substrate/zombienet"
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+
+      - uses: actions/download-artifact@v4.1.8
+        with:
+          name: build-linux-substrate-${{ needs.preflight.outputs.SOURCE_REF_NAME }}
+
+      - name: script
+        run: |
+          DEBUG=${{ needs.preflight.outputs.DEBUG }} zombie -p native ${LOCAL_DIR}/0000-block-building/block-building.zndsl
+
+      - name: upload logs
+        uses: actions/upload-artifact@v4
+        with:
+          name: zombienet-logs-scale-net
+          path: |
+            /tmp/zombie*/logs/*
diff --git a/.github/zombienet-env b/.github/zombienet-env
new file mode 100644
index 0000000000000000000000000000000000000000..e6da1a49c4bb38602bdc9d7a21623ed136dee61b
--- /dev/null
+++ b/.github/zombienet-env
@@ -0,0 +1,9 @@
+    ZOMBIENET_IMAGE="docker.io/paritytech/zombienet:v1.3.116"
+    ZOMBIE_RUNNER="zombienet-arc-runner"
+    PUSHGATEWAY_URL="http://zombienet-prometheus-pushgateway.managed-monitoring:9091/metrics/job/zombie-metrics"
+    DEBUG="zombie,zombie::network-node,zombie::kube::client::logs"
+    ZOMBIE_PROVIDER="k8s"
+    RUST_LOG="info,zombienet_orchestrator=debug"
+    RUN_IN_CI="1"
+    KUBERNETES_CPU_REQUEST="512m"
+    KUBERNETES_MEMORY_REQUEST="1Gi"
diff --git a/Cargo.lock b/Cargo.lock
index 989e8f8706a236654f09eef1bcf0408faa1baad9..2b9ca56a779e3542825b0a24d7d21775c273843c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -910,6 +910,7 @@ dependencies = [
  "cumulus-primitives-core 0.7.0",
  "emulated-integration-tests-common",
  "frame-support 28.0.0",
+ "pallet-asset-rewards",
  "parachains-common 7.0.0",
  "rococo-emulated-chain",
  "sp-core 28.0.0",
@@ -928,6 +929,7 @@ dependencies = [
  "emulated-integration-tests-common",
  "frame-support 28.0.0",
  "pallet-asset-conversion 10.0.0",
+ "pallet-asset-rewards",
  "pallet-assets 29.1.0",
  "pallet-balances 28.0.0",
  "pallet-message-queue 31.0.0",
@@ -978,6 +980,7 @@ dependencies = [
  "pallet-asset-conversion 10.0.0",
  "pallet-asset-conversion-ops 0.1.0",
  "pallet-asset-conversion-tx-payment 10.0.0",
+ "pallet-asset-rewards",
  "pallet-assets 29.1.0",
  "pallet-assets-freezer 0.1.0",
  "pallet-aura 27.0.0",
@@ -1063,6 +1066,7 @@ dependencies = [
  "frame-support 28.0.0",
  "frame-system 28.0.0",
  "pallet-asset-conversion 10.0.0",
+ "pallet-asset-rewards",
  "pallet-asset-tx-payment 28.0.0",
  "pallet-assets 29.1.0",
  "pallet-balances 28.0.0",
@@ -1114,6 +1118,7 @@ dependencies = [
  "pallet-asset-conversion 10.0.0",
  "pallet-asset-conversion-ops 0.1.0",
  "pallet-asset-conversion-tx-payment 10.0.0",
+ "pallet-asset-rewards",
  "pallet-assets 29.1.0",
  "pallet-assets-freezer 0.1.0",
  "pallet-aura 27.0.0",
@@ -4874,6 +4879,8 @@ name = "cumulus-pallet-aura-ext"
 version = "0.7.0"
 dependencies = [
  "cumulus-pallet-parachain-system 0.7.0",
+ "cumulus-primitives-core 0.7.0",
+ "cumulus-test-relay-sproof-builder 0.7.0",
  "frame-support 28.0.0",
  "frame-system 28.0.0",
  "pallet-aura 27.0.0",
@@ -4882,7 +4889,10 @@ dependencies = [
  "scale-info",
  "sp-application-crypto 30.0.0",
  "sp-consensus-aura 0.32.0",
+ "sp-core 28.0.0",
+ "sp-io 30.0.0",
  "sp-runtime 31.0.1",
+ "sp-version 29.0.0",
 ]
 
 [[package]]
@@ -10441,9 +10451,9 @@ dependencies = [
 
 [[package]]
 name = "litep2p"
-version = "0.8.4"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b0fef34af8847e816003bf7fdeac5ea50b9a7a88441ac927a6166b5e812ab79"
+checksum = "6ca6ee50a125dc4fc4e9a3ae3640010796d1d07bc517a0ac715fdf0b24a0b6ac"
 dependencies = [
  "async-trait",
  "bs58",
@@ -12032,6 +12042,27 @@ dependencies = [
  "sp-runtime 39.0.2",
 ]
 
+[[package]]
+name = "pallet-asset-rewards"
+version = "0.1.0"
+dependencies = [
+ "frame-benchmarking 28.0.0",
+ "frame-support 28.0.0",
+ "frame-system 28.0.0",
+ "pallet-assets 29.1.0",
+ "pallet-assets-freezer 0.1.0",
+ "pallet-balances 28.0.0",
+ "parity-scale-codec",
+ "primitive-types 0.13.1",
+ "scale-info",
+ "sp-api 26.0.0",
+ "sp-arithmetic 23.0.0",
+ "sp-core 28.0.0",
+ "sp-io 30.0.0",
+ "sp-runtime 31.0.1",
+ "sp-std 14.0.0",
+]
+
 [[package]]
 name = "pallet-asset-tx-payment"
 version = "28.0.0"
@@ -13339,6 +13370,7 @@ dependencies = [
  "log",
  "pallet-bags-list 27.0.0",
  "pallet-balances 28.0.0",
+ "pallet-delegated-staking 1.0.0",
  "pallet-election-provider-multi-phase 27.0.0",
  "pallet-nomination-pools 25.0.0",
  "pallet-session 28.0.0",
@@ -14429,29 +14461,6 @@ dependencies = [
  "sp-tracing 16.0.0",
 ]
 
-[[package]]
-name = "pallet-nomination-pools-test-transfer-stake"
-version = "1.0.0"
-dependencies = [
- "frame-election-provider-support 28.0.0",
- "frame-support 28.0.0",
- "frame-system 28.0.0",
- "log",
- "pallet-bags-list 27.0.0",
- "pallet-balances 28.0.0",
- "pallet-nomination-pools 25.0.0",
- "pallet-staking 28.0.0",
- "pallet-staking-reward-curve",
- "pallet-timestamp 27.0.0",
- "parity-scale-codec",
- "scale-info",
- "sp-core 28.0.0",
- "sp-io 30.0.0",
- "sp-runtime 31.0.1",
- "sp-staking 26.0.0",
- "sp-tracing 16.0.0",
-]
-
 [[package]]
 name = "pallet-offences"
 version = "27.0.0"
@@ -14849,7 +14858,7 @@ dependencies = [
  "pallet-utility 28.0.0",
  "parity-scale-codec",
  "paste",
- "polkavm 0.18.0",
+ "polkavm 0.19.0",
  "pretty_assertions",
  "rlp 0.6.1",
  "scale-info",
@@ -14938,7 +14947,7 @@ name = "pallet-revive-fixtures"
 version = "0.1.0"
 dependencies = [
  "anyhow",
- "polkavm-linker 0.18.0",
+ "polkavm-linker 0.19.0",
  "sp-core 28.0.0",
  "sp-io 30.0.0",
  "toml 0.8.19",
@@ -15053,7 +15062,7 @@ dependencies = [
  "pallet-revive-proc-macro 0.1.0",
  "parity-scale-codec",
  "paste",
- "polkavm-derive 0.18.0",
+ "polkavm-derive 0.19.0",
  "scale-info",
 ]
 
@@ -18711,6 +18720,7 @@ dependencies = [
  "pallet-asset-conversion-ops 0.1.0",
  "pallet-asset-conversion-tx-payment 10.0.0",
  "pallet-asset-rate 7.0.0",
+ "pallet-asset-rewards",
  "pallet-asset-tx-payment 28.0.0",
  "pallet-assets 29.1.0",
  "pallet-assets-freezer 0.1.0",
@@ -19924,6 +19934,19 @@ dependencies = [
  "polkavm-linux-raw 0.18.0",
 ]
 
+[[package]]
+name = "polkavm"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8379bb48ff026aa8ae0645ea45f27920bfd21c82b2e82ed914224bb233d59f83"
+dependencies = [
+ "libc",
+ "log",
+ "polkavm-assembler 0.19.0",
+ "polkavm-common 0.19.0",
+ "polkavm-linux-raw 0.19.0",
+]
+
 [[package]]
 name = "polkavm-assembler"
 version = "0.9.0"
@@ -19951,6 +19974,15 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "polkavm-assembler"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57513b596cf0bafb052dab48e9c168f473c35f7522e17f70cc9f96603012d9b7"
+dependencies = [
+ "log",
+]
+
 [[package]]
 name = "polkavm-common"
 version = "0.9.0"
@@ -19980,6 +20012,16 @@ dependencies = [
  "polkavm-assembler 0.18.0",
 ]
 
+[[package]]
+name = "polkavm-common"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a972bd305ba8cbf0de79951d6d49d2abfad47c277596be5a2c6a0924a163abbd"
+dependencies = [
+ "log",
+ "polkavm-assembler 0.19.0",
+]
+
 [[package]]
 name = "polkavm-derive"
 version = "0.9.1"
@@ -20007,6 +20049,15 @@ dependencies = [
  "polkavm-derive-impl-macro 0.18.0",
 ]
 
+[[package]]
+name = "polkavm-derive"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8d866972a7532d82d05c26b4516563660dd6676d7ab9e64e681d8ef0e29255c"
+dependencies = [
+ "polkavm-derive-impl-macro 0.19.0",
+]
+
 [[package]]
 name = "polkavm-derive-impl"
 version = "0.9.0"
@@ -20043,6 +20094,18 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "polkavm-derive-impl"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cffca9d51b21153395a192b65698457687bc51daa41026629895542ccaa65c2"
+dependencies = [
+ "polkavm-common 0.19.0",
+ "proc-macro2 1.0.86",
+ "quote 1.0.37",
+ "syn 2.0.87",
+]
+
 [[package]]
 name = "polkavm-derive-impl-macro"
 version = "0.9.0"
@@ -20073,6 +20136,16 @@ dependencies = [
  "syn 2.0.87",
 ]
 
+[[package]]
+name = "polkavm-derive-impl-macro"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc0dc0cf2e8f4d30874131eccfa36bdabd4a52cfb79c15f8630508abaf06a2a6"
+dependencies = [
+ "polkavm-derive-impl 0.19.0",
+ "syn 2.0.87",
+]
+
 [[package]]
 name = "polkavm-linker"
 version = "0.9.2"
@@ -20119,6 +20192,22 @@ dependencies = [
  "rustc-demangle",
 ]
 
+[[package]]
+name = "polkavm-linker"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "caec2308f1328b5a667da45322c04fad7ff97ad8b36817d18c7635ea4dd6c6f4"
+dependencies = [
+ "dirs",
+ "gimli 0.31.1",
+ "hashbrown 0.14.5",
+ "log",
+ "object 0.36.1",
+ "polkavm-common 0.19.0",
+ "regalloc2 0.9.3",
+ "rustc-demangle",
+]
+
 [[package]]
 name = "polkavm-linux-raw"
 version = "0.9.0"
@@ -20137,6 +20226,12 @@ version = "0.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "23eff02c070c70f31878a3d915e88a914ecf3e153741e2fb572dde28cce20fde"
 
+[[package]]
+name = "polkavm-linux-raw"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "136ae072ab6fa38e584a06d12b1b216cff19f54d5cd202a8f8c5ec2e92e7e4bb"
+
 [[package]]
 name = "polling"
 version = "2.8.0"
diff --git a/Cargo.toml b/Cargo.toml
index c30a9949e85e4212a9ead1f895889ee8bf69a53c..e17f08148b163206bc0073e3610cb5c64586cf69 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -315,6 +315,7 @@ members = [
 	"substrate/frame/asset-conversion",
 	"substrate/frame/asset-conversion/ops",
 	"substrate/frame/asset-rate",
+	"substrate/frame/asset-rewards",
 	"substrate/frame/assets",
 	"substrate/frame/assets-freezer",
 	"substrate/frame/atomic-swap",
@@ -388,7 +389,6 @@ members = [
 	"substrate/frame/nomination-pools/fuzzer",
 	"substrate/frame/nomination-pools/runtime-api",
 	"substrate/frame/nomination-pools/test-delegate-stake",
-	"substrate/frame/nomination-pools/test-transfer-stake",
 	"substrate/frame/offences",
 	"substrate/frame/offences/benchmarking",
 	"substrate/frame/paged-list",
@@ -850,7 +850,7 @@ linked-hash-map = { version = "0.5.4" }
 linked_hash_set = { version = "0.1.4" }
 linregress = { version = "0.5.1" }
 lite-json = { version = "0.2.0", default-features = false }
-litep2p = { version = "0.8.4", features = ["websocket"] }
+litep2p = { version = "0.9.0", features = ["websocket"] }
 log = { version = "0.4.22", default-features = false }
 macro_magic = { version = "0.5.1" }
 maplit = { version = "1.0.2" }
@@ -893,6 +893,7 @@ pallet-asset-conversion = { path = "substrate/frame/asset-conversion", default-f
 pallet-asset-conversion-ops = { path = "substrate/frame/asset-conversion/ops", default-features = false }
 pallet-asset-conversion-tx-payment = { path = "substrate/frame/transaction-payment/asset-conversion-tx-payment", default-features = false }
 pallet-asset-rate = { path = "substrate/frame/asset-rate", default-features = false }
+pallet-asset-rewards = { path = "substrate/frame/asset-rewards", default-features = false }
 pallet-asset-tx-payment = { path = "substrate/frame/transaction-payment/asset-tx-payment", default-features = false }
 pallet-assets = { path = "substrate/frame/assets", default-features = false }
 pallet-assets-freezer = { path = "substrate/frame/assets-freezer", default-features = false }
diff --git a/bridges/relays/lib-substrate-relay/src/error.rs b/bridges/relays/lib-substrate-relay/src/error.rs
index 2ebd9130f3912ba4c0552860a2259b222220e8f8..3a62f30838c7588317234409a6107b7d1d6cacf1 100644
--- a/bridges/relays/lib-substrate-relay/src/error.rs
+++ b/bridges/relays/lib-substrate-relay/src/error.rs
@@ -47,7 +47,7 @@ pub enum Error<Hash: Debug + MaybeDisplay, HeaderNumber: Debug + MaybeDisplay> {
 	#[error("Failed to guess initial {0} GRANDPA authorities set id: checked all possible ids in range [0; {1}]")]
 	GuessInitialAuthorities(&'static str, HeaderNumber),
 	/// Failed to retrieve GRANDPA authorities at the given header from the source chain.
-	#[error("Failed to retrive {0} GRANDPA authorities set at header {1}: {2:?}")]
+	#[error("Failed to retrieve {0} GRANDPA authorities set at header {1}: {2:?}")]
 	RetrieveAuthorities(&'static str, Hash, client::Error),
 	/// Failed to decode GRANDPA authorities at the given header of the source chain.
 	#[error("Failed to decode {0} GRANDPA authorities set at header {1}: {2:?}")]
diff --git a/bridges/relays/messages/src/message_lane_loop.rs b/bridges/relays/messages/src/message_lane_loop.rs
index 36de637f04c437450e9054dd5088c3da5eb329cd..cdc94b9fae493cc9701449a1485308476d5c758f 100644
--- a/bridges/relays/messages/src/message_lane_loop.rs
+++ b/bridges/relays/messages/src/message_lane_loop.rs
@@ -1041,7 +1041,7 @@ pub(crate) mod tests {
 	#[test]
 	fn message_lane_loop_is_able_to_recover_from_unsuccessful_transaction() {
 		// with this configuration, both source and target clients will mine their transactions, but
-		// their corresponding nonce won't be udpated => reconnect will happen
+		// their corresponding nonce won't be updated => reconnect will happen
 		let (exit_sender, exit_receiver) = unbounded();
 		let result = run_loop_test(
 			Arc::new(Mutex::new(TestClientData {
diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs
index f49a245c4126f410994390908a81b6d55e613b15..eb5ac66d46db7192d249141dbe7104fce8d619b0 100644
--- a/bridges/snowbridge/primitives/core/src/location.rs
+++ b/bridges/snowbridge/primitives/core/src/location.rs
@@ -206,7 +206,7 @@ mod tests {
 		for token in token_locations {
 			assert!(
 				TokenIdOf::convert_location(&token).is_some(),
-				"Valid token = {token:?} yeilds no TokenId."
+				"Valid token = {token:?} yields no TokenId."
 			);
 		}
 
@@ -220,7 +220,7 @@ mod tests {
 		for token in non_token_locations {
 			assert!(
 				TokenIdOf::convert_location(&token).is_none(),
-				"Invalid token = {token:?} yeilds a TokenId."
+				"Invalid token = {token:?} yields a TokenId."
 			);
 		}
 	}
diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs
index 2dbcf5eb58e96b42fa988380cb3ebe6a1654edee..7723de5a576a2bd87fe46687b2cf33aba2a931bc 100644
--- a/cumulus/client/consensus/aura/src/collators/lookahead.rs
+++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs
@@ -336,6 +336,7 @@ where
 				);
 				Some(super::can_build_upon::<_, _, P>(
 					slot_now,
+					relay_slot,
 					timestamp,
 					block_hash,
 					included_block,
diff --git a/cumulus/client/consensus/aura/src/collators/mod.rs b/cumulus/client/consensus/aura/src/collators/mod.rs
index 89070607fbaba9e919a9dd3e924ddb84f64261de..031fa963ba6ae825264d8d1f0264660125f68ea8 100644
--- a/cumulus/client/consensus/aura/src/collators/mod.rs
+++ b/cumulus/client/consensus/aura/src/collators/mod.rs
@@ -34,7 +34,7 @@ use polkadot_primitives::{
 	ValidationCodeHash,
 };
 use sc_consensus_aura::{standalone as aura_internal, AuraApi};
-use sp_api::ProvideRuntimeApi;
+use sp_api::{ApiExt, ProvideRuntimeApi};
 use sp_core::Pair;
 use sp_keystore::KeystorePtr;
 use sp_timestamp::Timestamp;
@@ -160,7 +160,8 @@ async fn cores_scheduled_for_para(
 // Checks if we own the slot at the given block and whether there
 // is space in the unincluded segment.
 async fn can_build_upon<Block: BlockT, Client, P>(
-	slot: Slot,
+	para_slot: Slot,
+	relay_slot: Slot,
 	timestamp: Timestamp,
 	parent_hash: Block::Hash,
 	included_block: Block::Hash,
@@ -169,25 +170,28 @@ async fn can_build_upon<Block: BlockT, Client, P>(
 ) -> Option<SlotClaim<P::Public>>
 where
 	Client: ProvideRuntimeApi<Block>,
-	Client::Api: AuraApi<Block, P::Public> + AuraUnincludedSegmentApi<Block>,
+	Client::Api: AuraApi<Block, P::Public> + AuraUnincludedSegmentApi<Block> + ApiExt<Block>,
 	P: Pair,
 	P::Public: Codec,
 	P::Signature: Codec,
 {
 	let runtime_api = client.runtime_api();
 	let authorities = runtime_api.authorities(parent_hash).ok()?;
-	let author_pub = aura_internal::claim_slot::<P>(slot, &authorities, keystore).await?;
+	let author_pub = aura_internal::claim_slot::<P>(para_slot, &authorities, keystore).await?;
 
-	// Here we lean on the property that building on an empty unincluded segment must always
-	// be legal. Skipping the runtime API query here allows us to seamlessly run this
-	// collator against chains which have not yet upgraded their runtime.
-	if parent_hash != included_block &&
-		!runtime_api.can_build_upon(parent_hash, included_block, slot).ok()?
-	{
-		return None
-	}
+	let Ok(Some(api_version)) =
+		runtime_api.api_version::<dyn AuraUnincludedSegmentApi<Block>>(parent_hash)
+	else {
+		return (parent_hash == included_block)
+			.then(|| SlotClaim::unchecked::<P>(author_pub, para_slot, timestamp));
+	};
+
+	let slot = if api_version > 1 { relay_slot } else { para_slot };
 
-	Some(SlotClaim::unchecked::<P>(author_pub, slot, timestamp))
+	runtime_api
+		.can_build_upon(parent_hash, included_block, slot)
+		.ok()?
+		.then(|| SlotClaim::unchecked::<P>(author_pub, para_slot, timestamp))
 }
 
 /// Use [`cumulus_client_consensus_common::find_potential_parents`] to find parachain blocks that
diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs
index 41751f1db5302916d9401d357461f51ab3bab660..48287555dea65f3ee6726728dd0ea1522aa2b586 100644
--- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs
+++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs
@@ -23,7 +23,7 @@ use cumulus_primitives_aura::AuraUnincludedSegmentApi;
 use cumulus_primitives_core::{GetCoreSelectorApi, PersistedValidationData};
 use cumulus_relay_chain_interface::RelayChainInterface;
 
-use polkadot_primitives::Id as ParaId;
+use polkadot_primitives::{Block as RelayBlock, Id as ParaId};
 
 use futures::prelude::*;
 use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf, UsageProvider};
@@ -302,8 +302,17 @@ where
 			// on-chain data.
 			collator.collator_service().check_block_status(parent_hash, &parent_header);
 
+			let Ok(relay_slot) =
+				sc_consensus_babe::find_pre_digest::<RelayBlock>(relay_parent_header)
+					.map(|babe_pre_digest| babe_pre_digest.slot())
+			else {
+				tracing::error!(target: crate::LOG_TARGET, "Relay chain does not contain babe slot. This should never happen.");
+				continue;
+			};
+
 			let slot_claim = match crate::collators::can_build_upon::<_, _, P>(
 				para_slot.slot,
+				relay_slot,
 				para_slot.timestamp,
 				parent_hash,
 				included_block,
diff --git a/cumulus/client/parachain-inherent/src/mock.rs b/cumulus/client/parachain-inherent/src/mock.rs
index e08aca932564b94b66dcbe053c03be59620085e3..8dbc6ace0f06cbf426ea33cdf6f832bfcb5670ed 100644
--- a/cumulus/client/parachain-inherent/src/mock.rs
+++ b/cumulus/client/parachain-inherent/src/mock.rs
@@ -17,8 +17,9 @@
 use crate::{ParachainInherentData, INHERENT_IDENTIFIER};
 use codec::Decode;
 use cumulus_primitives_core::{
-	relay_chain, relay_chain::UpgradeGoAhead, InboundDownwardMessage, InboundHrmpMessage, ParaId,
-	PersistedValidationData,
+	relay_chain,
+	relay_chain::{Slot, UpgradeGoAhead},
+	InboundDownwardMessage, InboundHrmpMessage, ParaId, PersistedValidationData,
 };
 use cumulus_primitives_parachain_inherent::MessageQueueChain;
 use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
@@ -28,9 +29,6 @@ use sp_inherents::{InherentData, InherentDataProvider};
 use sp_runtime::traits::Block;
 use std::collections::BTreeMap;
 
-/// Relay chain slot duration, in milliseconds.
-pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
-
 /// Inherent data provider that supplies mocked validation data.
 ///
 /// This is useful when running a node that is not actually backed by any relay chain.
@@ -175,8 +173,7 @@ impl<R: Send + Sync + GenerateRandomness<u64>> InherentDataProvider
 		// Calculate the mocked relay block based on the current para block
 		let relay_parent_number =
 			self.relay_offset + self.relay_blocks_per_para_block * self.current_para_block;
-		sproof_builder.current_slot =
-			((relay_parent_number / RELAY_CHAIN_SLOT_DURATION_MILLIS) as u64).into();
+		sproof_builder.current_slot = Slot::from(relay_parent_number as u64);
 
 		sproof_builder.upgrade_go_ahead = self.upgrade_go_ahead;
 		// Process the downward messages and set up the correct head
diff --git a/cumulus/pallets/aura-ext/Cargo.toml b/cumulus/pallets/aura-ext/Cargo.toml
index fcda79f1d5c129af8f0bdb56ce47ebc480b71ae2..82638de71aa129154af0e4abdd24767e5370c15d 100644
--- a/cumulus/pallets/aura-ext/Cargo.toml
+++ b/cumulus/pallets/aura-ext/Cargo.toml
@@ -28,9 +28,15 @@ sp-runtime = { workspace = true }
 cumulus-pallet-parachain-system = { workspace = true }
 
 [dev-dependencies]
-
 # Cumulus
 cumulus-pallet-parachain-system = { workspace = true, default-features = true }
+cumulus-primitives-core = { workspace = true, default-features = true }
+cumulus-test-relay-sproof-builder = { workspace = true, default-features = true }
+
+# Substrate
+sp-core = { workspace = true, default-features = true }
+sp-io = { workspace = true, default-features = true }
+sp-version = { workspace = true, default-features = true }
 
 [features]
 default = ["std"]
diff --git a/cumulus/pallets/aura-ext/src/consensus_hook.rs b/cumulus/pallets/aura-ext/src/consensus_hook.rs
index c1a8568bdd834f0e754bdad20a5a11246cc6ac31..56966aa0c8f82578dcc3da8294a4299d6a333e23 100644
--- a/cumulus/pallets/aura-ext/src/consensus_hook.rs
+++ b/cumulus/pallets/aura-ext/src/consensus_hook.rs
@@ -18,7 +18,6 @@
 //! block velocity.
 //!
 //! The velocity `V` refers to the rate of block processing by the relay chain.
-
 use super::{pallet, Aura};
 use core::{marker::PhantomData, num::NonZeroU32};
 use cumulus_pallet_parachain_system::{
@@ -54,8 +53,23 @@ where
 		let velocity = V.max(1);
 		let relay_chain_slot = state_proof.read_slot().expect("failed to read relay chain slot");
 
-		let (slot, authored) =
-			pallet::SlotInfo::<T>::get().expect("slot info is inserted on block initialization");
+		let (relay_chain_slot, authored_in_relay) = match pallet::RelaySlotInfo::<T>::get() {
+			Some((slot, authored)) if slot == relay_chain_slot => (slot, authored),
+			Some((slot, _)) if slot < relay_chain_slot => (relay_chain_slot, 0),
+			Some((slot, _)) => {
+				panic!("Slot moved backwards: stored_slot={slot:?}, relay_chain_slot={relay_chain_slot:?}")
+			},
+			None => (relay_chain_slot, 0),
+		};
+
+		// We need to allow one additional block to be built to fill the unincluded segment.
+		if authored_in_relay > velocity {
+			panic!("authored blocks limit is reached for the slot: relay_chain_slot={relay_chain_slot:?}, authored={authored_in_relay:?}, velocity={velocity:?}");
+		}
+
+		pallet::RelaySlotInfo::<T>::put((relay_chain_slot, authored_in_relay + 1));
+
+		let para_slot = pallet_aura::CurrentSlot::<T>::get();
 
 		// Convert relay chain timestamp.
 		let relay_chain_timestamp =
@@ -67,19 +81,16 @@ where
 
 		// Check that we are not too far in the future. Since we expect `V` parachain blocks
 		// during the relay chain slot, we can allow for `V` parachain slots into the future.
-		if *slot > *para_slot_from_relay + u64::from(velocity) {
+		if *para_slot > *para_slot_from_relay + u64::from(velocity) {
 			panic!(
-				"Parachain slot is too far in the future: parachain_slot: {:?}, derived_from_relay_slot: {:?} velocity: {:?}",
-				slot,
+				"Parachain slot is too far in the future: parachain_slot={:?}, derived_from_relay_slot={:?} velocity={:?}, relay_chain_slot={:?}",
+				para_slot,
 				para_slot_from_relay,
-				velocity
+				velocity,
+				relay_chain_slot
 			);
 		}
 
-		// We need to allow authoring multiple blocks in the same slot.
-		if slot != para_slot_from_relay && authored > velocity {
-			panic!("authored blocks limit is reached for the slot")
-		}
 		let weight = T::DbWeight::get().reads(1);
 
 		(
@@ -110,7 +121,7 @@ impl<
 	/// is more recent than the included block itself.
 	pub fn can_build_upon(included_hash: T::Hash, new_slot: Slot) -> bool {
 		let velocity = V.max(1);
-		let (last_slot, authored_so_far) = match pallet::SlotInfo::<T>::get() {
+		let (last_slot, authored_so_far) = match pallet::RelaySlotInfo::<T>::get() {
 			None => return true,
 			Some(x) => x,
 		};
@@ -123,11 +134,8 @@ impl<
 			return false
 		}
 
-		// TODO: This logic needs to be adjusted.
-		// It checks that we have not authored more than `V + 1` blocks in the slot.
-		// As a slot however, we take the parachain slot here. Velocity should
-		// be measured in relation to the relay chain slot.
-		// https://github.com/paritytech/polkadot-sdk/issues/3967
+		// Check that we have not authored more than `V + 1` parachain blocks in the current relay
+		// chain slot.
 		if last_slot == new_slot {
 			authored_so_far < velocity + 1
 		} else {
diff --git a/cumulus/pallets/aura-ext/src/lib.rs b/cumulus/pallets/aura-ext/src/lib.rs
index dc854eb820184cbc79c5c150b1ce008c0d1955ce..19c2634ca708a063fcefbf6c7405fed0476c93a2 100644
--- a/cumulus/pallets/aura-ext/src/lib.rs
+++ b/cumulus/pallets/aura-ext/src/lib.rs
@@ -40,6 +40,9 @@ use sp_consensus_aura::{digests::CompatibleDigestItem, Slot};
 use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
 
 pub mod consensus_hook;
+pub mod migration;
+mod test;
+
 pub use consensus_hook::FixedVelocityConsensusHook;
 
 type Aura<T> = pallet_aura::Pallet<T>;
@@ -57,6 +60,7 @@ pub mod pallet {
 	pub trait Config: pallet_aura::Config + frame_system::Config {}
 
 	#[pallet::pallet]
+	#[pallet::storage_version(migration::STORAGE_VERSION)]
 	pub struct Pallet<T>(_);
 
 	#[pallet::hooks]
@@ -70,20 +74,7 @@ pub mod pallet {
 			// Fetch the authorities once to get them into the storage proof of the PoV.
 			Authorities::<T>::get();
 
-			let new_slot = pallet_aura::CurrentSlot::<T>::get();
-
-			let (new_slot, authored) = match SlotInfo::<T>::get() {
-				Some((slot, authored)) if slot == new_slot => (slot, authored + 1),
-				Some((slot, _)) if slot < new_slot => (new_slot, 1),
-				Some(..) => {
-					panic!("slot moved backwards")
-				},
-				None => (new_slot, 1),
-			};
-
-			SlotInfo::<T>::put((new_slot, authored));
-
-			T::DbWeight::get().reads_writes(4, 2)
+			T::DbWeight::get().reads_writes(1, 0)
 		}
 	}
 
@@ -99,11 +90,12 @@ pub mod pallet {
 		ValueQuery,
 	>;
 
-	/// Current slot paired with a number of authored blocks.
+	/// Current relay chain slot paired with a number of authored blocks.
 	///
-	/// Updated on each block initialization.
+	/// This is updated in [`FixedVelocityConsensusHook::on_state_proof`] with the current relay
+	/// chain slot as provided by the relay chain state proof.
 	#[pallet::storage]
-	pub(crate) type SlotInfo<T: Config> = StorageValue<_, (Slot, u32), OptionQuery>;
+	pub(crate) type RelaySlotInfo<T: Config> = StorageValue<_, (Slot, u32), OptionQuery>;
 
 	#[pallet::genesis_config]
 	#[derive(frame_support::DefaultNoBound)]
diff --git a/cumulus/pallets/aura-ext/src/migration.rs b/cumulus/pallets/aura-ext/src/migration.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b580c19fc733cb6e513a756741fcbaba1dbe4e92
--- /dev/null
+++ b/cumulus/pallets/aura-ext/src/migration.rs
@@ -0,0 +1,74 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+extern crate alloc;
+
+use crate::{Config, Pallet};
+#[cfg(feature = "try-runtime")]
+use alloc::vec::Vec;
+use frame_support::{migrations::VersionedMigration, pallet_prelude::StorageVersion};
+
+/// The in-code storage version.
+pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
+
+mod v0 {
+	use super::*;
+	use frame_support::{pallet_prelude::OptionQuery, storage_alias};
+	use sp_consensus_aura::Slot;
+
+	/// Current slot paired with a number of authored blocks.
+	///
+	/// Updated on each block initialization.
+	#[storage_alias]
+	pub(super) type SlotInfo<T: Config> = StorageValue<Pallet<T>, (Slot, u32), OptionQuery>;
+}
+mod v1 {
+	use super::*;
+	use frame_support::{pallet_prelude::*, traits::UncheckedOnRuntimeUpgrade};
+
+	pub struct UncheckedMigrationToV1<T: Config>(PhantomData<T>);
+
+	impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrationToV1<T> {
+		fn on_runtime_upgrade() -> Weight {
+			let mut weight: Weight = Weight::zero();
+			weight += migrate::<T>();
+			weight
+		}
+
+		#[cfg(feature = "try-runtime")]
+		fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
+			Ok(Vec::new())
+		}
+		#[cfg(feature = "try-runtime")]
+		fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
+			ensure!(!v0::SlotInfo::<T>::exists(), "SlotInfo should not exist");
+			Ok(())
+		}
+	}
+
+	pub fn migrate<T: Config>() -> Weight {
+		v0::SlotInfo::<T>::kill();
+		T::DbWeight::get().writes(1)
+	}
+}
+
+/// Migrate `V0` to `V1`.
+pub type MigrateV0ToV1<T> = VersionedMigration<
+	0,
+	1,
+	v1::UncheckedMigrationToV1<T>,
+	Pallet<T>,
+	<T as frame_system::Config>::DbWeight,
+>;
diff --git a/cumulus/pallets/aura-ext/src/test.rs b/cumulus/pallets/aura-ext/src/test.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b0099381e682d3805363eec230b57a31fa1c7b51
--- /dev/null
+++ b/cumulus/pallets/aura-ext/src/test.rs
@@ -0,0 +1,338 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+#![cfg(test)]
+extern crate alloc;
+
+use super::*;
+
+use core::num::NonZeroU32;
+use cumulus_pallet_parachain_system::{
+	consensus_hook::ExpectParentIncluded, AnyRelayNumber, DefaultCoreSelector, ParachainSetCode,
+};
+use cumulus_primitives_core::ParaId;
+use frame_support::{
+	derive_impl,
+	pallet_prelude::ConstU32,
+	parameter_types,
+	traits::{ConstBool, ConstU64, EnqueueWithOrigin},
+};
+use sp_io::TestExternalities;
+use sp_version::RuntimeVersion;
+
+type Block = frame_system::mocking::MockBlock<Test>;
+
+frame_support::construct_runtime!(
+	pub enum Test {
+		System: frame_system,
+		ParachainSystem: cumulus_pallet_parachain_system,
+		Aura: pallet_aura,
+		AuraExt: crate,
+	}
+);
+
+parameter_types! {
+	pub Version: RuntimeVersion = RuntimeVersion {
+		spec_name: "test".into(),
+		impl_name: "system-test".into(),
+		authoring_version: 1,
+		spec_version: 1,
+		impl_version: 1,
+		apis: sp_version::create_apis_vec!([]),
+		transaction_version: 1,
+		system_version: 1,
+	};
+}
+
+#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
+impl frame_system::Config for Test {
+	type Block = Block;
+	type Version = Version;
+	type OnSetCode = ParachainSetCode<Test>;
+	type RuntimeEvent = ();
+}
+
+impl crate::Config for Test {}
+
+impl pallet_aura::Config for Test {
+	type AuthorityId = sp_consensus_aura::sr25519::AuthorityId;
+	type MaxAuthorities = ConstU32<100_000>;
+	type DisabledValidators = ();
+	type AllowMultipleBlocksPerSlot = ConstBool<true>;
+	type SlotDuration = ConstU64<6000>;
+}
+
+impl pallet_timestamp::Config for Test {
+	type Moment = u64;
+	type OnTimestampSet = ();
+	type MinimumPeriod = ();
+	type WeightInfo = ();
+}
+
+impl cumulus_pallet_parachain_system::Config for Test {
+	type WeightInfo = ();
+	type RuntimeEvent = ();
+	type OnSystemEvent = ();
+	type SelfParaId = ();
+	type OutboundXcmpMessageSource = ();
+	// Ignore all DMP messages by enqueueing them into `()`:
+	type DmpQueue = EnqueueWithOrigin<(), sp_core::ConstU8<0>>;
+	type ReservedDmpWeight = ();
+	type XcmpMessageHandler = ();
+	type ReservedXcmpWeight = ();
+	type CheckAssociatedRelayNumber = AnyRelayNumber;
+	type ConsensusHook = ExpectParentIncluded;
+	type SelectCore = DefaultCoreSelector<Test>;
+}
+
+#[cfg(test)]
+mod test {
+	use crate::test::*;
+	use cumulus_pallet_parachain_system::{
+		Ancestor, ConsensusHook, RelayChainStateProof, UsedBandwidth,
+	};
+	use sp_core::H256;
+
+	fn set_ancestors() {
+		let mut ancestors = Vec::new();
+		for i in 0..3 {
+			let mut ancestor = Ancestor::new_unchecked(UsedBandwidth::default(), None);
+			ancestor.replace_para_head_hash(H256::repeat_byte(i + 1));
+			ancestors.push(ancestor);
+		}
+		cumulus_pallet_parachain_system::UnincludedSegment::<Test>::put(ancestors);
+	}
+
+	pub fn new_test_ext(para_slot: u64) -> sp_io::TestExternalities {
+		let mut ext = TestExternalities::new_empty();
+		ext.execute_with(|| {
+			set_ancestors();
+			// Set initial parachain slot
+			pallet_aura::CurrentSlot::<Test>::put(Slot::from(para_slot));
+		});
+		ext
+	}
+
+	fn set_relay_slot(slot: u64, authored: u32) {
+		RelaySlotInfo::<Test>::put((Slot::from(slot), authored))
+	}
+
+	fn relay_chain_state_proof(relay_slot: u64) -> RelayChainStateProof {
+		let mut builder = cumulus_test_relay_sproof_builder::RelayStateSproofBuilder::default();
+		builder.current_slot = relay_slot.into();
+
+		let (hash, state_proof) = builder.into_state_root_and_proof();
+
+		RelayChainStateProof::new(ParaId::from(200), hash, state_proof)
+			.expect("Should be able to construct state proof.")
+	}
+
+	fn assert_slot_info(expected_slot: u64, expected_authored: u32) {
+		let (slot, authored) = pallet::RelaySlotInfo::<Test>::get().unwrap();
+		assert_eq!(slot, Slot::from(expected_slot), "Slot stored in RelaySlotInfo is incorrect.");
+		assert_eq!(
+			authored, expected_authored,
+			"Number of authored blocks stored in RelaySlotInfo is incorrect."
+		);
+	}
+
+	#[test]
+	fn test_velocity() {
+		type Hook = FixedVelocityConsensusHook<Test, 6000, 2, 1>;
+
+		new_test_ext(1).execute_with(|| {
+			let state_proof = relay_chain_state_proof(10);
+			let (_, capacity) = Hook::on_state_proof(&state_proof);
+			assert_eq!(capacity, NonZeroU32::new(1).unwrap().into());
+			assert_slot_info(10, 1);
+
+			let (_, capacity) = Hook::on_state_proof(&state_proof);
+			assert_eq!(capacity, NonZeroU32::new(1).unwrap().into());
+			assert_slot_info(10, 2);
+		});
+	}
+
+	#[test]
+	#[should_panic(expected = "authored blocks limit is reached for the slot")]
+	fn test_exceeding_velocity_limit() {
+		const VELOCITY: u32 = 2;
+		type Hook = FixedVelocityConsensusHook<Test, 6000, VELOCITY, 1>;
+
+		new_test_ext(1).execute_with(|| {
+			let state_proof = relay_chain_state_proof(10);
+			for authored in 0..=VELOCITY + 1 {
+				Hook::on_state_proof(&state_proof);
+				assert_slot_info(10, authored + 1);
+			}
+		});
+	}
+
+	#[test]
+	fn test_para_slot_calculated_from_slot_duration() {
+		const VELOCITY: u32 = 2;
+		type Hook = FixedVelocityConsensusHook<Test, 3000, VELOCITY, 1>;
+
+		new_test_ext(6).execute_with(|| {
+			let state_proof = relay_chain_state_proof(10);
+			Hook::on_state_proof(&state_proof);
+
+			let para_slot = Slot::from(7);
+			pallet_aura::CurrentSlot::<Test>::put(para_slot);
+			Hook::on_state_proof(&state_proof);
+		});
+	}
+
+	#[test]
+	fn test_velocity_at_least_one() {
+		// Even though this is 0, one block should always be allowed.
+		const VELOCITY: u32 = 0;
+		type Hook = FixedVelocityConsensusHook<Test, 6000, VELOCITY, 1>;
+
+		new_test_ext(6).execute_with(|| {
+			let state_proof = relay_chain_state_proof(10);
+			Hook::on_state_proof(&state_proof);
+		});
+	}
+
+	#[test]
+	#[should_panic(
+		expected = "Parachain slot is too far in the future: parachain_slot=Slot(8), derived_from_relay_slot=Slot(5) velocity=2"
+	)]
+	fn test_para_slot_calculated_from_slot_duration_2() {
+		const VELOCITY: u32 = 2;
+		type Hook = FixedVelocityConsensusHook<Test, 3000, VELOCITY, 1>;
+
+		new_test_ext(8).execute_with(|| {
+			let state_proof = relay_chain_state_proof(10);
+			let (_, _) = Hook::on_state_proof(&state_proof);
+		});
+	}
+
+	#[test]
+	fn test_velocity_resets_on_new_relay_slot() {
+		const VELOCITY: u32 = 2;
+		type Hook = FixedVelocityConsensusHook<Test, 6000, VELOCITY, 1>;
+
+		new_test_ext(1).execute_with(|| {
+			let state_proof = relay_chain_state_proof(10);
+			for authored in 0..=VELOCITY {
+				Hook::on_state_proof(&state_proof);
+				assert_slot_info(10, authored + 1);
+			}
+
+			let state_proof = relay_chain_state_proof(11);
+			for authored in 0..=VELOCITY {
+				Hook::on_state_proof(&state_proof);
+				assert_slot_info(11, authored + 1);
+			}
+		});
+	}
+
+	#[test]
+	#[should_panic(
+		expected = "Slot moved backwards: stored_slot=Slot(10), relay_chain_slot=Slot(9)"
+	)]
+	fn test_backward_relay_slot_not_tolerated() {
+		type Hook = FixedVelocityConsensusHook<Test, 6000, 2, 1>;
+
+		new_test_ext(1).execute_with(|| {
+			let state_proof = relay_chain_state_proof(10);
+			Hook::on_state_proof(&state_proof);
+			assert_slot_info(10, 1);
+
+			let state_proof = relay_chain_state_proof(9);
+			Hook::on_state_proof(&state_proof);
+		});
+	}
+
+	#[test]
+	#[should_panic(
+		expected = "Parachain slot is too far in the future: parachain_slot=Slot(13), derived_from_relay_slot=Slot(10) velocity=2"
+	)]
+	fn test_future_parachain_slot_errors() {
+		type Hook = FixedVelocityConsensusHook<Test, 6000, 2, 1>;
+
+		new_test_ext(13).execute_with(|| {
+			let state_proof = relay_chain_state_proof(10);
+			Hook::on_state_proof(&state_proof);
+		});
+	}
+
+	#[test]
+	fn test_can_build_upon_true_when_empty() {
+		const VELOCITY: u32 = 2;
+		type Hook = FixedVelocityConsensusHook<Test, 6000, VELOCITY, 1>;
+
+		new_test_ext(1).execute_with(|| {
+			let hash = H256::repeat_byte(0x1);
+			assert!(Hook::can_build_upon(hash, Slot::from(1)));
+		});
+	}
+
+	#[test]
+	fn test_can_build_upon_respects_velocity() {
+		const VELOCITY: u32 = 2;
+		type Hook = FixedVelocityConsensusHook<Test, 6000, VELOCITY, 10>;
+
+		new_test_ext(1).execute_with(|| {
+			let hash = H256::repeat_byte(0x1);
+			let relay_slot = Slot::from(10);
+
+			set_relay_slot(10, VELOCITY - 1);
+			assert!(Hook::can_build_upon(hash, relay_slot));
+
+			set_relay_slot(10, VELOCITY);
+			assert!(Hook::can_build_upon(hash, relay_slot));
+
+			set_relay_slot(10, VELOCITY + 1);
+			// Velocity too high
+			assert!(!Hook::can_build_upon(hash, relay_slot));
+		});
+	}
+
+	#[test]
+	fn test_can_build_upon_slot_can_not_decrease() {
+		const VELOCITY: u32 = 2;
+		type Hook = FixedVelocityConsensusHook<Test, 6000, VELOCITY, 10>;
+
+		new_test_ext(1).execute_with(|| {
+			let hash = H256::repeat_byte(0x1);
+
+			set_relay_slot(10, VELOCITY);
+			// Slot moves backwards
+			assert!(!Hook::can_build_upon(hash, Slot::from(9)));
+		});
+	}
+
+	#[test]
+	fn test_can_build_upon_unincluded_segment_size() {
+		const VELOCITY: u32 = 2;
+		type Hook = FixedVelocityConsensusHook<Test, 6000, VELOCITY, 2>;
+
+		new_test_ext(1).execute_with(|| {
+			let relay_slot = Slot::from(10);
+
+			set_relay_slot(10, VELOCITY);
+			// Size after included is two, we can not build
+			let hash = H256::repeat_byte(0x1);
+			assert!(!Hook::can_build_upon(hash, relay_slot));
+
+			// Size after included is one, we can build
+			let hash = H256::repeat_byte(0x2);
+			assert!(Hook::can_build_upon(hash, relay_slot));
+		});
+	}
+}
diff --git a/cumulus/pallets/dmp-queue/src/tests.rs b/cumulus/pallets/dmp-queue/src/tests.rs
index 70d542ea2ed2abe9f09573205560dc96bf5c3011..368a1c0b436430495ffd005eb1a632dca16ab249 100644
--- a/cumulus/pallets/dmp-queue/src/tests.rs
+++ b/cumulus/pallets/dmp-queue/src/tests.rs
@@ -21,11 +21,7 @@
 use super::{migration::*, mock::*};
 use crate::*;
 
-use frame_support::{
-	pallet_prelude::*,
-	traits::{OnFinalize, OnIdle, OnInitialize},
-	StorageNoopGuard,
-};
+use frame_support::{pallet_prelude::*, traits::OnIdle, StorageNoopGuard};
 
 #[test]
 fn migration_works() {
@@ -183,14 +179,12 @@ fn migration_too_long_ignored() {
 }
 
 fn run_to_block(n: u64) {
-	assert!(n > System::block_number(), "Cannot go back in time");
-
-	while System::block_number() < n {
-		AllPalletsWithSystem::on_finalize(System::block_number());
-		System::set_block_number(System::block_number() + 1);
-		AllPalletsWithSystem::on_initialize(System::block_number());
-		AllPalletsWithSystem::on_idle(System::block_number(), Weight::MAX);
-	}
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default().after_initialize(|bn| {
+			AllPalletsWithSystem::on_idle(bn, Weight::MAX);
+		}),
+	);
 }
 
 fn assert_only_event(e: Event<Runtime>) {
diff --git a/cumulus/pallets/parachain-system/src/consensus_hook.rs b/cumulus/pallets/parachain-system/src/consensus_hook.rs
index 3062396a4e7865bf4e3545b39c6ad9fa6ded6a24..6d65bdc77186f4abf5da06b16ec4c36cf27e5fbb 100644
--- a/cumulus/pallets/parachain-system/src/consensus_hook.rs
+++ b/cumulus/pallets/parachain-system/src/consensus_hook.rs
@@ -22,7 +22,7 @@ use core::num::NonZeroU32;
 use frame_support::weights::Weight;
 
 /// The possible capacity of the unincluded segment.
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq)]
 pub struct UnincludedSegmentCapacity(UnincludedSegmentCapacityInner);
 
 impl UnincludedSegmentCapacity {
@@ -41,7 +41,7 @@ impl UnincludedSegmentCapacity {
 	}
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq)]
 pub(crate) enum UnincludedSegmentCapacityInner {
 	ExpectParentIncluded,
 	Value(NonZeroU32),
diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs
index 0fa759357f653dc98948f38d768ea4ddf749ab92..6857b08e66b7d78a2c3dbb1ad6e4c65e88b536b7 100644
--- a/cumulus/pallets/parachain-system/src/lib.rs
+++ b/cumulus/pallets/parachain-system/src/lib.rs
@@ -80,8 +80,7 @@ pub mod relay_state_snapshot;
 pub mod validate_block;
 
 use unincluded_segment::{
-	Ancestor, HrmpChannelUpdate, HrmpWatermarkUpdate, OutboundBandwidthLimits, SegmentTracker,
-	UsedBandwidth,
+	HrmpChannelUpdate, HrmpWatermarkUpdate, OutboundBandwidthLimits, SegmentTracker,
 };
 
 pub use consensus_hook::{ConsensusHook, ExpectParentIncluded};
@@ -109,6 +108,7 @@ pub use consensus_hook::{ConsensusHook, ExpectParentIncluded};
 /// ```
 pub use cumulus_pallet_parachain_system_proc_macro::register_validate_block;
 pub use relay_state_snapshot::{MessagingStateSnapshot, RelayChainStateProof};
+pub use unincluded_segment::{Ancestor, UsedBandwidth};
 
 pub use pallet::*;
 
diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml
index a164a8197f72e6e22bf9b4a1e3a49815583992dc..c6a8baeff3b37b147c82b21bfcf1668a90d48e1d 100644
--- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml
@@ -14,6 +14,7 @@ workspace = true
 
 # Substrate
 frame-support = { workspace = true }
+pallet-asset-rewards = { workspace = true }
 sp-core = { workspace = true }
 sp-keyring = { workspace = true }
 
diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs
index e2757f8b9a35b7ba07067ac708fe37babdd16af4..f5466a63f1f5b9b7018465268c15e2175d976a3c 100644
--- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs
@@ -58,6 +58,8 @@ pub const USDT_ID: u32 = 1984;
 
 pub const PENPAL_A_ID: u32 = 2000;
 pub const PENPAL_B_ID: u32 = 2001;
+pub const ASSET_HUB_ROCOCO_ID: u32 = 1000;
+pub const ASSET_HUB_WESTEND_ID: u32 = 1000;
 pub const ASSETS_PALLET_ID: u8 = 50;
 
 parameter_types! {
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml
index 9e8b8f2a52d7819e3af0422f9a4f9f534a407a1d..b53edb39c73b59555994a279f122af6c234a4ce0 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml
@@ -17,6 +17,7 @@ codec = { workspace = true }
 # Substrate
 frame-support = { workspace = true }
 pallet-asset-conversion = { workspace = true }
+pallet-asset-rewards = { workspace = true }
 pallet-assets = { workspace = true }
 pallet-balances = { workspace = true }
 pallet-message-queue = { workspace = true }
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs
index f3a1b3f5bfa282b21aba49bbe79a81780508f975..513ca278a319e49e326ae0725d62cbb1324f48bf 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs
@@ -76,10 +76,11 @@ mod imports {
 			genesis::ED as ROCOCO_ED,
 			rococo_runtime::{
 				governance as rococo_governance,
+				governance::pallet_custom_origins::Origin::Treasurer,
 				xcm_config::{
 					UniversalLocation as RococoUniversalLocation, XcmConfig as RococoXcmConfig,
 				},
-				OriginCaller as RococoOriginCaller,
+				Dmp, OriginCaller as RococoOriginCaller,
 			},
 			RococoRelayPallet as RococoPallet,
 		},
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs
index 88fa379c4072b97e867f3eccb68bb23b0eeedff2..75714acb07cd9e2b126bd4238e6c0377c8561e8f 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/mod.rs
@@ -16,6 +16,7 @@
 mod claim_assets;
 mod hybrid_transfers;
 mod reserve_transfer;
+mod reward_pool;
 mod send;
 mod set_xcm_versions;
 mod swap;
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reward_pool.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reward_pool.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2f3ee536a7b9948e17d6be9ccf2033cce625b580
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reward_pool.rs
@@ -0,0 +1,114 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::imports::*;
+use codec::Encode;
+use frame_support::{assert_ok, sp_runtime::traits::Dispatchable, traits::schedule::DispatchTime};
+use xcm_executor::traits::ConvertLocation;
+
+#[test]
+fn treasury_creates_asset_reward_pool() {
+	AssetHubRococo::execute_with(|| {
+		type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
+		type Balances = <AssetHubRococo as AssetHubRococoPallet>::Balances;
+
+		let treasurer =
+			Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]);
+		let treasurer_account =
+			ahr_xcm_config::LocationToAccountId::convert_location(&treasurer).unwrap();
+
+		assert_ok!(Balances::force_set_balance(
+			<AssetHubRococo as Chain>::RuntimeOrigin::root(),
+			treasurer_account.clone().into(),
+			ASSET_HUB_ROCOCO_ED * 100_000,
+		));
+
+		let events = AssetHubRococo::events();
+		match events.iter().last() {
+			Some(RuntimeEvent::Balances(pallet_balances::Event::BalanceSet { who, .. })) =>
+				assert_eq!(*who, treasurer_account),
+			_ => panic!("Expected Balances::BalanceSet event"),
+		}
+	});
+
+	Rococo::execute_with(|| {
+		type AssetHubRococoRuntimeCall = <AssetHubRococo as Chain>::RuntimeCall;
+		type AssetHubRococoRuntime = <AssetHubRococo as Chain>::Runtime;
+		type RococoRuntimeCall = <Rococo as Chain>::RuntimeCall;
+		type RococoRuntime = <Rococo as Chain>::Runtime;
+		type RococoRuntimeEvent = <Rococo as Chain>::RuntimeEvent;
+		type RococoRuntimeOrigin = <Rococo as Chain>::RuntimeOrigin;
+
+		Dmp::make_parachain_reachable(AssetHubRococo::para_id());
+
+		let staked_asset_id = bx!(RelayLocation::get());
+		let reward_asset_id = bx!(RelayLocation::get());
+
+		let reward_rate_per_block = 1_000_000_000;
+		let lifetime = 1_000_000_000;
+		let admin = None;
+
+		let create_pool_call =
+			RococoRuntimeCall::XcmPallet(pallet_xcm::Call::<RococoRuntime>::send {
+				dest: bx!(VersionedLocation::V4(
+					xcm::v4::Junction::Parachain(AssetHubRococo::para_id().into()).into()
+				)),
+				message: bx!(VersionedXcm::V5(Xcm(vec![
+					UnpaidExecution { weight_limit: Unlimited, check_origin: None },
+					Transact {
+						origin_kind: OriginKind::SovereignAccount,
+						fallback_max_weight: None,
+						call: AssetHubRococoRuntimeCall::AssetRewards(
+							pallet_asset_rewards::Call::<AssetHubRococoRuntime>::create_pool {
+								staked_asset_id,
+								reward_asset_id,
+								reward_rate_per_block,
+								expiry: DispatchTime::After(lifetime),
+								admin
+							}
+						)
+						.encode()
+						.into(),
+					}
+				]))),
+			});
+
+		let treasury_origin: RococoRuntimeOrigin = Treasurer.into();
+		assert_ok!(create_pool_call.dispatch(treasury_origin));
+
+		assert_expected_events!(
+			Rococo,
+			vec![
+				RococoRuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
+			]
+		);
+	});
+
+	AssetHubRococo::execute_with(|| {
+		type Runtime = <AssetHubRococo as Chain>::Runtime;
+		type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
+
+		assert_eq!(1, pallet_asset_rewards::Pools::<Runtime>::iter().count());
+
+		let events = AssetHubRococo::events();
+		match events.iter().last() {
+			Some(RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed {
+				success: true,
+				..
+			})) => (),
+			_ => panic!("Expected MessageQueue::Processed event"),
+		}
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml
index 5cd00c239e60d24df018bccb5937fe4ed4277659..ef68a53c3b18bd629ca2feda51bb7b76defb081e 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml
@@ -19,6 +19,7 @@ frame-metadata-hash-extension = { workspace = true, default-features = true }
 frame-support = { workspace = true }
 frame-system = { workspace = true }
 pallet-asset-conversion = { workspace = true }
+pallet-asset-rewards = { workspace = true }
 pallet-asset-tx-payment = { workspace = true }
 pallet-assets = { workspace = true }
 pallet-balances = { workspace = true }
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
index 36630e2d22217a73f691d6ed780f0471a7ba2b05..68dc87250f76bdcd458166069cf4e4b78f61e94f 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
@@ -79,8 +79,12 @@ mod imports {
 		},
 		westend_emulated_chain::{
 			genesis::ED as WESTEND_ED,
-			westend_runtime::xcm_config::{
-				UniversalLocation as WestendUniversalLocation, XcmConfig as WestendXcmConfig,
+			westend_runtime::{
+				governance::pallet_custom_origins::Origin::Treasurer,
+				xcm_config::{
+					UniversalLocation as WestendUniversalLocation, XcmConfig as WestendXcmConfig,
+				},
+				Dmp,
 			},
 			WestendRelayPallet as WestendPallet,
 		},
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs
index 0dfe7a85f4c2a08ecb7d7701cabe64d0045a13f2..576c44fc542fdc5fd26365078d96ac7ee4796515 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs
@@ -17,6 +17,7 @@ mod claim_assets;
 mod fellowship_treasury;
 mod hybrid_transfers;
 mod reserve_transfer;
+mod reward_pool;
 mod send;
 mod set_asset_claimer;
 mod set_xcm_versions;
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reward_pool.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reward_pool.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4df51abcacebf4ff0102b5d50271ca9cad7e14e6
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reward_pool.rs
@@ -0,0 +1,113 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::imports::*;
+use codec::Encode;
+use frame_support::{assert_ok, sp_runtime::traits::Dispatchable, traits::schedule::DispatchTime};
+use xcm_executor::traits::ConvertLocation;
+
+#[test]
+fn treasury_creates_asset_reward_pool() {
+	AssetHubWestend::execute_with(|| {
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+		type Balances = <AssetHubWestend as AssetHubWestendPallet>::Balances;
+
+		let treasurer =
+			Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]);
+		let treasurer_account =
+			ahw_xcm_config::LocationToAccountId::convert_location(&treasurer).unwrap();
+
+		assert_ok!(Balances::force_set_balance(
+			<AssetHubWestend as Chain>::RuntimeOrigin::root(),
+			treasurer_account.clone().into(),
+			ASSET_HUB_WESTEND_ED * 100_000,
+		));
+
+		let events = AssetHubWestend::events();
+		match events.iter().last() {
+			Some(RuntimeEvent::Balances(pallet_balances::Event::BalanceSet { who, .. })) =>
+				assert_eq!(*who, treasurer_account),
+			_ => panic!("Expected Balances::BalanceSet event"),
+		}
+	});
+	Westend::execute_with(|| {
+		type AssetHubWestendRuntimeCall = <AssetHubWestend as Chain>::RuntimeCall;
+		type AssetHubWestendRuntime = <AssetHubWestend as Chain>::Runtime;
+		type WestendRuntimeCall = <Westend as Chain>::RuntimeCall;
+		type WestendRuntime = <Westend as Chain>::Runtime;
+		type WestendRuntimeEvent = <Westend as Chain>::RuntimeEvent;
+		type WestendRuntimeOrigin = <Westend as Chain>::RuntimeOrigin;
+
+		Dmp::make_parachain_reachable(AssetHubWestend::para_id());
+
+		let staked_asset_id = bx!(RelayLocation::get());
+		let reward_asset_id = bx!(RelayLocation::get());
+
+		let reward_rate_per_block = 1_000_000_000;
+		let lifetime = 1_000_000_000;
+		let admin = None;
+
+		let create_pool_call =
+			WestendRuntimeCall::XcmPallet(pallet_xcm::Call::<WestendRuntime>::send {
+				dest: bx!(VersionedLocation::V4(
+					xcm::v4::Junction::Parachain(AssetHubWestend::para_id().into()).into()
+				)),
+				message: bx!(VersionedXcm::V5(Xcm(vec![
+					UnpaidExecution { weight_limit: Unlimited, check_origin: None },
+					Transact {
+						origin_kind: OriginKind::SovereignAccount,
+						fallback_max_weight: None,
+						call: AssetHubWestendRuntimeCall::AssetRewards(
+							pallet_asset_rewards::Call::<AssetHubWestendRuntime>::create_pool {
+								staked_asset_id,
+								reward_asset_id,
+								reward_rate_per_block,
+								expiry: DispatchTime::After(lifetime),
+								admin
+							}
+						)
+						.encode()
+						.into(),
+					}
+				]))),
+			});
+
+		let treasury_origin: WestendRuntimeOrigin = Treasurer.into();
+		assert_ok!(create_pool_call.dispatch(treasury_origin));
+
+		assert_expected_events!(
+			Westend,
+			vec![
+				WestendRuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
+			]
+		);
+	});
+
+	AssetHubWestend::execute_with(|| {
+		type Runtime = <AssetHubWestend as Chain>::Runtime;
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+
+		assert_eq!(1, pallet_asset_rewards::Pools::<Runtime>::iter().count());
+
+		let events = AssetHubWestend::events();
+		match events.iter().last() {
+			Some(RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed {
+				success: true,
+				..
+			})) => (),
+			_ => panic!("Expected MessageQueue::Processed event"),
+		}
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/governance.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/governance.rs
index ea438f80552e283cc0f9c1501d2d204e8bb2e709..3b1779e40b60a0f407d6001d3ffd6c4723ae349a 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/governance.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/governance.rs
@@ -396,7 +396,7 @@ fn relay_commands_add_remove_username_authority() {
 		);
 	});
 
-	// Now, remove the username authority with another priviledged XCM call.
+	// Now, remove the username authority with another privileged XCM call.
 	Westend::execute_with(|| {
 		type Runtime = <Westend as Chain>::Runtime;
 		type RuntimeCall = <Westend as Chain>::RuntimeCall;
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml
index abe59a8439a8af9f064dd179f07e629a9e7a5038..d612dd03c247a101049de02c4bf7822c73302783 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml
@@ -30,6 +30,7 @@ frame-try-runtime = { optional = true, workspace = true }
 pallet-asset-conversion = { workspace = true }
 pallet-asset-conversion-ops = { workspace = true }
 pallet-asset-conversion-tx-payment = { workspace = true }
+pallet-asset-rewards = { workspace = true }
 pallet-assets = { workspace = true }
 pallet-assets-freezer = { workspace = true }
 pallet-aura = { workspace = true }
@@ -61,6 +62,7 @@ sp-storage = { workspace = true }
 sp-transaction-pool = { workspace = true }
 sp-version = { workspace = true }
 sp-weights = { workspace = true }
+
 # num-traits feature needed for dex integer sq root:
 primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true }
 
@@ -123,6 +125,7 @@ runtime-benchmarks = [
 	"pallet-asset-conversion-ops/runtime-benchmarks",
 	"pallet-asset-conversion-tx-payment/runtime-benchmarks",
 	"pallet-asset-conversion/runtime-benchmarks",
+	"pallet-asset-rewards/runtime-benchmarks",
 	"pallet-assets-freezer/runtime-benchmarks",
 	"pallet-assets/runtime-benchmarks",
 	"pallet-balances/runtime-benchmarks",
@@ -162,6 +165,7 @@ try-runtime = [
 	"pallet-asset-conversion-ops/try-runtime",
 	"pallet-asset-conversion-tx-payment/try-runtime",
 	"pallet-asset-conversion/try-runtime",
+	"pallet-asset-rewards/try-runtime",
 	"pallet-assets-freezer/try-runtime",
 	"pallet-assets/try-runtime",
 	"pallet-aura/try-runtime",
@@ -212,6 +216,7 @@ std = [
 	"pallet-asset-conversion-ops/std",
 	"pallet-asset-conversion-tx-payment/std",
 	"pallet-asset-conversion/std",
+	"pallet-asset-rewards/std",
 	"pallet-assets-freezer/std",
 	"pallet-assets/std",
 	"pallet-aura/std",
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
index 1db152e39fd9818b60e0b19ee27b7646bae1d791..43b7bf0ba1184e095c2f53eeefceb2b88b154a9a 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs
@@ -35,7 +35,7 @@ use assets_common::{
 	foreign_creators::ForeignCreators,
 	local_and_foreign_assets::{LocalFromLeft, TargetFromLeft},
 	matching::{FromNetwork, FromSiblingParachain},
-	AssetIdForTrustBackedAssetsConvert,
+	AssetIdForPoolAssets, AssetIdForPoolAssetsConvert, AssetIdForTrustBackedAssetsConvert,
 };
 use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
 use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector};
@@ -61,9 +61,9 @@ use frame_support::{
 	genesis_builder_helper::{build_state, get_preset},
 	ord_parameter_types, parameter_types,
 	traits::{
-		fungible, fungibles, tokens::imbalance::ResolveAssetTo, AsEnsureOriginWithArg, ConstBool,
-		ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Equals, InstanceFilter,
-		TransformOrigin,
+		fungible, fungible::HoldConsideration, fungibles, tokens::imbalance::ResolveAssetTo,
+		AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8,
+		ConstantStoragePrice, EitherOfDiverse, Equals, InstanceFilter, TransformOrigin,
 	},
 	weights::{ConstantMultiplier, Weight, WeightToFee as _},
 	BoundedVec, PalletId,
@@ -84,8 +84,8 @@ use sp_runtime::{Perbill, RuntimeDebug};
 use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*};
 use xcm_config::{
 	ForeignAssetsConvertedConcreteId, GovernanceLocation, LocationToAccountId,
-	PoolAssetsConvertedConcreteId, TokenLocation, TrustBackedAssetsConvertedConcreteId,
-	TrustBackedAssetsPalletLocation,
+	PoolAssetsConvertedConcreteId, PoolAssetsPalletLocation, TokenLocation,
+	TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation,
 };
 
 #[cfg(test)]
@@ -111,6 +111,9 @@ use xcm_runtime_apis::{
 	fees::Error as XcmPaymentApiError,
 };
 
+#[cfg(feature = "runtime-benchmarks")]
+use frame_support::traits::PalletInfoAccess;
+
 use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
 
 impl_opaque_keys! {
@@ -217,8 +220,8 @@ impl pallet_balances::Config for Runtime {
 	type ReserveIdentifier = [u8; 8];
 	type RuntimeHoldReason = RuntimeHoldReason;
 	type RuntimeFreezeReason = RuntimeFreezeReason;
-	type FreezeIdentifier = ();
-	type MaxFreezes = ConstU32<0>;
+	type FreezeIdentifier = RuntimeFreezeReason;
+	type MaxFreezes = ConstU32<50>;
 	type DoneSlashHandler = ();
 }
 
@@ -302,7 +305,7 @@ impl pallet_assets::Config<PoolAssetsInstance> for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type Balance = Balance;
 	type RemoveItemsLimit = ConstU32<1000>;
-	type AssetId = u32;
+	type AssetId = AssetIdForPoolAssets;
 	type AssetIdParameter = u32;
 	type Currency = Balances;
 	type CreateOrigin =
@@ -343,8 +346,21 @@ pub type LocalAndForeignAssets = fungibles::UnionOf<
 	AccountId,
 >;
 
-/// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`.
-pub type NativeAndAssets = fungible::UnionOf<
+/// Union fungibles implementation for `AssetsFreezer` and `ForeignAssetsFreezer`.
+pub type LocalAndForeignAssetsFreezer = fungibles::UnionOf<
+	AssetsFreezer,
+	ForeignAssetsFreezer,
+	LocalFromLeft<
+		AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation, xcm::v5::Location>,
+		AssetIdForTrustBackedAssets,
+		xcm::v5::Location,
+	>,
+	xcm::v5::Location,
+	AccountId,
+>;
+
+/// Union fungibles implementation for [`LocalAndForeignAssets`] and [`Balances`].
+pub type NativeAndNonPoolAssets = fungible::UnionOf<
 	Balances,
 	LocalAndForeignAssets,
 	TargetFromLeft<TokenLocation, xcm::v5::Location>,
@@ -352,6 +368,45 @@ pub type NativeAndAssets = fungible::UnionOf<
 	AccountId,
 >;
 
+/// Union fungibles implementation for [`LocalAndForeignAssetsFreezer`] and [`Balances`].
+pub type NativeAndNonPoolAssetsFreezer = fungible::UnionOf<
+	Balances,
+	LocalAndForeignAssetsFreezer,
+	TargetFromLeft<TokenLocation, xcm::v5::Location>,
+	xcm::v5::Location,
+	AccountId,
+>;
+
+/// Union fungibles implementation for [`PoolAssets`] and [`NativeAndNonPoolAssets`].
+///
+/// NOTE: Should be kept updated to include ALL balances and assets in the runtime.
+pub type NativeAndAllAssets = fungibles::UnionOf<
+	PoolAssets,
+	NativeAndNonPoolAssets,
+	LocalFromLeft<
+		AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, xcm::v5::Location>,
+		AssetIdForPoolAssets,
+		xcm::v5::Location,
+	>,
+	xcm::v5::Location,
+	AccountId,
+>;
+
+/// Union fungibles implementation for [`PoolAssetsFreezer`] and [`NativeAndNonPoolAssetsFreezer`].
+///
+/// NOTE: Should be kept updated to include ALL balances and assets in the runtime.
+pub type NativeAndAllAssetsFreezer = fungibles::UnionOf<
+	PoolAssetsFreezer,
+	NativeAndNonPoolAssetsFreezer,
+	LocalFromLeft<
+		AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, xcm::v5::Location>,
+		AssetIdForPoolAssets,
+		xcm::v5::Location,
+	>,
+	xcm::v5::Location,
+	AccountId,
+>;
+
 pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter<
 	AssetConversionPalletId,
 	(xcm::v5::Location, xcm::v5::Location),
@@ -362,7 +417,7 @@ impl pallet_asset_conversion::Config for Runtime {
 	type Balance = Balance;
 	type HigherPrecisionBalance = sp_core::U256;
 	type AssetKind = xcm::v5::Location;
-	type Assets = NativeAndAssets;
+	type Assets = NativeAndNonPoolAssets;
 	type PoolId = (Self::AssetKind, Self::AssetKind);
 	type PoolLocator = pallet_asset_conversion::WithFirstAsset<
 		TokenLocation,
@@ -823,9 +878,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime {
 	type AssetId = xcm::v5::Location;
 	type OnChargeAssetTransaction = SwapAssetAdapter<
 		TokenLocation,
-		NativeAndAssets,
+		NativeAndNonPoolAssets,
 		AssetConversion,
-		ResolveAssetTo<StakingPot, NativeAndAssets>,
+		ResolveAssetTo<StakingPot, NativeAndNonPoolAssets>,
 	>;
 	type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo<Runtime>;
 	#[cfg(feature = "runtime-benchmarks")]
@@ -953,6 +1008,55 @@ impl pallet_xcm_bridge_hub_router::Config<ToWestendXcmRouterInstance> for Runtim
 	type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId;
 }
 
+#[cfg(feature = "runtime-benchmarks")]
+pub struct PalletAssetRewardsBenchmarkHelper;
+
+#[cfg(feature = "runtime-benchmarks")]
+impl pallet_asset_rewards::benchmarking::BenchmarkHelper<xcm::v5::Location>
+	for PalletAssetRewardsBenchmarkHelper
+{
+	fn staked_asset() -> Location {
+		Location::new(
+			0,
+			[PalletInstance(<Assets as PalletInfoAccess>::index() as u8), GeneralIndex(100)],
+		)
+	}
+	fn reward_asset() -> Location {
+		Location::new(
+			0,
+			[PalletInstance(<Assets as PalletInfoAccess>::index() as u8), GeneralIndex(101)],
+		)
+	}
+}
+
+parameter_types! {
+	pub const AssetRewardsPalletId: PalletId = PalletId(*b"py/astrd");
+	pub const RewardsPoolCreationHoldReason: RuntimeHoldReason =
+		RuntimeHoldReason::AssetRewards(pallet_asset_rewards::HoldReason::PoolCreation);
+	// 1 item, 135 bytes into the storage on pool creation.
+	pub const StakePoolCreationDeposit: Balance = deposit(1, 135);
+}
+
+impl pallet_asset_rewards::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type PalletId = AssetRewardsPalletId;
+	type Balance = Balance;
+	type Assets = NativeAndAllAssets;
+	type AssetsFreezer = NativeAndAllAssetsFreezer;
+	type AssetId = xcm::v5::Location;
+	type CreatePoolOrigin = EnsureSigned<AccountId>;
+	type RuntimeFreezeReason = RuntimeFreezeReason;
+	type Consideration = HoldConsideration<
+		AccountId,
+		Balances,
+		RewardsPoolCreationHoldReason,
+		ConstantStoragePrice<StakePoolCreationDeposit, Balance>,
+	>;
+	type WeightInfo = weights::pallet_asset_rewards::WeightInfo<Runtime>;
+	#[cfg(feature = "runtime-benchmarks")]
+	type BenchmarkHelper = PalletAssetRewardsBenchmarkHelper;
+}
+
 // Create the runtime by composing the FRAME pallets that were previously configured.
 construct_runtime!(
 	pub enum Runtime
@@ -998,10 +1102,13 @@ construct_runtime!(
 		NftFractionalization: pallet_nft_fractionalization = 54,
 		PoolAssets: pallet_assets::<Instance3> = 55,
 		AssetConversion: pallet_asset_conversion = 56,
+
 		AssetsFreezer: pallet_assets_freezer::<Instance1> = 57,
 		ForeignAssetsFreezer: pallet_assets_freezer::<Instance2> = 58,
 		PoolAssetsFreezer: pallet_assets_freezer::<Instance3> = 59,
 
+		AssetRewards: pallet_asset_rewards = 60,
+
 		// TODO: the pallet instance should be removed once all pools have migrated
 		// to the new account IDs.
 		AssetConversionMigration: pallet_asset_conversion_ops = 200,
@@ -1050,6 +1157,7 @@ pub type Migrations = (
 	>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 parameter_types! {
@@ -1192,6 +1300,7 @@ mod benches {
 		[pallet_assets, Foreign]
 		[pallet_assets, Pool]
 		[pallet_asset_conversion, AssetConversion]
+		[pallet_asset_rewards, AssetRewards]
 		[pallet_asset_conversion_tx_payment, AssetTxPayment]
 		[pallet_balances, Balances]
 		[pallet_message_queue, MessageQueue]
@@ -1502,6 +1611,12 @@ impl_runtime_apis! {
 		}
 	}
 
+	impl pallet_asset_rewards::AssetRewards<Block, Balance> for Runtime {
+		fn pool_creation_cost() -> Balance {
+			StakePoolCreationDeposit::get()
+		}
+	}
+
 	impl cumulus_primitives_core::GetCoreSelectorApi<Block> for Runtime {
 		fn core_selector() -> (CoreSelector, ClaimQueueOffset) {
 			ParachainSystem::core_selector()
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs
index ae78a56d8b3c1fef27e54e37b940098396426883..6893766ac72d2113f993e54d3b28e5e136acac3d 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs
@@ -24,6 +24,7 @@ pub mod frame_system_extensions;
 pub mod pallet_asset_conversion;
 pub mod pallet_asset_conversion_ops;
 pub mod pallet_asset_conversion_tx_payment;
+pub mod pallet_asset_rewards;
 pub mod pallet_assets_foreign;
 pub mod pallet_assets_local;
 pub mod pallet_assets_pool;
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_rewards.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_rewards.rs
new file mode 100644
index 0000000000000000000000000000000000000000..218c93c51035037e5d325dc5b3d31897e164f0ec
--- /dev/null
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_asset_rewards.rs
@@ -0,0 +1,217 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Autogenerated weights for `pallet_asset_rewards`
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
+//! DATE: 2025-01-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! WORST CASE MAP SIZE: `1000000`
+//! HOSTNAME: `runner-ys-ssygq-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024
+
+// Executed Command:
+// target/production/polkadot-parachain
+// benchmark
+// pallet
+// --steps=50
+// --repeat=20
+// --extrinsic=*
+// --wasm-execution=compiled
+// --heap-pages=4096
+// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json
+// --pallet=pallet_asset_rewards
+// --chain=asset-hub-rococo-dev
+// --header=./cumulus/file_header.txt
+// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(missing_docs)]
+
+use frame_support::{traits::Get, weights::Weight};
+use core::marker::PhantomData;
+
+/// Weight functions for `pallet_asset_rewards`.
+pub struct WeightInfo<T>(PhantomData<T>);
+impl<T: frame_system::Config> pallet_asset_rewards::WeightInfo for WeightInfo<T> {
+	/// Storage: `Assets::Asset` (r:2 w:0)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::NextPoolId` (r:1 w:1)
+	/// Proof: `AssetRewards::NextPoolId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolCost` (r:0 w:1)
+	/// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::Pools` (r:0 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	fn create_pool() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `358`
+		//  Estimated: `6360`
+		// Minimum execution time: 65_882_000 picoseconds.
+		Weight::from_parts(67_073_000, 0)
+			.saturating_add(Weight::from_parts(0, 6360))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(5))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:1 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:1 w:0)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn stake() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `872`
+		//  Estimated: `4809`
+		// Minimum execution time: 56_950_000 picoseconds.
+		Weight::from_parts(58_088_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(4))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:1 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:1 w:0)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn unstake() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `872`
+		//  Estimated: `4809`
+		// Minimum execution time: 59_509_000 picoseconds.
+		Weight::from_parts(61_064_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(4))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:0)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:0)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn harvest_rewards() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1072`
+		//  Estimated: `6208`
+		// Minimum execution time: 80_685_000 picoseconds.
+		Weight::from_parts(83_505_000, 0)
+			.saturating_add(Weight::from_parts(0, 6208))
+			.saturating_add(T::DbWeight::get().reads(6))
+			.saturating_add(T::DbWeight::get().writes(4))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	fn set_pool_reward_rate_per_block() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `318`
+		//  Estimated: `4809`
+		// Minimum execution time: 17_032_000 picoseconds.
+		Weight::from_parts(17_628_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	fn set_pool_admin() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `318`
+		//  Estimated: `4809`
+		// Minimum execution time: 15_290_000 picoseconds.
+		Weight::from_parts(16_212_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	fn set_pool_expiry_block() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `318`
+		//  Estimated: `4809`
+		// Minimum execution time: 17_721_000 picoseconds.
+		Weight::from_parts(18_603_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:0)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:0)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	fn deposit_reward_tokens() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `747`
+		//  Estimated: `6208`
+		// Minimum execution time: 67_754_000 picoseconds.
+		Weight::from_parts(69_428_000, 0)
+			.saturating_add(Weight::from_parts(0, 6208))
+			.saturating_add(T::DbWeight::get().reads(6))
+			.saturating_add(T::DbWeight::get().writes(4))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:0)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:2 w:2)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolCost` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:0 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`)
+	fn cleanup_pool() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1105`
+		//  Estimated: `6208`
+		// Minimum execution time: 127_524_000 picoseconds.
+		Weight::from_parts(130_238_000, 0)
+			.saturating_add(Weight::from_parts(0, 6208))
+			.saturating_add(T::DbWeight::get().reads(10))
+			.saturating_add(T::DbWeight::get().writes(10))
+	}
+}
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs
index 08b2f520c4b9ac1abb39f9d185c821dec7241d0f..0c6ff5e4bfddc6c236fe1ec4a5a5f425027f41f5 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs
@@ -76,6 +76,10 @@ parameter_types! {
 	pub TrustBackedAssetsPalletLocation: Location =
 		PalletInstance(TrustBackedAssetsPalletIndex::get()).into();
 	pub TrustBackedAssetsPalletIndex: u8 = <Assets as PalletInfoAccess>::index() as u8;
+	pub TrustBackedAssetsPalletLocationV3: xcm::v3::Location =
+		xcm::v3::Junction::PalletInstance(<Assets as PalletInfoAccess>::index() as u8).into();
+	pub PoolAssetsPalletLocationV3: xcm::v3::Location =
+		xcm::v3::Junction::PalletInstance(<PoolAssets as PalletInfoAccess>::index() as u8).into();
 	pub ForeignAssetsPalletLocation: Location =
 		PalletInstance(<ForeignAssets as PalletInfoAccess>::index() as u8).into();
 	pub PoolAssetsPalletLocation: Location =
@@ -336,7 +340,7 @@ pub type TrustedTeleporters = (
 /// asset and the asset required for fee payment.
 pub type PoolAssetsExchanger = SingleAssetExchangeAdapter<
 	crate::AssetConversion,
-	crate::NativeAndAssets,
+	crate::NativeAndNonPoolAssets,
 	(
 		TrustBackedAssetsAsLocation<TrustBackedAssetsPalletLocation, Balance, xcm::v5::Location>,
 		ForeignAssetsConvertedConcreteId,
@@ -387,7 +391,7 @@ impl xcm_executor::Config for XcmConfig {
 			TokenLocation,
 			crate::AssetConversion,
 			WeightToFee,
-			crate::NativeAndAssets,
+			crate::NativeAndNonPoolAssets,
 			(
 				TrustBackedAssetsAsLocation<
 					TrustBackedAssetsPalletLocation,
@@ -396,7 +400,7 @@ impl xcm_executor::Config for XcmConfig {
 				>,
 				ForeignAssetsConvertedConcreteId,
 			),
-			ResolveAssetTo<StakingPot, crate::NativeAndAssets>,
+			ResolveAssetTo<StakingPot, crate::NativeAndNonPoolAssets>,
 			AccountId,
 		>,
 		// This trader allows to pay with `is_sufficient=true` "Trust Backed" assets from dedicated
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml
index cb10ae9a480034b3067e436a4760692357789645..65ef63a7fb356c1b2c20a9fcddbc4bdc3d3b8bff 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml
@@ -30,6 +30,7 @@ frame-try-runtime = { optional = true, workspace = true }
 pallet-asset-conversion = { workspace = true }
 pallet-asset-conversion-ops = { workspace = true }
 pallet-asset-conversion-tx-payment = { workspace = true }
+pallet-asset-rewards = { workspace = true }
 pallet-assets = { workspace = true }
 pallet-assets-freezer = { workspace = true }
 pallet-aura = { workspace = true }
@@ -62,6 +63,7 @@ sp-std = { workspace = true }
 sp-storage = { workspace = true }
 sp-transaction-pool = { workspace = true }
 sp-version = { workspace = true }
+
 # num-traits feature needed for dex integer sq root:
 primitive-types = { features = ["codec", "num-traits", "scale-info"], workspace = true }
 
@@ -125,6 +127,7 @@ runtime-benchmarks = [
 	"pallet-asset-conversion-ops/runtime-benchmarks",
 	"pallet-asset-conversion-tx-payment/runtime-benchmarks",
 	"pallet-asset-conversion/runtime-benchmarks",
+	"pallet-asset-rewards/runtime-benchmarks",
 	"pallet-assets-freezer/runtime-benchmarks",
 	"pallet-assets/runtime-benchmarks",
 	"pallet-balances/runtime-benchmarks",
@@ -166,6 +169,7 @@ try-runtime = [
 	"pallet-asset-conversion-ops/try-runtime",
 	"pallet-asset-conversion-tx-payment/try-runtime",
 	"pallet-asset-conversion/try-runtime",
+	"pallet-asset-rewards/try-runtime",
 	"pallet-assets-freezer/try-runtime",
 	"pallet-assets/try-runtime",
 	"pallet-aura/try-runtime",
@@ -218,6 +222,7 @@ std = [
 	"pallet-asset-conversion-ops/std",
 	"pallet-asset-conversion-tx-payment/std",
 	"pallet-asset-conversion/std",
+	"pallet-asset-rewards/std",
 	"pallet-assets-freezer/std",
 	"pallet-assets/std",
 	"pallet-aura/std",
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
index 71cfdc58cceb3071e9c4e251579c9d6e4ff2168d..3ef5e87f24c47fa8375ced87be441ee025d8b3aa 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
@@ -33,7 +33,7 @@ extern crate alloc;
 use alloc::{vec, vec::Vec};
 use assets_common::{
 	local_and_foreign_assets::{LocalFromLeft, TargetFromLeft},
-	AssetIdForTrustBackedAssetsConvert,
+	AssetIdForPoolAssets, AssetIdForPoolAssetsConvert, AssetIdForTrustBackedAssetsConvert,
 };
 use codec::{Decode, Encode, MaxEncodedLen};
 use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
@@ -44,10 +44,12 @@ use frame_support::{
 	genesis_builder_helper::{build_state, get_preset},
 	ord_parameter_types, parameter_types,
 	traits::{
-		fungible, fungibles,
+		fungible,
+		fungible::HoldConsideration,
+		fungibles,
 		tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect},
-		AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Equals,
-		InstanceFilter, Nothing, TransformOrigin,
+		AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8,
+		ConstantStoragePrice, Equals, InstanceFilter, Nothing, TransformOrigin,
 	},
 	weights::{ConstantMultiplier, Weight, WeightToFee as _},
 	BoundedVec, PalletId,
@@ -81,8 +83,8 @@ use testnet_parachains_constants::westend::{
 };
 use xcm_config::{
 	ForeignAssetsConvertedConcreteId, LocationToAccountId, PoolAssetsConvertedConcreteId,
-	TrustBackedAssetsConvertedConcreteId, TrustBackedAssetsPalletLocation, WestendLocation,
-	XcmOriginToTransactDispatchOrigin,
+	PoolAssetsPalletLocation, TrustBackedAssetsConvertedConcreteId,
+	TrustBackedAssetsPalletLocation, WestendLocation, XcmOriginToTransactDispatchOrigin,
 };
 
 #[cfg(any(feature = "std", test))]
@@ -93,11 +95,15 @@ use assets_common::{
 	matching::{FromNetwork, FromSiblingParachain},
 };
 use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate};
+use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
 use xcm::{
 	latest::prelude::AssetId,
 	prelude::{VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm},
 };
 
+#[cfg(feature = "runtime-benchmarks")]
+use frame_support::traits::PalletInfoAccess;
+
 #[cfg(feature = "runtime-benchmarks")]
 use xcm::latest::prelude::{
 	Asset, Assets as XcmAssets, Fungible, Here, InteriorLocation, Junction, Junction::*, Location,
@@ -109,8 +115,6 @@ use xcm_runtime_apis::{
 	fees::Error as XcmPaymentApiError,
 };
 
-use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
-
 impl_opaque_keys! {
 	pub struct SessionKeys {
 		pub aura: Aura,
@@ -125,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
 	spec_name: alloc::borrow::Cow::Borrowed("westmint"),
 	impl_name: alloc::borrow::Cow::Borrowed("westmint"),
 	authoring_version: 1,
-	spec_version: 1_017_003,
+	spec_version: 1_017_004,
 	impl_version: 0,
 	apis: RUNTIME_API_VERSIONS,
 	transaction_version: 16,
@@ -218,8 +222,8 @@ impl pallet_balances::Config for Runtime {
 	type ReserveIdentifier = [u8; 8];
 	type RuntimeHoldReason = RuntimeHoldReason;
 	type RuntimeFreezeReason = RuntimeFreezeReason;
-	type FreezeIdentifier = ();
-	type MaxFreezes = ConstU32<0>;
+	type FreezeIdentifier = RuntimeFreezeReason;
+	type MaxFreezes = ConstU32<50>;
 	type DoneSlashHandler = ();
 }
 
@@ -342,8 +346,21 @@ pub type LocalAndForeignAssets = fungibles::UnionOf<
 	AccountId,
 >;
 
+/// Union fungibles implementation for `AssetsFreezer` and `ForeignAssetsFreezer`.
+pub type LocalAndForeignAssetsFreezer = fungibles::UnionOf<
+	AssetsFreezer,
+	ForeignAssetsFreezer,
+	LocalFromLeft<
+		AssetIdForTrustBackedAssetsConvert<TrustBackedAssetsPalletLocation, xcm::v5::Location>,
+		AssetIdForTrustBackedAssets,
+		xcm::v5::Location,
+	>,
+	xcm::v5::Location,
+	AccountId,
+>;
+
 /// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`.
-pub type NativeAndAssets = fungible::UnionOf<
+pub type NativeAndNonPoolAssets = fungible::UnionOf<
 	Balances,
 	LocalAndForeignAssets,
 	TargetFromLeft<WestendLocation, xcm::v5::Location>,
@@ -351,6 +368,45 @@ pub type NativeAndAssets = fungible::UnionOf<
 	AccountId,
 >;
 
+/// Union fungibles implementation for [`LocalAndForeignAssetsFreezer`] and [`Balances`].
+pub type NativeAndNonPoolAssetsFreezer = fungible::UnionOf<
+	Balances,
+	LocalAndForeignAssetsFreezer,
+	TargetFromLeft<WestendLocation, xcm::v5::Location>,
+	xcm::v5::Location,
+	AccountId,
+>;
+
+/// Union fungibles implementation for [`PoolAssets`] and [`NativeAndNonPoolAssets`].
+///
+/// NOTE: Should be kept updated to include ALL balances and assets in the runtime.
+pub type NativeAndAllAssets = fungibles::UnionOf<
+	PoolAssets,
+	NativeAndNonPoolAssets,
+	LocalFromLeft<
+		AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, xcm::v5::Location>,
+		AssetIdForPoolAssets,
+		xcm::v5::Location,
+	>,
+	xcm::v5::Location,
+	AccountId,
+>;
+
+/// Union fungibles implementation for [`PoolAssetsFreezer`] and [`NativeAndNonPoolAssetsFreezer`].
+///
+/// NOTE: Should be kept updated to include ALL balances and assets in the runtime.
+pub type NativeAndAllAssetsFreezer = fungibles::UnionOf<
+	PoolAssetsFreezer,
+	NativeAndNonPoolAssetsFreezer,
+	LocalFromLeft<
+		AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, xcm::v5::Location>,
+		AssetIdForPoolAssets,
+		xcm::v5::Location,
+	>,
+	xcm::v5::Location,
+	AccountId,
+>;
+
 pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter<
 	AssetConversionPalletId,
 	(xcm::v5::Location, xcm::v5::Location),
@@ -361,7 +417,7 @@ impl pallet_asset_conversion::Config for Runtime {
 	type Balance = Balance;
 	type HigherPrecisionBalance = sp_core::U256;
 	type AssetKind = xcm::v5::Location;
-	type Assets = NativeAndAssets;
+	type Assets = NativeAndNonPoolAssets;
 	type PoolId = (Self::AssetKind, Self::AssetKind);
 	type PoolLocator = pallet_asset_conversion::WithFirstAsset<
 		WestendLocation,
@@ -389,6 +445,55 @@ impl pallet_asset_conversion::Config for Runtime {
 	>;
 }
 
+#[cfg(feature = "runtime-benchmarks")]
+pub struct PalletAssetRewardsBenchmarkHelper;
+
+#[cfg(feature = "runtime-benchmarks")]
+impl pallet_asset_rewards::benchmarking::BenchmarkHelper<xcm::v5::Location>
+	for PalletAssetRewardsBenchmarkHelper
+{
+	fn staked_asset() -> Location {
+		Location::new(
+			0,
+			[PalletInstance(<Assets as PalletInfoAccess>::index() as u8), GeneralIndex(100)],
+		)
+	}
+	fn reward_asset() -> Location {
+		Location::new(
+			0,
+			[PalletInstance(<Assets as PalletInfoAccess>::index() as u8), GeneralIndex(101)],
+		)
+	}
+}
+
+parameter_types! {
+	pub const AssetRewardsPalletId: PalletId = PalletId(*b"py/astrd");
+	pub const RewardsPoolCreationHoldReason: RuntimeHoldReason =
+		RuntimeHoldReason::AssetRewards(pallet_asset_rewards::HoldReason::PoolCreation);
+	// 1 item, 135 bytes into the storage on pool creation.
+	pub const StakePoolCreationDeposit: Balance = deposit(1, 135);
+}
+
+impl pallet_asset_rewards::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type PalletId = AssetRewardsPalletId;
+	type Balance = Balance;
+	type Assets = NativeAndAllAssets;
+	type AssetsFreezer = NativeAndAllAssetsFreezer;
+	type AssetId = xcm::v5::Location;
+	type CreatePoolOrigin = EnsureSigned<AccountId>;
+	type RuntimeFreezeReason = RuntimeFreezeReason;
+	type Consideration = HoldConsideration<
+		AccountId,
+		Balances,
+		RewardsPoolCreationHoldReason,
+		ConstantStoragePrice<StakePoolCreationDeposit, Balance>,
+	>;
+	type WeightInfo = weights::pallet_asset_rewards::WeightInfo<Runtime>;
+	#[cfg(feature = "runtime-benchmarks")]
+	type BenchmarkHelper = PalletAssetRewardsBenchmarkHelper;
+}
+
 impl pallet_asset_conversion_ops::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type PriorAccountIdConverter = pallet_asset_conversion::AccountIdConverterNoSeed<
@@ -817,9 +922,9 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime {
 	type AssetId = xcm::v5::Location;
 	type OnChargeAssetTransaction = SwapAssetAdapter<
 		WestendLocation,
-		NativeAndAssets,
+		NativeAndNonPoolAssets,
 		AssetConversion,
-		ResolveAssetTo<StakingPot, NativeAndAssets>,
+		ResolveAssetTo<StakingPot, NativeAndNonPoolAssets>,
 	>;
 	type WeightInfo = weights::pallet_asset_conversion_tx_payment::WeightInfo<Runtime>;
 	#[cfg(feature = "runtime-benchmarks")]
@@ -953,11 +1058,6 @@ parameter_types! {
 	pub CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(30);
 }
 
-type EventRecord = frame_system::EventRecord<
-	<Runtime as frame_system::Config>::RuntimeEvent,
-	<Runtime as frame_system::Config>::Hash,
->;
-
 impl pallet_revive::Config for Runtime {
 	type Time = Timestamp;
 	type Currency = Balances;
@@ -1041,11 +1141,14 @@ construct_runtime!(
 		NftFractionalization: pallet_nft_fractionalization = 54,
 		PoolAssets: pallet_assets::<Instance3> = 55,
 		AssetConversion: pallet_asset_conversion = 56,
+
 		AssetsFreezer: pallet_assets_freezer::<Instance1> = 57,
 		ForeignAssetsFreezer: pallet_assets_freezer::<Instance2> = 58,
 		PoolAssetsFreezer: pallet_assets_freezer::<Instance3> = 59,
 		Revive: pallet_revive = 60,
 
+		AssetRewards: pallet_asset_rewards = 61,
+
 		StateTrieMigration: pallet_state_trie_migration = 70,
 
 		// TODO: the pallet instance should be removed once all pools have migrated
@@ -1129,6 +1232,7 @@ pub type Migrations = (
 	>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 /// Asset Hub Westend has some undecodable storage, delete it.
@@ -1322,6 +1426,7 @@ mod benches {
 		[pallet_assets, Foreign]
 		[pallet_assets, Pool]
 		[pallet_asset_conversion, AssetConversion]
+		[pallet_asset_rewards, AssetRewards]
 		[pallet_asset_conversion_tx_payment, AssetTxPayment]
 		[pallet_balances, Balances]
 		[pallet_message_queue, MessageQueue]
@@ -1679,6 +1784,12 @@ impl_runtime_apis! {
 		}
 	}
 
+	impl pallet_asset_rewards::AssetRewards<Block, Balance> for Runtime {
+		fn pool_creation_cost() -> Balance {
+			StakePoolCreationDeposit::get()
+		}
+	}
+
 	impl cumulus_primitives_core::GetCoreSelectorApi<Block> for Runtime {
 		fn core_selector() -> (CoreSelector, ClaimQueueOffset) {
 			ParachainSystem::core_selector()
@@ -2073,7 +2184,7 @@ impl_runtime_apis! {
 		}
 	}
 
-	impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, EventRecord> for Runtime
+	impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber> for Runtime
 	{
 		fn balance(address: H160) -> U256 {
 			Revive::evm_balance(&address)
@@ -2108,7 +2219,7 @@ impl_runtime_apis! {
 			gas_limit: Option<Weight>,
 			storage_deposit_limit: Option<Balance>,
 			input_data: Vec<u8>,
-		) -> pallet_revive::ContractResult<pallet_revive::ExecReturnValue, Balance, EventRecord> {
+		) -> pallet_revive::ContractResult<pallet_revive::ExecReturnValue, Balance> {
 			let blockweights= <Runtime as frame_system::Config>::BlockWeights::get();
 			Revive::bare_call(
 				RuntimeOrigin::signed(origin),
@@ -2117,8 +2228,6 @@ impl_runtime_apis! {
 				gas_limit.unwrap_or(blockweights.max_block),
 				pallet_revive::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
 				input_data,
-				pallet_revive::DebugInfo::UnsafeDebug,
-				pallet_revive::CollectEvents::UnsafeCollect,
 			)
 		}
 
@@ -2130,7 +2239,7 @@ impl_runtime_apis! {
 			code: pallet_revive::Code,
 			data: Vec<u8>,
 			salt: Option<[u8; 32]>,
-		) -> pallet_revive::ContractResult<pallet_revive::InstantiateReturnValue, Balance, EventRecord>
+		) -> pallet_revive::ContractResult<pallet_revive::InstantiateReturnValue, Balance>
 		{
 			let blockweights= <Runtime as frame_system::Config>::BlockWeights::get();
 			Revive::bare_instantiate(
@@ -2141,8 +2250,6 @@ impl_runtime_apis! {
 				code,
 				data,
 				salt,
-				pallet_revive::DebugInfo::UnsafeDebug,
-				pallet_revive::CollectEvents::UnsafeCollect,
 			)
 		}
 
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs
index 442b58635f48ad5ae910cc3612d45cab512e4a51..d653838ad80e61d9dd96a705a1871fe4c4858fa8 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs
@@ -23,6 +23,7 @@ pub mod frame_system_extensions;
 pub mod pallet_asset_conversion;
 pub mod pallet_asset_conversion_ops;
 pub mod pallet_asset_conversion_tx_payment;
+pub mod pallet_asset_rewards;
 pub mod pallet_assets_foreign;
 pub mod pallet_assets_local;
 pub mod pallet_assets_pool;
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_rewards.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_rewards.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3bbc289fec7b61433041c8007cc1d579a74ea7e5
--- /dev/null
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_asset_rewards.rs
@@ -0,0 +1,217 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Cumulus is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Autogenerated weights for `pallet_asset_rewards`
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
+//! DATE: 2025-01-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! WORST CASE MAP SIZE: `1000000`
+//! HOSTNAME: `runner-ys-ssygq-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024
+
+// Executed Command:
+// target/production/polkadot-parachain
+// benchmark
+// pallet
+// --steps=50
+// --repeat=20
+// --extrinsic=*
+// --wasm-execution=compiled
+// --heap-pages=4096
+// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json
+// --pallet=pallet_asset_rewards
+// --chain=asset-hub-westend-dev
+// --header=./cumulus/file_header.txt
+// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(missing_docs)]
+
+use frame_support::{traits::Get, weights::Weight};
+use core::marker::PhantomData;
+
+/// Weight functions for `pallet_asset_rewards`.
+pub struct WeightInfo<T>(PhantomData<T>);
+impl<T: frame_system::Config> pallet_asset_rewards::WeightInfo for WeightInfo<T> {
+	/// Storage: `Assets::Asset` (r:2 w:0)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::NextPoolId` (r:1 w:1)
+	/// Proof: `AssetRewards::NextPoolId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolCost` (r:0 w:1)
+	/// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::Pools` (r:0 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	fn create_pool() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `392`
+		//  Estimated: `6360`
+		// Minimum execution time: 60_734_000 picoseconds.
+		Weight::from_parts(61_828_000, 0)
+			.saturating_add(Weight::from_parts(0, 6360))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(5))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:1 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:1 w:0)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn stake() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `906`
+		//  Estimated: `4809`
+		// Minimum execution time: 56_014_000 picoseconds.
+		Weight::from_parts(58_487_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(4))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:1 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:1 w:0)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn unstake() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `906`
+		//  Estimated: `4809`
+		// Minimum execution time: 59_071_000 picoseconds.
+		Weight::from_parts(60_631_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(4))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:0)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:0)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn harvest_rewards() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1106`
+		//  Estimated: `6208`
+		// Minimum execution time: 80_585_000 picoseconds.
+		Weight::from_parts(82_186_000, 0)
+			.saturating_add(Weight::from_parts(0, 6208))
+			.saturating_add(T::DbWeight::get().reads(6))
+			.saturating_add(T::DbWeight::get().writes(4))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	fn set_pool_reward_rate_per_block() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `318`
+		//  Estimated: `4809`
+		// Minimum execution time: 17_083_000 picoseconds.
+		Weight::from_parts(17_816_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	fn set_pool_admin() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `318`
+		//  Estimated: `4809`
+		// Minimum execution time: 15_269_000 picoseconds.
+		Weight::from_parts(15_881_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	fn set_pool_expiry_block() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `318`
+		//  Estimated: `4809`
+		// Minimum execution time: 17_482_000 picoseconds.
+		Weight::from_parts(18_124_000, 0)
+			.saturating_add(Weight::from_parts(0, 4809))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:0)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:0)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	fn deposit_reward_tokens() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `781`
+		//  Estimated: `6208`
+		// Minimum execution time: 66_644_000 picoseconds.
+		Weight::from_parts(67_950_000, 0)
+			.saturating_add(Weight::from_parts(0, 6208))
+			.saturating_add(T::DbWeight::get().reads(6))
+			.saturating_add(T::DbWeight::get().writes(4))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(1344), added: 3819, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:0)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:2 w:2)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolCost` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(157), added: 2632, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:0 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(87), added: 2562, mode: `MaxEncodedLen`)
+	fn cleanup_pool() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1139`
+		//  Estimated: `6208`
+		// Minimum execution time: 124_136_000 picoseconds.
+		Weight::from_parts(128_642_000, 0)
+			.saturating_add(Weight::from_parts(0, 6208))
+			.saturating_add(T::DbWeight::get().reads(10))
+			.saturating_add(T::DbWeight::get().writes(10))
+	}
+}
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
index b4e938f1f8b5709515a855302d1e26b03afb968e..1ea2ce5136abd09348e8843b48b7a356b00c57f0 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
@@ -65,6 +65,7 @@ use xcm_executor::XcmExecutor;
 parameter_types! {
 	pub const RootLocation: Location = Location::here();
 	pub const WestendLocation: Location = Location::parent();
+	pub const GovernanceLocation: Location = Location::parent();
 	pub const RelayNetwork: Option<NetworkId> = Some(NetworkId::ByGenesis(WESTEND_GENESIS_HASH));
 	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
 	pub UniversalLocation: InteriorLocation =
@@ -359,7 +360,7 @@ pub type TrustedTeleporters = (
 /// asset and the asset required for fee payment.
 pub type PoolAssetsExchanger = SingleAssetExchangeAdapter<
 	crate::AssetConversion,
-	crate::NativeAndAssets,
+	crate::NativeAndNonPoolAssets,
 	(
 		TrustBackedAssetsAsLocation<TrustBackedAssetsPalletLocation, Balance, xcm::v5::Location>,
 		ForeignAssetsConvertedConcreteId,
@@ -409,7 +410,7 @@ impl xcm_executor::Config for XcmConfig {
 			WestendLocation,
 			crate::AssetConversion,
 			WeightToFee,
-			crate::NativeAndAssets,
+			crate::NativeAndNonPoolAssets,
 			(
 				TrustBackedAssetsAsLocation<
 					TrustBackedAssetsPalletLocation,
@@ -418,7 +419,7 @@ impl xcm_executor::Config for XcmConfig {
 				>,
 				ForeignAssetsConvertedConcreteId,
 			),
-			ResolveAssetTo<StakingPot, crate::NativeAndAssets>,
+			ResolveAssetTo<StakingPot, crate::NativeAndNonPoolAssets>,
 			AccountId,
 		>,
 		// This trader allows to pay with `is_sufficient=true` "Trust Backed" assets from dedicated
diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs
index 25c2df6b68d16c9eb282b564e8b7a4acf23f9e3a..50b1b63146bc863945aa9602797db4478d08bd53 100644
--- a/cumulus/parachains/runtimes/assets/common/src/lib.rs
+++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs
@@ -123,10 +123,11 @@ pub type ForeignAssetsConvertedConcreteId<
 	BalanceConverter,
 >;
 
-type AssetIdForPoolAssets = u32;
+pub type AssetIdForPoolAssets = u32;
+
 /// `Location` vs `AssetIdForPoolAssets` converter for `PoolAssets`.
-pub type AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation> =
-	AsPrefixedGeneralIndex<PoolAssetsPalletLocation, AssetIdForPoolAssets, TryConvertInto>;
+pub type AssetIdForPoolAssetsConvert<PoolAssetsPalletLocation, L = Location> =
+	AsPrefixedGeneralIndex<PoolAssetsPalletLocation, AssetIdForPoolAssets, TryConvertInto, L>;
 /// [`MatchedConvertedConcreteId`] converter dedicated for `PoolAssets`
 pub type PoolAssetsConvertedConcreteId<PoolAssetsPalletLocation, Balance> =
 	MatchedConvertedConcreteId<
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
index 35af034310d9202337f38984e8c92c8480bf8a9a..67bc06a9321ece8e8b3684b2ea302b19db24d76a 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs
@@ -184,6 +184,7 @@ pub type Migrations = (
 	pallet_bridge_relayers::migration::v1::MigrationToV1<Runtime, ()>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 parameter_types! {
diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
index 2c2e01b4d21dc7160e73b09d3612da05a594af87..3824a4e9a7cb3bd9eb66b524ed7525dcb92c5a69 100644
--- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs
@@ -171,6 +171,7 @@ pub type Migrations = (
 	bridge_to_ethereum_config::migrations::MigrationForXcmV5<Runtime>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 parameter_types! {
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
index e9adc4d1eae74fe0098a5de59889c10b7a101867..5eafc2960cc88cefe2be69d26467b7a60bb4c5d5 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
@@ -770,6 +770,7 @@ type Migrations = (
 	pallet_core_fellowship::migration::MigrateV0ToV1<Runtime, FellowshipCoreInstance>,
 	// unreleased
 	pallet_core_fellowship::migration::MigrateV0ToV1<Runtime, AmbassadorCoreInstance>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 /// Executive: handles dispatch to the various modules.
diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
index 3348a635df01a63c18369e9258e8f1b3a316bb18..eaaaf0a9a9a7b019f245885e823c6cf635b60551 100644
--- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs
@@ -118,6 +118,7 @@ pub type Migrations = (
 	cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5<Runtime>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 type EventRecord = frame_system::EventRecord<
diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs
index e9171c79afaec8b871b9e88d52e283d04b1437fb..622a40e1d8dc09c9f9f31f0fd80f136b724763b2 100644
--- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs
@@ -129,6 +129,7 @@ pub type Migrations = (
 	pallet_broker::migration::MigrateV3ToV4<Runtime, BrokerMigrationV4BlockConversion>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 /// Executive: handles dispatch to the various modules.
diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs
index 975856b3b6ff8eeebdc65427d693181eb9919eee..7312c9c1639d29a69b11ee520e0b21d6289f815a 100644
--- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs
@@ -129,6 +129,7 @@ pub type Migrations = (
 	pallet_broker::migration::MigrateV3ToV4<Runtime, BrokerMigrationV4BlockConversion>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 /// Executive: handles dispatch to the various modules.
diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs
index ffdd86c500e5b4da5eb56a3fb7f16c16083745cb..cb0282b17a6ce54bfbd13061d71c14ae284cf301 100644
--- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs
+++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs
@@ -116,6 +116,7 @@ pub type Migrations = (
 	cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5<Runtime>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 /// Executive: handles dispatch to the various modules.
diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs
index ee6b0db55b91ad02155225bf8f72f6c00123886b..050256dd4f6a38a2908c16c57c9a0d3ee255bdaa 100644
--- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs
@@ -115,6 +115,7 @@ pub type Migrations = (
 	pallet_collator_selection::migration::v2::MigrationToV2<Runtime>,
 	// permanent
 	pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
+	cumulus_pallet_aura_ext::migration::MigrateV0ToV1<Runtime>,
 );
 
 /// Executive: handles dispatch to the various modules.
diff --git a/cumulus/primitives/aura/src/lib.rs b/cumulus/primitives/aura/src/lib.rs
index aeeee5f8bafa16e02577f164dfe69e106c49c702..4e7d7dc3e79d629f593fa59900021dea6390b0e9 100644
--- a/cumulus/primitives/aura/src/lib.rs
+++ b/cumulus/primitives/aura/src/lib.rs
@@ -34,10 +34,14 @@ sp_api::decl_runtime_apis! {
 	/// When the unincluded segment is short, Aura chains will allow authors to create multiple
 	/// blocks per slot in order to build a backlog. When it is saturated, this API will limit
 	/// the amount of blocks that can be created.
+	///
+	/// Changes:
+	/// - Version 2: Update to `can_build_upon` to take a relay chain `Slot` instead of a parachain `Slot`.
+	#[api_version(2)]
 	pub trait AuraUnincludedSegmentApi {
 		/// Whether it is legal to extend the chain, assuming the given block is the most
 		/// recently included one as-of the relay parent that will be built against, and
-		/// the given slot.
+		/// the given relay chain slot.
 		///
 		/// This should be consistent with the logic the runtime uses when validating blocks to
 		/// avoid issues.
diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs
index ff14b747973cf3e1b0658dc147a1749e2ca20ed1..d9b1e7fd9d04cbcf28933e10b91961bf4bdd8432 100644
--- a/cumulus/xcm/xcm-emulator/src/lib.rs
+++ b/cumulus/xcm/xcm-emulator/src/lib.rs
@@ -1118,6 +1118,7 @@ macro_rules! decl_test_networks {
 				) -> $crate::ParachainInherentData {
 					let mut sproof = $crate::RelayStateSproofBuilder::default();
 					sproof.para_id = para_id.into();
+					sproof.current_slot = $crate::polkadot_primitives::Slot::from(relay_parent_number as u64);
 
 					// egress channel
 					let e_index = sproof.hrmp_egress_channel_index.get_or_insert_with(Vec::new);
diff --git a/docs/contributor/prdoc.md b/docs/contributor/prdoc.md
index 1f6252425e69538c5d724a89dbac414cb1174aab..b3f7a7e94f0ce840aa0401d5828579f4cd130531 100644
--- a/docs/contributor/prdoc.md
+++ b/docs/contributor/prdoc.md
@@ -81,9 +81,6 @@ picked if no other applies. The `None` option is equivalent to the `R0-silent` l
 level. Experimental and private APIs are exempt from bumping and can be broken at any time. Please
 read the [Crate Section](../RELEASE.md) of the RELEASE doc about them.
 
-> **Note**: There is currently no CI in place to sanity check this information, but should be added
-> soon.
-
 ### Example
 
 For example when you modified two crates and record the changes:
@@ -106,3 +103,21 @@ you do not need to bump a crate that had a SemVer breaking change only from re-e
 crate with a breaking change.  
 `minor` an `patch` bumps do not need to be inherited, since `cargo` will automatically update them
 to the latest compatible version.
+
+### Overwrite CI check
+
+The `check-semver` CI check is doing sanity checks based on the provided `PRDoc` and the mentioned
+crate version bumps. The tooling is not perfect and it may recommends incorrect bumps of the version.
+The CI check can be forced to accept the provided version bump. This can be done like:
+
+```yaml
+crates:
+  - name: frame-example
+    bump: major
+    validate: false
+  - name: frame-example-pallet
+    bump: minor
+```
+
+By putting `validate: false` for `frame-example`, the version bump is ignored by the tooling. For
+`frame-example-pallet` the version bump is still validated by the CI check.
diff --git a/docs/sdk/packages/guides/first-pallet/Cargo.toml b/docs/sdk/packages/guides/first-pallet/Cargo.toml
index a1411580119da456e389715c0f47bc2df8691c8f..e6325c31781a6571055983607879f0c920a13f0f 100644
--- a/docs/sdk/packages/guides/first-pallet/Cargo.toml
+++ b/docs/sdk/packages/guides/first-pallet/Cargo.toml
@@ -18,7 +18,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 [dependencies]
 codec = { workspace = true }
 docify = { workspace = true }
-frame = { workspace = true, features = ["experimental", "runtime"] }
+frame = { workspace = true, features = ["runtime"] }
 scale-info = { workspace = true }
 
 [features]
diff --git a/docs/sdk/packages/guides/first-runtime/Cargo.toml b/docs/sdk/packages/guides/first-runtime/Cargo.toml
index 303d5c5e7f5fc8a11c52d48d0105f4872e77bceb..8ed17dea1b71ec90295a2a75d6fa064e289ed6ca 100644
--- a/docs/sdk/packages/guides/first-runtime/Cargo.toml
+++ b/docs/sdk/packages/guides/first-runtime/Cargo.toml
@@ -18,7 +18,7 @@ scale-info = { workspace = true }
 serde_json = { workspace = true }
 
 # this is a frame-based runtime, thus importing `frame` with runtime feature enabled.
-frame = { workspace = true, features = ["experimental", "runtime"] }
+frame = { workspace = true, features = ["runtime"] }
 
 # pallets that we want to use
 pallet-balances = { workspace = true }
diff --git a/polkadot/node/core/approval-voting/src/approval_checking.rs b/polkadot/node/core/approval-voting/src/approval_checking.rs
index 3b7262a46826eeba32811ad58f449b93dfaae61a..c7f38619ea1ff0c2db37c014a3f6c69ebf3332ba 100644
--- a/polkadot/node/core/approval-voting/src/approval_checking.rs
+++ b/polkadot/node/core/approval-voting/src/approval_checking.rs
@@ -712,13 +712,13 @@ mod tests {
 		}
 		.into();
 
-		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
-		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
+		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
+		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
 
-		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1);
-		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1);
+		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1, false);
+		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1, false);
 
-		approval_entry.import_assignment(2, ValidatorIndex(4), block_tick + 2);
+		approval_entry.import_assignment(2, ValidatorIndex(4), block_tick + 2, false);
 
 		let approvals = bitvec![u8, BitOrderLsb0; 1; 5];
 
@@ -757,8 +757,10 @@ mod tests {
 		}
 		.into();
 
-		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
-		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick);
+		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
+		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, true);
+		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick, false);
+		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick, true);
 
 		let approvals = bitvec![u8, BitOrderLsb0; 0; 10];
 
@@ -798,10 +800,10 @@ mod tests {
 		}
 		.into();
 
-		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
-		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
+		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
+		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
 
-		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick);
+		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick, false);
 
 		let mut approvals = bitvec![u8, BitOrderLsb0; 0; 10];
 		approvals.set(0, true);
@@ -844,11 +846,11 @@ mod tests {
 		}
 		.into();
 
-		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
-		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
+		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
+		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
 
-		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick);
-		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick);
+		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick, false);
+		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick, false);
 
 		let mut approvals = bitvec![u8, BitOrderLsb0; 0; n_validators];
 		approvals.set(0, true);
@@ -913,14 +915,24 @@ mod tests {
 		}
 		.into();
 
-		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
-		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
+		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
+		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
 
-		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1);
-		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1);
+		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1, false);
+		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1, false);
 
-		approval_entry.import_assignment(2, ValidatorIndex(4), block_tick + no_show_duration + 2);
-		approval_entry.import_assignment(2, ValidatorIndex(5), block_tick + no_show_duration + 2);
+		approval_entry.import_assignment(
+			2,
+			ValidatorIndex(4),
+			block_tick + no_show_duration + 2,
+			false,
+		);
+		approval_entry.import_assignment(
+			2,
+			ValidatorIndex(5),
+			block_tick + no_show_duration + 2,
+			false,
+		);
 
 		let mut approvals = bitvec![u8, BitOrderLsb0; 0; n_validators];
 		approvals.set(0, true);
@@ -1007,14 +1019,24 @@ mod tests {
 		}
 		.into();
 
-		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
-		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
+		approval_entry.import_assignment(0, ValidatorIndex(0), block_tick, false);
+		approval_entry.import_assignment(0, ValidatorIndex(1), block_tick, false);
 
-		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1);
-		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1);
+		approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1, false);
+		approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1, false);
 
-		approval_entry.import_assignment(2, ValidatorIndex(4), block_tick + no_show_duration + 2);
-		approval_entry.import_assignment(2, ValidatorIndex(5), block_tick + no_show_duration + 2);
+		approval_entry.import_assignment(
+			2,
+			ValidatorIndex(4),
+			block_tick + no_show_duration + 2,
+			false,
+		);
+		approval_entry.import_assignment(
+			2,
+			ValidatorIndex(5),
+			block_tick + no_show_duration + 2,
+			false,
+		);
 
 		let mut approvals = bitvec![u8, BitOrderLsb0; 0; n_validators];
 		approvals.set(0, true);
@@ -1066,7 +1088,7 @@ mod tests {
 			},
 		);
 
-		approval_entry.import_assignment(3, ValidatorIndex(6), block_tick);
+		approval_entry.import_assignment(3, ValidatorIndex(6), block_tick, false);
 		approvals.set(6, true);
 
 		let tranche_now = no_show_duration as DelayTranche + 3;
@@ -1176,7 +1198,7 @@ mod tests {
 			// Populate the requested tranches. The assignments aren't inspected in
 			// this test.
 			for &t in &test_tranche {
-				approval_entry.import_assignment(t, ValidatorIndex(0), 0)
+				approval_entry.import_assignment(t, ValidatorIndex(0), 0, false);
 			}
 
 			let filled_tranches = filled_tranche_iterator(approval_entry.tranches());
diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs
index 27361df3731047614c436345460a2d29703aa15c..2deca5a1aba8a55168c5f6ddf6463b5d0b8ed480 100644
--- a/polkadot/node/core/approval-voting/src/lib.rs
+++ b/polkadot/node/core/approval-voting/src/lib.rs
@@ -132,6 +132,16 @@ pub(crate) const LOG_TARGET: &str = "parachain::approval-voting";
 // The max number of ticks we delay sending the approval after we are ready to issue the approval
 const MAX_APPROVAL_COALESCE_WAIT_TICKS: Tick = 12;
 
+// If the node restarted and the tranche has passed without the assignment
+// being trigger, we won't trigger the assignment at restart because we don't have
+// an wakeup schedule for it.
+// The solution, is to always schedule a wake up after the restart and let the
+// process_wakeup to decide if the assignment needs to be triggered.
+// We need to have a delay after restart to give time to the node to catch up with
+// messages and not trigger its assignment unnecessarily, because it hasn't seen
+// the assignments from the other validators.
+const RESTART_WAKEUP_DELAY: Tick = 12;
+
 /// Configuration for the approval voting subsystem
 #[derive(Debug, Clone)]
 pub struct Config {
@@ -1837,7 +1847,20 @@ async fn distribution_messages_for_activation<Sender: SubsystemSender<RuntimeApi
 			match candidate_entry.approval_entry(&block_hash) {
 				Some(approval_entry) => {
 					match approval_entry.local_statements() {
-						(None, None) | (None, Some(_)) => {}, // second is impossible case.
+						(None, None) =>
+							if approval_entry
+								.our_assignment()
+								.map(|assignment| !assignment.triggered())
+								.unwrap_or(false)
+							{
+								actions.push(Action::ScheduleWakeup {
+									block_hash,
+									block_number: block_entry.block_number(),
+									candidate_hash: *candidate_hash,
+									tick: state.clock.tick_now() + RESTART_WAKEUP_DELAY,
+								})
+							},
+						(None, Some(_)) => {}, // second is impossible case.
 						(Some(assignment), None) => {
 							let claimed_core_indices =
 								get_core_indices_on_startup(&assignment.cert().kind, *core_index);
@@ -2785,8 +2808,15 @@ where
 						Vec::new(),
 					)),
 			};
-			is_duplicate &= approval_entry.is_assigned(assignment.validator);
-			approval_entry.import_assignment(tranche, assignment.validator, tick_now);
+
+			let is_duplicate_for_candidate = approval_entry.is_assigned(assignment.validator);
+			is_duplicate &= is_duplicate_for_candidate;
+			approval_entry.import_assignment(
+				tranche,
+				assignment.validator,
+				tick_now,
+				is_duplicate_for_candidate,
+			);
 
 			// We've imported a new assignment, so we need to schedule a wake-up for when that might
 			// no-show.
diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs
index a5d42d9fd6e6520087dcad75b862917b60047a07..14c678913dc3b163e221211212a09cc1dcf8a0d4 100644
--- a/polkadot/node/core/approval-voting/src/persisted_entries.rs
+++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs
@@ -172,7 +172,7 @@ impl ApprovalEntry {
 		});
 
 		our.map(|a| {
-			self.import_assignment(a.tranche(), a.validator_index(), tick_now);
+			self.import_assignment(a.tranche(), a.validator_index(), tick_now, false);
 
 			(a.cert().clone(), a.validator_index(), a.tranche())
 		})
@@ -197,6 +197,7 @@ impl ApprovalEntry {
 		tranche: DelayTranche,
 		validator_index: ValidatorIndex,
 		tick_now: Tick,
+		is_duplicate: bool,
 	) {
 		// linear search probably faster than binary. not many tranches typically.
 		let idx = match self.tranches.iter().position(|t| t.tranche >= tranche) {
@@ -213,8 +214,15 @@ impl ApprovalEntry {
 				self.tranches.len() - 1
 			},
 		};
-
-		self.tranches[idx].assignments.push((validator_index, tick_now));
+		// At restart we might have duplicate assignments because approval-distribution is not
+		// persistent across restarts, so avoid adding duplicates.
+		// We already know if we have seen an assignment from this validator and since this
+		// function is on the hot path we can avoid iterating through tranches by using
+		// !is_duplicate to determine if it is already present in the vector and does not need
+		// adding.
+		if !is_duplicate {
+			self.tranches[idx].assignments.push((validator_index, tick_now));
+		}
 		self.assigned_validators.set(validator_index.0 as _, true);
 	}
 
diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs
index b72993fe1a94a82a4657ac8397b3713e9ccb8405..9fe716833b882880ca8fb62c13c3b277be966785 100644
--- a/polkadot/node/core/approval-voting/src/tests.rs
+++ b/polkadot/node/core/approval-voting/src/tests.rs
@@ -5380,6 +5380,252 @@ fn subsystem_sends_assignment_approval_in_correct_order_on_approval_restart() {
 	});
 }
 
+// Test that if the subsystem missed the triggering of some tranches because it was not running
+// it launches the missed assignements on restart.
+#[test]
+fn subsystem_launches_missed_assignments_on_restart() {
+	let test_tranche = 20;
+	let assignment_criteria = Box::new(MockAssignmentCriteria(
+		move || {
+			let mut assignments = HashMap::new();
+			let _ = assignments.insert(
+				CoreIndex(0),
+				approval_db::v2::OurAssignment {
+					cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFDelay {
+						core_index: CoreIndex(0),
+					}),
+					tranche: test_tranche,
+					validator_index: ValidatorIndex(0),
+					triggered: false,
+				}
+				.into(),
+			);
+
+			assignments
+		},
+		|_| Ok(0),
+	));
+	let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build();
+	let store = config.backend();
+	let store_clone = config.backend();
+
+	test_harness(config, |test_harness| async move {
+		let TestHarness { mut virtual_overseer, clock, sync_oracle_handle } = test_harness;
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => {
+				rx.send(Ok(0)).unwrap();
+			}
+		);
+
+		let block_hash = Hash::repeat_byte(0x01);
+		let fork_block_hash = Hash::repeat_byte(0x02);
+		let candidate_commitments = CandidateCommitments::default();
+		let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash);
+		candidate_receipt.commitments_hash = candidate_commitments.hash();
+		let candidate_hash = candidate_receipt.hash();
+		let slot = Slot::from(1);
+		let (chain_builder, _session_info) = build_chain_with_two_blocks_with_one_candidate_each(
+			block_hash,
+			fork_block_hash,
+			slot,
+			sync_oracle_handle,
+			candidate_receipt,
+		)
+		.await;
+		chain_builder.build(&mut virtual_overseer).await;
+
+		assert!(!clock.inner.lock().current_wakeup_is(1));
+		clock.inner.lock().wakeup_all(1);
+
+		assert!(clock.inner.lock().current_wakeup_is(slot_to_tick(slot) + test_tranche as u64));
+		clock.inner.lock().wakeup_all(slot_to_tick(slot));
+
+		futures_timer::Delay::new(Duration::from_millis(200)).await;
+
+		clock.inner.lock().wakeup_all(slot_to_tick(slot + 2));
+
+		assert_eq!(clock.inner.lock().wakeups.len(), 0);
+
+		futures_timer::Delay::new(Duration::from_millis(200)).await;
+
+		let candidate_entry = store.load_candidate_entry(&candidate_hash).unwrap().unwrap();
+		let our_assignment =
+			candidate_entry.approval_entry(&block_hash).unwrap().our_assignment().unwrap();
+		assert!(!our_assignment.triggered());
+
+		// Assignment is not triggered because its tranches has not been reached.
+		virtual_overseer
+	});
+
+	// Restart a new approval voting subsystem with the same database and major syncing true until
+	// the last leaf.
+	let config = HarnessConfigBuilder::default().backend(store_clone).major_syncing(true).build();
+
+	test_harness(config, |test_harness| async move {
+		let TestHarness { mut virtual_overseer, clock, sync_oracle_handle } = test_harness;
+		let slot = Slot::from(1);
+		// 1. Set the clock to the to a tick past the tranche where the assignment should be
+		//    triggered.
+		clock.inner.lock().set_tick(slot_to_tick(slot) + 2 * test_tranche as u64);
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => {
+				rx.send(Ok(0)).unwrap();
+			}
+		);
+
+		let block_hash = Hash::repeat_byte(0x01);
+		let fork_block_hash = Hash::repeat_byte(0x02);
+		let candidate_commitments = CandidateCommitments::default();
+		let mut candidate_receipt = dummy_candidate_receipt_v2(block_hash);
+		candidate_receipt.commitments_hash = candidate_commitments.hash();
+		let (chain_builder, session_info) = build_chain_with_two_blocks_with_one_candidate_each(
+			block_hash,
+			fork_block_hash,
+			slot,
+			sync_oracle_handle,
+			candidate_receipt,
+		)
+		.await;
+
+		chain_builder.build(&mut virtual_overseer).await;
+
+		futures_timer::Delay::new(Duration::from_millis(2000)).await;
+
+		// On major syncing ending Approval voting should send all the necessary messages for a
+		// candidate to be approved.
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NewBlocks(
+				_,
+			)) => {
+			}
+		);
+
+		clock
+			.inner
+			.lock()
+			.wakeup_all(slot_to_tick(slot) + 2 * test_tranche as u64 + RESTART_WAKEUP_DELAY - 1);
+
+		// Subsystem should not send any messages because the assignment is not triggered yet.
+		assert!(overseer_recv(&mut virtual_overseer).timeout(TIMEOUT / 2).await.is_none());
+
+		// Set the clock to the tick where the assignment should be triggered.
+		clock
+			.inner
+			.lock()
+			.wakeup_all(slot_to_tick(slot) + 2 * test_tranche as u64 + RESTART_WAKEUP_DELAY);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::RuntimeApi(
+				RuntimeApiMessage::Request(
+					_,
+					RuntimeApiRequest::SessionInfo(_, si_tx),
+				)
+			) => {
+				si_tx.send(Ok(Some(session_info.clone()))).unwrap();
+			}
+		);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::RuntimeApi(
+				RuntimeApiMessage::Request(
+					_,
+					RuntimeApiRequest::SessionExecutorParams(_, si_tx),
+				)
+			) => {
+				// Make sure all SessionExecutorParams calls are not made for the leaf (but for its relay parent)
+				si_tx.send(Ok(Some(ExecutorParams::default()))).unwrap();
+			}
+		);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::RuntimeApi(
+				RuntimeApiMessage::Request(_, RuntimeApiRequest::NodeFeatures(_, si_tx), )
+			) => {
+				si_tx.send(Ok(NodeFeatures::EMPTY)).unwrap();
+			}
+		);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment(
+				_,
+				_,
+			)) => {
+			}
+		);
+
+		// Guarantees the approval work has been relaunched.
+		recover_available_data(&mut virtual_overseer).await;
+		fetch_validation_code(&mut virtual_overseer).await;
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive {
+				exec_kind,
+				response_sender,
+				..
+			}) if exec_kind == PvfExecKind::Approval => {
+				response_sender.send(Ok(ValidationResult::Valid(Default::default(), Default::default())))
+					.unwrap();
+			}
+		);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => {
+				let _ = sender.send(Ok(ApprovalVotingParams {
+					max_approval_coalesce_count: 1,
+				}));
+			}
+		);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_))
+		);
+
+		clock
+			.inner
+			.lock()
+			.wakeup_all(slot_to_tick(slot) + 2 * test_tranche as u64 + RESTART_WAKEUP_DELAY);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeAssignment(
+				_,
+				_,
+			)) => {
+			}
+		);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::ApprovalVotingParams(_, sender))) => {
+				let _ = sender.send(Ok(ApprovalVotingParams {
+					max_approval_coalesce_count: 1,
+				}));
+			}
+		);
+
+		assert_matches!(
+			overseer_recv(&mut virtual_overseer).await,
+			AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval(_))
+		);
+
+		// Assert that there are no more messages being sent by the subsystem
+		assert!(overseer_recv(&mut virtual_overseer).timeout(TIMEOUT / 2).await.is_none());
+
+		virtual_overseer
+	});
+}
+
 // Test we correctly update the timer when we mark the beginning of gathering assignments.
 #[test]
 fn test_gathering_assignments_statements() {
diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs
index 65942c127b1cc05260c67c44b2350cf8b8b4490c..dea29f53cad4f586c49c6261c6862a2b1eb05f71 100644
--- a/polkadot/runtime/common/src/assigned_slots/mod.rs
+++ b/polkadot/runtime/common/src/assigned_slots/mod.rs
@@ -788,39 +788,14 @@ mod tests {
 		t.into()
 	}
 
-	fn run_to_block(n: BlockNumber) {
-		while System::block_number() < n {
-			let mut block = System::block_number();
-			// on_finalize hooks
-			AssignedSlots::on_finalize(block);
-			Slots::on_finalize(block);
-			Parachains::on_finalize(block);
-			ParasShared::on_finalize(block);
-			Configuration::on_finalize(block);
-			Balances::on_finalize(block);
-			System::on_finalize(block);
-			// Set next block
-			System::set_block_number(block + 1);
-			block = System::block_number();
-			// on_initialize hooks
-			System::on_initialize(block);
-			Balances::on_initialize(block);
-			Configuration::on_initialize(block);
-			ParasShared::on_initialize(block);
-			Parachains::on_initialize(block);
-			Slots::on_initialize(block);
-			AssignedSlots::on_initialize(block);
-		}
-	}
-
 	#[test]
 	fn basic_setup_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 			assert_eq!(AssignedSlots::current_lease_period_index(), 0);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 
-			run_to_block(3);
+			System::run_to_block::<AllPalletsWithSystem>(3);
 			assert_eq!(AssignedSlots::current_lease_period_index(), 1);
 		});
 	}
@@ -828,7 +803,7 @@ mod tests {
 	#[test]
 	fn assign_perm_slot_fails_for_unknown_para() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_noop!(
 				AssignedSlots::assign_perm_parachain_slot(
@@ -843,7 +818,7 @@ mod tests {
 	#[test]
 	fn assign_perm_slot_fails_for_invalid_origin() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_noop!(
 				AssignedSlots::assign_perm_parachain_slot(
@@ -858,7 +833,7 @@ mod tests {
 	#[test]
 	fn assign_perm_slot_fails_when_not_parathread() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -881,7 +856,7 @@ mod tests {
 	#[test]
 	fn assign_perm_slot_fails_when_existing_lease() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -920,7 +895,7 @@ mod tests {
 	#[test]
 	fn assign_perm_slot_fails_when_max_perm_slots_exceeded() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -967,7 +942,7 @@ mod tests {
 	fn assign_perm_slot_succeeds_for_parathread() {
 		new_test_ext().execute_with(|| {
 			let mut block = 1;
-			run_to_block(block);
+			System::run_to_block::<AllPalletsWithSystem>(block);
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
 				ParaId::from(1_u32),
@@ -1000,7 +975,7 @@ mod tests {
 				assert_eq!(Slots::already_leased(ParaId::from(1_u32), 0, 2), true);
 
 				block += 1;
-				run_to_block(block);
+				System::run_to_block::<AllPalletsWithSystem>(block);
 			}
 
 			// Para lease ended, downgraded back to parathread (on-demand parachain)
@@ -1012,7 +987,7 @@ mod tests {
 	#[test]
 	fn assign_temp_slot_fails_for_unknown_para() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_noop!(
 				AssignedSlots::assign_temp_parachain_slot(
@@ -1028,7 +1003,7 @@ mod tests {
 	#[test]
 	fn assign_temp_slot_fails_for_invalid_origin() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_noop!(
 				AssignedSlots::assign_temp_parachain_slot(
@@ -1044,7 +1019,7 @@ mod tests {
 	#[test]
 	fn assign_temp_slot_fails_when_not_parathread() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -1068,7 +1043,7 @@ mod tests {
 	#[test]
 	fn assign_temp_slot_fails_when_existing_lease() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -1109,7 +1084,7 @@ mod tests {
 	#[test]
 	fn assign_temp_slot_fails_when_max_temp_slots_exceeded() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			// Register 6 paras & a temp slot for each
 			for n in 0..=5 {
@@ -1151,7 +1126,7 @@ mod tests {
 	fn assign_temp_slot_succeeds_for_single_parathread() {
 		new_test_ext().execute_with(|| {
 			let mut block = 1;
-			run_to_block(block);
+			System::run_to_block::<AllPalletsWithSystem>(block);
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
 				ParaId::from(1_u32),
@@ -1195,7 +1170,7 @@ mod tests {
 				assert_eq!(Slots::already_leased(ParaId::from(1_u32), 0, 1), true);
 
 				block += 1;
-				run_to_block(block);
+				System::run_to_block::<AllPalletsWithSystem>(block);
 			}
 
 			// Block 6
@@ -1210,7 +1185,7 @@ mod tests {
 
 			// Block 12
 			// Para should get a turn after TemporarySlotLeasePeriodLength * LeasePeriod blocks
-			run_to_block(12);
+			System::run_to_block::<AllPalletsWithSystem>(12);
 			println!("block #{}", block);
 			println!("lease period #{}", AssignedSlots::current_lease_period_index());
 			println!("lease {:?}", slots::Leases::<Test>::get(ParaId::from(1_u32)));
@@ -1225,7 +1200,7 @@ mod tests {
 	fn assign_temp_slot_succeeds_for_multiple_parathreads() {
 		new_test_ext().execute_with(|| {
 			// Block 1, Period 0
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			// Register 6 paras & a temp slot for each
 			// (3 slots in current lease period, 3 in the next one)
@@ -1251,7 +1226,7 @@ mod tests {
 			// Block 1-5, Period 0-1
 			for n in 1..=5 {
 				if n > 1 {
-					run_to_block(n);
+					System::run_to_block::<AllPalletsWithSystem>(n);
 				}
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), true);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), false);
@@ -1264,7 +1239,7 @@ mod tests {
 
 			// Block 6-11, Period 2-3
 			for n in 6..=11 {
-				run_to_block(n);
+				System::run_to_block::<AllPalletsWithSystem>(n);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), false);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), false);
@@ -1276,7 +1251,7 @@ mod tests {
 
 			// Block 12-17, Period 4-5
 			for n in 12..=17 {
-				run_to_block(n);
+				System::run_to_block::<AllPalletsWithSystem>(n);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), false);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), false);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), false);
@@ -1288,7 +1263,7 @@ mod tests {
 
 			// Block 18-23, Period 6-7
 			for n in 18..=23 {
-				run_to_block(n);
+				System::run_to_block::<AllPalletsWithSystem>(n);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), true);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), false);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), true);
@@ -1300,7 +1275,7 @@ mod tests {
 
 			// Block 24-29, Period 8-9
 			for n in 24..=29 {
-				run_to_block(n);
+				System::run_to_block::<AllPalletsWithSystem>(n);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), false);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), true);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), false);
@@ -1312,7 +1287,7 @@ mod tests {
 
 			// Block 30-35, Period 10-11
 			for n in 30..=35 {
-				run_to_block(n);
+				System::run_to_block::<AllPalletsWithSystem>(n);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(0)), false);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(1_u32)), false);
 				assert_eq!(TestRegistrar::<Test>::is_parachain(ParaId::from(2_u32)), false);
@@ -1327,7 +1302,7 @@ mod tests {
 	#[test]
 	fn unassign_slot_fails_for_unknown_para() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_noop!(
 				AssignedSlots::unassign_parachain_slot(RuntimeOrigin::root(), ParaId::from(1_u32),),
@@ -1339,7 +1314,7 @@ mod tests {
 	#[test]
 	fn unassign_slot_fails_for_invalid_origin() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_noop!(
 				AssignedSlots::assign_perm_parachain_slot(
@@ -1354,7 +1329,7 @@ mod tests {
 	#[test]
 	fn unassign_perm_slot_succeeds() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -1386,7 +1361,7 @@ mod tests {
 	#[test]
 	fn unassign_temp_slot_succeeds() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -1419,7 +1394,7 @@ mod tests {
 	#[test]
 	fn set_max_permanent_slots_fails_for_no_root_origin() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_noop!(
 				AssignedSlots::set_max_permanent_slots(RuntimeOrigin::signed(1), 5),
@@ -1430,7 +1405,7 @@ mod tests {
 	#[test]
 	fn set_max_permanent_slots_succeeds() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_eq!(MaxPermanentSlots::<Test>::get(), 2);
 			assert_ok!(AssignedSlots::set_max_permanent_slots(RuntimeOrigin::root(), 10),);
@@ -1441,7 +1416,7 @@ mod tests {
 	#[test]
 	fn set_max_temporary_slots_fails_for_no_root_origin() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_noop!(
 				AssignedSlots::set_max_temporary_slots(RuntimeOrigin::signed(1), 5),
@@ -1452,7 +1427,7 @@ mod tests {
 	#[test]
 	fn set_max_temporary_slots_succeeds() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_eq!(MaxTemporarySlots::<Test>::get(), 6);
 			assert_ok!(AssignedSlots::set_max_temporary_slots(RuntimeOrigin::root(), 12),);
diff --git a/polkadot/runtime/common/src/auctions/mock.rs b/polkadot/runtime/common/src/auctions/mock.rs
index 9fe19e579cfa2ce8b19eda13863f3888920a1610..e0365d363ca23e2823498c0f81f221b6bde394e5 100644
--- a/polkadot/runtime/common/src/auctions/mock.rs
+++ b/polkadot/runtime/common/src/auctions/mock.rs
@@ -20,8 +20,7 @@
 use super::*;
 use crate::{auctions, mock::TestRegistrar};
 use frame_support::{
-	assert_ok, derive_impl, ord_parameter_types, parameter_types,
-	traits::{EitherOfDiverse, OnFinalize, OnInitialize},
+	assert_ok, derive_impl, ord_parameter_types, parameter_types, traits::EitherOfDiverse,
 };
 use frame_system::{EnsureRoot, EnsureSignedBy};
 use pallet_balances;
@@ -244,15 +243,3 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 	});
 	ext
 }
-
-pub fn run_to_block(n: BlockNumber) {
-	while System::block_number() < n {
-		Auctions::on_finalize(System::block_number());
-		Balances::on_finalize(System::block_number());
-		System::on_finalize(System::block_number());
-		System::set_block_number(System::block_number() + 1);
-		System::on_initialize(System::block_number());
-		Balances::on_initialize(System::block_number());
-		Auctions::on_initialize(System::block_number());
-	}
-}
diff --git a/polkadot/runtime/common/src/auctions/tests.rs b/polkadot/runtime/common/src/auctions/tests.rs
index 07574eeb295d334adceb4deffa406862206b62d1..26e2ac47df84e49e05652108d788a7e4a011a70e 100644
--- a/polkadot/runtime/common/src/auctions/tests.rs
+++ b/polkadot/runtime/common/src/auctions/tests.rs
@@ -36,7 +36,7 @@ fn basic_setup_works() {
 			AuctionStatus::<u32>::NotStarted
 		);
 
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 
 		assert_eq!(AuctionCounter::<Test>::get(), 0);
 		assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0);
@@ -50,7 +50,7 @@ fn basic_setup_works() {
 #[test]
 fn can_start_auction() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 
 		assert_noop!(Auctions::new_auction(RuntimeOrigin::signed(1), 5, 1), BadOrigin);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
@@ -66,7 +66,7 @@ fn can_start_auction() {
 #[test]
 fn bidding_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 5));
 
@@ -82,7 +82,7 @@ fn bidding_works() {
 #[test]
 fn under_bidding_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 5));
@@ -96,7 +96,7 @@ fn under_bidding_works() {
 #[test]
 fn over_bidding_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 5));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), 0.into(), 1, 1, 4, 6));
@@ -115,7 +115,7 @@ fn over_bidding_works() {
 #[test]
 fn auction_proceeds_correctly() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 
@@ -125,49 +125,49 @@ fn auction_proceeds_correctly() {
 			AuctionStatus::<u32>::StartingPeriod
 		);
 
-		run_to_block(2);
+		System::run_to_block::<AllPalletsWithSystem>(2);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::StartingPeriod
 		);
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::StartingPeriod
 		);
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::StartingPeriod
 		);
 
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::StartingPeriod
 		);
 
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(0, 0)
 		);
 
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(1, 0)
 		);
 
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(2, 0)
 		);
 
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::NotStarted
@@ -178,12 +178,12 @@ fn auction_proceeds_correctly() {
 #[test]
 fn can_win_auction() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 1));
 		assert_eq!(Balances::reserved_balance(1), 1);
 		assert_eq!(Balances::free_balance(1), 9);
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 
 		assert_eq!(
 			leases(),
@@ -201,7 +201,7 @@ fn can_win_auction() {
 #[test]
 fn can_win_auction_with_late_randomness() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 1));
 		assert_eq!(Balances::reserved_balance(1), 1);
@@ -210,7 +210,7 @@ fn can_win_auction_with_late_randomness() {
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::StartingPeriod
 		);
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		// Auction has not yet ended.
 		assert_eq!(leases(), vec![]);
 		assert_eq!(
@@ -222,7 +222,7 @@ fn can_win_auction_with_late_randomness() {
 		set_last_random(H256::zero(), 8);
 		// Auction definitely ended now, but we don't know exactly when in the last 3 blocks yet
 		// since no randomness available yet.
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		// Auction has now ended... But auction winner still not yet decided, so no leases yet.
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
@@ -233,7 +233,7 @@ fn can_win_auction_with_late_randomness() {
 		// Random seed now updated to a value known at block 9, when the auction ended. This
 		// means that the winner can now be chosen.
 		set_last_random(H256::zero(), 9);
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		// Auction ended and winner selected
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
@@ -255,10 +255,10 @@ fn can_win_auction_with_late_randomness() {
 #[test]
 fn can_win_incomplete_auction() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 4, 4, 5));
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 
 		assert_eq!(leases(), vec![((0.into(), 4), LeaseData { leaser: 1, amount: 5 }),]);
 		assert_eq!(TestLeaser::deposit_held(0.into(), &1), 5);
@@ -268,13 +268,13 @@ fn can_win_incomplete_auction() {
 #[test]
 fn should_choose_best_combination() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 1, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), 0.into(), 1, 2, 3, 4));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(3), 0.into(), 1, 4, 4, 2));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 1.into(), 1, 1, 4, 2));
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 
 		assert_eq!(
 			leases(),
@@ -295,7 +295,7 @@ fn should_choose_best_combination() {
 #[test]
 fn gap_bid_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 
 		// User 1 will make a bid for period 1 and 4 for the same Para 0
@@ -314,7 +314,7 @@ fn gap_bid_works() {
 		assert_eq!(Balances::reserved_balance(3), 3);
 
 		// End the auction.
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 
 		assert_eq!(
 			leases(),
@@ -334,11 +334,11 @@ fn gap_bid_works() {
 #[test]
 fn deposit_credit_should_work() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 1, 5));
 		assert_eq!(Balances::reserved_balance(1), 5);
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 
 		assert_eq!(leases(), vec![((0.into(), 1), LeaseData { leaser: 1, amount: 5 }),]);
 		assert_eq!(TestLeaser::deposit_held(0.into(), &1), 5);
@@ -347,7 +347,7 @@ fn deposit_credit_should_work() {
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 2, 2, 2, 6));
 		// Only 1 reserved since we have a deposit credit of 5.
 		assert_eq!(Balances::reserved_balance(1), 1);
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 
 		assert_eq!(
 			leases(),
@@ -363,11 +363,11 @@ fn deposit_credit_should_work() {
 #[test]
 fn deposit_credit_on_alt_para_should_not_count() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 1, 5));
 		assert_eq!(Balances::reserved_balance(1), 5);
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 
 		assert_eq!(leases(), vec![((0.into(), 1), LeaseData { leaser: 1, amount: 5 }),]);
 		assert_eq!(TestLeaser::deposit_held(0.into(), &1), 5);
@@ -376,7 +376,7 @@ fn deposit_credit_on_alt_para_should_not_count() {
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 1.into(), 2, 2, 2, 6));
 		// 6 reserved since we are bidding on a new para; only works because we don't
 		assert_eq!(Balances::reserved_balance(1), 6);
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 
 		assert_eq!(
 			leases(),
@@ -393,12 +393,12 @@ fn deposit_credit_on_alt_para_should_not_count() {
 #[test]
 fn multiple_bids_work_pre_ending() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 
 		for i in 1..6u64 {
-			run_to_block(i as _);
+			System::run_to_block::<AllPalletsWithSystem>(i as _);
 			assert_ok!(Auctions::bid(RuntimeOrigin::signed(i), 0.into(), 1, 1, 4, i));
 			for j in 1..6 {
 				assert_eq!(Balances::reserved_balance(j), if j == i { j } else { 0 });
@@ -406,7 +406,7 @@ fn multiple_bids_work_pre_ending() {
 			}
 		}
 
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert_eq!(
 			leases(),
 			vec![
@@ -422,12 +422,12 @@ fn multiple_bids_work_pre_ending() {
 #[test]
 fn multiple_bids_work_post_ending() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 0, 1));
 
 		for i in 1..6u64 {
-			run_to_block(((i - 1) / 2 + 1) as _);
+			System::run_to_block::<AllPalletsWithSystem>(((i - 1) / 2 + 1) as _);
 			assert_ok!(Auctions::bid(RuntimeOrigin::signed(i), 0.into(), 1, 1, 4, i));
 			for j in 1..6 {
 				assert_eq!(Balances::reserved_balance(j), if j <= i { j } else { 0 });
@@ -438,7 +438,7 @@ fn multiple_bids_work_post_ending() {
 			assert_eq!(ReservedAmounts::<Test>::get((i, ParaId::from(0))).unwrap(), i);
 		}
 
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_eq!(
 			leases(),
 			(1..=4)
@@ -501,7 +501,7 @@ fn calculate_winners_works() {
 #[test]
 fn lower_bids_are_correctly_refunded() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 1, 1));
 		let para_1 = ParaId::from(1_u32);
 		let para_2 = ParaId::from(2_u32);
@@ -527,7 +527,7 @@ fn initialize_winners_in_ending_period_works() {
 	new_test_ext().execute_with(|| {
 		let ed: u64 = <Test as pallet_balances::Config>::ExistentialDeposit::get();
 		assert_eq!(ed, 1);
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 9, 1));
 		let para_1 = ParaId::from(1_u32);
 		let para_2 = ParaId::from(2_u32);
@@ -546,20 +546,20 @@ fn initialize_winners_in_ending_period_works() {
 		winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 19));
 		assert_eq!(Winning::<Test>::get(0), Some(winning));
 
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::StartingPeriod
 		);
 
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(0, 0)
 		);
 		assert_eq!(Winning::<Test>::get(0), Some(winning));
 
-		run_to_block(11);
+		System::run_to_block::<AllPalletsWithSystem>(11);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(1, 0)
@@ -567,7 +567,7 @@ fn initialize_winners_in_ending_period_works() {
 		assert_eq!(Winning::<Test>::get(1), Some(winning));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(3), para_3, 1, 3, 4, 29));
 
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(2, 0)
@@ -580,7 +580,7 @@ fn initialize_winners_in_ending_period_works() {
 #[test]
 fn handle_bid_requires_registered_para() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_noop!(
 			Auctions::bid(RuntimeOrigin::signed(1), 1337.into(), 1, 1, 4, 1),
@@ -599,12 +599,12 @@ fn handle_bid_requires_registered_para() {
 #[test]
 fn handle_bid_checks_existing_lease_periods() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 2, 3, 1));
 		assert_eq!(Balances::reserved_balance(1), 1);
 		assert_eq!(Balances::free_balance(1), 9);
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 
 		assert_eq!(
 			leases(),
@@ -644,7 +644,7 @@ fn less_winning_samples_work() {
 		EndingPeriod::set(30);
 		SampleLength::set(10);
 
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 9, 11));
 		let para_1 = ParaId::from(1_u32);
 		let para_2 = ParaId::from(2_u32);
@@ -663,13 +663,13 @@ fn less_winning_samples_work() {
 		winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 19));
 		assert_eq!(Winning::<Test>::get(0), Some(winning));
 
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::StartingPeriod
 		);
 
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(0, 0)
@@ -681,19 +681,19 @@ fn less_winning_samples_work() {
 		winning[SlotRange::ThreeThree as u8 as usize] = Some((3, para_3, 29));
 		assert_eq!(Winning::<Test>::get(0), Some(winning));
 
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(1, 0)
 		);
 		assert_eq!(Winning::<Test>::get(1), Some(winning));
-		run_to_block(25);
+		System::run_to_block::<AllPalletsWithSystem>(25);
 		// Overbid mid sample
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(3), para_3, 1, 13, 14, 29));
 		winning[SlotRange::TwoThree as u8 as usize] = Some((3, para_3, 29));
 		assert_eq!(Winning::<Test>::get(1), Some(winning));
 
-		run_to_block(30);
+		System::run_to_block::<AllPalletsWithSystem>(30);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(2, 0)
@@ -701,7 +701,7 @@ fn less_winning_samples_work() {
 		assert_eq!(Winning::<Test>::get(2), Some(winning));
 
 		set_last_random(H256::from([254; 32]), 40);
-		run_to_block(40);
+		System::run_to_block::<AllPalletsWithSystem>(40);
 		// Auction ended and winner selected
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
@@ -729,71 +729,71 @@ fn auction_status_works() {
 			AuctionStatus::<u32>::NotStarted
 		);
 
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 9, 11));
 
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::StartingPeriod
 		);
 
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(0, 0)
 		);
 
-		run_to_block(11);
+		System::run_to_block::<AllPalletsWithSystem>(11);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(0, 1)
 		);
 
-		run_to_block(19);
+		System::run_to_block::<AllPalletsWithSystem>(19);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(0, 9)
 		);
 
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(1, 0)
 		);
 
-		run_to_block(25);
+		System::run_to_block::<AllPalletsWithSystem>(25);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(1, 5)
 		);
 
-		run_to_block(30);
+		System::run_to_block::<AllPalletsWithSystem>(30);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(2, 0)
 		);
 
-		run_to_block(39);
+		System::run_to_block::<AllPalletsWithSystem>(39);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::EndingPeriod(2, 9)
 		);
 
-		run_to_block(40);
+		System::run_to_block::<AllPalletsWithSystem>(40);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::VrfDelay(0)
 		);
 
-		run_to_block(44);
+		System::run_to_block::<AllPalletsWithSystem>(44);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::VrfDelay(4)
 		);
 
 		set_last_random(dummy_hash(), 45);
-		run_to_block(45);
+		System::run_to_block::<AllPalletsWithSystem>(45);
 		assert_eq!(
 			Auctions::auction_status(System::block_number()),
 			AuctionStatus::<u32>::NotStarted
@@ -804,7 +804,7 @@ fn auction_status_works() {
 #[test]
 fn can_cancel_auction() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
 		assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 1));
 		assert_eq!(Balances::reserved_balance(1), 1);
diff --git a/polkadot/runtime/common/src/crowdloan/mod.rs b/polkadot/runtime/common/src/crowdloan/mod.rs
index 8cf288197e3dd56085f96c928ae6a5b7f79a2a86..f8b3169407e83e87e999729ab6c247b8a67bdc2a 100644
--- a/polkadot/runtime/common/src/crowdloan/mod.rs
+++ b/polkadot/runtime/common/src/crowdloan/mod.rs
@@ -858,10 +858,7 @@ mod crypto {
 mod tests {
 	use super::*;
 
-	use frame_support::{
-		assert_noop, assert_ok, derive_impl, parameter_types,
-		traits::{OnFinalize, OnInitialize},
-	};
+	use frame_support::{assert_noop, assert_ok, derive_impl, parameter_types};
 	use polkadot_primitives::Id as ParaId;
 	use sp_core::H256;
 	use std::{cell::RefCell, collections::BTreeMap, sync::Arc};
@@ -1111,18 +1108,6 @@ mod tests {
 		unreachable!()
 	}
 
-	fn run_to_block(n: u64) {
-		while System::block_number() < n {
-			Crowdloan::on_finalize(System::block_number());
-			Balances::on_finalize(System::block_number());
-			System::on_finalize(System::block_number());
-			System::set_block_number(System::block_number() + 1);
-			System::on_initialize(System::block_number());
-			Balances::on_initialize(System::block_number());
-			Crowdloan::on_initialize(System::block_number());
-		}
-	}
-
 	fn last_event() -> RuntimeEvent {
 		System::events().pop().expect("RuntimeEvent expected").event
 	}
@@ -1426,7 +1411,7 @@ mod tests {
 			);
 
 			// Move past end date
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 
 			// Cannot contribute to ended fund
 			assert_noop!(
@@ -1451,7 +1436,7 @@ mod tests {
 			// crowdloan that has starting period 1.
 			let para_3 = new_para();
 			assert_ok!(Crowdloan::create(RuntimeOrigin::signed(1), para_3, 1000, 1, 4, 40, None));
-			run_to_block(40);
+			System::run_to_block::<AllPalletsWithSystem>(40);
 			let now = System::block_number();
 			assert_eq!(TestAuctioneer::lease_period_index(now).unwrap().0, 2);
 			assert_noop!(
@@ -1483,12 +1468,12 @@ mod tests {
 				None
 			));
 
-			run_to_block(8);
+			System::run_to_block::<AllPalletsWithSystem>(8);
 			// Can def contribute when auction is running.
 			assert!(TestAuctioneer::auction_status(System::block_number()).is_ending().is_some());
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 250, None));
 
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 			// Can't contribute when auction is in the VRF delay period.
 			assert!(TestAuctioneer::auction_status(System::block_number()).is_vrf());
 			assert_noop!(
@@ -1496,7 +1481,7 @@ mod tests {
 				Error::<Test>::VrfDelayInProgress
 			);
 
-			run_to_block(15);
+			System::run_to_block::<AllPalletsWithSystem>(15);
 			// Its fine to contribute when no auction is running.
 			assert!(!TestAuctioneer::auction_status(System::block_number()).is_in_progress());
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 250, None));
@@ -1526,15 +1511,15 @@ mod tests {
 			let bidder = Crowdloan::fund_account_id(index);
 
 			// Fund crowdloan
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 100, None));
-			run_to_block(3);
+			System::run_to_block::<AllPalletsWithSystem>(3);
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(3), para, 150, None));
-			run_to_block(5);
+			System::run_to_block::<AllPalletsWithSystem>(5);
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(4), para, 200, None));
-			run_to_block(8);
+			System::run_to_block::<AllPalletsWithSystem>(8);
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 250, None));
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 
 			assert_eq!(
 				bids(),
@@ -1561,7 +1546,7 @@ mod tests {
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 100, None));
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(3), para, 50, None));
 
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 			let account_id = Crowdloan::fund_account_id(index);
 			// para has no reserved funds, indicating it did not win the auction.
 			assert_eq!(Balances::reserved_balance(&account_id), 0);
@@ -1591,7 +1576,7 @@ mod tests {
 			assert_ok!(Crowdloan::create(RuntimeOrigin::signed(1), para, 1000, 1, 1, 9, None));
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 100, None));
 
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 			let account_id = Crowdloan::fund_account_id(index);
 
 			// user sends the crowdloan funds trying to make an accounting error
@@ -1636,7 +1621,7 @@ mod tests {
 			);
 
 			// Move to the end of the crowdloan
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 			assert_ok!(Crowdloan::refund(RuntimeOrigin::signed(1337), para));
 
 			// Funds are returned
@@ -1671,7 +1656,7 @@ mod tests {
 			assert_eq!(Balances::free_balance(account_id), 21000);
 
 			// Move to the end of the crowdloan
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 			assert_ok!(Crowdloan::refund(RuntimeOrigin::signed(1337), para));
 			assert_eq!(
 				last_event(),
@@ -1705,7 +1690,7 @@ mod tests {
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 100, None));
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(3), para, 50, None));
 
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 			// All funds are refunded
 			assert_ok!(Crowdloan::refund(RuntimeOrigin::signed(2), para));
 
@@ -1730,7 +1715,7 @@ mod tests {
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para, 100, None));
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(3), para, 50, None));
 
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 
 			// We test the historic case where crowdloan accounts only have one provider:
 			{
@@ -1770,7 +1755,7 @@ mod tests {
 				Error::<Test>::NotReadyToDissolve
 			);
 
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 			set_winner(para, 1, true);
 			// Can't dissolve when it won.
 			assert_noop!(
@@ -1815,13 +1800,13 @@ mod tests {
 			// simulate the reserving of para's funds. this actually happens in the Slots pallet.
 			assert_ok!(Balances::reserve(&account_id, 149));
 
-			run_to_block(19);
+			System::run_to_block::<AllPalletsWithSystem>(19);
 			assert_noop!(
 				Crowdloan::withdraw(RuntimeOrigin::signed(2), 2, para),
 				Error::<Test>::BidOrLeaseActive
 			);
 
-			run_to_block(20);
+			System::run_to_block::<AllPalletsWithSystem>(20);
 			// simulate the unreserving of para's funds, now that the lease expired. this actually
 			// happens in the Slots pallet.
 			Balances::unreserve(&account_id, 150);
@@ -1949,7 +1934,7 @@ mod tests {
 				Error::<Test>::NoContributions
 			);
 			assert_ok!(Crowdloan::contribute(RuntimeOrigin::signed(2), para_1, 100, None));
-			run_to_block(6);
+			System::run_to_block::<AllPalletsWithSystem>(6);
 			assert_ok!(Crowdloan::poke(RuntimeOrigin::signed(1), para_1));
 			assert_eq!(crowdloan::NewRaise::<Test>::get(), vec![para_1]);
 			assert_noop!(
diff --git a/polkadot/runtime/common/src/integration_tests.rs b/polkadot/runtime/common/src/integration_tests.rs
index 8a76a138305ea8ffe3aa098c06d35bfae3dab7c7..bb4ad8b75065cb3761bc6690baf0fd1fce8a0cca 100644
--- a/polkadot/runtime/common/src/integration_tests.rs
+++ b/polkadot/runtime/common/src/integration_tests.rs
@@ -28,7 +28,7 @@ use alloc::sync::Arc;
 use codec::Encode;
 use frame_support::{
 	assert_noop, assert_ok, derive_impl, parameter_types,
-	traits::{ConstU32, Currency, OnFinalize, OnInitialize},
+	traits::{ConstU32, Currency},
 	weights::Weight,
 	PalletId,
 };
@@ -377,14 +377,12 @@ fn add_blocks(n: u32) {
 }
 
 fn run_to_block(n: u32) {
-	assert!(System::block_number() < n);
-	while System::block_number() < n {
-		let block_number = System::block_number();
-		AllPalletsWithSystem::on_finalize(block_number);
-		System::set_block_number(block_number + 1);
-		maybe_new_session(block_number + 1);
-		AllPalletsWithSystem::on_initialize(block_number + 1);
-	}
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default().before_initialize(|bn| {
+			maybe_new_session(bn);
+		}),
+	);
 }
 
 fn run_to_session(n: u32) {
diff --git a/polkadot/runtime/common/src/paras_registrar/mock.rs b/polkadot/runtime/common/src/paras_registrar/mock.rs
index 1627fd70365d450ccf91025f1cf3a71e45b052d9..07b8fbca5189fc9d361b574ec4af7ad28be16cb6 100644
--- a/polkadot/runtime/common/src/paras_registrar/mock.rs
+++ b/polkadot/runtime/common/src/paras_registrar/mock.rs
@@ -20,10 +20,7 @@
 use super::*;
 use crate::paras_registrar;
 use alloc::collections::btree_map::BTreeMap;
-use frame_support::{
-	derive_impl, parameter_types,
-	traits::{OnFinalize, OnInitialize},
-};
+use frame_support::{derive_impl, parameter_types};
 use frame_system::limits;
 use polkadot_primitives::{Balance, BlockNumber, MAX_CODE_SIZE};
 use polkadot_runtime_parachains::{configuration, origin, shared};
@@ -205,26 +202,21 @@ pub const VALIDATORS: &[Sr25519Keyring] = &[
 pub fn run_to_block(n: BlockNumber) {
 	// NOTE that this function only simulates modules of interest. Depending on new pallet may
 	// require adding it here.
-	assert!(System::block_number() < n);
-	while System::block_number() < n {
-		let b = System::block_number();
-
-		if System::block_number() > 1 {
-			System::on_finalize(System::block_number());
-		}
-		// Session change every 3 blocks.
-		if (b + 1) % BLOCKS_PER_SESSION == 0 {
-			let session_index = shared::CurrentSessionIndex::<Test>::get() + 1;
-			let validators_pub_keys = VALIDATORS.iter().map(|v| v.public().into()).collect();
-
-			shared::Pallet::<Test>::set_session_index(session_index);
-			shared::Pallet::<Test>::set_active_validators_ascending(validators_pub_keys);
-
-			Parachains::test_on_new_session();
-		}
-		System::set_block_number(b + 1);
-		System::on_initialize(System::block_number());
-	}
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default().before_finalize(|bn| {
+			// Session change every 3 blocks.
+			if (bn + 1) % BLOCKS_PER_SESSION == 0 {
+				let session_index = shared::CurrentSessionIndex::<Test>::get() + 1;
+				let validators_pub_keys = VALIDATORS.iter().map(|v| v.public().into()).collect();
+
+				shared::Pallet::<Test>::set_session_index(session_index);
+				shared::Pallet::<Test>::set_active_validators_ascending(validators_pub_keys);
+
+				Parachains::test_on_new_session();
+			}
+		}),
+	);
 }
 
 pub fn run_to_session(n: BlockNumber) {
diff --git a/polkadot/runtime/common/src/slots/mod.rs b/polkadot/runtime/common/src/slots/mod.rs
index 333f14c6608acb1d62df7fadbfe297741a39d6cd..59a1f1870b2dcbf0193d2d321c2b24f6984c55ca 100644
--- a/polkadot/runtime/common/src/slots/mod.rs
+++ b/polkadot/runtime/common/src/slots/mod.rs
@@ -584,28 +584,16 @@ mod tests {
 		t.into()
 	}
 
-	fn run_to_block(n: BlockNumber) {
-		while System::block_number() < n {
-			Slots::on_finalize(System::block_number());
-			Balances::on_finalize(System::block_number());
-			System::on_finalize(System::block_number());
-			System::set_block_number(System::block_number() + 1);
-			System::on_initialize(System::block_number());
-			Balances::on_initialize(System::block_number());
-			Slots::on_initialize(System::block_number());
-		}
-	}
-
 	#[test]
 	fn basic_setup_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 			assert_eq!(Slots::lease_period_length(), (10, 0));
 			let now = System::block_number();
 			assert_eq!(Slots::lease_period_index(now).unwrap().0, 0);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 
-			run_to_block(10);
+			System::run_to_block::<AllPalletsWithSystem>(10);
 			let now = System::block_number();
 			assert_eq!(Slots::lease_period_index(now).unwrap().0, 1);
 		});
@@ -614,7 +602,7 @@ mod tests {
 	#[test]
 	fn lease_lifecycle_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -627,11 +615,11 @@ mod tests {
 			assert_eq!(Slots::deposit_held(1.into(), &1), 1);
 			assert_eq!(Balances::reserved_balance(1), 1);
 
-			run_to_block(19);
+			System::run_to_block::<AllPalletsWithSystem>(19);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 1);
 			assert_eq!(Balances::reserved_balance(1), 1);
 
-			run_to_block(20);
+			System::run_to_block::<AllPalletsWithSystem>(20);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 			assert_eq!(Balances::reserved_balance(1), 0);
 
@@ -645,7 +633,7 @@ mod tests {
 	#[test]
 	fn lease_interrupted_lifecycle_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -657,19 +645,19 @@ mod tests {
 			assert_ok!(Slots::lease_out(1.into(), &1, 6, 1, 1));
 			assert_ok!(Slots::lease_out(1.into(), &1, 4, 3, 1));
 
-			run_to_block(19);
+			System::run_to_block::<AllPalletsWithSystem>(19);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 6);
 			assert_eq!(Balances::reserved_balance(1), 6);
 
-			run_to_block(20);
+			System::run_to_block::<AllPalletsWithSystem>(20);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 4);
 			assert_eq!(Balances::reserved_balance(1), 4);
 
-			run_to_block(39);
+			System::run_to_block::<AllPalletsWithSystem>(39);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 4);
 			assert_eq!(Balances::reserved_balance(1), 4);
 
-			run_to_block(40);
+			System::run_to_block::<AllPalletsWithSystem>(40);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 			assert_eq!(Balances::reserved_balance(1), 0);
 
@@ -688,7 +676,7 @@ mod tests {
 	#[test]
 	fn lease_relayed_lifecycle_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -704,25 +692,25 @@ mod tests {
 			assert_eq!(Slots::deposit_held(1.into(), &2), 4);
 			assert_eq!(Balances::reserved_balance(2), 4);
 
-			run_to_block(19);
+			System::run_to_block::<AllPalletsWithSystem>(19);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 6);
 			assert_eq!(Balances::reserved_balance(1), 6);
 			assert_eq!(Slots::deposit_held(1.into(), &2), 4);
 			assert_eq!(Balances::reserved_balance(2), 4);
 
-			run_to_block(20);
+			System::run_to_block::<AllPalletsWithSystem>(20);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 			assert_eq!(Balances::reserved_balance(1), 0);
 			assert_eq!(Slots::deposit_held(1.into(), &2), 4);
 			assert_eq!(Balances::reserved_balance(2), 4);
 
-			run_to_block(29);
+			System::run_to_block::<AllPalletsWithSystem>(29);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 			assert_eq!(Balances::reserved_balance(1), 0);
 			assert_eq!(Slots::deposit_held(1.into(), &2), 4);
 			assert_eq!(Balances::reserved_balance(2), 4);
 
-			run_to_block(30);
+			System::run_to_block::<AllPalletsWithSystem>(30);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 			assert_eq!(Balances::reserved_balance(1), 0);
 			assert_eq!(Slots::deposit_held(1.into(), &2), 0);
@@ -738,7 +726,7 @@ mod tests {
 	#[test]
 	fn lease_deposit_increase_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -755,11 +743,11 @@ mod tests {
 			assert_eq!(Slots::deposit_held(1.into(), &1), 6);
 			assert_eq!(Balances::reserved_balance(1), 6);
 
-			run_to_block(29);
+			System::run_to_block::<AllPalletsWithSystem>(29);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 6);
 			assert_eq!(Balances::reserved_balance(1), 6);
 
-			run_to_block(30);
+			System::run_to_block::<AllPalletsWithSystem>(30);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 			assert_eq!(Balances::reserved_balance(1), 0);
 
@@ -773,7 +761,7 @@ mod tests {
 	#[test]
 	fn lease_deposit_decrease_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -790,19 +778,19 @@ mod tests {
 			assert_eq!(Slots::deposit_held(1.into(), &1), 6);
 			assert_eq!(Balances::reserved_balance(1), 6);
 
-			run_to_block(19);
+			System::run_to_block::<AllPalletsWithSystem>(19);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 6);
 			assert_eq!(Balances::reserved_balance(1), 6);
 
-			run_to_block(20);
+			System::run_to_block::<AllPalletsWithSystem>(20);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 4);
 			assert_eq!(Balances::reserved_balance(1), 4);
 
-			run_to_block(29);
+			System::run_to_block::<AllPalletsWithSystem>(29);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 4);
 			assert_eq!(Balances::reserved_balance(1), 4);
 
-			run_to_block(30);
+			System::run_to_block::<AllPalletsWithSystem>(30);
 			assert_eq!(Slots::deposit_held(1.into(), &1), 0);
 			assert_eq!(Balances::reserved_balance(1), 0);
 
@@ -816,7 +804,7 @@ mod tests {
 	#[test]
 	fn clear_all_leases_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -852,7 +840,7 @@ mod tests {
 	#[test]
 	fn lease_out_current_lease_period() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
@@ -867,7 +855,7 @@ mod tests {
 				dummy_validation_code()
 			));
 
-			run_to_block(20);
+			System::run_to_block::<AllPalletsWithSystem>(20);
 			let now = System::block_number();
 			assert_eq!(Slots::lease_period_index(now).unwrap().0, 2);
 			// Can't lease from the past
@@ -884,7 +872,7 @@ mod tests {
 	#[test]
 	fn trigger_onboard_works() {
 		new_test_ext().execute_with(|| {
-			run_to_block(1);
+			System::run_to_block::<AllPalletsWithSystem>(1);
 			assert_ok!(TestRegistrar::<Test>::register(
 				1,
 				ParaId::from(1_u32),
diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs
index e5d703700fee57cb99d33bf1298ebafab7bd10f7..b3f2a0033278aab7bd77a7ac302f60ec9b40762c 100644
--- a/polkadot/runtime/rococo/src/lib.rs
+++ b/polkadot/runtime/rococo/src/lib.rs
@@ -1885,7 +1885,8 @@ sp_api::impl_runtime_apis! {
 		}
 
 		fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
-			match asset.try_as::<AssetId>() {
+			let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
+			match latest_asset_id {
 				Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => {
 					// for native token
 					Ok(WeightToFee::weight_to_fee(&weight))
diff --git a/polkadot/runtime/rococo/src/xcm_config.rs b/polkadot/runtime/rococo/src/xcm_config.rs
index bb77ec0000e56888f2bb5068a4dfba919bc527e0..10c3f6c0cbfcf2a25efc02524e46f6c4d256674d 100644
--- a/polkadot/runtime/rococo/src/xcm_config.rs
+++ b/polkadot/runtime/rococo/src/xcm_config.rs
@@ -18,7 +18,8 @@
 
 use super::{
 	parachains_origin, AccountId, AllPalletsWithSystem, Balances, Dmp, Fellows, ParaId, Runtime,
-	RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, Treasury, WeightToFee, XcmPallet,
+	RuntimeCall, RuntimeEvent, RuntimeOrigin, TransactionByteFee, Treasurer, Treasury, WeightToFee,
+	XcmPallet,
 };
 
 use crate::governance::StakingAdmin;
@@ -228,11 +229,14 @@ impl xcm_executor::Config for XcmConfig {
 }
 
 parameter_types! {
+	/// Collective pluralistic body.
 	pub const CollectiveBodyId: BodyId = BodyId::Unit;
-	// StakingAdmin pluralistic body.
+	/// StakingAdmin pluralistic body.
 	pub const StakingAdminBodyId: BodyId = BodyId::Defense;
-	// Fellows pluralistic body.
+	/// Fellows pluralistic body.
 	pub const FellowsBodyId: BodyId = BodyId::Technical;
+	/// Treasury pluralistic body.
+	pub const TreasuryBodyId: BodyId = BodyId::Treasury;
 }
 
 /// Type to convert an `Origin` type value into a `Location` value which represents an interior
@@ -249,6 +253,9 @@ pub type StakingAdminToPlurality =
 /// Type to convert the Fellows origin to a Plurality `Location` value.
 pub type FellowsToPlurality = OriginToPluralityVoice<RuntimeOrigin, Fellows, FellowsBodyId>;
 
+/// Type to convert the Treasury origin to a Plurality `Location` value.
+pub type TreasurerToPlurality = OriginToPluralityVoice<RuntimeOrigin, Treasurer, TreasuryBodyId>;
+
 /// Type to convert a pallet `Origin` type value into a `Location` value which represents an
 /// interior location of this chain for a destination chain.
 pub type LocalPalletOriginToLocation = (
@@ -256,13 +263,18 @@ pub type LocalPalletOriginToLocation = (
 	StakingAdminToPlurality,
 	// Fellows origin to be used in XCM as a corresponding Plurality `Location` value.
 	FellowsToPlurality,
+	// Treasurer origin to be used in XCM as a corresponding Plurality `Location` value.
+	TreasurerToPlurality,
 );
 
 impl pallet_xcm::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	// Note that this configuration of `SendXcmOrigin` is different from the one present in
 	// production.
-	type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
+	type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<
+		RuntimeOrigin,
+		(LocalPalletOriginToLocation, LocalOriginToLocation),
+	>;
 	type XcmRouter = XcmRouter;
 	// Anyone can execute XCM messages locally.
 	type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs
index 4f9ba8d8508cdecc904efefcde280e386fddc1d3..cdf6fa92da2f5d5f09c6f48b21e2e4f13035d4ef 100644
--- a/polkadot/runtime/test-runtime/src/lib.rs
+++ b/polkadot/runtime/test-runtime/src/lib.rs
@@ -366,11 +366,13 @@ impl onchain::Config for OnChainSeqPhragmen {
 const MAX_QUOTA_NOMINATIONS: u32 = 16;
 
 impl pallet_staking::Config for Runtime {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = Balance;
 	type UnixTime = Timestamp;
 	type CurrencyToVote = polkadot_runtime_common::CurrencyToVote;
 	type RewardRemainder = ();
+	type RuntimeHoldReason = RuntimeHoldReason;
 	type RuntimeEvent = RuntimeEvent;
 	type Slash = ();
 	type Reward = ();
diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs
index 9d77a5e5eea1f33b13c59762fe97f4f5e5d70a97..8a5771fe7cc08f7750951304ef63325f458cf20b 100644
--- a/polkadot/runtime/westend/src/lib.rs
+++ b/polkadot/runtime/westend/src/lib.rs
@@ -728,8 +728,10 @@ parameter_types! {
 }
 
 impl pallet_staking::Config for Runtime {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = Balance;
+	type RuntimeHoldReason = RuntimeHoldReason;
 	type UnixTime = Timestamp;
 	type CurrencyToVote = CurrencyToVote;
 	type RewardRemainder = ();
@@ -2445,7 +2447,8 @@ sp_api::impl_runtime_apis! {
 		}
 
 		fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> {
-			match asset.try_as::<AssetId>() {
+			let latest_asset_id: Result<AssetId, ()> = asset.clone().try_into();
+			match latest_asset_id {
 				Ok(asset_id) if asset_id.0 == xcm_config::TokenLocation::get() => {
 					// for native token
 					Ok(WeightToFee::weight_to_fee(&weight))
diff --git a/polkadot/runtime/westend/src/tests.rs b/polkadot/runtime/westend/src/tests.rs
index fcdaf7ff2de6d3c95207291bd6b2f47043877860..65b81cc00f06975e7be235cb7d7700824b356853 100644
--- a/polkadot/runtime/westend/src/tests.rs
+++ b/polkadot/runtime/westend/src/tests.rs
@@ -155,25 +155,27 @@ mod remote_tests {
 
 		let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9900".to_string()).into();
 		let maybe_state_snapshot: Option<SnapshotConfig> = var("SNAP").map(|s| s.into()).ok();
+		let online_config = OnlineConfig {
+			transport,
+			state_snapshot: maybe_state_snapshot.clone(),
+			child_trie: false,
+			pallets: vec![
+				"Staking".into(),
+				"System".into(),
+				"Balances".into(),
+				"NominationPools".into(),
+				"DelegatedStaking".into(),
+			],
+			..Default::default()
+		};
 		let mut ext = Builder::<Block>::default()
 			.mode(if let Some(state_snapshot) = maybe_state_snapshot {
 				Mode::OfflineOrElseOnline(
 					OfflineConfig { state_snapshot: state_snapshot.clone() },
-					OnlineConfig {
-						transport,
-						state_snapshot: Some(state_snapshot),
-						pallets: vec![
-							"staking".into(),
-							"system".into(),
-							"balances".into(),
-							"nomination-pools".into(),
-							"delegated-staking".into(),
-						],
-						..Default::default()
-					},
+					online_config,
 				)
 			} else {
-				Mode::Online(OnlineConfig { transport, ..Default::default() })
+				Mode::Online(online_config)
 			})
 			.build()
 			.await
@@ -241,6 +243,77 @@ mod remote_tests {
 			);
 		});
 	}
+
+	#[tokio::test]
+	async fn staking_curr_fun_migrate() {
+		// Intended to be run only manually.
+		if var("RUN_MIGRATION_TESTS").is_err() {
+			return;
+		}
+		sp_tracing::try_init_simple();
+
+		let transport: Transport = var("WS").unwrap_or("ws://127.0.0.1:9944".to_string()).into();
+		let maybe_state_snapshot: Option<SnapshotConfig> = var("SNAP").map(|s| s.into()).ok();
+		let online_config = OnlineConfig {
+			transport,
+			state_snapshot: maybe_state_snapshot.clone(),
+			child_trie: false,
+			pallets: vec!["Staking".into(), "System".into(), "Balances".into()],
+			..Default::default()
+		};
+		let mut ext = Builder::<Block>::default()
+			.mode(if let Some(state_snapshot) = maybe_state_snapshot {
+				Mode::OfflineOrElseOnline(
+					OfflineConfig { state_snapshot: state_snapshot.clone() },
+					online_config,
+				)
+			} else {
+				Mode::Online(online_config)
+			})
+			.build()
+			.await
+			.unwrap();
+		ext.execute_with(|| {
+			// create an account with some balance
+			let alice = AccountId::from([1u8; 32]);
+			use frame_support::traits::Currency;
+			let _ = Balances::deposit_creating(&alice, 100_000 * UNITS);
+
+			let mut success = 0;
+			let mut err = 0;
+			let mut force_withdraw_acc = 0;
+			// iterate over all pools
+			pallet_staking::Ledger::<Runtime>::iter().for_each(|(ctrl, ledger)| {
+				match pallet_staking::Pallet::<Runtime>::migrate_currency(
+					RuntimeOrigin::signed(alice.clone()).into(),
+					ledger.stash.clone(),
+				) {
+					Ok(_) => {
+						let updated_ledger =
+							pallet_staking::Ledger::<Runtime>::get(&ctrl).expect("ledger exists");
+						let force_withdraw = ledger.total - updated_ledger.total;
+						if force_withdraw > 0 {
+							force_withdraw_acc += force_withdraw;
+							log::info!(target: "remote_test", "Force withdraw from stash {:?}: value {:?}", ledger.stash, force_withdraw);
+						}
+						success += 1;
+					},
+					Err(e) => {
+						log::error!(target: "remote_test", "Error migrating {:?}: {:?}", ledger.stash, e);
+						err += 1;
+					},
+				}
+			});
+
+			log::info!(
+				target: "remote_test",
+				"Migration stats: success: {}, err: {}, total force withdrawn stake: {}",
+				success,
+				err,
+				force_withdraw_acc
+			);
+		});
+	}
 }
 
 #[test]
diff --git a/polkadot/runtime/westend/src/weights/pallet_staking.rs b/polkadot/runtime/westend/src/weights/pallet_staking.rs
index 393fa0b37176a0ca5ce8160eec7e8e3ac56c3458..f1e7f5ba1576eafbd768f51cf98faa41cbe02b5c 100644
--- a/polkadot/runtime/westend/src/weights/pallet_staking.rs
+++ b/polkadot/runtime/westend/src/weights/pallet_staking.rs
@@ -17,9 +17,9 @@
 //! Autogenerated weights for `pallet_staking`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-03-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-09-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-h2rr8wx7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `runner-obbyq9g6-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024
 
 // Executed Command:
@@ -52,19 +52,19 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Payee` (r:0 w:1)
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn bond() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1009`
-		//  Estimated: `4764`
-		// Minimum execution time: 40_585_000 picoseconds.
-		Weight::from_parts(41_800_000, 0)
-			.saturating_add(Weight::from_parts(0, 4764))
+		//  Measured:  `1035`
+		//  Estimated: `4556`
+		// Minimum execution time: 70_147_000 picoseconds.
+		Weight::from_parts(71_795_000, 0)
+			.saturating_add(Weight::from_parts(0, 4556))
 			.saturating_add(T::DbWeight::get().reads(4))
 			.saturating_add(T::DbWeight::get().writes(4))
 	}
@@ -72,20 +72,20 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
 	/// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`)
 	fn bond_extra() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1921`
+		//  Measured:  `1947`
 		//  Estimated: `8877`
-		// Minimum execution time: 81_809_000 picoseconds.
-		Weight::from_parts(84_387_000, 0)
+		// Minimum execution time: 125_203_000 picoseconds.
+		Weight::from_parts(128_088_000, 0)
 			.saturating_add(Weight::from_parts(0, 8877))
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(7))
@@ -100,23 +100,23 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::CurrentEra` (r:1 w:0)
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
 	/// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`)
 	fn unbond() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2128`
+		//  Measured:  `2051`
 		//  Estimated: `8877`
-		// Minimum execution time: 89_419_000 picoseconds.
-		Weight::from_parts(91_237_000, 0)
+		// Minimum execution time: 101_991_000 picoseconds.
+		Weight::from_parts(104_567_000, 0)
 			.saturating_add(Weight::from_parts(0, 8877))
 			.saturating_add(T::DbWeight::get().reads(12))
-			.saturating_add(T::DbWeight::get().writes(7))
+			.saturating_add(T::DbWeight::get().writes(6))
 	}
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
@@ -124,23 +124,25 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::CurrentEra` (r:1 w:0)
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0)
 	/// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
+	/// Storage: `DelegatedStaking::Agents` (r:1 w:0)
+	/// Proof: `DelegatedStaking::Agents` (`max_values`: None, `max_size`: Some(120), added: 2595, mode: `MaxEncodedLen`)
 	/// The range of component `s` is `[0, 100]`.
 	fn withdraw_unbonded_update(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1223`
-		//  Estimated: `4764`
-		// Minimum execution time: 45_152_000 picoseconds.
-		Weight::from_parts(46_460_819, 0)
-			.saturating_add(Weight::from_parts(0, 4764))
-			// Standard Error: 972
-			.saturating_add(Weight::from_parts(55_473, 0).saturating_mul(s.into()))
-			.saturating_add(T::DbWeight::get().reads(6))
+		//  Measured:  `1253`
+		//  Estimated: `4556`
+		// Minimum execution time: 76_450_000 picoseconds.
+		Weight::from_parts(78_836_594, 0)
+			.saturating_add(Weight::from_parts(0, 4556))
+			// Standard Error: 1_529
+			.saturating_add(Weight::from_parts(66_662, 0).saturating_mul(s.into()))
+			.saturating_add(T::DbWeight::get().reads(7))
 			.saturating_add(T::DbWeight::get().writes(2))
 	}
 	/// Storage: `Staking::Ledger` (r:1 w:1)
@@ -151,10 +153,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::SlashingSpans` (r:1 w:1)
 	/// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Nominators` (r:1 w:1)
@@ -174,15 +176,15 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `s` is `[0, 100]`.
 	fn withdraw_unbonded_kill(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2127 + s * (4 ±0)`
+		//  Measured:  `2153 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 82_762_000 picoseconds.
-		Weight::from_parts(91_035_077, 0)
+		// Minimum execution time: 121_962_000 picoseconds.
+		Weight::from_parts(131_000_151, 0)
 			.saturating_add(Weight::from_parts(0, 6248))
-			// Standard Error: 3_771
-			.saturating_add(Weight::from_parts(1_217_871, 0).saturating_mul(s.into()))
+			// Standard Error: 3_846
+			.saturating_add(Weight::from_parts(1_277_843, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(13))
-			.saturating_add(T::DbWeight::get().writes(11))
+			.saturating_add(T::DbWeight::get().writes(12))
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -210,10 +212,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::CounterForValidators` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn validate() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1301`
+		//  Measured:  `1334`
 		//  Estimated: `4556`
-		// Minimum execution time: 50_555_000 picoseconds.
-		Weight::from_parts(52_052_000, 0)
+		// Minimum execution time: 66_450_000 picoseconds.
+		Weight::from_parts(68_302_000, 0)
 			.saturating_add(Weight::from_parts(0, 4556))
 			.saturating_add(T::DbWeight::get().reads(11))
 			.saturating_add(T::DbWeight::get().writes(5))
@@ -227,13 +229,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `k` is `[1, 128]`.
 	fn kick(k: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1778 + k * (572 ±0)`
+		//  Measured:  `1811 + k * (572 ±0)`
 		//  Estimated: `4556 + k * (3033 ±0)`
-		// Minimum execution time: 35_037_000 picoseconds.
-		Weight::from_parts(35_081_878, 0)
+		// Minimum execution time: 43_875_000 picoseconds.
+		Weight::from_parts(47_332_240, 0)
 			.saturating_add(Weight::from_parts(0, 4556))
-			// Standard Error: 5_473
-			.saturating_add(Weight::from_parts(6_667_924, 0).saturating_mul(k.into()))
+			// Standard Error: 6_530
+			.saturating_add(Weight::from_parts(7_398_001, 0).saturating_mul(k.into()))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into())))
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into())))
@@ -264,13 +266,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `n` is `[1, 16]`.
 	fn nominate(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1797 + n * (102 ±0)`
+		//  Measured:  `1830 + n * (102 ±0)`
 		//  Estimated: `6248 + n * (2520 ±0)`
-		// Minimum execution time: 62_098_000 picoseconds.
-		Weight::from_parts(60_154_061, 0)
+		// Minimum execution time: 80_640_000 picoseconds.
+		Weight::from_parts(78_801_092, 0)
 			.saturating_add(Weight::from_parts(0, 6248))
-			// Standard Error: 19_257
-			.saturating_add(Weight::from_parts(3_839_855, 0).saturating_mul(n.into()))
+			// Standard Error: 22_249
+			.saturating_add(Weight::from_parts(4_996_344, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(12))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
 			.saturating_add(T::DbWeight::get().writes(6))
@@ -294,10 +296,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn chill() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1747`
+		//  Measured:  `1780`
 		//  Estimated: `6248`
-		// Minimum execution time: 54_993_000 picoseconds.
-		Weight::from_parts(56_698_000, 0)
+		// Minimum execution time: 71_494_000 picoseconds.
+		Weight::from_parts(73_487_000, 0)
 			.saturating_add(Weight::from_parts(0, 6248))
 			.saturating_add(T::DbWeight::get().reads(9))
 			.saturating_add(T::DbWeight::get().writes(6))
@@ -310,10 +312,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn set_payee() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `865`
+		//  Measured:  `898`
 		//  Estimated: `4556`
-		// Minimum execution time: 18_100_000 picoseconds.
-		Weight::from_parts(18_547_000, 0)
+		// Minimum execution time: 24_310_000 picoseconds.
+		Weight::from_parts(24_676_000, 0)
 			.saturating_add(Weight::from_parts(0, 4556))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -326,10 +328,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn update_payee() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `932`
+		//  Measured:  `965`
 		//  Estimated: `4556`
-		// Minimum execution time: 23_428_000 picoseconds.
-		Weight::from_parts(24_080_000, 0)
+		// Minimum execution time: 31_348_000 picoseconds.
+		Weight::from_parts(32_384_000, 0)
 			.saturating_add(Weight::from_parts(0, 4556))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -340,10 +342,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	fn set_controller() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `865`
+		//  Measured:  `898`
 		//  Estimated: `8122`
-		// Minimum execution time: 21_159_000 picoseconds.
-		Weight::from_parts(21_706_000, 0)
+		// Minimum execution time: 27_537_000 picoseconds.
+		Weight::from_parts(28_714_000, 0)
 			.saturating_add(Weight::from_parts(0, 8122))
 			.saturating_add(T::DbWeight::get().reads(3))
 			.saturating_add(T::DbWeight::get().writes(3))
@@ -354,8 +356,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_910_000 picoseconds.
-		Weight::from_parts(2_003_000, 0)
+		// Minimum execution time: 2_362_000 picoseconds.
+		Weight::from_parts(2_518_000, 0)
 			.saturating_add(Weight::from_parts(0, 0))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -365,8 +367,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 7_076_000 picoseconds.
-		Weight::from_parts(7_349_000, 0)
+		// Minimum execution time: 7_752_000 picoseconds.
+		Weight::from_parts(8_105_000, 0)
 			.saturating_add(Weight::from_parts(0, 0))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -376,8 +378,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 7_067_000 picoseconds.
-		Weight::from_parts(7_389_000, 0)
+		// Minimum execution time: 7_868_000 picoseconds.
+		Weight::from_parts(8_175_000, 0)
 			.saturating_add(Weight::from_parts(0, 0))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -387,8 +389,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 7_148_000 picoseconds.
-		Weight::from_parts(7_446_000, 0)
+		// Minimum execution time: 7_945_000 picoseconds.
+		Weight::from_parts(8_203_000, 0)
 			.saturating_add(Weight::from_parts(0, 0))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -399,11 +401,11 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_025_000 picoseconds.
-		Weight::from_parts(2_229_953, 0)
+		// Minimum execution time: 2_458_000 picoseconds.
+		Weight::from_parts(2_815_664, 0)
 			.saturating_add(Weight::from_parts(0, 0))
 			// Standard Error: 67
-			.saturating_add(Weight::from_parts(11_785, 0).saturating_mul(v.into()))
+			.saturating_add(Weight::from_parts(12_287, 0).saturating_mul(v.into()))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
 	/// Storage: `Staking::Ledger` (r:1502 w:1502)
@@ -415,13 +417,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `i` is `[0, 751]`.
 	fn deprecate_controller_batch(i: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `680 + i * (227 ±0)`
+		//  Measured:  `713 + i * (227 ±0)`
 		//  Estimated: `990 + i * (7132 ±0)`
-		// Minimum execution time: 4_321_000 picoseconds.
-		Weight::from_parts(4_407_000, 0)
+		// Minimum execution time: 4_976_000 picoseconds.
+		Weight::from_parts(5_102_000, 0)
 			.saturating_add(Weight::from_parts(0, 990))
-			// Standard Error: 37_239
-			.saturating_add(Weight::from_parts(21_300_598, 0).saturating_mul(i.into()))
+			// Standard Error: 36_458
+			.saturating_add(Weight::from_parts(25_359_275, 0).saturating_mul(i.into()))
 			.saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(i.into())))
 			.saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(i.into())))
 			.saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into()))
@@ -432,10 +434,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `System::Account` (r:1 w:1)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
@@ -457,15 +459,15 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `s` is `[0, 100]`.
 	fn force_unstake(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2127 + s * (4 ±0)`
+		//  Measured:  `2153 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 78_908_000 picoseconds.
-		Weight::from_parts(84_886_373, 0)
+		// Minimum execution time: 116_776_000 picoseconds.
+		Weight::from_parts(125_460_389, 0)
 			.saturating_add(Weight::from_parts(0, 6248))
-			// Standard Error: 3_376
-			.saturating_add(Weight::from_parts(1_217_850, 0).saturating_mul(s.into()))
+			// Standard Error: 3_095
+			.saturating_add(Weight::from_parts(1_300_502, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(13))
-			.saturating_add(T::DbWeight::get().writes(12))
+			.saturating_add(T::DbWeight::get().writes(13))
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -474,13 +476,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `s` is `[1, 1000]`.
 	fn cancel_deferred_slash(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `66639`
-		//  Estimated: `70104`
-		// Minimum execution time: 136_389_000 picoseconds.
-		Weight::from_parts(1_207_241_524, 0)
-			.saturating_add(Weight::from_parts(0, 70104))
-			// Standard Error: 77_138
-			.saturating_add(Weight::from_parts(6_443_948, 0).saturating_mul(s.into()))
+		//  Measured:  `66672`
+		//  Estimated: `70137`
+		// Minimum execution time: 135_135_000 picoseconds.
+		Weight::from_parts(937_565_332, 0)
+			.saturating_add(Weight::from_parts(0, 70137))
+			// Standard Error: 57_675
+			.saturating_add(Weight::from_parts(4_828_080, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
@@ -498,12 +500,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::ErasValidatorReward` (r:1 w:0)
 	/// Proof: `Staking::ErasValidatorReward` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:65 w:65)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:65 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
-	/// Storage: `System::Account` (r:65 w:65)
-	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:65 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:65 w:65)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::ErasStakersPaged` (r:1 w:0)
 	/// Proof: `Staking::ErasStakersPaged` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Staking::ErasRewardPoints` (r:1 w:0)
@@ -512,30 +512,32 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::ErasValidatorPrefs` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Payee` (r:65 w:0)
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:65 w:65)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// The range of component `n` is `[0, 64]`.
 	fn payout_stakers_alive_staked(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `8249 + n * (396 ±0)`
-		//  Estimated: `10779 + n * (3774 ±0)`
-		// Minimum execution time: 130_222_000 picoseconds.
-		Weight::from_parts(167_236_150, 0)
-			.saturating_add(Weight::from_parts(0, 10779))
-			// Standard Error: 34_051
-			.saturating_add(Weight::from_parts(39_899_917, 0).saturating_mul(n.into()))
+		//  Measured:  `8275 + n * (389 ±0)`
+		//  Estimated: `10805 + n * (3566 ±0)`
+		// Minimum execution time: 180_144_000 picoseconds.
+		Weight::from_parts(237_134_733, 0)
+			.saturating_add(Weight::from_parts(0, 10805))
+			// Standard Error: 52_498
+			.saturating_add(Weight::from_parts(73_633_326, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(14))
 			.saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into())))
 			.saturating_add(T::DbWeight::get().writes(4))
 			.saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into())))
-			.saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into()))
+			.saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into()))
 	}
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:0)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
@@ -543,26 +545,26 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `l` is `[1, 32]`.
 	fn rebond(l: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1922 + l * (5 ±0)`
+		//  Measured:  `1845 + l * (5 ±0)`
 		//  Estimated: `8877`
-		// Minimum execution time: 79_136_000 picoseconds.
-		Weight::from_parts(82_129_497, 0)
+		// Minimum execution time: 89_307_000 picoseconds.
+		Weight::from_parts(92_902_634, 0)
 			.saturating_add(Weight::from_parts(0, 8877))
-			// Standard Error: 3_867
-			.saturating_add(Weight::from_parts(75_156, 0).saturating_mul(l.into()))
+			// Standard Error: 4_446
+			.saturating_add(Weight::from_parts(73_546, 0).saturating_mul(l.into()))
 			.saturating_add(T::DbWeight::get().reads(9))
-			.saturating_add(T::DbWeight::get().writes(7))
+			.saturating_add(T::DbWeight::get().writes(6))
 	}
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:1)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::SlashingSpans` (r:1 w:1)
 	/// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Nominators` (r:1 w:1)
@@ -582,15 +584,15 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `s` is `[1, 100]`.
 	fn reap_stash(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2127 + s * (4 ±0)`
+		//  Measured:  `2153 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 89_375_000 picoseconds.
-		Weight::from_parts(91_224_907, 0)
+		// Minimum execution time: 130_544_000 picoseconds.
+		Weight::from_parts(133_260_598, 0)
 			.saturating_add(Weight::from_parts(0, 6248))
-			// Standard Error: 3_424
-			.saturating_add(Weight::from_parts(1_219_542, 0).saturating_mul(s.into()))
+			// Standard Error: 3_545
+			.saturating_add(Weight::from_parts(1_313_348, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(12))
-			.saturating_add(T::DbWeight::get().writes(11))
+			.saturating_add(T::DbWeight::get().writes(12))
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -633,14 +635,14 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	fn new_era(v: u32, n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `0 + n * (716 ±0) + v * (3594 ±0)`
-		//  Estimated: `456136 + n * (3566 ±4) + v * (3566 ±0)`
-		// Minimum execution time: 520_905_000 picoseconds.
-		Weight::from_parts(523_771_000, 0)
+		//  Estimated: `456136 + n * (3566 ±4) + v * (3566 ±40)`
+		// Minimum execution time: 654_756_000 picoseconds.
+		Weight::from_parts(658_861_000, 0)
 			.saturating_add(Weight::from_parts(0, 456136))
-			// Standard Error: 2_142_714
-			.saturating_add(Weight::from_parts(68_631_588, 0).saturating_mul(v.into()))
-			// Standard Error: 213_509
-			.saturating_add(Weight::from_parts(19_343_025, 0).saturating_mul(n.into()))
+			// Standard Error: 2_078_102
+			.saturating_add(Weight::from_parts(67_775_668, 0).saturating_mul(v.into()))
+			// Standard Error: 207_071
+			.saturating_add(Weight::from_parts(22_624_711, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(184))
 			.saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into())))
 			.saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into())))
@@ -669,15 +671,15 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `n` is `[500, 1000]`.
 	fn get_npos_voters(v: u32, n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3108 + n * (907 ±0) + v * (391 ±0)`
+		//  Measured:  `3141 + n * (907 ±0) + v * (391 ±0)`
 		//  Estimated: `456136 + n * (3566 ±0) + v * (3566 ±0)`
-		// Minimum execution time: 36_848_619_000 picoseconds.
-		Weight::from_parts(37_362_442_000, 0)
+		// Minimum execution time: 42_790_195_000 picoseconds.
+		Weight::from_parts(42_954_437_000, 0)
 			.saturating_add(Weight::from_parts(0, 456136))
-			// Standard Error: 415_031
-			.saturating_add(Weight::from_parts(5_204_987, 0).saturating_mul(v.into()))
-			// Standard Error: 415_031
-			.saturating_add(Weight::from_parts(4_132_636, 0).saturating_mul(n.into()))
+			// Standard Error: 478_107
+			.saturating_add(Weight::from_parts(6_744_044, 0).saturating_mul(v.into()))
+			// Standard Error: 478_107
+			.saturating_add(Weight::from_parts(4_837_739, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(179))
 			.saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into())))
 			.saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into())))
@@ -692,13 +694,13 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// The range of component `v` is `[500, 1000]`.
 	fn get_npos_targets(v: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `946 + v * (50 ±0)`
+		//  Measured:  `979 + v * (50 ±0)`
 		//  Estimated: `3510 + v * (2520 ±0)`
-		// Minimum execution time: 2_512_817_000 picoseconds.
-		Weight::from_parts(119_401_374, 0)
+		// Minimum execution time: 2_851_801_000 picoseconds.
+		Weight::from_parts(4_477_533, 0)
 			.saturating_add(Weight::from_parts(0, 3510))
-			// Standard Error: 8_463
-			.saturating_add(Weight::from_parts(4_860_364, 0).saturating_mul(v.into()))
+			// Standard Error: 8_644
+			.saturating_add(Weight::from_parts(5_811_682, 0).saturating_mul(v.into()))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into())))
 			.saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into()))
@@ -721,8 +723,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 3_686_000 picoseconds.
-		Weight::from_parts(3_881_000, 0)
+		// Minimum execution time: 4_250_000 picoseconds.
+		Weight::from_parts(4_472_000, 0)
 			.saturating_add(Weight::from_parts(0, 0))
 			.saturating_add(T::DbWeight::get().writes(7))
 	}
@@ -744,8 +746,8 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 3_143_000 picoseconds.
-		Weight::from_parts(3_424_000, 0)
+		// Minimum execution time: 3_986_000 picoseconds.
+		Weight::from_parts(4_144_000, 0)
 			.saturating_add(Weight::from_parts(0, 0))
 			.saturating_add(T::DbWeight::get().writes(7))
 	}
@@ -773,10 +775,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn chill_other() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1870`
+		//  Measured:  `1903`
 		//  Estimated: `6248`
-		// Minimum execution time: 66_946_000 picoseconds.
-		Weight::from_parts(69_382_000, 0)
+		// Minimum execution time: 87_291_000 picoseconds.
+		Weight::from_parts(89_344_000, 0)
 			.saturating_add(Weight::from_parts(0, 6248))
 			.saturating_add(T::DbWeight::get().reads(12))
 			.saturating_add(T::DbWeight::get().writes(6))
@@ -787,10 +789,10 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	fn force_apply_min_commission() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `658`
+		//  Measured:  `691`
 		//  Estimated: `3510`
-		// Minimum execution time: 11_278_000 picoseconds.
-		Weight::from_parts(11_603_000, 0)
+		// Minimum execution time: 16_113_000 picoseconds.
+		Weight::from_parts(16_593_000, 0)
 			.saturating_add(Weight::from_parts(0, 3510))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(1))
@@ -801,29 +803,53 @@ impl<T: frame_system::Config> pallet_staking::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 1_963_000 picoseconds.
-		Weight::from_parts(2_077_000, 0)
+		// Minimum execution time: 2_433_000 picoseconds.
+		Weight::from_parts(2_561_000, 0)
 			.saturating_add(Weight::from_parts(0, 0))
 			.saturating_add(T::DbWeight::get().writes(1))
 	}
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `System::Account` (r:1 w:1)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:0)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Locks` (r:1 w:0)
+	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:1)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
+	fn restore_ledger() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1040`
+		//  Estimated: `4764`
+		// Minimum execution time: 50_167_000 picoseconds.
+		Weight::from_parts(51_108_000, 0)
+			.saturating_add(Weight::from_parts(0, 4764))
+			.saturating_add(T::DbWeight::get().reads(6))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::Bonded` (r:1 w:0)
+	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::Ledger` (r:1 w:0)
+	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Locks` (r:1 w:1)
+	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(103), added: 2578, mode: `MaxEncodedLen`)
 	/// Storage: `Balances::Freezes` (r:1 w:0)
 	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
-	fn restore_ledger() -> Weight {
+	fn migrate_currency() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1014`
+		//  Measured:  `1209`
 		//  Estimated: `4764`
-		// Minimum execution time: 40_258_000 picoseconds.
-		Weight::from_parts(41_210_000, 0)
+		// Minimum execution time: 91_790_000 picoseconds.
+		Weight::from_parts(92_991_000, 0)
 			.saturating_add(Weight::from_parts(0, 4764))
-			.saturating_add(T::DbWeight::get().reads(5))
-			.saturating_add(T::DbWeight::get().writes(4))
+			.saturating_add(T::DbWeight::get().reads(6))
+			.saturating_add(T::DbWeight::get().writes(2))
 	}
 }
diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs
index 3f6a7304c8a94b8b502cb19f0016e57b69ddee2d..4235edf82b24de71f82c21dda1bd69c231048ac9 100644
--- a/polkadot/runtime/westend/src/xcm_config.rs
+++ b/polkadot/runtime/westend/src/xcm_config.rs
@@ -280,7 +280,10 @@ impl pallet_xcm::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	// Note that this configuration of `SendXcmOrigin` is different from the one present in
 	// production.
-	type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
+	type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<
+		RuntimeOrigin,
+		(LocalPalletOriginToLocation, LocalOriginToLocation),
+	>;
 	type XcmRouter = XcmRouter;
 	// Anyone can execute XCM messages locally.
 	type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
diff --git a/polkadot/xcm/docs/Cargo.toml b/polkadot/xcm/docs/Cargo.toml
index 6fa7ea9a23a92c2e3f86082a630ca3994bfce1d1..c3bda50619c1572202db2a3334bc14d4d11c6b4b 100644
--- a/polkadot/xcm/docs/Cargo.toml
+++ b/polkadot/xcm/docs/Cargo.toml
@@ -18,7 +18,7 @@ xcm-simulator = { workspace = true, default-features = true }
 
 # For building FRAME runtimes
 codec = { workspace = true, default-features = true }
-frame = { features = ["experimental", "runtime"], workspace = true, default-features = true }
+frame = { features = ["runtime"], workspace = true, default-features = true }
 polkadot-parachain-primitives = { workspace = true, default-features = true }
 polkadot-primitives = { workspace = true, default-features = true }
 polkadot-runtime-parachains = { workspace = true, default-features = true }
diff --git a/prdoc/pr_3926.prdoc b/prdoc/pr_3926.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..7f352f7a45fb39876d7d2d0771d3e5cee496ead3
--- /dev/null
+++ b/prdoc/pr_3926.prdoc
@@ -0,0 +1,30 @@
+title: Introduce pallet-asset-rewards
+
+doc:
+  - audience: Runtime Dev
+    description: |
+        Introduce pallet-asset-rewards, which allows accounts to be rewarded for freezing fungible 
+        tokens. The motivation for creating this pallet is to allow incentivising LPs.
+        See the pallet docs for more info about the pallet.
+
+crates:
+  - name: pallet-asset-rewards
+    bump: major
+  - name: polkadot-sdk
+    bump: minor
+  - name: kitchensink-runtime
+    bump: major
+  - name: asset-hub-rococo-runtime
+    bump: major
+  - name: asset-hub-westend-runtime
+    bump: major
+  - name: assets-common
+    bump: minor
+  - name: rococo-runtime
+    bump: minor
+  - name: westend-runtime
+    bump: patch
+  - name: frame-support
+    bump: minor
+  - name: emulated-integration-tests-common
+    bump: minor
diff --git a/prdoc/pr_5501.prdoc b/prdoc/pr_5501.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..f2a5aa9a4667980452d4ac4dc728752da3591634
--- /dev/null
+++ b/prdoc/pr_5501.prdoc
@@ -0,0 +1,47 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: Currency to Fungible migration for pallet-staking
+
+doc:
+  - audience: Runtime User
+    description: |
+      Lazy migration of staking balance from `Currency::locks` to `Fungible::holds`. New extrinsic
+      `staking::migrate_currency` removes the old lock along with other housekeeping. Additionally, any ledger mutation
+      creates hold if it does not exist.
+
+      The pallet-staking configuration item `Currency` is updated to use `fungible::hold::Mutate` type while still
+      requiring `LockableCurrency` type to be passed as `OldCurrency` for migration purposes.
+
+
+crates:
+  - name: westend-runtime
+    bump: major
+  - name: kitchensink-runtime
+    bump: minor
+  - name: pallet-delegated-staking
+    bump: patch
+  - name: pallet-nomination-pools
+    bump: minor
+  - name: pallet-nomination-pools-runtime-api
+    bump: patch
+  - name: sp-staking
+    bump: patch
+  - name: pallet-beefy
+    bump: patch
+  - name: pallet-fast-unstake
+    bump: patch
+  - name: pallet-staking
+    bump: major
+  - name: pallet-grandpa
+    bump: patch
+  - name: pallet-babe
+    bump: patch
+  - name: pallet-nomination-pools-benchmarking
+    bump: patch
+  - name: pallet-session-benchmarking
+    bump: patch
+  - name: pallet-root-offences
+    bump: patch
+  - name: pallet-offences-benchmarking
+    bump: patch
diff --git a/prdoc/pr_6825.prdoc b/prdoc/pr_6825.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..d57b2b573a1014c603a1022dd9b49760c8d7eb61
--- /dev/null
+++ b/prdoc/pr_6825.prdoc
@@ -0,0 +1,50 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: Use relay chain slot for velocity measurement on parachains
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      The AuraExt pallets `ConsensusHook` is performing checks based on a parachains velocity. It was previously
+      checking how many blocks where produced in a given parachain slot. This only works well when the parachain
+      and relay chain slot length is the same. After this PR, we are checking against the relay chain slot.
+
+      **🚨 Action Required:** A migration of name `cumulus_pallet_aura_ext::migration::MigrateV0ToV1` is included
+      that cleans up a renamed storage item. Parachain must add it to their runtimes. More information is available in
+      the [reference docs](https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_upgrades_and_migrations/index.html#single-block-migrations).
+
+crates:
+  - name: cumulus-pallet-parachain-system
+    bump: minor
+  - name: cumulus-pallet-aura-ext
+    bump: major
+  - name: cumulus-primitives-aura
+    bump: major
+  - name: cumulus-client-parachain-inherent
+    bump: minor
+  - name: cumulus-client-consensus-aura
+    bump: minor
+  - name: xcm-emulator
+    bump: minor
+  - name: asset-hub-rococo-runtime
+    bump: minor
+  - name: asset-hub-westend-runtime
+    bump: minor
+  - name: bridge-hub-rococo-runtime
+    bump: minor
+  - name: bridge-hub-westend-runtime
+    bump: minor
+  - name: collectives-westend-runtime
+    bump: minor
+  - name: coretime-rococo-runtime
+    bump: minor
+  - name: coretime-westend-runtime
+    bump: minor
+  - name: people-rococo-runtime
+    bump: minor
+  - name: people-westend-runtime
+    bump: minor
+  - name: contracts-rococo-runtime
+    bump: minor
+        
diff --git a/prdoc/pr_6890.prdoc b/prdoc/pr_6890.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..b22a339035d81be722b139bd4de1337d4179872d
--- /dev/null
+++ b/prdoc/pr_6890.prdoc
@@ -0,0 +1,19 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: Alter semantic meaning of 0 in metering limits of EVM contract calls
+
+doc:
+  - audience: [ Runtime Dev, Runtime User ]
+    description: |
+      A limit of 0, for gas meters and storage meters, no longer has the meaning of unlimited metering.
+
+crates:
+  - name: pallet-revive
+    bump: patch
+  - name: pallet-revive-fixtures
+    bump: patch
+  - name: pallet-revive-uapi
+    bump: patch
+  - name: pallet-revive-eth-rpc
+    bump: patch
diff --git a/prdoc/pr_6971.prdoc b/prdoc/pr_6971.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..4790d773fee4e66af262abaa6f506ba7f2aedd91
--- /dev/null
+++ b/prdoc/pr_6971.prdoc
@@ -0,0 +1,16 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: Make importing of duplicate assignment idempotent
+
+doc:
+  - audience: Node Dev
+    description: |
+      Normally, approval-voting wouldn't receive duplicate assignments because approval-distribution makes
+      sure of it, however in the situation where we restart we might receive the same assignment again and
+      since approval-voting already persisted it we will end up inserting it twice in ApprovalEntry.tranches.assignments 
+      because that's an array. Fix this by inserting only assignments that are not duplicate.
+
+crates:
+  - name: polkadot-node-core-approval-voting
+    bump: minor
diff --git a/prdoc/pr_6973.prdoc b/prdoc/pr_6973.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..416789b9171ad3ad779e36add96158731ee22eb9
--- /dev/null
+++ b/prdoc/pr_6973.prdoc
@@ -0,0 +1,16 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: approval-voting fix sending of assignments after restart
+
+doc:
+  - audience: Node Dev
+    description: |
+      There is a problem on restart where nodes will not trigger their needed assignment if 
+      they were offline and the time of the assignment passed, so after restart always 
+      schedule a wakeup so that nodes a have the opportunity of triggering their assignments
+      if they are still needed.
+
+crates:
+  - name: polkadot-node-core-approval-voting
+    bump: minor
diff --git a/prdoc/pr_7086.prdoc b/prdoc/pr_7086.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..55fed9bca3e6c6e1f7df0c641dac73db4c6aa185
--- /dev/null
+++ b/prdoc/pr_7086.prdoc
@@ -0,0 +1,11 @@
+title: '[pallet-revive] Fix `caller_is_root` return value'
+doc:
+- audience: Runtime Dev
+  description: The return type of the host function `caller_is_root` was denoted as `u32`
+    in `pallet_revive_uapi`. This PR fixes the return type to `bool`. As a drive-by, the
+    PR re-exports `pallet_revive::exec::Origin` to extend what can be tested externally.
+crates:
+- name: pallet-revive
+  bump: minor
+- name: pallet-revive-uapi
+  bump: major
diff --git a/prdoc/pr_7091.prdoc b/prdoc/pr_7091.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..badea4e82fdbec9a48eb8948bd762b90cd49275a
--- /dev/null
+++ b/prdoc/pr_7091.prdoc
@@ -0,0 +1,12 @@
+title: '[pallet-revive] Add new host function `to_account_id`'
+doc:
+- audience: Runtime Dev
+  description: A new host function `to_account_id` is added. It allows retrieving
+    the account id for a `H160` address.
+crates:
+- name: pallet-revive-fixtures
+  bump: minor
+- name: pallet-revive
+  bump: minor
+- name: pallet-revive-uapi
+  bump: minor
diff --git a/prdoc/pr_7099.prdoc b/prdoc/pr_7099.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..58d809f3c0909b9236381aa0ab34a2f17ba84a40
--- /dev/null
+++ b/prdoc/pr_7099.prdoc
@@ -0,0 +1,16 @@
+title: Provide partial results to speedup GetRecord queries
+
+doc:
+  - audience: Node Dev
+    description: |
+      This PR provides the partial results of the GetRecord kademlia query.
+      
+      This significantly improves the authority discovery records, from ~37 minutes to ~2/3 minutes.
+      In contrast, libp2p discovers authority records in around ~10 minutes.
+      
+      The authority discovery was slow because litep2p provided the records only after the Kademlia query was completed. A normal Kademlia query completes in around 40 seconds to a few minutes.
+      In this PR, partial records are provided as soon as they are discovered from the network.
+
+crates:
+  - name: sc-network
+    bump: patch
diff --git a/prdoc/pr_7109.prdoc b/prdoc/pr_7109.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..e54ef3295135c50222a11efed540b82d5d8f64c9
--- /dev/null
+++ b/prdoc/pr_7109.prdoc
@@ -0,0 +1,11 @@
+title: Add "run to block" tools
+doc:
+- audience: Runtime Dev
+  description: |-
+    Introduce `frame_system::Pallet::run_to_block`, `frame_system::Pallet::run_to_block_with`, and `frame_system::RunToBlockHooks` to establish a generic `run_to_block` mechanism for mock tests, minimizing redundant implementations across various pallets.
+
+    Closes #299.
+
+crates:
+- name: frame-system
+  bump: minor
diff --git a/prdoc/pr_7134.prdoc b/prdoc/pr_7134.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..095d4757f43800bd402bbe988a471133f2914dcb
--- /dev/null
+++ b/prdoc/pr_7134.prdoc
@@ -0,0 +1,11 @@
+title: 'xcm: convert properly assets in xcmpayment apis'
+doc:
+- audience: Runtime User
+  description: |-
+    Port #6459 changes to relays as well, which were probably forgotten in that PR.
+    Thanks!
+crates:
+- name: rococo-runtime
+  bump: patch
+- name: westend-runtime
+  bump: patch
diff --git a/prdoc/pr_7158.prdoc b/prdoc/pr_7158.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..e113a7fdcd1c56ec37227db9282df0acc576e2bb
--- /dev/null
+++ b/prdoc/pr_7158.prdoc
@@ -0,0 +1,12 @@
+title: Reject litep2p inbound requests from banned peers
+
+doc:
+  - audience: Node Dev
+    description: |
+      This PR rejects inbound requests from banned peers (reputation is below the banned threshold).
+      This mirrors the request-response implementation from the libp2p side.
+      While at it, have registered a new inbound failure metric to have visibility into this.
+
+crates:
+- name: sc-network
+  bump: patch
diff --git a/prdoc/pr_7163.prdoc b/prdoc/pr_7163.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..669c480b835bd982699f6a0eec21fbb343d8b6e3
--- /dev/null
+++ b/prdoc/pr_7163.prdoc
@@ -0,0 +1,13 @@
+title: '[pallet-revive] Remove debug buffer'
+doc:
+- audience: Runtime Dev
+  description: Remove the `debug_buffer` feature
+crates:
+- name: asset-hub-westend-runtime
+  bump: minor
+- name: pallet-revive
+  bump: major
+- name: pallet-revive-proc-macro
+  bump: minor
+- name: pallet-revive-uapi
+  bump: minor
diff --git a/prdoc/pr_7164.prdoc b/prdoc/pr_7164.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..cb0410a9de79954bab775de09629610ba420b2b4
--- /dev/null
+++ b/prdoc/pr_7164.prdoc
@@ -0,0 +1,8 @@
+title: '[pallet-revive] Remove revive events'
+doc:
+- audience: Runtime Dev
+  description: Remove all pallet::events except for the `ContractEmitted` event that
+    is emitted by contracts
+crates:
+- name: pallet-revive
+  bump: major
diff --git a/prdoc/pr_7170.prdoc b/prdoc/pr_7170.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..fae908f7407d65096910c06ea765c72fb071620d
--- /dev/null
+++ b/prdoc/pr_7170.prdoc
@@ -0,0 +1,8 @@
+title: Fix reversed error message in DispatchInfo
+doc:
+- audience: Runtime Dev
+  description: "Fix error message in `DispatchInfo` where post-dispatch and pre-dispatch\
+    \ weight was reversed.\r\n"
+crates:
+- name: frame-support
+  bump: patch
diff --git a/prdoc/pr_7176.prdoc b/prdoc/pr_7176.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..b78f42014afea5e04d6c3057e91c62d9f9928db5
--- /dev/null
+++ b/prdoc/pr_7176.prdoc
@@ -0,0 +1,9 @@
+title: '[pallet-revive] Bump asset-hub westend spec version'
+doc:
+- audience: Runtime Dev
+  description: Bump asset-hub westend spec version
+crates:
+- name: asset-hub-westend-runtime
+  bump: minor
+- name: pallet-revive-eth-rpc
+  bump: minor
diff --git a/prdoc/pr_7177.prdoc b/prdoc/pr_7177.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..9ab0be1f20a936efe9feadf81c79a5f74f3531f6
--- /dev/null
+++ b/prdoc/pr_7177.prdoc
@@ -0,0 +1,20 @@
+title: Make frame crate not experimental
+doc:
+- audience: Runtime Dev
+  description: |-
+    Frame crate may still be unstable, but it is no longer feature gated by the feature `experimental`.
+crates:
+- name: polkadot-sdk-frame
+  bump: minor
+- name: pallet-salary
+  bump: patch
+- name: pallet-multisig
+  bump: patch
+- name: pallet-proxy
+  bump: patch
+- name: pallet-atomic-swap
+  bump: patch
+- name: pallet-mixnet
+  bump: patch
+- name: pallet-node-authorization
+  bump: patch
diff --git a/prdoc/pr_7194.prdoc b/prdoc/pr_7194.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..3a9db46ceae96fdbb9f352a4558b1ab95ffd1450
--- /dev/null
+++ b/prdoc/pr_7194.prdoc
@@ -0,0 +1,15 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: '[FRAME] `pallet_asset_tx_payment`: replace `AssetId` bound from `Copy` to `Clone`'
+
+doc:
+  - audience: Runtime Dev
+    description: |
+      `OnChargeAssetTransaction`'s associated type `AssetId` is bounded by `Copy` which makes it impossible
+      to use `staging_xcm::v4::Location` as `AssetId`. This PR bounds `AssetId` to `Clone` instead, which is 
+      more lenient.
+
+crates:
+  - name: pallet-asset-tx-payment
+    bump: minor
diff --git a/prdoc/pr_7203.prdoc b/prdoc/pr_7203.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..96a3d19472e9f77189eaaf0f9ad3c829aa27e8a4
--- /dev/null
+++ b/prdoc/pr_7203.prdoc
@@ -0,0 +1,13 @@
+title: 'pallet_revive: Bump PolkaVM'
+doc:
+- audience: Runtime Dev
+  description: Update to PolkaVM `0.19`. This version renumbers the opcodes in order
+    to be in-line with the grey paper. Hopefully, for the last time. This means that
+    it breaks existing contracts.
+crates:
+- name: pallet-revive
+  bump: patch
+- name: pallet-revive-fixtures
+  bump: patch
+- name: pallet-revive-uapi
+  bump: patch
diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs
index e11a009c1c3f5d4081f9bffc6b6a73d24800f17e..117d306e3060eab232744aa7b4103ad819c6e40d 100644
--- a/substrate/bin/node/runtime/src/lib.rs
+++ b/substrate/bin/node/runtime/src/lib.rs
@@ -53,13 +53,15 @@ use frame_support::{
 			Balanced, Credit, HoldConsideration, ItemOf, NativeFromLeft, NativeOrWithId, UnionOf,
 		},
 		tokens::{
-			imbalance::ResolveAssetTo, nonfungibles_v2::Inspect, pay::PayAssetFromAccount,
+			imbalance::{ResolveAssetTo, ResolveTo},
+			nonfungibles_v2::Inspect,
+			pay::PayAssetFromAccount,
 			GetSalary, PayFromAccount,
 		},
-		AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, Contains,
-		Currency, EitherOfDiverse, EnsureOriginWithArg, EqualPrivilegeOnly, Imbalance, InsideBoth,
-		InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, LockIdentifier, Nothing,
-		OnUnbalanced, VariantCountOf, WithdrawReasons,
+		AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, ConstU64,
+		ConstantStoragePrice, Contains, Currency, EitherOfDiverse, EnsureOriginWithArg,
+		EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, KeyOwnerProofSystem,
+		LinearStoragePrice, LockIdentifier, Nothing, OnUnbalanced, VariantCountOf, WithdrawReasons,
 	},
 	weights::{
 		constants::{
@@ -511,7 +513,8 @@ impl pallet_glutton::Config for Runtime {
 }
 
 parameter_types! {
-	pub const PreimageHoldReason: RuntimeHoldReason = RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage);
+	pub const PreimageHoldReason: RuntimeHoldReason =
+		RuntimeHoldReason::Preimage(pallet_preimage::HoldReason::Preimage);
 }
 
 impl pallet_preimage::Config for Runtime {
@@ -618,6 +621,12 @@ impl pallet_transaction_payment::Config for Runtime {
 	type WeightInfo = pallet_transaction_payment::weights::SubstrateWeight<Runtime>;
 }
 
+pub type AssetsFreezerInstance = pallet_assets_freezer::Instance1;
+impl pallet_assets_freezer::Config<AssetsFreezerInstance> for Runtime {
+	type RuntimeFreezeReason = RuntimeFreezeReason;
+	type RuntimeEvent = RuntimeEvent;
+}
+
 impl pallet_asset_conversion_tx_payment::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type AssetId = NativeOrWithId<u32>;
@@ -712,13 +721,15 @@ impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig {
 }
 
 impl pallet_staking::Config for Runtime {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = Balance;
 	type UnixTime = Timestamp;
 	type CurrencyToVote = sp_staking::currency_to_vote::U128CurrencyToVote;
-	type RewardRemainder = Treasury;
+	type RewardRemainder = ResolveTo<TreasuryAccount, Balances>;
 	type RuntimeEvent = RuntimeEvent;
-	type Slash = Treasury; // send the slashed funds to the treasury.
+	type RuntimeHoldReason = RuntimeHoldReason;
+	type Slash = ResolveTo<TreasuryAccount, Balances>; // send the slashed funds to the treasury.
 	type Reward = (); // rewards are minted from the void
 	type SessionsPerEra = SessionsPerEra;
 	type BondingDuration = BondingDuration;
@@ -741,7 +752,7 @@ impl pallet_staking::Config for Runtime {
 	type MaxUnlockingChunks = ConstU32<32>;
 	type MaxControllersInDeprecationBatch = MaxControllersInDeprecationBatch;
 	type HistoryDepth = HistoryDepth;
-	type EventListeners = NominationPools;
+	type EventListeners = (NominationPools, DelegatedStaking);
 	type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
 	type BenchmarkingConfig = StakingBenchmarkingConfig;
 	type DisablingStrategy = pallet_staking::UpToLimitWithReEnablingDisablingStrategy;
@@ -925,6 +936,21 @@ impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime {
 	type WeightInfo = pallet_bags_list::weights::SubstrateWeight<Runtime>;
 }
 
+parameter_types! {
+	pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk");
+	pub const SlashRewardFraction: Perbill = Perbill::from_percent(1);
+}
+
+impl pallet_delegated_staking::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type PalletId = DelegatedStakingPalletId;
+	type Currency = Balances;
+	type OnSlash = ();
+	type SlashRewardFraction = SlashRewardFraction;
+	type RuntimeHoldReason = RuntimeHoldReason;
+	type CoreStaking = Staking;
+}
+
 parameter_types! {
 	pub const PostUnbondPoolsWindow: u32 = 4;
 	pub const NominationPoolsPalletId: PalletId = PalletId(*b"py/nopls");
@@ -953,7 +979,8 @@ impl pallet_nomination_pools::Config for Runtime {
 	type RewardCounter = FixedU128;
 	type BalanceToU256 = BalanceToU256;
 	type U256ToBalance = U256ToBalance;
-	type StakeAdapter = pallet_nomination_pools::adapter::TransferStake<Self, Staking>;
+	type StakeAdapter =
+		pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>;
 	type PostUnbondingPoolsWindow = PostUnbondPoolsWindow;
 	type MaxMetadataLen = ConstU32<256>;
 	type MaxUnbonding = ConstU32<8>;
@@ -1858,6 +1885,53 @@ impl pallet_asset_conversion::Config for Runtime {
 	type BenchmarkHelper = ();
 }
 
+pub type NativeAndAssetsFreezer =
+	UnionOf<Balances, AssetsFreezer, NativeFromLeft, NativeOrWithId<u32>, AccountId>;
+
+/// Benchmark Helper
+#[cfg(feature = "runtime-benchmarks")]
+pub struct AssetRewardsBenchmarkHelper;
+
+#[cfg(feature = "runtime-benchmarks")]
+impl pallet_asset_rewards::benchmarking::BenchmarkHelper<NativeOrWithId<u32>>
+	for AssetRewardsBenchmarkHelper
+{
+	fn staked_asset() -> NativeOrWithId<u32> {
+		NativeOrWithId::<u32>::WithId(100)
+	}
+	fn reward_asset() -> NativeOrWithId<u32> {
+		NativeOrWithId::<u32>::WithId(101)
+	}
+}
+
+parameter_types! {
+	pub const StakingRewardsPalletId: PalletId = PalletId(*b"py/stkrd");
+	pub const CreationHoldReason: RuntimeHoldReason =
+		RuntimeHoldReason::AssetRewards(pallet_asset_rewards::HoldReason::PoolCreation);
+	// 1 item, 135 bytes into the storage on pool creation.
+	pub const StakePoolCreationDeposit: Balance = deposit(1, 135);
+}
+
+impl pallet_asset_rewards::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type RuntimeFreezeReason = RuntimeFreezeReason;
+	type AssetId = NativeOrWithId<u32>;
+	type Balance = Balance;
+	type Assets = NativeAndAssets;
+	type PalletId = StakingRewardsPalletId;
+	type CreatePoolOrigin = EnsureSigned<AccountId>;
+	type WeightInfo = ();
+	type AssetsFreezer = NativeAndAssetsFreezer;
+	type Consideration = HoldConsideration<
+		AccountId,
+		Balances,
+		CreationHoldReason,
+		ConstantStoragePrice<StakePoolCreationDeposit, Balance>,
+	>;
+	#[cfg(feature = "runtime-benchmarks")]
+	type BenchmarkHelper = AssetRewardsBenchmarkHelper;
+}
+
 impl pallet_asset_conversion_ops::Config for Runtime {
 	type RuntimeEvent = RuntimeEvent;
 	type PriorAccountIdConverter = pallet_asset_conversion::AccountIdConverterNoSeed<(
@@ -2636,6 +2710,15 @@ mod runtime {
 
 	#[runtime::pallet_index(81)]
 	pub type VerifySignature = pallet_verify_signature::Pallet<Runtime>;
+
+	#[runtime::pallet_index(82)]
+	pub type DelegatedStaking = pallet_delegated_staking::Pallet<Runtime>;
+
+	#[runtime::pallet_index(83)]
+	pub type AssetRewards = pallet_asset_rewards::Pallet<Runtime>;
+
+	#[runtime::pallet_index(84)]
+	pub type AssetsFreezer = pallet_assets_freezer::Pallet<Runtime, Instance1>;
 }
 
 impl TryFrom<RuntimeCall> for pallet_revive::Call<Runtime> {
@@ -2846,6 +2929,7 @@ mod benches {
 		[pallet_example_tasks, TasksExample]
 		[pallet_democracy, Democracy]
 		[pallet_asset_conversion, AssetConversion]
+		[pallet_asset_rewards, AssetRewards]
 		[pallet_asset_conversion_tx_payment, AssetConversionTxPayment]
 		[pallet_transaction_payment, TransactionPayment]
 		[pallet_election_provider_multi_phase, ElectionProviderMultiPhase]
@@ -3212,7 +3296,7 @@ impl_runtime_apis! {
 		}
 	}
 
-	impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, EventRecord> for Runtime
+	impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber> for Runtime
 	{
 		fn balance(address: H160) -> U256 {
 			Revive::evm_balance(&address)
@@ -3247,7 +3331,7 @@ impl_runtime_apis! {
 			gas_limit: Option<Weight>,
 			storage_deposit_limit: Option<Balance>,
 			input_data: Vec<u8>,
-		) -> pallet_revive::ContractResult<pallet_revive::ExecReturnValue, Balance, EventRecord> {
+		) -> pallet_revive::ContractResult<pallet_revive::ExecReturnValue, Balance> {
 			Revive::bare_call(
 				RuntimeOrigin::signed(origin),
 				dest,
@@ -3255,8 +3339,6 @@ impl_runtime_apis! {
 				gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block),
 				pallet_revive::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
 				input_data,
-				pallet_revive::DebugInfo::UnsafeDebug,
-				pallet_revive::CollectEvents::UnsafeCollect,
 			)
 		}
 
@@ -3268,7 +3350,7 @@ impl_runtime_apis! {
 			code: pallet_revive::Code,
 			data: Vec<u8>,
 			salt: Option<[u8; 32]>,
-		) -> pallet_revive::ContractResult<pallet_revive::InstantiateReturnValue, Balance, EventRecord>
+		) -> pallet_revive::ContractResult<pallet_revive::InstantiateReturnValue, Balance>
 		{
 			Revive::bare_instantiate(
 				RuntimeOrigin::signed(origin),
@@ -3278,8 +3360,6 @@ impl_runtime_apis! {
 				code,
 				data,
 				salt,
-				pallet_revive::DebugInfo::UnsafeDebug,
-				pallet_revive::CollectEvents::UnsafeCollect,
 			)
 		}
 
@@ -3557,6 +3637,12 @@ impl_runtime_apis! {
 		}
 	}
 
+	impl pallet_asset_rewards::AssetRewards<Block, Balance> for Runtime {
+		fn pool_creation_cost() -> Balance {
+			StakePoolCreationDeposit::get()
+		}
+	}
+
 	#[cfg(feature = "try-runtime")]
 	impl frame_try_runtime::TryRuntime<Block> for Runtime {
 		fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) {
diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs
index 7f5364744c667f711a5e2c32e20636e904ee02c2..0394f6cd7394d3715c268229ba254053d6de5f53 100644
--- a/substrate/bin/node/testing/src/genesis.rs
+++ b/substrate/bin/node/testing/src/genesis.rs
@@ -38,9 +38,9 @@ pub fn config_endowed(extra_endowed: Vec<AccountId>) -> RuntimeGenesisConfig {
 		(alice(), 111 * DOLLARS),
 		(bob(), 100 * DOLLARS),
 		(charlie(), 100_000_000 * DOLLARS),
-		(dave(), 111 * DOLLARS),
+		(dave(), 112 * DOLLARS),
 		(eve(), 101 * DOLLARS),
-		(ferdie(), 100 * DOLLARS),
+		(ferdie(), 101 * DOLLARS),
 	];
 
 	endowed.extend(extra_endowed.into_iter().map(|endowed| (endowed, 100 * DOLLARS)));
diff --git a/substrate/client/network/src/litep2p/discovery.rs b/substrate/client/network/src/litep2p/discovery.rs
index b55df374f60ecc71959327c063c86b21e960f0b3..eb571804f30e61841ace4f985fe7a439bd9a43b7 100644
--- a/substrate/client/network/src/litep2p/discovery.rs
+++ b/substrate/client/network/src/litep2p/discovery.rs
@@ -33,8 +33,8 @@ use litep2p::{
 			identify::{Config as IdentifyConfig, IdentifyEvent},
 			kademlia::{
 				Config as KademliaConfig, ConfigBuilder as KademliaConfigBuilder, ContentProvider,
-				IncomingRecordValidationMode, KademliaEvent, KademliaHandle, QueryId, Quorum,
-				Record, RecordKey, RecordsType,
+				IncomingRecordValidationMode, KademliaEvent, KademliaHandle, PeerRecord, QueryId,
+				Quorum, Record, RecordKey,
 			},
 			ping::{Config as PingConfig, PingEvent},
 		},
@@ -129,13 +129,19 @@ pub enum DiscoveryEvent {
 		address: Multiaddr,
 	},
 
-	/// Record was found from the DHT.
+	/// `GetRecord` query succeeded.
 	GetRecordSuccess {
 		/// Query ID.
 		query_id: QueryId,
+	},
 
-		/// Records.
-		records: RecordsType,
+	/// Record was found from the DHT.
+	GetRecordPartialResult {
+		/// Query ID.
+		query_id: QueryId,
+
+		/// Record.
+		record: PeerRecord,
 	},
 
 	/// Record was successfully stored on the DHT.
@@ -573,13 +579,24 @@ impl Stream for Discovery {
 					peers: peers.into_iter().collect(),
 				}))
 			},
-			Poll::Ready(Some(KademliaEvent::GetRecordSuccess { query_id, records })) => {
+			Poll::Ready(Some(KademliaEvent::GetRecordSuccess { query_id })) => {
 				log::trace!(
 					target: LOG_TARGET,
-					"`GET_RECORD` succeeded for {query_id:?}: {records:?}",
+					"`GET_RECORD` succeeded for {query_id:?}",
 				);
 
-				return Poll::Ready(Some(DiscoveryEvent::GetRecordSuccess { query_id, records }));
+				return Poll::Ready(Some(DiscoveryEvent::GetRecordSuccess { query_id }));
+			},
+			Poll::Ready(Some(KademliaEvent::GetRecordPartialResult { query_id, record })) => {
+				log::trace!(
+					target: LOG_TARGET,
+					"`GET_RECORD` intermediary succeeded for {query_id:?}: {record:?}",
+				);
+
+				return Poll::Ready(Some(DiscoveryEvent::GetRecordPartialResult {
+					query_id,
+					record,
+				}));
 			},
 			Poll::Ready(Some(KademliaEvent::PutRecordSuccess { query_id, key: _ })) =>
 				return Poll::Ready(Some(DiscoveryEvent::PutRecordSuccess { query_id })),
diff --git a/substrate/client/network/src/litep2p/mod.rs b/substrate/client/network/src/litep2p/mod.rs
index 52b2970525df1e0a58c9daf409a2e65355fdea45..fc4cce47628396bd22c8785edd18227c27a7326a 100644
--- a/substrate/client/network/src/litep2p/mod.rs
+++ b/substrate/client/network/src/litep2p/mod.rs
@@ -58,7 +58,7 @@ use litep2p::{
 	protocol::{
 		libp2p::{
 			bitswap::Config as BitswapConfig,
-			kademlia::{QueryId, Record, RecordsType},
+			kademlia::{QueryId, Record},
 		},
 		request_response::ConfigBuilder as RequestResponseConfigBuilder,
 	},
@@ -836,23 +836,45 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
 							self.peerstore_handle.add_known_peer(peer.into());
 						}
 					}
-					Some(DiscoveryEvent::GetRecordSuccess { query_id, records }) => {
+					Some(DiscoveryEvent::GetRecordPartialResult { query_id, record }) => {
+						if !self.pending_queries.contains_key(&query_id) {
+							log::error!(
+								target: LOG_TARGET,
+								"Missing/invalid pending query for `GET_VALUE` partial result: {query_id:?}"
+							);
+
+							continue
+						}
+
+						let peer_id: sc_network_types::PeerId = record.peer.into();
+						let record = PeerRecord {
+							record: P2PRecord {
+								key: record.record.key.to_vec().into(),
+								value: record.record.value,
+								publisher: record.record.publisher.map(|peer_id| {
+									let peer_id: sc_network_types::PeerId = peer_id.into();
+									peer_id.into()
+								}),
+								expires: record.record.expires,
+							},
+							peer: Some(peer_id.into()),
+						};
+
+						self.event_streams.send(
+							Event::Dht(
+								DhtEvent::ValueFound(
+									record.into()
+								)
+							)
+						);
+					}
+					Some(DiscoveryEvent::GetRecordSuccess { query_id }) => {
 						match self.pending_queries.remove(&query_id) {
 							Some(KadQuery::GetValue(key, started)) => {
 								log::trace!(
 									target: LOG_TARGET,
-									"`GET_VALUE` for {:?} ({query_id:?}) succeeded",
-									key,
+									"`GET_VALUE` for {key:?} ({query_id:?}) succeeded",
 								);
-								for record in litep2p_to_libp2p_peer_record(records) {
-									self.event_streams.send(
-										Event::Dht(
-											DhtEvent::ValueFound(
-												record.into()
-											)
-										)
-									);
-								}
 
 								if let Some(ref metrics) = self.metrics {
 									metrics
@@ -1165,42 +1187,3 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
 		}
 	}
 }
-
-// Glue code to convert from a litep2p records type to a libp2p2 PeerRecord.
-fn litep2p_to_libp2p_peer_record(records: RecordsType) -> Vec<PeerRecord> {
-	match records {
-		litep2p::protocol::libp2p::kademlia::RecordsType::LocalStore(record) => {
-			vec![PeerRecord {
-				record: P2PRecord {
-					key: record.key.to_vec().into(),
-					value: record.value,
-					publisher: record.publisher.map(|peer_id| {
-						let peer_id: sc_network_types::PeerId = peer_id.into();
-						peer_id.into()
-					}),
-					expires: record.expires,
-				},
-				peer: None,
-			}]
-		},
-		litep2p::protocol::libp2p::kademlia::RecordsType::Network(records) => records
-			.into_iter()
-			.map(|record| {
-				let peer_id: sc_network_types::PeerId = record.peer.into();
-
-				PeerRecord {
-					record: P2PRecord {
-						key: record.record.key.to_vec().into(),
-						value: record.record.value,
-						publisher: record.record.publisher.map(|peer_id| {
-							let peer_id: sc_network_types::PeerId = peer_id.into();
-							peer_id.into()
-						}),
-						expires: record.record.expires,
-					},
-					peer: Some(peer_id.into()),
-				}
-			})
-			.collect::<Vec<_>>(),
-	}
-}
diff --git a/substrate/client/network/src/litep2p/shim/request_response/mod.rs b/substrate/client/network/src/litep2p/shim/request_response/mod.rs
index 146f2e4add978b240563d1d63c757317848bdad4..690d5a31e6ad645103dcc227a088217a94ffb9a1 100644
--- a/substrate/client/network/src/litep2p/shim/request_response/mod.rs
+++ b/substrate/client/network/src/litep2p/shim/request_response/mod.rs
@@ -273,6 +273,13 @@ impl RequestResponseProtocol {
 		request_id: RequestId,
 		request: Vec<u8>,
 	) {
+		log::trace!(
+			target: LOG_TARGET,
+			"{}: request received from {peer:?} ({fallback:?} {request_id:?}), request size {:?}",
+			self.protocol,
+			request.len(),
+		);
+
 		let Some(inbound_queue) = &self.inbound_queue else {
 			log::trace!(
 				target: LOG_TARGET,
@@ -284,12 +291,18 @@ impl RequestResponseProtocol {
 			return;
 		};
 
-		log::trace!(
-			target: LOG_TARGET,
-			"{}: request received from {peer:?} ({fallback:?} {request_id:?}), request size {:?}",
-			self.protocol,
-			request.len(),
-		);
+		if self.peerstore_handle.is_banned(&peer.into()) {
+			log::trace!(
+				target: LOG_TARGET,
+				"{}: rejecting inbound request from banned {peer:?} ({request_id:?})",
+				self.protocol,
+			);
+
+			self.handle.reject_request(request_id);
+			self.metrics.register_inbound_request_failure("banned-peer");
+			return;
+		}
+
 		let (tx, rx) = oneshot::channel();
 
 		match inbound_queue.try_send(IncomingRequest {
diff --git a/substrate/frame/asset-rewards/Cargo.toml b/substrate/frame/asset-rewards/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..a03fa17cf0dc0e1911dadab39a4dbadbb542a372
--- /dev/null
+++ b/substrate/frame/asset-rewards/Cargo.toml
@@ -0,0 +1,71 @@
+[package]
+name = "pallet-asset-rewards"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+license = "Apache-2.0"
+homepage.workspace = true
+repository.workspace = true
+description = "FRAME asset rewards pallet"
+
+[lints]
+workspace = true
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[dependencies]
+codec = { workspace = true }
+frame-benchmarking = { workspace = true, optional = true }
+frame-support = { workspace = true, features = ["experimental"] }
+frame-system = { workspace = true }
+scale-info = { workspace = true, features = ["derive"] }
+sp-api = { workspace = true }
+sp-arithmetic = { workspace = true }
+sp-core = { workspace = true }
+sp-io = { workspace = true }
+sp-runtime = { workspace = true }
+sp-std = { workspace = true }
+
+[dev-dependencies]
+pallet-assets = { workspace = true }
+pallet-assets-freezer = { workspace = true }
+pallet-balances = { workspace = true }
+primitive-types = { workspace = true, features = ["codec", "num-traits", "scale-info"] }
+
+[features]
+default = ["std"]
+std = [
+	"codec/std",
+	"frame-benchmarking?/std",
+	"frame-support/std",
+	"frame-system/std",
+	"pallet-assets-freezer/std",
+	"pallet-assets/std",
+	"pallet-balances/std",
+	"primitive-types/std",
+	"scale-info/std",
+	"sp-api/std",
+	"sp-arithmetic/std",
+	"sp-core/std",
+	"sp-io/std",
+	"sp-runtime/std",
+	"sp-std/std",
+]
+runtime-benchmarks = [
+	"frame-benchmarking/runtime-benchmarks",
+	"frame-support/runtime-benchmarks",
+	"frame-system/runtime-benchmarks",
+	"pallet-assets-freezer/runtime-benchmarks",
+	"pallet-assets/runtime-benchmarks",
+	"pallet-balances/runtime-benchmarks",
+	"sp-runtime/runtime-benchmarks",
+]
+try-runtime = [
+	"frame-support/try-runtime",
+	"frame-system/try-runtime",
+	"pallet-assets-freezer/try-runtime",
+	"pallet-assets/try-runtime",
+	"pallet-balances/try-runtime",
+	"sp-runtime/try-runtime",
+]
diff --git a/substrate/frame/asset-rewards/src/benchmarking.rs b/substrate/frame/asset-rewards/src/benchmarking.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5605804dd20ece8eb27509de9bb4fe44d0037ab3
--- /dev/null
+++ b/substrate/frame/asset-rewards/src/benchmarking.rs
@@ -0,0 +1,355 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Asset Rewards pallet benchmarking.
+
+use super::*;
+use crate::Pallet as AssetRewards;
+use frame_benchmarking::{v2::*, whitelisted_caller, BenchmarkError};
+use frame_support::{
+	assert_ok,
+	traits::{
+		fungibles::{Create, Inspect, Mutate},
+		Consideration, EnsureOrigin, Footprint,
+	},
+};
+use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System, RawOrigin};
+use sp_runtime::{traits::One, Saturating};
+use sp_std::prelude::*;
+
+/// Benchmark Helper
+pub trait BenchmarkHelper<AssetId> {
+	/// Returns the staked asset id.
+	///
+	/// If the asset does not exist, it will be created by the benchmark.
+	fn staked_asset() -> AssetId;
+	/// Returns the reward asset id.
+	///
+	/// If the asset does not exist, it will be created by the benchmark.
+	fn reward_asset() -> AssetId;
+}
+
+fn pool_expire<T: Config>() -> DispatchTime<BlockNumberFor<T>> {
+	DispatchTime::At(BlockNumberFor::<T>::from(100u32))
+}
+
+fn create_reward_pool<T: Config>() -> Result<T::RuntimeOrigin, BenchmarkError>
+where
+	T::Assets: Create<T::AccountId> + Mutate<T::AccountId>,
+{
+	let caller_origin =
+		T::CreatePoolOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
+	let caller = T::CreatePoolOrigin::ensure_origin(caller_origin.clone()).unwrap();
+
+	let footprint = Footprint::from_mel::<(PoolId, PoolInfoFor<T>)>();
+	T::Consideration::ensure_successful(&caller, footprint);
+
+	let staked_asset = T::BenchmarkHelper::staked_asset();
+	let reward_asset = T::BenchmarkHelper::reward_asset();
+
+	let min_staked_balance =
+		T::Assets::minimum_balance(staked_asset.clone()).max(T::Balance::one());
+	if !T::Assets::asset_exists(staked_asset.clone()) {
+		assert_ok!(T::Assets::create(
+			staked_asset.clone(),
+			caller.clone(),
+			true,
+			min_staked_balance
+		));
+	}
+	let min_reward_balance =
+		T::Assets::minimum_balance(reward_asset.clone()).max(T::Balance::one());
+	if !T::Assets::asset_exists(reward_asset.clone()) {
+		assert_ok!(T::Assets::create(
+			reward_asset.clone(),
+			caller.clone(),
+			true,
+			min_reward_balance
+		));
+	}
+
+	assert_ok!(AssetRewards::<T>::create_pool(
+		caller_origin.clone(),
+		Box::new(staked_asset),
+		Box::new(reward_asset),
+		// reward rate per block
+		min_reward_balance,
+		pool_expire::<T>(),
+		Some(caller),
+	));
+
+	Ok(caller_origin)
+}
+
+fn mint_into<T: Config>(caller: &T::AccountId, asset: &T::AssetId) -> T::Balance
+where
+	T::Assets: Mutate<T::AccountId>,
+{
+	let min_balance = T::Assets::minimum_balance(asset.clone());
+	assert_ok!(T::Assets::mint_into(
+		asset.clone(),
+		&caller,
+		min_balance.saturating_mul(10u32.into())
+	));
+	min_balance
+}
+
+fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
+	System::<T>::assert_last_event(generic_event.into());
+}
+
+#[benchmarks(where T::Assets: Create<T::AccountId> + Mutate<T::AccountId>)]
+mod benchmarks {
+	use super::*;
+
+	#[benchmark]
+	fn create_pool() -> Result<(), BenchmarkError> {
+		let caller_origin =
+			T::CreatePoolOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
+		let caller = T::CreatePoolOrigin::ensure_origin(caller_origin.clone()).unwrap();
+
+		let footprint = Footprint::from_mel::<(PoolId, PoolInfoFor<T>)>();
+		T::Consideration::ensure_successful(&caller, footprint);
+
+		let staked_asset = T::BenchmarkHelper::staked_asset();
+		let reward_asset = T::BenchmarkHelper::reward_asset();
+
+		let min_balance = T::Assets::minimum_balance(staked_asset.clone()).max(T::Balance::one());
+		if !T::Assets::asset_exists(staked_asset.clone()) {
+			assert_ok!(T::Assets::create(staked_asset.clone(), caller.clone(), true, min_balance));
+		}
+		let min_balance = T::Assets::minimum_balance(reward_asset.clone()).max(T::Balance::one());
+		if !T::Assets::asset_exists(reward_asset.clone()) {
+			assert_ok!(T::Assets::create(reward_asset.clone(), caller.clone(), true, min_balance));
+		}
+
+		#[extrinsic_call]
+		_(
+			caller_origin as T::RuntimeOrigin,
+			Box::new(staked_asset.clone()),
+			Box::new(reward_asset.clone()),
+			min_balance,
+			pool_expire::<T>(),
+			Some(caller.clone()),
+		);
+
+		assert_last_event::<T>(
+			Event::PoolCreated {
+				creator: caller.clone(),
+				admin: caller,
+				staked_asset_id: staked_asset,
+				reward_asset_id: reward_asset,
+				reward_rate_per_block: min_balance,
+				expiry_block: pool_expire::<T>().evaluate(System::<T>::block_number()),
+				pool_id: 0,
+			}
+			.into(),
+		);
+
+		Ok(())
+	}
+
+	#[benchmark]
+	fn stake() -> Result<(), BenchmarkError> {
+		create_reward_pool::<T>()?;
+
+		let staker: T::AccountId = whitelisted_caller();
+		let min_balance = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset());
+
+		// stake first to get worth case benchmark.
+		assert_ok!(AssetRewards::<T>::stake(
+			RawOrigin::Signed(staker.clone()).into(),
+			0,
+			min_balance
+		));
+
+		#[extrinsic_call]
+		_(RawOrigin::Signed(staker.clone()), 0, min_balance);
+
+		assert_last_event::<T>(Event::Staked { staker, pool_id: 0, amount: min_balance }.into());
+
+		Ok(())
+	}
+
+	#[benchmark]
+	fn unstake() -> Result<(), BenchmarkError> {
+		create_reward_pool::<T>()?;
+
+		let staker: T::AccountId = whitelisted_caller();
+		let min_balance = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset());
+
+		assert_ok!(AssetRewards::<T>::stake(
+			RawOrigin::Signed(staker.clone()).into(),
+			0,
+			min_balance,
+		));
+
+		#[extrinsic_call]
+		_(RawOrigin::Signed(staker.clone()), 0, min_balance, None);
+
+		assert_last_event::<T>(
+			Event::Unstaked { caller: staker.clone(), staker, pool_id: 0, amount: min_balance }
+				.into(),
+		);
+
+		Ok(())
+	}
+
+	#[benchmark]
+	fn harvest_rewards() -> Result<(), BenchmarkError> {
+		create_reward_pool::<T>()?;
+
+		let pool_acc = AssetRewards::<T>::pool_account_id(&0u32);
+		let min_reward_balance = mint_into::<T>(&pool_acc, &T::BenchmarkHelper::reward_asset());
+
+		let staker = whitelisted_caller();
+		let _ = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset());
+		assert_ok!(AssetRewards::<T>::stake(
+			RawOrigin::Signed(staker.clone()).into(),
+			0,
+			T::Balance::one(),
+		));
+
+		System::<T>::set_block_number(System::<T>::block_number() + BlockNumberFor::<T>::one());
+
+		#[extrinsic_call]
+		_(RawOrigin::Signed(staker.clone()), 0, None);
+
+		assert_last_event::<T>(
+			Event::RewardsHarvested {
+				caller: staker.clone(),
+				staker,
+				pool_id: 0,
+				amount: min_reward_balance,
+			}
+			.into(),
+		);
+
+		Ok(())
+	}
+
+	#[benchmark]
+	fn set_pool_reward_rate_per_block() -> Result<(), BenchmarkError> {
+		let caller_origin = create_reward_pool::<T>()?;
+
+		// stake first to get worth case benchmark.
+		{
+			let staker: T::AccountId = whitelisted_caller();
+			let min_balance = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset());
+
+			assert_ok!(AssetRewards::<T>::stake(RawOrigin::Signed(staker).into(), 0, min_balance));
+		}
+
+		let new_reward_rate_per_block =
+			T::Assets::minimum_balance(T::BenchmarkHelper::reward_asset()).max(T::Balance::one()) +
+				T::Balance::one();
+
+		#[extrinsic_call]
+		_(caller_origin as T::RuntimeOrigin, 0, new_reward_rate_per_block);
+
+		assert_last_event::<T>(
+			Event::PoolRewardRateModified { pool_id: 0, new_reward_rate_per_block }.into(),
+		);
+		Ok(())
+	}
+
+	#[benchmark]
+	fn set_pool_admin() -> Result<(), BenchmarkError> {
+		let caller_origin = create_reward_pool::<T>()?;
+		let new_admin: T::AccountId = whitelisted_caller();
+
+		#[extrinsic_call]
+		_(caller_origin as T::RuntimeOrigin, 0, new_admin.clone());
+
+		assert_last_event::<T>(Event::PoolAdminModified { pool_id: 0, new_admin }.into());
+
+		Ok(())
+	}
+
+	#[benchmark]
+	fn set_pool_expiry_block() -> Result<(), BenchmarkError> {
+		let create_origin = create_reward_pool::<T>()?;
+
+		// stake first to get worth case benchmark.
+		{
+			let staker: T::AccountId = whitelisted_caller();
+			let min_balance = mint_into::<T>(&staker, &T::BenchmarkHelper::staked_asset());
+
+			assert_ok!(AssetRewards::<T>::stake(RawOrigin::Signed(staker).into(), 0, min_balance));
+		}
+
+		let new_expiry_block =
+			pool_expire::<T>().evaluate(System::<T>::block_number()) + BlockNumberFor::<T>::one();
+
+		#[extrinsic_call]
+		_(create_origin as T::RuntimeOrigin, 0, DispatchTime::At(new_expiry_block));
+
+		assert_last_event::<T>(
+			Event::PoolExpiryBlockModified { pool_id: 0, new_expiry_block }.into(),
+		);
+
+		Ok(())
+	}
+
+	#[benchmark]
+	fn deposit_reward_tokens() -> Result<(), BenchmarkError> {
+		create_reward_pool::<T>()?;
+		let caller = whitelisted_caller();
+
+		let reward_asset = T::BenchmarkHelper::reward_asset();
+		let pool_acc = AssetRewards::<T>::pool_account_id(&0u32);
+		let min_balance = mint_into::<T>(&caller, &reward_asset);
+
+		let balance_before = T::Assets::balance(reward_asset.clone(), &pool_acc);
+
+		#[extrinsic_call]
+		_(RawOrigin::Signed(caller), 0, min_balance);
+
+		let balance_after = T::Assets::balance(reward_asset, &pool_acc);
+
+		assert_eq!(balance_after, balance_before + min_balance);
+
+		Ok(())
+	}
+
+	#[benchmark]
+	fn cleanup_pool() -> Result<(), BenchmarkError> {
+		let create_origin = create_reward_pool::<T>()?;
+		let caller = T::CreatePoolOrigin::ensure_origin(create_origin.clone()).unwrap();
+
+		// deposit rewards tokens to get worth case benchmark.
+		{
+			let caller = whitelisted_caller();
+			let reward_asset = T::BenchmarkHelper::reward_asset();
+			let min_balance = mint_into::<T>(&caller, &reward_asset);
+			assert_ok!(AssetRewards::<T>::deposit_reward_tokens(
+				RawOrigin::Signed(caller).into(),
+				0,
+				min_balance
+			));
+		}
+
+		#[extrinsic_call]
+		_(RawOrigin::Signed(caller), 0);
+
+		assert_last_event::<T>(Event::PoolCleanedUp { pool_id: 0 }.into());
+
+		Ok(())
+	}
+
+	impl_benchmark_test_suite!(AssetRewards, crate::mock::new_test_ext(), crate::mock::MockRuntime);
+}
diff --git a/substrate/frame/asset-rewards/src/lib.rs b/substrate/frame/asset-rewards/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4ce73e9febf96f8c363ffa9203d0209f3b599d88
--- /dev/null
+++ b/substrate/frame/asset-rewards/src/lib.rs
@@ -0,0 +1,905 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! # FRAME Staking Rewards Pallet
+//!
+//! Allows accounts to be rewarded for holding `fungible` asset/s, for example LP tokens.
+//!
+//! ## Overview
+//!
+//! Initiate an incentive program for a fungible asset by creating a new pool.
+//!
+//! During pool creation, a 'staking asset', 'reward asset', 'reward rate per block', 'expiry
+//! block', and 'admin' are specified.
+//!
+//! Once created, holders of the 'staking asset' can 'stake' them in a corresponding pool, which
+//! creates a Freeze on the asset.
+//!
+//! Once staked, rewards denominated in 'reward asset' begin accumulating to the staker,
+//! proportional to their share of the total staked tokens in the pool.
+//!
+//! Reward assets pending distribution are held in an account unique to each pool.
+//!
+//! Care should be taken by the pool operator to keep pool accounts adequately funded with the
+//! reward asset.
+//!
+//! The pool admin may increase reward rate per block, increase expiry block, and change admin.
+//!
+//! ## Disambiguation
+//!
+//! While this pallet shares some terminology with the `staking-pool` and similar native staking
+//! related pallets, it is distinct and is entirely unrelated to native staking.
+//!
+//! ## Permissioning
+//!
+//! Currently, pool creation and management restricted to a configured Origin.
+//!
+//! Future iterations of this pallet may allow permissionless creation and management of pools.
+//!
+//! Note: The permissioned origin must return an AccountId. This can be achieved for any Origin by
+//! wrapping it with `EnsureSuccess`.
+//!
+//! ## Implementation Notes
+//!
+//! Internal logic functions such as `update_pool_and_staker_rewards` were deliberately written
+//! without side-effects.
+//!
+//! Storage interaction such as reads and writes are instead all performed in the top level
+//! pallet Call method, which while slightly more verbose, makes it easier to understand the
+//! code and reason about how storage reads and writes occur in the pallet.
+//!
+//! ## Rewards Algorithm
+//!
+//! The rewards algorithm is based on the Synthetix [StakingRewards.sol](https://github.com/Synthetixio/synthetix/blob/develop/contracts/StakingRewards.sol)
+//! smart contract.
+//!
+//! Rewards are calculated JIT (just-in-time), and all operations are O(1) making the approach
+//! scalable to many pools and stakers.
+//!
+//! ### Resources
+//!
+//! - [This video series](https://www.youtube.com/watch?v=6ZO5aYg1GI8), which walks through the math
+//!   of the algorithm.
+//! - [This dev.to article](https://dev.to/heymarkkop/understanding-sushiswaps-masterchef-staking-rewards-1m6f),
+//!   which explains the algorithm of the SushiSwap MasterChef staking. While not identical to the
+//!   Synthetix approach, they are quite similar.
+#![deny(missing_docs)]
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+use codec::{Codec, Decode, Encode, MaxEncodedLen};
+use frame_support::{
+	traits::{
+		fungibles::{Inspect, Mutate},
+		schedule::DispatchTime,
+		tokens::Balance,
+	},
+	PalletId,
+};
+use frame_system::pallet_prelude::BlockNumberFor;
+use scale_info::TypeInfo;
+use sp_core::Get;
+use sp_runtime::{
+	traits::{MaybeDisplay, Zero},
+	DispatchError,
+};
+use sp_std::boxed::Box;
+
+#[cfg(feature = "runtime-benchmarks")]
+pub mod benchmarking;
+#[cfg(test)]
+mod mock;
+#[cfg(test)]
+mod tests;
+mod weights;
+
+pub use weights::WeightInfo;
+
+/// Unique id type for each pool.
+pub type PoolId = u32;
+
+/// Multiplier to maintain precision when calculating rewards.
+pub(crate) const PRECISION_SCALING_FACTOR: u16 = 4096;
+
+/// Convenience alias for `PoolInfo`.
+pub type PoolInfoFor<T> = PoolInfo<
+	<T as frame_system::Config>::AccountId,
+	<T as Config>::AssetId,
+	<T as Config>::Balance,
+	BlockNumberFor<T>,
+>;
+
+/// The state of a staker in a pool.
+#[derive(Debug, Default, Clone, Decode, Encode, MaxEncodedLen, TypeInfo)]
+pub struct PoolStakerInfo<Balance> {
+	/// Amount of tokens staked.
+	amount: Balance,
+	/// Accumulated, unpaid rewards.
+	rewards: Balance,
+	/// Reward per token value at the time of the staker's last interaction with the contract.
+	reward_per_token_paid: Balance,
+}
+
+/// The state and configuration of an incentive pool.
+#[derive(Debug, Clone, Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
+pub struct PoolInfo<AccountId, AssetId, Balance, BlockNumber> {
+	/// The asset staked in this pool.
+	staked_asset_id: AssetId,
+	/// The asset distributed as rewards by this pool.
+	reward_asset_id: AssetId,
+	/// The amount of tokens rewarded per block.
+	reward_rate_per_block: Balance,
+	/// The block the pool will cease distributing rewards.
+	expiry_block: BlockNumber,
+	/// The account authorized to manage this pool.
+	admin: AccountId,
+	/// The total amount of tokens staked in this pool.
+	total_tokens_staked: Balance,
+	/// Total rewards accumulated per token, up to the `last_update_block`.
+	reward_per_token_stored: Balance,
+	/// Last block number the pool was updated.
+	last_update_block: BlockNumber,
+	/// The account that holds the pool's rewards.
+	account: AccountId,
+}
+
+sp_api::decl_runtime_apis! {
+	/// The runtime API for the asset rewards pallet.
+	pub trait AssetRewards<Cost: MaybeDisplay + Codec> {
+		/// Get the cost of creating a pool.
+		///
+		/// This is especially useful when the cost is dynamic.
+		fn pool_creation_cost() -> Cost;
+	}
+}
+
+#[frame_support::pallet]
+pub mod pallet {
+	use super::*;
+	use frame_support::{
+		pallet_prelude::*,
+		traits::{
+			fungibles::MutateFreeze,
+			tokens::{AssetId, Fortitude, Preservation},
+			Consideration, Footprint,
+		},
+	};
+	use frame_system::pallet_prelude::*;
+	use sp_runtime::{
+		traits::{
+			AccountIdConversion, BadOrigin, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureMul,
+			EnsureSub, EnsureSubAssign,
+		},
+		DispatchResult,
+	};
+
+	#[pallet::pallet]
+	pub struct Pallet<T>(_);
+
+	/// A reason for the pallet placing a hold on funds.
+	#[pallet::composite_enum]
+	pub enum FreezeReason {
+		/// Funds are staked in the pallet.
+		#[codec(index = 0)]
+		Staked,
+	}
+
+	/// A reason for the pallet placing a hold on funds.
+	#[pallet::composite_enum]
+	pub enum HoldReason {
+		/// Cost associated with storing pool information on-chain.
+		#[codec(index = 0)]
+		PoolCreation,
+	}
+
+	#[pallet::config]
+	pub trait Config: frame_system::Config {
+		/// Overarching event type.
+		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+
+		/// The pallet's unique identifier, used to derive the pool's account ID.
+		///
+		/// The account ID is derived once during pool creation and stored in the storage.
+		#[pallet::constant]
+		type PalletId: Get<PalletId>;
+
+		/// Identifier for each type of asset.
+		type AssetId: AssetId + Member + Parameter;
+
+		/// The type in which the assets are measured.
+		type Balance: Balance + TypeInfo;
+
+		/// The origin with permission to create pools.
+		///
+		/// The Origin must return an AccountId.
+		type CreatePoolOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
+
+		/// Registry of assets that can be configured to either stake for rewards, or be offered as
+		/// rewards for staking.
+		type Assets: Inspect<Self::AccountId, AssetId = Self::AssetId, Balance = Self::Balance>
+			+ Mutate<Self::AccountId>;
+
+		/// Freezer for the Assets.
+		type AssetsFreezer: MutateFreeze<
+			Self::AccountId,
+			Id = Self::RuntimeFreezeReason,
+			AssetId = Self::AssetId,
+			Balance = Self::Balance,
+		>;
+
+		/// The overarching freeze reason.
+		type RuntimeFreezeReason: From<FreezeReason>;
+
+		/// Means for associating a cost with the on-chain storage of pool information, which
+		/// is incurred by the pool creator.
+		///
+		/// The passed `Footprint` specifically accounts for the storage footprint of the pool's
+		/// information itself, excluding any potential storage footprint related to the stakers.
+		type Consideration: Consideration<Self::AccountId, Footprint>;
+
+		/// Weight information for extrinsics in this pallet.
+		type WeightInfo: WeightInfo;
+
+		/// Helper for benchmarking.
+		#[cfg(feature = "runtime-benchmarks")]
+		type BenchmarkHelper: benchmarking::BenchmarkHelper<Self::AssetId>;
+	}
+
+	/// State of pool stakers.
+	#[pallet::storage]
+	pub type PoolStakers<T: Config> = StorageDoubleMap<
+		_,
+		Blake2_128Concat,
+		PoolId,
+		Blake2_128Concat,
+		T::AccountId,
+		PoolStakerInfo<T::Balance>,
+	>;
+
+	/// State and configuration of each staking pool.
+	#[pallet::storage]
+	pub type Pools<T: Config> = StorageMap<_, Blake2_128Concat, PoolId, PoolInfoFor<T>>;
+
+	/// The cost associated with storing pool information on-chain which was incurred by the pool
+	/// creator.
+	///
+	/// This cost may be [`None`], as determined by [`Config::Consideration`].
+	#[pallet::storage]
+	pub type PoolCost<T: Config> =
+		StorageMap<_, Blake2_128Concat, PoolId, (T::AccountId, T::Consideration)>;
+
+	/// Stores the [`PoolId`] to use for the next pool.
+	///
+	/// Incremented when a new pool is created.
+	#[pallet::storage]
+	pub type NextPoolId<T: Config> = StorageValue<_, PoolId, ValueQuery>;
+
+	#[pallet::event]
+	#[pallet::generate_deposit(pub(super) fn deposit_event)]
+	pub enum Event<T: Config> {
+		/// An account staked some tokens in a pool.
+		Staked {
+			/// The account that staked assets.
+			staker: T::AccountId,
+			/// The pool.
+			pool_id: PoolId,
+			/// The staked asset amount.
+			amount: T::Balance,
+		},
+		/// An account unstaked some tokens from a pool.
+		Unstaked {
+			/// The account that signed transaction.
+			caller: T::AccountId,
+			/// The account that unstaked assets.
+			staker: T::AccountId,
+			/// The pool.
+			pool_id: PoolId,
+			/// The unstaked asset amount.
+			amount: T::Balance,
+		},
+		/// An account harvested some rewards.
+		RewardsHarvested {
+			/// The account that signed transaction.
+			caller: T::AccountId,
+			/// The staker whos rewards were harvested.
+			staker: T::AccountId,
+			/// The pool.
+			pool_id: PoolId,
+			/// The amount of harvested tokens.
+			amount: T::Balance,
+		},
+		/// A new reward pool was created.
+		PoolCreated {
+			/// The account that created the pool.
+			creator: T::AccountId,
+			/// The unique ID for the new pool.
+			pool_id: PoolId,
+			/// The staking asset.
+			staked_asset_id: T::AssetId,
+			/// The reward asset.
+			reward_asset_id: T::AssetId,
+			/// The initial reward rate per block.
+			reward_rate_per_block: T::Balance,
+			/// The block the pool will cease to accumulate rewards.
+			expiry_block: BlockNumberFor<T>,
+			/// The account allowed to modify the pool.
+			admin: T::AccountId,
+		},
+		/// A pool reward rate was modified by the admin.
+		PoolRewardRateModified {
+			/// The modified pool.
+			pool_id: PoolId,
+			/// The new reward rate per block.
+			new_reward_rate_per_block: T::Balance,
+		},
+		/// A pool admin was modified.
+		PoolAdminModified {
+			/// The modified pool.
+			pool_id: PoolId,
+			/// The new admin.
+			new_admin: T::AccountId,
+		},
+		/// A pool expiry block was modified by the admin.
+		PoolExpiryBlockModified {
+			/// The modified pool.
+			pool_id: PoolId,
+			/// The new expiry block.
+			new_expiry_block: BlockNumberFor<T>,
+		},
+		/// A pool information was cleared after it's completion.
+		PoolCleanedUp {
+			/// The cleared pool.
+			pool_id: PoolId,
+		},
+	}
+
+	#[pallet::error]
+	pub enum Error<T> {
+		/// The staker does not have enough tokens to perform the operation.
+		NotEnoughTokens,
+		/// An operation was attempted on a non-existent pool.
+		NonExistentPool,
+		/// An operation was attempted for a non-existent staker.
+		NonExistentStaker,
+		/// An operation was attempted with a non-existent asset.
+		NonExistentAsset,
+		/// There was an error converting a block number.
+		BlockNumberConversionError,
+		/// The expiry block must be in the future.
+		ExpiryBlockMustBeInTheFuture,
+		/// Insufficient funds to create the freeze.
+		InsufficientFunds,
+		/// The expiry block can be only extended.
+		ExpiryCut,
+		/// The reward rate per block can be only increased.
+		RewardRateCut,
+		/// The pool still has staked tokens or rewards.
+		NonEmptyPool,
+	}
+
+	#[pallet::hooks]
+	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+		fn integrity_test() {
+			// The AccountId is at least 16 bytes to contain the unique PalletId.
+			let pool_id: PoolId = 1;
+			assert!(
+				<frame_support::PalletId as AccountIdConversion<T::AccountId>>::try_into_sub_account(
+					&T::PalletId::get(), pool_id,
+				)
+				.is_some()
+			);
+		}
+	}
+
+	/// Pallet's callable functions.
+	#[pallet::call(weight(<T as Config>::WeightInfo))]
+	impl<T: Config> Pallet<T> {
+		/// Create a new reward pool.
+		///
+		/// Parameters:
+		/// - `origin`: must be `Config::CreatePoolOrigin`;
+		/// - `staked_asset_id`: the asset to be staked in the pool;
+		/// - `reward_asset_id`: the asset to be distributed as rewards;
+		/// - `reward_rate_per_block`: the amount of reward tokens distributed per block;
+		/// - `expiry`: the block number at which the pool will cease to accumulate rewards. The
+		///   [`DispatchTime::After`] variant evaluated at the execution time.
+		/// - `admin`: the account allowed to extend the pool expiration, increase the rewards rate
+		///   and receive the unutilized reward tokens back after the pool completion. If `None`,
+		///   the caller is set as an admin.
+		#[pallet::call_index(0)]
+		pub fn create_pool(
+			origin: OriginFor<T>,
+			staked_asset_id: Box<T::AssetId>,
+			reward_asset_id: Box<T::AssetId>,
+			reward_rate_per_block: T::Balance,
+			expiry: DispatchTime<BlockNumberFor<T>>,
+			admin: Option<T::AccountId>,
+		) -> DispatchResult {
+			// Check the origin.
+			let creator = T::CreatePoolOrigin::ensure_origin(origin)?;
+
+			// Ensure the assets exist.
+			ensure!(
+				T::Assets::asset_exists(*staked_asset_id.clone()),
+				Error::<T>::NonExistentAsset
+			);
+			ensure!(
+				T::Assets::asset_exists(*reward_asset_id.clone()),
+				Error::<T>::NonExistentAsset
+			);
+
+			// Check the expiry block.
+			let expiry_block = expiry.evaluate(frame_system::Pallet::<T>::block_number());
+			ensure!(
+				expiry_block > frame_system::Pallet::<T>::block_number(),
+				Error::<T>::ExpiryBlockMustBeInTheFuture
+			);
+
+			let pool_id = NextPoolId::<T>::get();
+
+			let footprint = Self::pool_creation_footprint();
+			let cost = T::Consideration::new(&creator, footprint)?;
+			PoolCost::<T>::insert(pool_id, (creator.clone(), cost));
+
+			let admin = admin.unwrap_or(creator.clone());
+
+			// Create the pool.
+			let pool = PoolInfoFor::<T> {
+				staked_asset_id: *staked_asset_id.clone(),
+				reward_asset_id: *reward_asset_id.clone(),
+				reward_rate_per_block,
+				total_tokens_staked: 0u32.into(),
+				reward_per_token_stored: 0u32.into(),
+				last_update_block: 0u32.into(),
+				expiry_block,
+				admin: admin.clone(),
+				account: Self::pool_account_id(&pool_id),
+			};
+
+			// Insert it into storage.
+			Pools::<T>::insert(pool_id, pool);
+
+			NextPoolId::<T>::put(pool_id.ensure_add(1)?);
+
+			// Emit created event.
+			Self::deposit_event(Event::PoolCreated {
+				creator,
+				pool_id,
+				staked_asset_id: *staked_asset_id,
+				reward_asset_id: *reward_asset_id,
+				reward_rate_per_block,
+				expiry_block,
+				admin,
+			});
+
+			Ok(())
+		}
+
+		/// Stake additional tokens in a pool.
+		///
+		/// A freeze is placed on the staked tokens.
+		#[pallet::call_index(1)]
+		pub fn stake(origin: OriginFor<T>, pool_id: PoolId, amount: T::Balance) -> DispatchResult {
+			let staker = ensure_signed(origin)?;
+
+			// Always start by updating staker and pool rewards.
+			let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?;
+			let staker_info = PoolStakers::<T>::get(pool_id, &staker).unwrap_or_default();
+			let (mut pool_info, mut staker_info) =
+				Self::update_pool_and_staker_rewards(&pool_info, &staker_info)?;
+
+			T::AssetsFreezer::increase_frozen(
+				pool_info.staked_asset_id.clone(),
+				&FreezeReason::Staked.into(),
+				&staker,
+				amount,
+			)?;
+
+			// Update Pools.
+			pool_info.total_tokens_staked.ensure_add_assign(amount)?;
+
+			Pools::<T>::insert(pool_id, pool_info);
+
+			// Update PoolStakers.
+			staker_info.amount.ensure_add_assign(amount)?;
+			PoolStakers::<T>::insert(pool_id, &staker, staker_info);
+
+			// Emit event.
+			Self::deposit_event(Event::Staked { staker, pool_id, amount });
+
+			Ok(())
+		}
+
+		/// Unstake tokens from a pool.
+		///
+		/// Removes the freeze on the staked tokens.
+		///
+		/// Parameters:
+		/// - origin: must be the `staker` if the pool is still active. Otherwise, any account.
+		/// - pool_id: the pool to unstake from.
+		/// - amount: the amount of tokens to unstake.
+		/// - staker: the account to unstake from. If `None`, the caller is used.
+		#[pallet::call_index(2)]
+		pub fn unstake(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			amount: T::Balance,
+			staker: Option<T::AccountId>,
+		) -> DispatchResult {
+			let caller = ensure_signed(origin)?;
+			let staker = staker.unwrap_or(caller.clone());
+
+			// Always start by updating the pool rewards.
+			let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?;
+			let now = frame_system::Pallet::<T>::block_number();
+			ensure!(now > pool_info.expiry_block || caller == staker, BadOrigin);
+
+			let staker_info = PoolStakers::<T>::get(pool_id, &staker).unwrap_or_default();
+			let (mut pool_info, mut staker_info) =
+				Self::update_pool_and_staker_rewards(&pool_info, &staker_info)?;
+
+			// Check the staker has enough staked tokens.
+			ensure!(staker_info.amount >= amount, Error::<T>::NotEnoughTokens);
+
+			// Unfreeze staker assets.
+			T::AssetsFreezer::decrease_frozen(
+				pool_info.staked_asset_id.clone(),
+				&FreezeReason::Staked.into(),
+				&staker,
+				amount,
+			)?;
+
+			// Update Pools.
+			pool_info.total_tokens_staked.ensure_sub_assign(amount)?;
+			Pools::<T>::insert(pool_id, pool_info);
+
+			// Update PoolStakers.
+			staker_info.amount.ensure_sub_assign(amount)?;
+
+			if staker_info.amount.is_zero() && staker_info.rewards.is_zero() {
+				PoolStakers::<T>::remove(&pool_id, &staker);
+			} else {
+				PoolStakers::<T>::insert(&pool_id, &staker, staker_info);
+			}
+
+			// Emit event.
+			Self::deposit_event(Event::Unstaked { caller, staker, pool_id, amount });
+
+			Ok(())
+		}
+
+		/// Harvest unclaimed pool rewards.
+		///
+		/// Parameters:
+		/// - origin: must be the `staker` if the pool is still active. Otherwise, any account.
+		/// - pool_id: the pool to harvest from.
+		/// - staker: the account for which to harvest rewards. If `None`, the caller is used.
+		#[pallet::call_index(3)]
+		pub fn harvest_rewards(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			staker: Option<T::AccountId>,
+		) -> DispatchResult {
+			let caller = ensure_signed(origin)?;
+			let staker = staker.unwrap_or(caller.clone());
+
+			// Always start by updating the pool and staker rewards.
+			let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?;
+			let now = frame_system::Pallet::<T>::block_number();
+			ensure!(now > pool_info.expiry_block || caller == staker, BadOrigin);
+
+			let staker_info =
+				PoolStakers::<T>::get(pool_id, &staker).ok_or(Error::<T>::NonExistentStaker)?;
+			let (pool_info, mut staker_info) =
+				Self::update_pool_and_staker_rewards(&pool_info, &staker_info)?;
+
+			// Transfer unclaimed rewards from the pool to the staker.
+			T::Assets::transfer(
+				pool_info.reward_asset_id,
+				&pool_info.account,
+				&staker,
+				staker_info.rewards,
+				// Could kill the account, but only if the pool was already almost empty.
+				Preservation::Expendable,
+			)?;
+
+			// Emit event.
+			Self::deposit_event(Event::RewardsHarvested {
+				caller,
+				staker: staker.clone(),
+				pool_id,
+				amount: staker_info.rewards,
+			});
+
+			// Reset staker rewards.
+			staker_info.rewards = 0u32.into();
+
+			if staker_info.amount.is_zero() {
+				PoolStakers::<T>::remove(&pool_id, &staker);
+			} else {
+				PoolStakers::<T>::insert(&pool_id, &staker, staker_info);
+			}
+
+			Ok(())
+		}
+
+		/// Modify a pool reward rate.
+		///
+		/// Currently the reward rate can only be increased.
+		///
+		/// Only the pool admin may perform this operation.
+		#[pallet::call_index(4)]
+		pub fn set_pool_reward_rate_per_block(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			new_reward_rate_per_block: T::Balance,
+		) -> DispatchResult {
+			let caller = T::CreatePoolOrigin::ensure_origin(origin.clone())
+				.or_else(|_| ensure_signed(origin))?;
+
+			let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?;
+			ensure!(pool_info.admin == caller, BadOrigin);
+			ensure!(
+				new_reward_rate_per_block > pool_info.reward_rate_per_block,
+				Error::<T>::RewardRateCut
+			);
+
+			// Always start by updating the pool rewards.
+			let rewards_per_token = Self::reward_per_token(&pool_info)?;
+			let mut pool_info = Self::update_pool_rewards(&pool_info, rewards_per_token)?;
+
+			pool_info.reward_rate_per_block = new_reward_rate_per_block;
+			Pools::<T>::insert(pool_id, pool_info);
+
+			Self::deposit_event(Event::PoolRewardRateModified {
+				pool_id,
+				new_reward_rate_per_block,
+			});
+
+			Ok(())
+		}
+
+		/// Modify a pool admin.
+		///
+		/// Only the pool admin may perform this operation.
+		#[pallet::call_index(5)]
+		pub fn set_pool_admin(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			new_admin: T::AccountId,
+		) -> DispatchResult {
+			let caller = T::CreatePoolOrigin::ensure_origin(origin.clone())
+				.or_else(|_| ensure_signed(origin))?;
+
+			let mut pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?;
+			ensure!(pool_info.admin == caller, BadOrigin);
+
+			pool_info.admin = new_admin.clone();
+			Pools::<T>::insert(pool_id, pool_info);
+
+			Self::deposit_event(Event::PoolAdminModified { pool_id, new_admin });
+
+			Ok(())
+		}
+
+		/// Set when the pool should expire.
+		///
+		/// Currently the expiry block can only be extended.
+		///
+		/// Only the pool admin may perform this operation.
+		#[pallet::call_index(6)]
+		pub fn set_pool_expiry_block(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			new_expiry: DispatchTime<BlockNumberFor<T>>,
+		) -> DispatchResult {
+			let caller = T::CreatePoolOrigin::ensure_origin(origin.clone())
+				.or_else(|_| ensure_signed(origin))?;
+
+			let new_expiry = new_expiry.evaluate(frame_system::Pallet::<T>::block_number());
+			ensure!(
+				new_expiry > frame_system::Pallet::<T>::block_number(),
+				Error::<T>::ExpiryBlockMustBeInTheFuture
+			);
+
+			let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?;
+			ensure!(pool_info.admin == caller, BadOrigin);
+			ensure!(new_expiry > pool_info.expiry_block, Error::<T>::ExpiryCut);
+
+			// Always start by updating the pool rewards.
+			let reward_per_token = Self::reward_per_token(&pool_info)?;
+			let mut pool_info = Self::update_pool_rewards(&pool_info, reward_per_token)?;
+
+			pool_info.expiry_block = new_expiry;
+			Pools::<T>::insert(pool_id, pool_info);
+
+			Self::deposit_event(Event::PoolExpiryBlockModified {
+				pool_id,
+				new_expiry_block: new_expiry,
+			});
+
+			Ok(())
+		}
+
+		/// Convenience method to deposit reward tokens into a pool.
+		///
+		/// This method is not strictly necessary (tokens could be transferred directly to the
+		/// pool pot address), but is provided for convenience so manual derivation of the
+		/// account id is not required.
+		#[pallet::call_index(7)]
+		pub fn deposit_reward_tokens(
+			origin: OriginFor<T>,
+			pool_id: PoolId,
+			amount: T::Balance,
+		) -> DispatchResult {
+			let caller = ensure_signed(origin)?;
+			let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?;
+			T::Assets::transfer(
+				pool_info.reward_asset_id,
+				&caller,
+				&pool_info.account,
+				amount,
+				Preservation::Preserve,
+			)?;
+			Ok(())
+		}
+
+		/// Cleanup a pool.
+		///
+		/// Origin must be the pool admin.
+		///
+		/// Cleanup storage, release any associated storage cost and return the remaining reward
+		/// tokens to the admin.
+		#[pallet::call_index(8)]
+		pub fn cleanup_pool(origin: OriginFor<T>, pool_id: PoolId) -> DispatchResult {
+			let who = ensure_signed(origin)?;
+
+			let pool_info = Pools::<T>::get(pool_id).ok_or(Error::<T>::NonExistentPool)?;
+			ensure!(pool_info.admin == who, BadOrigin);
+
+			let stakers = PoolStakers::<T>::iter_key_prefix(pool_id).next();
+			ensure!(stakers.is_none(), Error::<T>::NonEmptyPool);
+
+			let pool_balance = T::Assets::reducible_balance(
+				pool_info.reward_asset_id.clone(),
+				&pool_info.account,
+				Preservation::Expendable,
+				Fortitude::Polite,
+			);
+			T::Assets::transfer(
+				pool_info.reward_asset_id,
+				&pool_info.account,
+				&pool_info.admin,
+				pool_balance,
+				Preservation::Expendable,
+			)?;
+
+			if let Some((who, cost)) = PoolCost::<T>::take(pool_id) {
+				T::Consideration::drop(cost, &who)?;
+			}
+
+			Pools::<T>::remove(pool_id);
+
+			Self::deposit_event(Event::PoolCleanedUp { pool_id });
+
+			Ok(())
+		}
+	}
+
+	impl<T: Config> Pallet<T> {
+		/// The pool creation footprint.
+		///
+		/// The footprint specifically accounts for the storage footprint of the pool's information
+		/// itself, excluding any potential storage footprint related to the stakers.
+		pub fn pool_creation_footprint() -> Footprint {
+			Footprint::from_mel::<(PoolId, PoolInfoFor<T>)>()
+		}
+
+		/// Derive a pool account ID from the pool's ID.
+		pub fn pool_account_id(id: &PoolId) -> T::AccountId {
+			T::PalletId::get().into_sub_account_truncating(id)
+		}
+
+		/// Computes update pool and staker reward state.
+		///
+		/// Should be called prior to any operation involving a staker.
+		///
+		/// Returns the updated pool and staker info.
+		///
+		/// NOTE: this function has no side-effects. Side-effects such as storage modifications are
+		/// the responsibility of the caller.
+		pub fn update_pool_and_staker_rewards(
+			pool_info: &PoolInfoFor<T>,
+			staker_info: &PoolStakerInfo<T::Balance>,
+		) -> Result<(PoolInfoFor<T>, PoolStakerInfo<T::Balance>), DispatchError> {
+			let reward_per_token = Self::reward_per_token(&pool_info)?;
+			let pool_info = Self::update_pool_rewards(pool_info, reward_per_token)?;
+
+			let mut new_staker_info = staker_info.clone();
+			new_staker_info.rewards = Self::derive_rewards(&staker_info, &reward_per_token)?;
+			new_staker_info.reward_per_token_paid = pool_info.reward_per_token_stored;
+			return Ok((pool_info, new_staker_info));
+		}
+
+		/// Computes update pool reward state.
+		///
+		/// Should be called every time the pool is adjusted, and a staker is not involved.
+		///
+		/// Returns the updated pool and staker info.
+		///
+		/// NOTE: this function has no side-effects. Side-effects such as storage modifications are
+		/// the responsibility of the caller.
+		pub fn update_pool_rewards(
+			pool_info: &PoolInfoFor<T>,
+			reward_per_token: T::Balance,
+		) -> Result<PoolInfoFor<T>, DispatchError> {
+			let mut new_pool_info = pool_info.clone();
+			new_pool_info.last_update_block = frame_system::Pallet::<T>::block_number();
+			new_pool_info.reward_per_token_stored = reward_per_token;
+
+			Ok(new_pool_info)
+		}
+
+		/// Derives the current reward per token for this pool.
+		fn reward_per_token(pool_info: &PoolInfoFor<T>) -> Result<T::Balance, DispatchError> {
+			if pool_info.total_tokens_staked.is_zero() {
+				return Ok(pool_info.reward_per_token_stored)
+			}
+
+			let rewardable_blocks_elapsed: u32 =
+				match Self::last_block_reward_applicable(pool_info.expiry_block)
+					.ensure_sub(pool_info.last_update_block)?
+					.try_into()
+				{
+					Ok(b) => b,
+					Err(_) => return Err(Error::<T>::BlockNumberConversionError.into()),
+				};
+
+			Ok(pool_info.reward_per_token_stored.ensure_add(
+				pool_info
+					.reward_rate_per_block
+					.ensure_mul(rewardable_blocks_elapsed.into())?
+					.ensure_mul(PRECISION_SCALING_FACTOR.into())?
+					.ensure_div(pool_info.total_tokens_staked)?,
+			)?)
+		}
+
+		/// Derives the amount of rewards earned by a staker.
+		///
+		/// This is a helper function for `update_pool_rewards` and should not be called directly.
+		fn derive_rewards(
+			staker_info: &PoolStakerInfo<T::Balance>,
+			reward_per_token: &T::Balance,
+		) -> Result<T::Balance, DispatchError> {
+			Ok(staker_info
+				.amount
+				.ensure_mul(reward_per_token.ensure_sub(staker_info.reward_per_token_paid)?)?
+				.ensure_div(PRECISION_SCALING_FACTOR.into())?
+				.ensure_add(staker_info.rewards)?)
+		}
+
+		fn last_block_reward_applicable(pool_expiry_block: BlockNumberFor<T>) -> BlockNumberFor<T> {
+			let now = frame_system::Pallet::<T>::block_number();
+			if now < pool_expiry_block {
+				now
+			} else {
+				pool_expiry_block
+			}
+		}
+	}
+}
diff --git a/substrate/frame/asset-rewards/src/mock.rs b/substrate/frame/asset-rewards/src/mock.rs
new file mode 100644
index 0000000000000000000000000000000000000000..87c8a8a0dea0b44c23baa1ed2fdfabd9b85eb999
--- /dev/null
+++ b/substrate/frame/asset-rewards/src/mock.rs
@@ -0,0 +1,221 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Test environment for Asset Rewards pallet.
+
+use super::*;
+use crate as pallet_asset_rewards;
+use core::default::Default;
+use frame_support::{
+	construct_runtime, derive_impl,
+	instances::Instance1,
+	parameter_types,
+	traits::{
+		tokens::fungible::{HoldConsideration, NativeFromLeft, NativeOrWithId, UnionOf},
+		AsEnsureOriginWithArg, ConstU128, ConstU32, EnsureOrigin, LinearStoragePrice,
+	},
+	PalletId,
+};
+use frame_system::EnsureSigned;
+use sp_runtime::{traits::IdentityLookup, BuildStorage};
+
+#[cfg(feature = "runtime-benchmarks")]
+use self::benchmarking::BenchmarkHelper;
+
+type Block = frame_system::mocking::MockBlock<MockRuntime>;
+
+construct_runtime!(
+	pub enum MockRuntime
+	{
+		System: frame_system,
+		Balances: pallet_balances,
+		Assets: pallet_assets::<Instance1>,
+		AssetsFreezer: pallet_assets_freezer::<Instance1>,
+		StakingRewards: pallet_asset_rewards,
+	}
+);
+
+#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
+impl frame_system::Config for MockRuntime {
+	type AccountId = u128;
+	type Lookup = IdentityLookup<Self::AccountId>;
+	type Block = Block;
+	type AccountData = pallet_balances::AccountData<u128>;
+}
+
+impl pallet_balances::Config for MockRuntime {
+	type Balance = u128;
+	type DustRemoval = ();
+	type RuntimeEvent = RuntimeEvent;
+	type ExistentialDeposit = ConstU128<100>;
+	type AccountStore = System;
+	type WeightInfo = ();
+	type MaxLocks = ();
+	type MaxReserves = ConstU32<50>;
+	type ReserveIdentifier = [u8; 8];
+	type FreezeIdentifier = RuntimeFreezeReason;
+	type MaxFreezes = ConstU32<50>;
+	type RuntimeHoldReason = RuntimeHoldReason;
+	type RuntimeFreezeReason = RuntimeFreezeReason;
+	type DoneSlashHandler = ();
+}
+
+impl pallet_assets::Config<Instance1> for MockRuntime {
+	type RuntimeEvent = RuntimeEvent;
+	type Balance = u128;
+	type RemoveItemsLimit = ConstU32<1000>;
+	type AssetId = u32;
+	type AssetIdParameter = u32;
+	type Currency = Balances;
+	type CreateOrigin = AsEnsureOriginWithArg<EnsureSigned<Self::AccountId>>;
+	type ForceOrigin = frame_system::EnsureRoot<Self::AccountId>;
+	type AssetDeposit = ConstU128<1>;
+	type AssetAccountDeposit = ConstU128<10>;
+	type MetadataDepositBase = ConstU128<1>;
+	type MetadataDepositPerByte = ConstU128<1>;
+	type ApprovalDeposit = ConstU128<1>;
+	type StringLimit = ConstU32<50>;
+	type Freezer = AssetsFreezer;
+	type Extra = ();
+	type WeightInfo = ();
+	type CallbackHandle = ();
+	pallet_assets::runtime_benchmarks_enabled! {
+		type BenchmarkHelper = ();
+	}
+}
+
+parameter_types! {
+	pub const StakingRewardsPalletId: PalletId = PalletId(*b"py/stkrd");
+	pub const Native: NativeOrWithId<u32> = NativeOrWithId::Native;
+	pub const PermissionedAccountId: u128 = 0;
+}
+
+/// Give Root Origin permission to create pools.
+pub struct MockPermissionedOrigin;
+impl EnsureOrigin<RuntimeOrigin> for MockPermissionedOrigin {
+	type Success = <MockRuntime as frame_system::Config>::AccountId;
+
+	fn try_origin(origin: RuntimeOrigin) -> Result<Self::Success, RuntimeOrigin> {
+		match origin.clone().into() {
+			Ok(frame_system::RawOrigin::Root) => Ok(PermissionedAccountId::get()),
+			_ => Err(origin),
+		}
+	}
+
+	#[cfg(feature = "runtime-benchmarks")]
+	fn try_successful_origin() -> Result<RuntimeOrigin, ()> {
+		Ok(RuntimeOrigin::root())
+	}
+}
+
+/// Allow Freezes for the `Assets` pallet
+impl pallet_assets_freezer::Config<pallet_assets_freezer::Instance1> for MockRuntime {
+	type RuntimeFreezeReason = RuntimeFreezeReason;
+	type RuntimeEvent = RuntimeEvent;
+}
+
+pub type NativeAndAssets = UnionOf<Balances, Assets, NativeFromLeft, NativeOrWithId<u32>, u128>;
+
+pub type NativeAndAssetsFreezer =
+	UnionOf<Balances, AssetsFreezer, NativeFromLeft, NativeOrWithId<u32>, u128>;
+
+#[cfg(feature = "runtime-benchmarks")]
+pub struct AssetRewardsBenchmarkHelper;
+#[cfg(feature = "runtime-benchmarks")]
+impl BenchmarkHelper<NativeOrWithId<u32>> for AssetRewardsBenchmarkHelper {
+	fn staked_asset() -> NativeOrWithId<u32> {
+		NativeOrWithId::<u32>::WithId(101)
+	}
+	fn reward_asset() -> NativeOrWithId<u32> {
+		NativeOrWithId::<u32>::WithId(102)
+	}
+}
+
+parameter_types! {
+	pub const CreationHoldReason: RuntimeHoldReason =
+		RuntimeHoldReason::StakingRewards(pallet_asset_rewards::HoldReason::PoolCreation);
+}
+
+impl Config for MockRuntime {
+	type RuntimeEvent = RuntimeEvent;
+	type AssetId = NativeOrWithId<u32>;
+	type Balance = <Self as pallet_balances::Config>::Balance;
+	type Assets = NativeAndAssets;
+	type AssetsFreezer = NativeAndAssetsFreezer;
+	type PalletId = StakingRewardsPalletId;
+	type CreatePoolOrigin = MockPermissionedOrigin;
+	type WeightInfo = ();
+	type RuntimeFreezeReason = RuntimeFreezeReason;
+	type Consideration = HoldConsideration<
+		u128,
+		Balances,
+		CreationHoldReason,
+		LinearStoragePrice<ConstU128<100>, ConstU128<0>, u128>,
+	>;
+	#[cfg(feature = "runtime-benchmarks")]
+	type BenchmarkHelper = AssetRewardsBenchmarkHelper;
+}
+
+pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
+	let mut t = frame_system::GenesisConfig::<MockRuntime>::default().build_storage().unwrap();
+
+	pallet_assets::GenesisConfig::<MockRuntime, Instance1> {
+		// Genesis assets: id, owner, is_sufficient, min_balance
+		// pub assets: Vec<(T::AssetId, T::AccountId, bool, T::Balance)>,
+		assets: vec![(1, 1, true, 1), (10, 1, true, 1), (20, 1, true, 1)],
+		// Genesis metadata: id, name, symbol, decimals
+		// pub metadata: Vec<(T::AssetId, Vec<u8>, Vec<u8>, u8)>,
+		metadata: vec![
+			(1, b"test".to_vec(), b"TST".to_vec(), 18),
+			(10, b"test10".to_vec(), b"T10".to_vec(), 18),
+			(20, b"test20".to_vec(), b"T20".to_vec(), 18),
+		],
+		// Genesis accounts: id, account_id, balance
+		// pub accounts: Vec<(T::AssetId, T::AccountId, T::Balance)>,
+		accounts: vec![
+			(1, 1, 10000),
+			(1, 2, 20000),
+			(1, 3, 30000),
+			(1, 4, 40000),
+			(1, 10, 40000),
+			(1, 20, 40000),
+		],
+		next_asset_id: None,
+	}
+	.assimilate_storage(&mut t)
+	.unwrap();
+
+	let pool_zero_account_id = 31086825966906540362769395565;
+	pallet_balances::GenesisConfig::<MockRuntime> {
+		balances: vec![
+			(0, 10000),
+			(1, 10000),
+			(2, 20000),
+			(3, 30000),
+			(4, 40000),
+			(10, 40000),
+			(20, 40000),
+			(pool_zero_account_id, 100_000), // Top up the default pool account id
+		],
+	}
+	.assimilate_storage(&mut t)
+	.unwrap();
+
+	let mut ext = sp_io::TestExternalities::new(t);
+	ext.execute_with(|| System::set_block_number(1));
+	ext
+}
diff --git a/substrate/frame/asset-rewards/src/tests.rs b/substrate/frame/asset-rewards/src/tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..399d6a54c939293e4fdc2c87118d85596e701374
--- /dev/null
+++ b/substrate/frame/asset-rewards/src/tests.rs
@@ -0,0 +1,1457 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::{mock::*, *};
+use frame_support::{
+	assert_err, assert_noop, assert_ok, hypothetically,
+	traits::{
+		fungible,
+		fungible::NativeOrWithId,
+		fungibles,
+		tokens::{Fortitude, Preservation},
+	},
+};
+use sp_runtime::{traits::BadOrigin, ArithmeticError, TokenError};
+
+const DEFAULT_STAKED_ASSET_ID: NativeOrWithId<u32> = NativeOrWithId::<u32>::WithId(1);
+const DEFAULT_REWARD_ASSET_ID: NativeOrWithId<u32> = NativeOrWithId::<u32>::Native;
+const DEFAULT_REWARD_RATE_PER_BLOCK: u128 = 100;
+const DEFAULT_EXPIRE_AFTER: u64 = 200;
+const DEFAULT_ADMIN: u128 = 1;
+
+/// Creates a basic pool with values:
+/// - Staking asset: 1
+/// - Reward asset: Native
+/// - Reward rate per block: 100
+/// - Lifetime: 100
+/// - Admin: 1
+///
+/// Useful to reduce boilerplate in tests when it's not important to customise or reuse pool
+/// params.
+pub fn create_default_pool() {
+	assert_ok!(StakingRewards::create_pool(
+		RuntimeOrigin::root(),
+		Box::new(DEFAULT_STAKED_ASSET_ID.clone()),
+		Box::new(DEFAULT_REWARD_ASSET_ID.clone()),
+		DEFAULT_REWARD_RATE_PER_BLOCK,
+		DispatchTime::After(DEFAULT_EXPIRE_AFTER),
+		Some(DEFAULT_ADMIN)
+	));
+}
+
+/// The same as [`create_default_pool`], but with the admin parameter set to the creator.
+pub fn create_default_pool_permissioned_admin() {
+	assert_ok!(StakingRewards::create_pool(
+		RuntimeOrigin::root(),
+		Box::new(DEFAULT_STAKED_ASSET_ID.clone()),
+		Box::new(DEFAULT_REWARD_ASSET_ID.clone()),
+		DEFAULT_REWARD_RATE_PER_BLOCK,
+		DispatchTime::After(DEFAULT_EXPIRE_AFTER),
+		Some(PermissionedAccountId::get()),
+	));
+}
+
+fn assert_hypothetically_earned(
+	staker: u128,
+	expected_earned: u128,
+	pool_id: u32,
+	reward_asset_id: NativeOrWithId<u32>,
+) {
+	hypothetically!({
+		// Get the pre-harvest balance.
+		let balance_before: <MockRuntime as Config>::Balance =
+			<<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker);
+
+		// Harvest the rewards.
+		assert_ok!(StakingRewards::harvest_rewards(RuntimeOrigin::signed(staker), pool_id, None),);
+
+		// Sanity check: staker rewards are reset to 0 if some `amount` is still staked, otherwise
+		// the storage item removed.
+		if let Some(staker_pool) = PoolStakers::<MockRuntime>::get(pool_id, staker) {
+			assert!(staker_pool.rewards == 0);
+			assert!(staker_pool.amount > 0);
+		}
+
+		// Check that the staker has earned the expected amount.
+		let balance_after =
+			<<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker);
+		assert_eq!(balance_after - balance_before, expected_earned);
+	});
+}
+
+fn events() -> Vec<Event<MockRuntime>> {
+	let result = System::events()
+		.into_iter()
+		.map(|r| r.event)
+		.filter_map(|e| {
+			if let mock::RuntimeEvent::StakingRewards(inner) = e {
+				Some(inner)
+			} else {
+				None
+			}
+		})
+		.collect();
+
+	System::reset_events();
+
+	result
+}
+
+fn pools() -> Vec<(u32, PoolInfo<u128, NativeOrWithId<u32>, u128, u64>)> {
+	Pools::<MockRuntime>::iter().collect()
+}
+
+mod create_pool {
+	use super::*;
+
+	#[test]
+	fn success() {
+		new_test_ext().execute_with(|| {
+			assert_eq!(NextPoolId::<MockRuntime>::get(), 0);
+
+			System::set_block_number(10);
+			let expected_expiry_block = DEFAULT_EXPIRE_AFTER + 10;
+
+			// Create a pool with default values, and no admin override so [`PermissionedAccountId`]
+			// is admin.
+			assert_ok!(StakingRewards::create_pool(
+				RuntimeOrigin::root(),
+				Box::new(DEFAULT_STAKED_ASSET_ID),
+				Box::new(DEFAULT_REWARD_ASSET_ID),
+				DEFAULT_REWARD_RATE_PER_BLOCK,
+				DispatchTime::After(DEFAULT_EXPIRE_AFTER),
+				Some(PermissionedAccountId::get())
+			));
+
+			// Event is emitted.
+			assert_eq!(
+				events(),
+				[Event::<MockRuntime>::PoolCreated {
+					creator: PermissionedAccountId::get(),
+					pool_id: 0,
+					staked_asset_id: DEFAULT_STAKED_ASSET_ID,
+					reward_asset_id: DEFAULT_REWARD_ASSET_ID,
+					reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK,
+					expiry_block: expected_expiry_block,
+					admin: PermissionedAccountId::get(),
+				}]
+			);
+
+			// State is updated correctly.
+			assert_eq!(NextPoolId::<MockRuntime>::get(), 1);
+			assert_eq!(
+				pools(),
+				vec![(
+					0,
+					PoolInfo {
+						staked_asset_id: DEFAULT_STAKED_ASSET_ID,
+						reward_asset_id: DEFAULT_REWARD_ASSET_ID,
+						reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK,
+						expiry_block: expected_expiry_block,
+						admin: PermissionedAccountId::get(),
+						total_tokens_staked: 0,
+						reward_per_token_stored: 0,
+						last_update_block: 0,
+						account: StakingRewards::pool_account_id(&0),
+					}
+				)]
+			);
+
+			// Create another pool with explicit admin and other overrides.
+			let admin = 2;
+			let staked_asset_id = NativeOrWithId::<u32>::WithId(10);
+			let reward_asset_id = NativeOrWithId::<u32>::WithId(20);
+			let reward_rate_per_block = 250;
+			let expiry_block = 500;
+			let expected_expiry_block = expiry_block + 10;
+			assert_ok!(StakingRewards::create_pool(
+				RuntimeOrigin::root(),
+				Box::new(staked_asset_id.clone()),
+				Box::new(reward_asset_id.clone()),
+				reward_rate_per_block,
+				DispatchTime::After(expiry_block),
+				Some(admin)
+			));
+
+			// Event is emitted.
+			assert_eq!(
+				events(),
+				[Event::<MockRuntime>::PoolCreated {
+					creator: PermissionedAccountId::get(),
+					pool_id: 1,
+					staked_asset_id: staked_asset_id.clone(),
+					reward_asset_id: reward_asset_id.clone(),
+					reward_rate_per_block,
+					admin,
+					expiry_block: expected_expiry_block,
+				}]
+			);
+
+			// State is updated correctly.
+			assert_eq!(NextPoolId::<MockRuntime>::get(), 2);
+			assert_eq!(
+				pools(),
+				vec![
+					(
+						0,
+						PoolInfo {
+							staked_asset_id: DEFAULT_STAKED_ASSET_ID,
+							reward_asset_id: DEFAULT_REWARD_ASSET_ID,
+							reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK,
+							admin: PermissionedAccountId::get(),
+							expiry_block: DEFAULT_EXPIRE_AFTER + 10,
+							total_tokens_staked: 0,
+							reward_per_token_stored: 0,
+							last_update_block: 0,
+							account: StakingRewards::pool_account_id(&0),
+						}
+					),
+					(
+						1,
+						PoolInfo {
+							staked_asset_id,
+							reward_asset_id,
+							reward_rate_per_block,
+							admin,
+							total_tokens_staked: 0,
+							expiry_block: expected_expiry_block,
+							reward_per_token_stored: 0,
+							last_update_block: 0,
+							account: StakingRewards::pool_account_id(&1),
+						}
+					)
+				]
+			);
+		});
+	}
+
+	#[test]
+	fn success_same_assets() {
+		new_test_ext().execute_with(|| {
+			assert_eq!(NextPoolId::<MockRuntime>::get(), 0);
+
+			System::set_block_number(10);
+			let expected_expiry_block = DEFAULT_EXPIRE_AFTER + 10;
+
+			// Create a pool with the same staking and reward asset.
+			let asset = NativeOrWithId::<u32>::Native;
+			assert_ok!(StakingRewards::create_pool(
+				RuntimeOrigin::root(),
+				Box::new(asset.clone()),
+				Box::new(asset.clone()),
+				DEFAULT_REWARD_RATE_PER_BLOCK,
+				DispatchTime::After(DEFAULT_EXPIRE_AFTER),
+				Some(PermissionedAccountId::get())
+			));
+
+			// Event is emitted.
+			assert_eq!(
+				events(),
+				[Event::<MockRuntime>::PoolCreated {
+					creator: PermissionedAccountId::get(),
+					pool_id: 0,
+					staked_asset_id: asset.clone(),
+					reward_asset_id: asset.clone(),
+					reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK,
+					expiry_block: expected_expiry_block,
+					admin: PermissionedAccountId::get(),
+				}]
+			);
+
+			// State is updated correctly.
+			assert_eq!(NextPoolId::<MockRuntime>::get(), 1);
+			assert_eq!(
+				pools(),
+				vec![(
+					0,
+					PoolInfo {
+						staked_asset_id: asset.clone(),
+						reward_asset_id: asset,
+						reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK,
+						expiry_block: expected_expiry_block,
+						admin: PermissionedAccountId::get(),
+						total_tokens_staked: 0,
+						reward_per_token_stored: 0,
+						last_update_block: 0,
+						account: StakingRewards::pool_account_id(&0),
+					}
+				)]
+			);
+		})
+	}
+
+	#[test]
+	fn fails_for_non_existent_asset() {
+		new_test_ext().execute_with(|| {
+			let valid_asset = NativeOrWithId::<u32>::WithId(1);
+			let invalid_asset = NativeOrWithId::<u32>::WithId(200);
+
+			assert_err!(
+				StakingRewards::create_pool(
+					RuntimeOrigin::root(),
+					Box::new(valid_asset.clone()),
+					Box::new(invalid_asset.clone()),
+					10,
+					DispatchTime::After(10u64),
+					None
+				),
+				Error::<MockRuntime>::NonExistentAsset
+			);
+
+			assert_err!(
+				StakingRewards::create_pool(
+					RuntimeOrigin::root(),
+					Box::new(invalid_asset.clone()),
+					Box::new(valid_asset.clone()),
+					10,
+					DispatchTime::After(10u64),
+					None
+				),
+				Error::<MockRuntime>::NonExistentAsset
+			);
+
+			assert_err!(
+				StakingRewards::create_pool(
+					RuntimeOrigin::root(),
+					Box::new(invalid_asset.clone()),
+					Box::new(invalid_asset.clone()),
+					10,
+					DispatchTime::After(10u64),
+					None
+				),
+				Error::<MockRuntime>::NonExistentAsset
+			);
+		})
+	}
+
+	#[test]
+	fn fails_for_not_permissioned() {
+		new_test_ext().execute_with(|| {
+			let user = 100;
+			let staked_asset_id = NativeOrWithId::<u32>::Native;
+			let reward_asset_id = NativeOrWithId::<u32>::WithId(1);
+			let reward_rate_per_block = 100;
+			let expiry_block = 100u64;
+			assert_err!(
+				StakingRewards::create_pool(
+					RuntimeOrigin::signed(user),
+					Box::new(staked_asset_id.clone()),
+					Box::new(reward_asset_id.clone()),
+					reward_rate_per_block,
+					DispatchTime::After(expiry_block),
+					None
+				),
+				BadOrigin
+			);
+		});
+	}
+
+	#[test]
+	fn create_pool_with_caller_admin() {
+		new_test_ext().execute_with(|| {
+			assert_eq!(NextPoolId::<MockRuntime>::get(), 0);
+
+			System::set_block_number(10);
+			let expected_expiry_block = DEFAULT_EXPIRE_AFTER + 10;
+
+			assert_ok!(StakingRewards::create_pool(
+				RuntimeOrigin::root(),
+				Box::new(DEFAULT_STAKED_ASSET_ID),
+				Box::new(DEFAULT_REWARD_ASSET_ID),
+				DEFAULT_REWARD_RATE_PER_BLOCK,
+				DispatchTime::After(DEFAULT_EXPIRE_AFTER),
+				None,
+			));
+
+			assert_eq!(
+				events(),
+				[Event::<MockRuntime>::PoolCreated {
+					creator: PermissionedAccountId::get(),
+					pool_id: 0,
+					staked_asset_id: DEFAULT_STAKED_ASSET_ID,
+					reward_asset_id: DEFAULT_REWARD_ASSET_ID,
+					reward_rate_per_block: DEFAULT_REWARD_RATE_PER_BLOCK,
+					expiry_block: expected_expiry_block,
+					admin: PermissionedAccountId::get(),
+				}]
+			);
+
+			assert_eq!(Pools::<MockRuntime>::get(0).unwrap().admin, PermissionedAccountId::get());
+		});
+	}
+}
+
+mod stake {
+	use super::*;
+
+	#[test]
+	fn success() {
+		new_test_ext().execute_with(|| {
+			let user = 1;
+			create_default_pool();
+			let pool_id = 0;
+			let initial_balance = <Assets as fungibles::Inspect<u128>>::reducible_balance(
+				1,
+				&user,
+				Preservation::Expendable,
+				Fortitude::Force,
+			);
+
+			// User stakes tokens
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, 1000));
+
+			// Check that the user's staked amount is updated
+			assert_eq!(PoolStakers::<MockRuntime>::get(pool_id, user).unwrap().amount, 1000);
+
+			// Event is emitted.
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::Staked { staker: user, amount: 1000, pool_id: 0 }
+			);
+
+			// Check that the pool's total tokens staked is updated
+			assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().total_tokens_staked, 1000);
+
+			// Check user's frozen balance is updated
+			assert_eq!(
+				<Assets as fungibles::Inspect<u128>>::reducible_balance(
+					1,
+					&user,
+					Preservation::Expendable,
+					Fortitude::Force,
+				),
+				// - extra 1 for ed
+				initial_balance - 1000 - 1
+			);
+
+			// User stakes more tokens
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, 500));
+
+			// Event is emitted.
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::Staked { staker: user, amount: 500, pool_id: 0 }
+			);
+
+			// Check that the user's staked amount is updated
+			assert_eq!(PoolStakers::<MockRuntime>::get(pool_id, user).unwrap().amount, 1000 + 500);
+
+			// Check that the pool's total tokens staked is updated
+			assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().total_tokens_staked, 1000 + 500);
+
+			assert_eq!(
+				<Assets as fungibles::Inspect<u128>>::reducible_balance(
+					1,
+					&user,
+					Preservation::Expendable,
+					Fortitude::Force,
+				),
+				// - extra 1 for ed
+				initial_balance - 1500 - 1
+			);
+
+			// Event is emitted.
+			assert_eq!(events(), []);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_existent_pool() {
+		new_test_ext().execute_with(|| {
+			let user = 1;
+			assert_err!(
+				StakingRewards::stake(RuntimeOrigin::signed(user), 999, 1000),
+				Error::<MockRuntime>::NonExistentPool
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_insufficient_balance() {
+		new_test_ext().execute_with(|| {
+			let user = 1;
+			create_default_pool();
+			let pool_id = 0;
+			let initial_balance = <Assets as fungibles::Inspect<u128>>::reducible_balance(
+				1,
+				&user,
+				Preservation::Expendable,
+				Fortitude::Force,
+			);
+			assert_err!(
+				StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, initial_balance + 1),
+				TokenError::FundsUnavailable,
+			);
+		})
+	}
+}
+
+mod unstake {
+	use super::*;
+
+	#[test]
+	fn success() {
+		new_test_ext().execute_with(|| {
+			let user = 1;
+			create_default_pool();
+			let pool_id = 0;
+
+			// User stakes tokens
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, 1000));
+
+			// User unstakes tokens
+			assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(user), pool_id, 500, None));
+
+			// Event is emitted.
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::Unstaked {
+					caller: user,
+					staker: user,
+					amount: 500,
+					pool_id: 0
+				}
+			);
+
+			// Check that the user's staked amount is updated
+			assert_eq!(PoolStakers::<MockRuntime>::get(pool_id, user).unwrap().amount, 500);
+
+			// Check that the pool's total tokens staked is updated
+			assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().total_tokens_staked, 500);
+
+			// User unstakes remaining tokens
+			assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(user), pool_id, 500, None));
+
+			// Check that the storage items is removed since stake amount and rewards are zero.
+			assert!(PoolStakers::<MockRuntime>::get(pool_id, user).is_none());
+
+			// Check that the pool's total tokens staked is zero
+			assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().total_tokens_staked, 0);
+		});
+	}
+
+	#[test]
+	fn unstake_for_other() {
+		new_test_ext().execute_with(|| {
+			let staker = 1;
+			let caller = 2;
+			let pool_id = 0;
+			let init_block = System::block_number();
+
+			create_default_pool();
+
+			// User stakes tokens
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000));
+
+			// Fails to unstake for other since pool is still active
+			assert_noop!(
+				StakingRewards::unstake(RuntimeOrigin::signed(caller), pool_id, 500, Some(staker)),
+				BadOrigin,
+			);
+
+			System::set_block_number(init_block + DEFAULT_EXPIRE_AFTER + 1);
+
+			assert_ok!(StakingRewards::unstake(
+				RuntimeOrigin::signed(caller),
+				pool_id,
+				500,
+				Some(staker)
+			));
+
+			// Event is emitted.
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::Unstaked { caller, staker, amount: 500, pool_id: 0 }
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_existent_pool() {
+		new_test_ext().execute_with(|| {
+			let user = 1;
+			let non_existent_pool_id = 999;
+
+			// User tries to unstake tokens from a non-existent pool
+			assert_err!(
+				StakingRewards::unstake(
+					RuntimeOrigin::signed(user),
+					non_existent_pool_id,
+					500,
+					None
+				),
+				Error::<MockRuntime>::NonExistentPool
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_insufficient_staked_amount() {
+		new_test_ext().execute_with(|| {
+			let user = 1;
+			create_default_pool();
+			let pool_id = 0;
+
+			// User stakes tokens
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(user), pool_id, 1000));
+
+			// User tries to unstake more tokens than they have staked
+			assert_err!(
+				StakingRewards::unstake(RuntimeOrigin::signed(user), pool_id, 1500, None),
+				Error::<MockRuntime>::NotEnoughTokens
+			);
+		});
+	}
+}
+
+mod harvest_rewards {
+	use super::*;
+
+	#[test]
+	fn success() {
+		new_test_ext().execute_with(|| {
+			let staker = 1;
+			let pool_id = 0;
+			let reward_asset_id = NativeOrWithId::<u32>::Native;
+			create_default_pool();
+
+			// Stake
+			System::set_block_number(10);
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000));
+
+			// Harvest
+			System::set_block_number(20);
+			let balance_before: <MockRuntime as Config>::Balance =
+				<<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker);
+			assert_ok!(StakingRewards::harvest_rewards(
+				RuntimeOrigin::signed(staker),
+				pool_id,
+				None
+			));
+			let balance_after =
+				<<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker);
+
+			// Assert
+			assert_eq!(
+				balance_after - balance_before,
+				10 * Pools::<MockRuntime>::get(pool_id).unwrap().reward_rate_per_block
+			);
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::RewardsHarvested {
+					caller: staker,
+					staker,
+					pool_id,
+					amount: 10 * Pools::<MockRuntime>::get(pool_id).unwrap().reward_rate_per_block
+				}
+			);
+		});
+	}
+
+	#[test]
+	fn harvest_for_other() {
+		new_test_ext().execute_with(|| {
+			let caller = 2;
+			let staker = 1;
+			let pool_id = 0;
+			let init_block = System::block_number();
+
+			create_default_pool();
+
+			// Stake
+			System::set_block_number(10);
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000));
+
+			System::set_block_number(20);
+
+			// Fails to harvest for staker since pool is still active
+			assert_noop!(
+				StakingRewards::harvest_rewards(
+					RuntimeOrigin::signed(caller),
+					pool_id,
+					Some(staker)
+				),
+				BadOrigin
+			);
+
+			System::set_block_number(init_block + DEFAULT_EXPIRE_AFTER + 1);
+
+			// Harvest for staker
+			assert_ok!(StakingRewards::harvest_rewards(
+				RuntimeOrigin::signed(caller),
+				pool_id,
+				Some(staker),
+			));
+
+			assert!(matches!(
+				events().last().unwrap(),
+				Event::<MockRuntime>::RewardsHarvested {
+					caller,
+					staker,
+					pool_id,
+					..
+				} if caller == caller && staker == staker && pool_id == pool_id
+			));
+		});
+	}
+
+	#[test]
+	fn fails_for_non_existent_staker() {
+		new_test_ext().execute_with(|| {
+			let non_existent_staker = 999;
+
+			create_default_pool();
+			assert_err!(
+				StakingRewards::harvest_rewards(
+					RuntimeOrigin::signed(non_existent_staker),
+					0,
+					None
+				),
+				Error::<MockRuntime>::NonExistentStaker
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_existent_pool() {
+		new_test_ext().execute_with(|| {
+			let staker = 1;
+			let non_existent_pool_id = 999;
+
+			assert_err!(
+				StakingRewards::harvest_rewards(
+					RuntimeOrigin::signed(staker),
+					non_existent_pool_id,
+					None,
+				),
+				Error::<MockRuntime>::NonExistentPool
+			);
+		});
+	}
+}
+
+mod set_pool_admin {
+	use super::*;
+
+	#[test]
+	fn success_signed_admin() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let new_admin = 2;
+			let pool_id = 0;
+			create_default_pool();
+
+			// Modify the pool admin
+			assert_ok!(StakingRewards::set_pool_admin(
+				RuntimeOrigin::signed(admin),
+				pool_id,
+				new_admin,
+			));
+
+			// Check state
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::PoolAdminModified { pool_id, new_admin }
+			);
+			assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().admin, new_admin);
+		});
+	}
+
+	#[test]
+	fn success_permissioned_admin() {
+		new_test_ext().execute_with(|| {
+			let new_admin = 2;
+			let pool_id = 0;
+			create_default_pool_permissioned_admin();
+
+			// Modify the pool admin
+			assert_ok!(StakingRewards::set_pool_admin(RuntimeOrigin::root(), pool_id, new_admin));
+
+			// Check state
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::PoolAdminModified { pool_id, new_admin }
+			);
+			assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().admin, new_admin);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_existent_pool() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let new_admin = 2;
+			let non_existent_pool_id = 999;
+
+			assert_err!(
+				StakingRewards::set_pool_admin(
+					RuntimeOrigin::signed(admin),
+					non_existent_pool_id,
+					new_admin
+				),
+				Error::<MockRuntime>::NonExistentPool
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_admin() {
+		new_test_ext().execute_with(|| {
+			let new_admin = 2;
+			let non_admin = 3;
+			let pool_id = 0;
+			create_default_pool();
+
+			assert_err!(
+				StakingRewards::set_pool_admin(
+					RuntimeOrigin::signed(non_admin),
+					pool_id,
+					new_admin
+				),
+				BadOrigin
+			);
+		});
+	}
+}
+
+mod set_pool_expiry_block {
+	use super::*;
+
+	#[test]
+	fn success_permissioned_admin() {
+		new_test_ext().execute_with(|| {
+			let pool_id = 0;
+			let new_expiry_block = System::block_number() + DEFAULT_EXPIRE_AFTER + 1u64;
+			create_default_pool_permissioned_admin();
+
+			assert_ok!(StakingRewards::set_pool_expiry_block(
+				RuntimeOrigin::root(),
+				pool_id,
+				DispatchTime::At(new_expiry_block),
+			));
+
+			// Check state
+			assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().expiry_block, new_expiry_block);
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::PoolExpiryBlockModified { pool_id, new_expiry_block }
+			);
+		});
+	}
+
+	#[test]
+	fn success_signed_admin() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let pool_id = 0;
+			let new_expiry_block = System::block_number() + DEFAULT_EXPIRE_AFTER + 1u64;
+			create_default_pool();
+
+			assert_ok!(StakingRewards::set_pool_expiry_block(
+				RuntimeOrigin::signed(admin),
+				pool_id,
+				DispatchTime::At(new_expiry_block)
+			));
+
+			// Check state
+			assert_eq!(Pools::<MockRuntime>::get(pool_id).unwrap().expiry_block, new_expiry_block);
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::PoolExpiryBlockModified { pool_id, new_expiry_block }
+			);
+		});
+	}
+
+	#[test]
+	fn extends_reward_accumulation() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let staker = 2;
+			let pool_id = 0;
+			let new_expiry_block = 300u64;
+			System::set_block_number(10);
+			create_default_pool();
+
+			// Regular reward accumulation
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000));
+			System::set_block_number(20);
+			assert_hypothetically_earned(
+				staker,
+				DEFAULT_REWARD_RATE_PER_BLOCK * 10,
+				pool_id,
+				NativeOrWithId::<u32>::Native,
+			);
+
+			// Expiry was block 210, so earned 200 at block 250
+			System::set_block_number(250);
+			assert_hypothetically_earned(
+				staker,
+				DEFAULT_REWARD_RATE_PER_BLOCK * 200,
+				pool_id,
+				NativeOrWithId::<u32>::Native,
+			);
+
+			// Extend expiry 50 more blocks
+			assert_ok!(StakingRewards::set_pool_expiry_block(
+				RuntimeOrigin::signed(admin),
+				pool_id,
+				DispatchTime::At(new_expiry_block)
+			));
+			System::set_block_number(350);
+
+			// Staker has been in pool with rewards active for 250 blocks total
+			assert_hypothetically_earned(
+				staker,
+				DEFAULT_REWARD_RATE_PER_BLOCK * 250,
+				pool_id,
+				NativeOrWithId::<u32>::Native,
+			);
+		});
+	}
+
+	#[test]
+	fn fails_to_cutback_expiration() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let pool_id = 0;
+			create_default_pool();
+
+			assert_noop!(
+				StakingRewards::set_pool_expiry_block(
+					RuntimeOrigin::signed(admin),
+					pool_id,
+					DispatchTime::After(30)
+				),
+				Error::<MockRuntime>::ExpiryCut
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_existent_pool() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let non_existent_pool_id = 999;
+			let new_expiry_block = 200u64;
+
+			assert_err!(
+				StakingRewards::set_pool_expiry_block(
+					RuntimeOrigin::signed(admin),
+					non_existent_pool_id,
+					DispatchTime::After(new_expiry_block)
+				),
+				Error::<MockRuntime>::NonExistentPool
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_admin() {
+		new_test_ext().execute_with(|| {
+			let non_admin = 2;
+			let pool_id = 0;
+			let new_expiry_block = 200u64;
+			create_default_pool();
+
+			assert_err!(
+				StakingRewards::set_pool_expiry_block(
+					RuntimeOrigin::signed(non_admin),
+					pool_id,
+					DispatchTime::After(new_expiry_block)
+				),
+				BadOrigin
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_expiry_block_in_the_past() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let pool_id = 0;
+			create_default_pool();
+			System::set_block_number(50);
+			assert_err!(
+				StakingRewards::set_pool_expiry_block(
+					RuntimeOrigin::signed(admin),
+					pool_id,
+					DispatchTime::At(40u64)
+				),
+				Error::<MockRuntime>::ExpiryBlockMustBeInTheFuture
+			);
+		});
+	}
+}
+
+mod set_pool_reward_rate_per_block {
+	use super::*;
+
+	#[test]
+	fn success_signed_admin() {
+		new_test_ext().execute_with(|| {
+			let pool_id = 0;
+			let new_reward_rate = 200;
+			create_default_pool();
+
+			// Pool Admin can modify
+			assert_ok!(StakingRewards::set_pool_reward_rate_per_block(
+				RuntimeOrigin::signed(DEFAULT_ADMIN),
+				pool_id,
+				new_reward_rate
+			));
+
+			// Check state
+			assert_eq!(
+				Pools::<MockRuntime>::get(pool_id).unwrap().reward_rate_per_block,
+				new_reward_rate
+			);
+
+			// Check event
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::PoolRewardRateModified {
+					pool_id,
+					new_reward_rate_per_block: new_reward_rate
+				}
+			);
+		});
+	}
+
+	#[test]
+	fn success_permissioned_admin() {
+		new_test_ext().execute_with(|| {
+			let pool_id = 0;
+			let new_reward_rate = 200;
+			create_default_pool_permissioned_admin();
+
+			// Root can modify
+			assert_ok!(StakingRewards::set_pool_reward_rate_per_block(
+				RuntimeOrigin::root(),
+				pool_id,
+				new_reward_rate
+			));
+
+			// Check state
+			assert_eq!(
+				Pools::<MockRuntime>::get(pool_id).unwrap().reward_rate_per_block,
+				new_reward_rate
+			);
+
+			// Check event
+			assert_eq!(
+				*events().last().unwrap(),
+				Event::<MockRuntime>::PoolRewardRateModified {
+					pool_id,
+					new_reward_rate_per_block: new_reward_rate
+				}
+			);
+		});
+	}
+
+	#[test]
+	fn staker_rewards_are_affected_correctly() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let staker = 2;
+			let pool_id = 0;
+			let new_reward_rate = 150;
+			create_default_pool();
+
+			// Stake some tokens, and accumulate 10 blocks of rewards at the default pool rate (100)
+			System::set_block_number(10);
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 1000));
+			System::set_block_number(20);
+
+			// Increase the reward rate
+			assert_ok!(StakingRewards::set_pool_reward_rate_per_block(
+				RuntimeOrigin::signed(admin),
+				pool_id,
+				new_reward_rate
+			));
+
+			// Accumulate 10 blocks of rewards at the new rate
+			System::set_block_number(30);
+
+			// Check that rewards are calculated correctly with the updated rate
+			assert_hypothetically_earned(
+				staker,
+				10 * 100 + 10 * new_reward_rate,
+				pool_id,
+				NativeOrWithId::<u32>::Native,
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_existent_pool() {
+		new_test_ext().execute_with(|| {
+			let admin = 1;
+			let non_existent_pool_id = 999;
+			let new_reward_rate = 200;
+
+			assert_err!(
+				StakingRewards::set_pool_reward_rate_per_block(
+					RuntimeOrigin::signed(admin),
+					non_existent_pool_id,
+					new_reward_rate
+				),
+				Error::<MockRuntime>::NonExistentPool
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_admin() {
+		new_test_ext().execute_with(|| {
+			let non_admin = 2;
+			let pool_id = 0;
+			let new_reward_rate = 200;
+			create_default_pool();
+
+			assert_err!(
+				StakingRewards::set_pool_reward_rate_per_block(
+					RuntimeOrigin::signed(non_admin),
+					pool_id,
+					new_reward_rate
+				),
+				BadOrigin
+			);
+		});
+	}
+
+	#[test]
+	fn fails_to_decrease() {
+		new_test_ext().execute_with(|| {
+			create_default_pool_permissioned_admin();
+
+			assert_noop!(
+				StakingRewards::set_pool_reward_rate_per_block(
+					RuntimeOrigin::root(),
+					0,
+					DEFAULT_REWARD_RATE_PER_BLOCK - 1
+				),
+				Error::<MockRuntime>::RewardRateCut
+			);
+		});
+	}
+}
+
+mod deposit_reward_tokens {
+	use super::*;
+
+	#[test]
+	fn success() {
+		new_test_ext().execute_with(|| {
+			let depositor = 1;
+			let pool_id = 0;
+			let amount = 1000;
+			let reward_asset_id = NativeOrWithId::<u32>::Native;
+			create_default_pool();
+			let pool_account_id = StakingRewards::pool_account_id(&pool_id);
+
+			let depositor_balance_before =
+				<<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &depositor);
+			let pool_balance_before = <<MockRuntime as Config>::Assets>::balance(
+				reward_asset_id.clone(),
+				&pool_account_id,
+			);
+			assert_ok!(StakingRewards::deposit_reward_tokens(
+				RuntimeOrigin::signed(depositor),
+				pool_id,
+				amount
+			));
+			let depositor_balance_after =
+				<<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &depositor);
+			let pool_balance_after =
+				<<MockRuntime as Config>::Assets>::balance(reward_asset_id, &pool_account_id);
+
+			assert_eq!(pool_balance_after - pool_balance_before, amount);
+			assert_eq!(depositor_balance_before - depositor_balance_after, amount);
+		});
+	}
+
+	#[test]
+	fn fails_for_non_existent_pool() {
+		new_test_ext().execute_with(|| {
+			assert_err!(
+				StakingRewards::deposit_reward_tokens(RuntimeOrigin::signed(1), 999, 100),
+				Error::<MockRuntime>::NonExistentPool
+			);
+		});
+	}
+
+	#[test]
+	fn fails_for_insufficient_balance() {
+		new_test_ext().execute_with(|| {
+			create_default_pool();
+			assert_err!(
+				StakingRewards::deposit_reward_tokens(RuntimeOrigin::signed(1), 0, 100_000_000),
+				ArithmeticError::Underflow
+			);
+		});
+	}
+}
+
+mod cleanup_pool {
+	use super::*;
+
+	#[test]
+	fn success() {
+		new_test_ext().execute_with(|| {
+			let pool_id = 0;
+			let admin = DEFAULT_ADMIN;
+			let admin_balance_before = <Balances as fungible::Inspect<u128>>::balance(&admin);
+
+			create_default_pool();
+			assert!(Pools::<MockRuntime>::get(pool_id).is_some());
+
+			assert_ok!(StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id));
+
+			assert_eq!(
+				<Balances as fungible::Inspect<u128>>::balance(&admin),
+				// `100_000` initial pool account balance from Genesis config
+				admin_balance_before + 100_000,
+			);
+			assert_eq!(Pools::<MockRuntime>::get(pool_id), None);
+			assert_eq!(PoolStakers::<MockRuntime>::iter_prefix_values(pool_id).count(), 0);
+			assert_eq!(PoolCost::<MockRuntime>::get(pool_id), None);
+		});
+	}
+
+	#[test]
+	fn success_only_when_pool_empty() {
+		new_test_ext().execute_with(|| {
+			let pool_id = 0;
+			let staker = 20;
+			let admin = DEFAULT_ADMIN;
+
+			create_default_pool();
+
+			// stake to prevent pool cleanup
+			assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker), pool_id, 100));
+
+			assert_noop!(
+				StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id),
+				Error::<MockRuntime>::NonEmptyPool
+			);
+
+			// unstake partially
+			assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(staker), pool_id, 50, None));
+
+			assert_noop!(
+				StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id),
+				Error::<MockRuntime>::NonEmptyPool
+			);
+
+			// unstake all
+			assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(staker), pool_id, 50, None));
+
+			assert_ok!(StakingRewards::cleanup_pool(RuntimeOrigin::signed(admin), pool_id),);
+
+			assert_eq!(Pools::<MockRuntime>::get(pool_id), None);
+			assert_eq!(PoolStakers::<MockRuntime>::iter_prefix_values(pool_id).count(), 0);
+			assert_eq!(PoolCost::<MockRuntime>::get(pool_id), None);
+		});
+	}
+
+	#[test]
+	fn fails_on_wrong_origin() {
+		new_test_ext().execute_with(|| {
+			let caller = 888;
+			let pool_id = 0;
+			create_default_pool();
+
+			assert_noop!(
+				StakingRewards::cleanup_pool(RuntimeOrigin::signed(caller), pool_id),
+				BadOrigin
+			);
+		});
+	}
+}
+
+/// This integration test
+/// 1. Considers 2 stakers each staking and unstaking at different intervals, asserts their
+///    claimable rewards are adjusted as expected, and that harvesting works.
+/// 2. Checks that rewards are correctly halted after the pool's expiry block, and resume when the
+///    pool is extended.
+/// 3. Checks that reward rates adjustment works correctly.
+///
+/// Note: There are occasionally off by 1 errors due to rounding. In practice this is
+/// insignificant.
+#[test]
+fn integration() {
+	new_test_ext().execute_with(|| {
+		let admin = 1;
+		let staker1 = 10u128;
+		let staker2 = 20;
+		let staked_asset_id = NativeOrWithId::<u32>::WithId(1);
+		let reward_asset_id = NativeOrWithId::<u32>::Native;
+		let reward_rate_per_block = 100;
+		let lifetime = 24u64.into();
+		System::set_block_number(1);
+		assert_ok!(StakingRewards::create_pool(
+			RuntimeOrigin::root(),
+			Box::new(staked_asset_id.clone()),
+			Box::new(reward_asset_id.clone()),
+			reward_rate_per_block,
+			DispatchTime::After(lifetime),
+			Some(admin)
+		));
+		let pool_id = 0;
+
+		// Block 7: Staker 1 stakes 100 tokens.
+		System::set_block_number(7);
+		assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker1), pool_id, 100));
+		// At this point
+		// - Staker 1 has earned 0 tokens.
+		// - Staker 1 is earning 100 tokens per block.
+
+		// Check that Staker 1 has earned 0 tokens.
+		assert_hypothetically_earned(staker1, 0, pool_id, reward_asset_id.clone());
+
+		// Block 9: Staker 2 stakes 100 tokens.
+		System::set_block_number(9);
+		assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker2), pool_id, 100));
+		// At this point
+		// - Staker 1 has earned 200 (100*2) tokens.
+		// - Staker 2 has earned 0 tokens.
+		// - Staker 1 is earning 50 tokens per block.
+		// - Staker 2 is earning 50 tokens per block.
+
+		// Check that Staker 1 has earned 200 tokens and Staker 2 has earned 0 tokens.
+		assert_hypothetically_earned(staker1, 200, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 0, pool_id, reward_asset_id.clone());
+
+		// Block 12: Staker 1 stakes an additional 100 tokens.
+		System::set_block_number(12);
+		assert_ok!(StakingRewards::stake(RuntimeOrigin::signed(staker1), pool_id, 100));
+		// At this point
+		// - Staker 1 has earned 350 (200 + (50 * 3)) tokens.
+		// - Staker 2 has earned 150 (50 * 3) tokens.
+		// - Staker 1 is earning 66.66 tokens per block.
+		// - Staker 2 is earning 33.33 tokens per block.
+
+		// Check that Staker 1 has earned 350 tokens and Staker 2 has earned 150 tokens.
+		assert_hypothetically_earned(staker1, 350, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 150, pool_id, reward_asset_id.clone());
+
+		// Block 22: Staker 1 unstakes 100 tokens.
+		System::set_block_number(22);
+		assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(staker1), pool_id, 100, None));
+		// - Staker 1 has earned 1016 (350 + 66.66 * 10) tokens.
+		// - Staker 2 has earned 483 (150 + 33.33 * 10) tokens.
+		// - Staker 1 is earning 50 tokens per block.
+		// - Staker 2 is earning 50 tokens per block.
+		assert_hypothetically_earned(staker1, 1016, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 483, pool_id, reward_asset_id.clone());
+
+		// Block 23: Staker 1 unstakes 100 tokens.
+		System::set_block_number(23);
+		assert_ok!(StakingRewards::unstake(RuntimeOrigin::signed(staker1), pool_id, 100, None));
+		// - Staker 1 has earned 1065 (1015 + 50) tokens.
+		// - Staker 2 has earned 533 (483 + 50) tokens.
+		// - Staker 1 is earning 0 tokens per block.
+		// - Staker 2 is earning 100 tokens per block.
+		assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 533, pool_id, reward_asset_id.clone());
+
+		// Block 50: Stakers should only have earned 2 blocks worth of tokens (expiry is 25).
+		System::set_block_number(50);
+		// - Staker 1 has earned 1065 tokens.
+		// - Staker 2 has earned 733 (533 + 2 * 100) tokens.
+		// - Staker 1 is earning 0 tokens per block.
+		// - Staker 2 is earning 0 tokens per block.
+		assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 733, pool_id, reward_asset_id.clone());
+
+		// Block 51: Extend the pool expiry block to 60.
+		System::set_block_number(51);
+		// - Staker 1 is earning 0 tokens per block.
+		// - Staker 2 is earning 100 tokens per block.
+		assert_ok!(StakingRewards::set_pool_expiry_block(
+			RuntimeOrigin::signed(admin),
+			pool_id,
+			DispatchTime::At(60u64),
+		));
+		assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 733, pool_id, reward_asset_id.clone());
+
+		// Block 53: Check rewards are resumed.
+		// - Staker 1 has earned 1065 tokens.
+		// - Staker 2 has earned 933 (733 + 2 * 100) tokens.
+		// - Staker 2 is earning 100 tokens per block.
+		System::set_block_number(53);
+		assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 933, pool_id, reward_asset_id.clone());
+
+		// Block 55: Increase the block reward.
+		// - Staker 1 has earned 1065 tokens.
+		// - Staker 2 has earned 1133 (933 + 2 * 100) tokens.
+		// - Staker 2 is earning 50 tokens per block.
+		System::set_block_number(55);
+		assert_ok!(StakingRewards::set_pool_reward_rate_per_block(
+			RuntimeOrigin::signed(admin),
+			pool_id,
+			150
+		));
+		assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 1133, pool_id, reward_asset_id.clone());
+
+		// Block 57: Staker2 harvests their rewards.
+		System::set_block_number(57);
+		// - Staker 2 has earned 1433 (1133 + 2 * 150) tokens.
+		assert_hypothetically_earned(staker2, 1433, pool_id, reward_asset_id.clone());
+		// Get the pre-harvest balance.
+		let balance_before: <MockRuntime as Config>::Balance =
+			<<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker2);
+		assert_ok!(StakingRewards::harvest_rewards(RuntimeOrigin::signed(staker2), pool_id, None));
+		let balance_after =
+			<<MockRuntime as Config>::Assets>::balance(reward_asset_id.clone(), &staker2);
+		assert_eq!(balance_after - balance_before, 1433u128);
+
+		// Block 60: Check rewards were adjusted correctly.
+		// - Staker 1 has earned 1065 tokens.
+		// - Staker 2 has earned 450 (3 * 150) tokens.
+		System::set_block_number(60);
+		assert_hypothetically_earned(staker1, 1066, pool_id, reward_asset_id.clone());
+		assert_hypothetically_earned(staker2, 450, pool_id, reward_asset_id.clone());
+
+		// Finally, check events.
+		assert_eq!(
+			events(),
+			[
+				Event::PoolCreated {
+					creator: PermissionedAccountId::get(),
+					pool_id,
+					staked_asset_id,
+					reward_asset_id,
+					reward_rate_per_block: 100,
+					expiry_block: 25,
+					admin,
+				},
+				Event::Staked { staker: staker1, pool_id, amount: 100 },
+				Event::Staked { staker: staker2, pool_id, amount: 100 },
+				Event::Staked { staker: staker1, pool_id, amount: 100 },
+				Event::Unstaked { caller: staker1, staker: staker1, pool_id, amount: 100 },
+				Event::Unstaked { caller: staker1, staker: staker1, pool_id, amount: 100 },
+				Event::PoolExpiryBlockModified { pool_id, new_expiry_block: 60 },
+				Event::PoolRewardRateModified { pool_id, new_reward_rate_per_block: 150 },
+				Event::RewardsHarvested { caller: staker2, staker: staker2, pool_id, amount: 1433 }
+			]
+		);
+	});
+}
diff --git a/substrate/frame/asset-rewards/src/weights.rs b/substrate/frame/asset-rewards/src/weights.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c9e2d0fd251aa0f09b9d9da54b2307327a752380
--- /dev/null
+++ b/substrate/frame/asset-rewards/src/weights.rs
@@ -0,0 +1,368 @@
+// This file is part of Substrate.
+
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Autogenerated weights for `pallet_asset_rewards`
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
+//! DATE: 2025-01-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! WORST CASE MAP SIZE: `1000000`
+//! HOSTNAME: `runner-ys-ssygq-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
+
+// Executed Command:
+// target/production/substrate-node
+// benchmark
+// pallet
+// --steps=50
+// --repeat=20
+// --extrinsic=*
+// --wasm-execution=compiled
+// --heap-pages=4096
+// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json
+// --pallet=pallet_asset_rewards
+// --chain=dev
+// --header=./substrate/HEADER-APACHE2
+// --output=./substrate/frame/asset-rewards/src/weights.rs
+// --template=./substrate/.maintain/frame-weight-template.hbs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(missing_docs)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use core::marker::PhantomData;
+
+/// Weight functions needed for `pallet_asset_rewards`.
+pub trait WeightInfo {
+	fn create_pool() -> Weight;
+	fn stake() -> Weight;
+	fn unstake() -> Weight;
+	fn harvest_rewards() -> Weight;
+	fn set_pool_reward_rate_per_block() -> Weight;
+	fn set_pool_admin() -> Weight;
+	fn set_pool_expiry_block() -> Weight;
+	fn deposit_reward_tokens() -> Weight;
+	fn cleanup_pool() -> Weight;
+}
+
+/// Weights for `pallet_asset_rewards` using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+	/// Storage: `Assets::Asset` (r:2 w:0)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::NextPoolId` (r:1 w:1)
+	/// Proof: `AssetRewards::NextPoolId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(373), added: 2848, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolCost` (r:0 w:1)
+	/// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::Pools` (r:0 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	fn create_pool() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `495`
+		//  Estimated: `6360`
+		// Minimum execution time: 62_655_000 picoseconds.
+		Weight::from_parts(63_723_000, 6360)
+			.saturating_add(T::DbWeight::get().reads(5_u64))
+			.saturating_add(T::DbWeight::get().writes(5_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:1 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:1 w:0)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn stake() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `935`
+		//  Estimated: `3615`
+		// Minimum execution time: 54_463_000 picoseconds.
+		Weight::from_parts(55_974_000, 3615)
+			.saturating_add(T::DbWeight::get().reads(5_u64))
+			.saturating_add(T::DbWeight::get().writes(4_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:1 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:1 w:0)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn unstake() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `935`
+		//  Estimated: `3615`
+		// Minimum execution time: 55_749_000 picoseconds.
+		Weight::from_parts(57_652_000, 3615)
+			.saturating_add(T::DbWeight::get().reads(5_u64))
+			.saturating_add(T::DbWeight::get().writes(4_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:0)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	fn harvest_rewards() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1021`
+		//  Estimated: `6208`
+		// Minimum execution time: 69_372_000 picoseconds.
+		Weight::from_parts(70_278_000, 6208)
+			.saturating_add(T::DbWeight::get().reads(5_u64))
+			.saturating_add(T::DbWeight::get().writes(4_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	fn set_pool_reward_rate_per_block() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `347`
+		//  Estimated: `3615`
+		// Minimum execution time: 19_284_000 picoseconds.
+		Weight::from_parts(19_791_000, 3615)
+			.saturating_add(T::DbWeight::get().reads(1_u64))
+			.saturating_add(T::DbWeight::get().writes(1_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	fn set_pool_admin() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `347`
+		//  Estimated: `3615`
+		// Minimum execution time: 17_388_000 picoseconds.
+		Weight::from_parts(18_390_000, 3615)
+			.saturating_add(T::DbWeight::get().reads(1_u64))
+			.saturating_add(T::DbWeight::get().writes(1_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	fn set_pool_expiry_block() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `347`
+		//  Estimated: `3615`
+		// Minimum execution time: 19_780_000 picoseconds.
+		Weight::from_parts(20_676_000, 3615)
+			.saturating_add(T::DbWeight::get().reads(1_u64))
+			.saturating_add(T::DbWeight::get().writes(1_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:0)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	fn deposit_reward_tokens() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `840`
+		//  Estimated: `6208`
+		// Minimum execution time: 57_746_000 picoseconds.
+		Weight::from_parts(59_669_000, 6208)
+			.saturating_add(T::DbWeight::get().reads(5_u64))
+			.saturating_add(T::DbWeight::get().writes(4_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:0)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:2 w:2)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolCost` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(373), added: 2848, mode: `MaxEncodedLen`)
+	fn cleanup_pool() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1236`
+		//  Estimated: `6208`
+		// Minimum execution time: 110_443_000 picoseconds.
+		Weight::from_parts(113_149_000, 6208)
+			.saturating_add(T::DbWeight::get().reads(9_u64))
+			.saturating_add(T::DbWeight::get().writes(8_u64))
+	}
+}
+
+// For backwards compatibility and tests.
+impl WeightInfo for () {
+	/// Storage: `Assets::Asset` (r:2 w:0)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::NextPoolId` (r:1 w:1)
+	/// Proof: `AssetRewards::NextPoolId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(373), added: 2848, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolCost` (r:0 w:1)
+	/// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::Pools` (r:0 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	fn create_pool() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `495`
+		//  Estimated: `6360`
+		// Minimum execution time: 62_655_000 picoseconds.
+		Weight::from_parts(63_723_000, 6360)
+			.saturating_add(RocksDbWeight::get().reads(5_u64))
+			.saturating_add(RocksDbWeight::get().writes(5_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:1 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:1 w:0)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn stake() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `935`
+		//  Estimated: `3615`
+		// Minimum execution time: 54_463_000 picoseconds.
+		Weight::from_parts(55_974_000, 3615)
+			.saturating_add(RocksDbWeight::get().reads(5_u64))
+			.saturating_add(RocksDbWeight::get().writes(4_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::Freezes` (r:1 w:1)
+	/// Proof: `AssetsFreezer::Freezes` (`max_values`: None, `max_size`: Some(105), added: 2580, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:1 w:0)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `AssetsFreezer::FrozenBalances` (r:1 w:1)
+	/// Proof: `AssetsFreezer::FrozenBalances` (`max_values`: None, `max_size`: Some(84), added: 2559, mode: `MaxEncodedLen`)
+	fn unstake() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `935`
+		//  Estimated: `3615`
+		// Minimum execution time: 55_749_000 picoseconds.
+		Weight::from_parts(57_652_000, 3615)
+			.saturating_add(RocksDbWeight::get().reads(5_u64))
+			.saturating_add(RocksDbWeight::get().writes(4_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:0)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	fn harvest_rewards() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1021`
+		//  Estimated: `6208`
+		// Minimum execution time: 69_372_000 picoseconds.
+		Weight::from_parts(70_278_000, 6208)
+			.saturating_add(RocksDbWeight::get().reads(5_u64))
+			.saturating_add(RocksDbWeight::get().writes(4_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	fn set_pool_reward_rate_per_block() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `347`
+		//  Estimated: `3615`
+		// Minimum execution time: 19_284_000 picoseconds.
+		Weight::from_parts(19_791_000, 3615)
+			.saturating_add(RocksDbWeight::get().reads(1_u64))
+			.saturating_add(RocksDbWeight::get().writes(1_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	fn set_pool_admin() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `347`
+		//  Estimated: `3615`
+		// Minimum execution time: 17_388_000 picoseconds.
+		Weight::from_parts(18_390_000, 3615)
+			.saturating_add(RocksDbWeight::get().reads(1_u64))
+			.saturating_add(RocksDbWeight::get().writes(1_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	fn set_pool_expiry_block() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `347`
+		//  Estimated: `3615`
+		// Minimum execution time: 19_780_000 picoseconds.
+		Weight::from_parts(20_676_000, 3615)
+			.saturating_add(RocksDbWeight::get().reads(1_u64))
+			.saturating_add(RocksDbWeight::get().writes(1_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:0)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	fn deposit_reward_tokens() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `840`
+		//  Estimated: `6208`
+		// Minimum execution time: 57_746_000 picoseconds.
+		Weight::from_parts(59_669_000, 6208)
+			.saturating_add(RocksDbWeight::get().reads(5_u64))
+			.saturating_add(RocksDbWeight::get().writes(4_u64))
+	}
+	/// Storage: `AssetRewards::Pools` (r:1 w:1)
+	/// Proof: `AssetRewards::Pools` (`max_values`: None, `max_size`: Some(150), added: 2625, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolStakers` (r:1 w:0)
+	/// Proof: `AssetRewards::PoolStakers` (`max_values`: None, `max_size`: Some(116), added: 2591, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Asset` (r:1 w:1)
+	/// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`)
+	/// Storage: `Assets::Account` (r:2 w:2)
+	/// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:2 w:2)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `AssetRewards::PoolCost` (r:1 w:1)
+	/// Proof: `AssetRewards::PoolCost` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(373), added: 2848, mode: `MaxEncodedLen`)
+	fn cleanup_pool() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1236`
+		//  Estimated: `6208`
+		// Minimum execution time: 110_443_000 picoseconds.
+		Weight::from_parts(113_149_000, 6208)
+			.saturating_add(RocksDbWeight::get().reads(9_u64))
+			.saturating_add(RocksDbWeight::get().writes(8_u64))
+	}
+}
diff --git a/substrate/frame/atomic-swap/Cargo.toml b/substrate/frame/atomic-swap/Cargo.toml
index 785bfee71b683653fba9f3f9c8be1e531927e01f..05a38ded91c516ac995d90cc54d92d688619d2f4 100644
--- a/substrate/frame/atomic-swap/Cargo.toml
+++ b/substrate/frame/atomic-swap/Cargo.toml
@@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { workspace = true }
-frame = { workspace = true, features = ["experimental", "runtime"] }
+frame = { workspace = true, features = ["runtime"] }
 scale-info = { features = ["derive"], workspace = true }
 
 [dev-dependencies]
diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs
index 23857470adc4a5c751ef276a4c2d72b34f078490..8d00509e800b6368696bdea8ca686ef51f6bc71a 100644
--- a/substrate/frame/babe/src/mock.rs
+++ b/substrate/frame/babe/src/mock.rs
@@ -157,6 +157,7 @@ impl onchain::Config for OnChainSeqPhragmen {
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Test {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type SessionsPerEra = SessionsPerEra;
 	type BondingDuration = BondingDuration;
diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs
index 7ae41c609180e4741c668372c2c2392bbb140808..38e0cc4cfc26641063af61a33b93cd8e996a2a84 100644
--- a/substrate/frame/beefy/src/mock.rs
+++ b/substrate/frame/beefy/src/mock.rs
@@ -235,6 +235,7 @@ impl onchain::Config for OnChainSeqPhragmen {
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Test {
 	type RuntimeEvent = RuntimeEvent;
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
 	type SessionInterface = Self;
diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs
index 1d181eb29cab7321d94b083149a8e58a17ecd924..0dacfe9c55792f53e07fe708323ed6e0f8d5c784 100644
--- a/substrate/frame/delegated-staking/src/lib.rs
+++ b/substrate/frame/delegated-staking/src/lib.rs
@@ -520,7 +520,7 @@ impl<T: Config> Pallet<T> {
 		let stake = T::CoreStaking::stake(who)?;
 
 		// release funds from core staking.
-		T::CoreStaking::migrate_to_virtual_staker(who);
+		T::CoreStaking::migrate_to_virtual_staker(who)?;
 
 		// transfer just released staked amount plus any free amount.
 		let amount_to_transfer =
diff --git a/substrate/frame/delegated-staking/src/mock.rs b/substrate/frame/delegated-staking/src/mock.rs
index 811d5739f4e98f24c30c7514455dac049fbbc421..875279864f7ab3794777b1f8bf1756a4c15b703b 100644
--- a/substrate/frame/delegated-staking/src/mock.rs
+++ b/substrate/frame/delegated-staking/src/mock.rs
@@ -102,6 +102,7 @@ impl onchain::Config for OnChainSeqPhragmen {
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Runtime {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type UnixTime = pallet_timestamp::Pallet<Self>;
 	type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs
index b7b82a43771eb388ee1a5e0e11145e614f56930f..c764e2741a2a4a0149853f2279bd8b556ba4b20b 100644
--- a/substrate/frame/delegated-staking/src/tests.rs
+++ b/substrate/frame/delegated-staking/src/tests.rs
@@ -671,12 +671,14 @@ mod staking_integration {
 			));
 			assert_ok!(Staking::nominate(RuntimeOrigin::signed(agent), vec![GENESIS_VALIDATOR],));
 			let init_stake = Staking::stake(&agent).unwrap();
+			// no extra provider added.
+			assert_eq!(System::providers(&agent), 1);
 
 			// scenario: 200 is a pool account, and the stake comes from its 4 delegators (300..304)
 			// in equal parts. lets try to migrate this nominator into delegate based stake.
 
 			// all balance currently is in 200
-			assert_eq!(pallet_staking::asset::stakeable_balance::<T>(&agent), agent_amount);
+			assert_eq!(pallet_staking::asset::total_balance::<T>(&agent), agent_amount);
 
 			// to migrate, nominator needs to set an account as a proxy delegator where staked funds
 			// will be moved and delegated back to this old nominator account. This should be funded
@@ -685,8 +687,9 @@ mod staking_integration {
 				DelegatedStaking::generate_proxy_delegator(Agent::from(agent)).get();
 
 			assert_ok!(DelegatedStaking::migrate_to_agent(RawOrigin::Signed(agent).into(), 201));
-			// after migration, funds are moved to proxy delegator, still a provider exists.
-			assert_eq!(System::providers(&agent), 1);
+			// after migration, no provider left since free balance is 0 and staking pallet released
+			// all funds.
+			assert_eq!(System::providers(&agent), 0);
 			assert_eq!(Balances::free_balance(agent), 0);
 			// proxy delegator has one provider as well with no free balance.
 			assert_eq!(System::providers(&proxy_delegator), 1);
@@ -798,8 +801,6 @@ mod staking_integration {
 				RawOrigin::Signed(agent).into(),
 				reward_acc
 			));
-			// becoming an agent adds another provider.
-			assert_eq!(System::providers(&agent), 2);
 
 			// delegate to this account
 			fund(&delegator, 1000);
diff --git a/substrate/frame/delegated-staking/src/types.rs b/substrate/frame/delegated-staking/src/types.rs
index a78aa3f559060080344b84002d099ca8cb18caaa..14f49466f0e2867fec4da4a360a53fe816d72d22 100644
--- a/substrate/frame/delegated-staking/src/types.rs
+++ b/substrate/frame/delegated-staking/src/types.rs
@@ -131,10 +131,6 @@ impl<T: Config> AgentLedger<T> {
 	///
 	/// Increments provider count if this is a new agent.
 	pub(crate) fn update(self, key: &T::AccountId) {
-		if !<Agents<T>>::contains_key(key) {
-			// This is a new agent. Provide for this account.
-			frame_system::Pallet::<T>::inc_providers(key);
-		}
 		<Agents<T>>::insert(key, self)
 	}
 
@@ -142,8 +138,6 @@ impl<T: Config> AgentLedger<T> {
 	pub(crate) fn remove(key: &T::AccountId) {
 		debug_assert!(<Agents<T>>::contains_key(key), "Agent should exist in storage");
 		<Agents<T>>::remove(key);
-		// Remove provider reference.
-		let _ = frame_system::Pallet::<T>::dec_providers(key).defensive();
 	}
 
 	/// Effective total balance of the `Agent`.
diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml b/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml
index 7a48ae868a5a2e63d6b0467753cf5ca5f8b56f2c..f11f9c04dbf4a6b5da7a04ebb17a1af89826df36 100644
--- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml
+++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/Cargo.toml
@@ -34,6 +34,7 @@ frame-system = { workspace = true, default-features = true }
 
 pallet-bags-list = { workspace = true, default-features = true }
 pallet-balances = { workspace = true, default-features = true }
+pallet-delegated-staking = { workspace = true, default-features = true }
 pallet-election-provider-multi-phase = { workspace = true, default-features = true }
 pallet-nomination-pools = { workspace = true, default-features = true }
 pallet-session = { workspace = true, default-features = true }
@@ -47,6 +48,7 @@ try-runtime = [
 	"frame-system/try-runtime",
 	"pallet-bags-list/try-runtime",
 	"pallet-balances/try-runtime",
+	"pallet-delegated-staking/try-runtime",
 	"pallet-election-provider-multi-phase/try-runtime",
 	"pallet-nomination-pools/try-runtime",
 	"pallet-session/try-runtime",
diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs
index 26a6345e145ff1050581e32534d8b6fa09ba450d..b1029e89fe85f65650fb5406314241c220cd2b28 100644
--- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs
+++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs
@@ -327,8 +327,8 @@ fn automatic_unbonding_pools() {
 		assert_eq!(<Runtime as pallet_nomination_pools::Config>::MaxUnbonding::get(), 1);
 
 		// init state of pool members.
-		let init_stakeable_balance_2 = pallet_staking::asset::stakeable_balance::<Runtime>(&2);
-		let init_stakeable_balance_3 = pallet_staking::asset::stakeable_balance::<Runtime>(&3);
+		let init_free_balance_2 = Balances::free_balance(2);
+		let init_free_balance_3 = Balances::free_balance(3);
 
 		let pool_bonded_account = Pools::generate_bonded_account(1);
 
@@ -378,7 +378,7 @@ fn automatic_unbonding_pools() {
 		System::reset_events();
 
 		let staked_before_withdraw_pool = staked_amount_for(pool_bonded_account);
-		assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 26);
+		assert_eq!(delegated_balance_for(pool_bonded_account), 5 + 10 + 10);
 
 		// now unbonding 3 will work, although the pool's ledger still has the unlocking chunks
 		// filled up.
@@ -390,13 +390,13 @@ fn automatic_unbonding_pools() {
 			[
 				// auto-withdraw happened as expected to release 2's unbonding funds, but the funds
 				// were not transferred to 2 and stay in the pool's transferrable balance instead.
-				pallet_staking::Event::Withdrawn { stash: 7939698191839293293, amount: 10 },
-				pallet_staking::Event::Unbonded { stash: 7939698191839293293, amount: 10 }
+				pallet_staking::Event::Withdrawn { stash: pool_bonded_account, amount: 10 },
+				pallet_staking::Event::Unbonded { stash: pool_bonded_account, amount: 10 }
 			]
 		);
 
 		// balance of the pool remains the same, it hasn't withdraw explicitly from the pool yet.
-		assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 26);
+		assert_eq!(delegated_balance_for(pool_bonded_account), 25);
 		// but the locked amount in the pool's account decreases due to the auto-withdraw:
 		assert_eq!(staked_before_withdraw_pool - 10, staked_amount_for(pool_bonded_account));
 
@@ -405,12 +405,12 @@ fn automatic_unbonding_pools() {
 
 		// however, note that the withdrawing from the pool still works for 2, the funds are taken
 		// from the pool's non staked balance.
-		assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 26);
-		assert_eq!(pallet_staking::asset::staked::<Runtime>(&pool_bonded_account), 15);
+		assert_eq!(delegated_balance_for(pool_bonded_account), 25);
+		assert_eq!(staked_amount_for(pool_bonded_account), 15);
 		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(2), 2, 10));
-		assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 16);
+		assert_eq!(delegated_balance_for(pool_bonded_account), 15);
 
-		assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&2), 20);
+		assert_eq!(Balances::free_balance(2), 20);
 		assert_eq!(TotalValueLocked::<Runtime>::get(), 15);
 
 		// 3 cannot withdraw yet.
@@ -429,15 +429,9 @@ fn automatic_unbonding_pools() {
 		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(3), 3, 10));
 
 		// final conditions are the expected.
-		assert_eq!(pallet_staking::asset::stakeable_balance::<Runtime>(&pool_bonded_account), 6); // 5 init bonded + ED
-		assert_eq!(
-			pallet_staking::asset::stakeable_balance::<Runtime>(&2),
-			init_stakeable_balance_2
-		);
-		assert_eq!(
-			pallet_staking::asset::stakeable_balance::<Runtime>(&3),
-			init_stakeable_balance_3
-		);
+		assert_eq!(delegated_balance_for(pool_bonded_account), 5); // 5 init bonded
+		assert_eq!(Balances::free_balance(2), init_free_balance_2);
+		assert_eq!(Balances::free_balance(3), init_free_balance_3);
 
 		assert_eq!(TotalValueLocked::<Runtime>::get(), init_tvl);
 	});
diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs
index eaab848c1694485c47e4274ea83f223be53cd183..bcb25f8287b35e07dd7a690362df7d0aca75677b 100644
--- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs
+++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs
@@ -21,6 +21,7 @@ use frame_support::{
 	assert_ok, parameter_types, traits,
 	traits::{Hooks, UnfilteredDispatchable, VariantCountOf},
 	weights::constants,
+	PalletId,
 };
 use frame_system::EnsureRoot;
 use sp_core::{ConstU32, Get};
@@ -36,7 +37,7 @@ use sp_runtime::{
 };
 use sp_staking::{
 	offence::{OffenceDetails, OnOffenceHandler},
-	EraIndex, SessionIndex,
+	Agent, DelegationInterface, EraIndex, SessionIndex, StakingInterface,
 };
 use std::collections::BTreeMap;
 
@@ -68,6 +69,7 @@ frame_support::construct_runtime!(
 		System: frame_system,
 		ElectionProviderMultiPhase: pallet_election_provider_multi_phase,
 		Staking: pallet_staking,
+		DelegatedStaking: pallet_delegated_staking,
 		Pools: pallet_nomination_pools,
 		Balances: pallet_balances,
 		BagsList: pallet_bags_list,
@@ -77,7 +79,7 @@ frame_support::construct_runtime!(
 	}
 );
 
-pub(crate) type AccountId = u64;
+pub(crate) type AccountId = u128;
 pub(crate) type AccountIndex = u32;
 pub(crate) type BlockNumber = u32;
 pub(crate) type Balance = u64;
@@ -87,8 +89,10 @@ pub(crate) type Moment = u32;
 
 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
 impl frame_system::Config for Runtime {
+	type AccountId = AccountId;
 	type Block = Block;
 	type AccountData = pallet_balances::AccountData<Balance>;
+	type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
 }
 
 const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
@@ -265,7 +269,8 @@ impl pallet_nomination_pools::Config for Runtime {
 	type RewardCounter = sp_runtime::FixedU128;
 	type BalanceToU256 = BalanceToU256;
 	type U256ToBalance = U256ToBalance;
-	type StakeAdapter = pallet_nomination_pools::adapter::TransferStake<Self, Staking>;
+	type StakeAdapter =
+		pallet_nomination_pools::adapter::DelegateStake<Self, Staking, DelegatedStaking>;
 	type PostUnbondingPoolsWindow = ConstU32<2>;
 	type PalletId = PoolsPalletId;
 	type MaxMetadataLen = ConstU32<256>;
@@ -274,6 +279,21 @@ impl pallet_nomination_pools::Config for Runtime {
 	type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
 }
 
+parameter_types! {
+	pub const DelegatedStakingPalletId: PalletId = PalletId(*b"py/dlstk");
+	pub const SlashRewardFraction: Perbill = Perbill::from_percent(1);
+}
+
+impl pallet_delegated_staking::Config for Runtime {
+	type RuntimeEvent = RuntimeEvent;
+	type PalletId = DelegatedStakingPalletId;
+	type Currency = Balances;
+	type OnSlash = ();
+	type SlashRewardFraction = SlashRewardFraction;
+	type RuntimeHoldReason = RuntimeHoldReason;
+	type CoreStaking = Staking;
+}
+
 parameter_types! {
 	pub static MaxUnlockingChunks: u32 = 32;
 }
@@ -285,6 +305,7 @@ pub(crate) const SLASHING_DISABLING_FACTOR: usize = 3;
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Runtime {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = Balance;
 	type UnixTime = Timestamp;
@@ -302,7 +323,7 @@ impl pallet_staking::Config for Runtime {
 	type NominationsQuota = pallet_staking::FixedNominationsQuota<MAX_QUOTA_NOMINATIONS>;
 	type TargetList = pallet_staking::UseValidatorsMap<Self>;
 	type MaxUnlockingChunks = MaxUnlockingChunks;
-	type EventListeners = Pools;
+	type EventListeners = (Pools, DelegatedStaking);
 	type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
 	type DisablingStrategy =
 		pallet_staking::UpToLimitWithReEnablingDisablingStrategy<SLASHING_DISABLING_FACTOR>;
@@ -502,7 +523,7 @@ impl Default for BalancesExtBuilder {
 			(100, 100),
 			(200, 100),
 			// stashes
-			(11, 1000),
+			(11, 1100),
 			(21, 2000),
 			(31, 3000),
 			(41, 4000),
@@ -581,7 +602,7 @@ impl ExtBuilder {
 			// set the keys for the first session.
 			keys: stakers
 				.into_iter()
-				.map(|(id, ..)| (id, id, SessionKeys { other: (id as u64).into() }))
+				.map(|(id, ..)| (id, id, SessionKeys { other: (id as AccountId as u64).into() }))
 				.collect(),
 			..Default::default()
 		}
@@ -926,7 +947,11 @@ pub(crate) fn set_minimum_election_score(
 }
 
 pub(crate) fn staked_amount_for(account_id: AccountId) -> Balance {
-	pallet_staking::asset::staked::<Runtime>(&account_id)
+	Staking::total_stake(&account_id).expect("account must be staker")
+}
+
+pub(crate) fn delegated_balance_for(account_id: AccountId) -> Balance {
+	DelegatedStaking::agent_balance(Agent::from(account_id)).unwrap_or_default()
 }
 
 pub(crate) fn staking_events() -> Vec<pallet_staking::Event<Runtime>> {
diff --git a/substrate/frame/examples/frame-crate/Cargo.toml b/substrate/frame/examples/frame-crate/Cargo.toml
index f174c6b9054b52d1f88d5d9853ffecd2df6f8014..46db1afc34643cef8b90a639e39aa2070a16919e 100644
--- a/substrate/frame/examples/frame-crate/Cargo.toml
+++ b/substrate/frame/examples/frame-crate/Cargo.toml
@@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 codec = { workspace = true }
 scale-info = { features = ["derive"], workspace = true }
 
-frame = { features = ["experimental", "runtime"], workspace = true }
+frame = { features = ["runtime"], workspace = true }
 
 
 [features]
diff --git a/substrate/frame/examples/multi-block-migrations/src/mock.rs b/substrate/frame/examples/multi-block-migrations/src/mock.rs
index b2a946e1c505c2bd71e6929ff031d0b365ad702e..64940db080c42872eaae9c24778185b582acd60b 100644
--- a/substrate/frame/examples/multi-block-migrations/src/mock.rs
+++ b/substrate/frame/examples/multi-block-migrations/src/mock.rs
@@ -25,10 +25,7 @@
 //! using the [`Migrations`] type.
 
 use frame_support::{
-	construct_runtime, derive_impl,
-	migrations::MultiStepMigrator,
-	pallet_prelude::Weight,
-	traits::{OnFinalize, OnInitialize},
+	construct_runtime, derive_impl, migrations::MultiStepMigrator, pallet_prelude::Weight,
 };
 
 type Block = frame_system::mocking::MockBlock<Runtime>;
@@ -81,13 +78,11 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 
 #[allow(dead_code)]
 pub fn run_to_block(n: u64) {
-	assert!(System::block_number() < n);
-	while System::block_number() < n {
-		let b = System::block_number();
-		AllPalletsWithSystem::on_finalize(b);
-		// Done by Executive:
-		<Runtime as frame_system::Config>::MultiBlockMigrator::step();
-		System::set_block_number(b + 1);
-		AllPalletsWithSystem::on_initialize(b + 1);
-	}
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default().after_initialize(|_| {
+			// Done by Executive:
+			<Runtime as frame_system::Config>::MultiBlockMigrator::step();
+		}),
+	);
 }
diff --git a/substrate/frame/fast-unstake/src/mock.rs b/substrate/frame/fast-unstake/src/mock.rs
index 757052e230a187ed9ce712a2b30bc5a5f2e9febb..cf4f5f49240e98eaf42a944687b3fd4df81fe431 100644
--- a/substrate/frame/fast-unstake/src/mock.rs
+++ b/substrate/frame/fast-unstake/src/mock.rs
@@ -105,6 +105,7 @@ impl frame_election_provider_support::ElectionProvider for MockElection {
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Runtime {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type UnixTime = pallet_timestamp::Pallet<Self>;
 	type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
@@ -223,8 +224,9 @@ impl ExtBuilder {
 				.clone()
 				.into_iter()
 				.map(|(stash, _, balance)| (stash, balance * 2))
-				.chain(validators_range.clone().map(|x| (x, 7 + 100)))
-				.chain(nominators_range.clone().map(|x| (x, 7 + 100)))
+				// give stakers enough balance for stake, ed and fast unstake deposit.
+				.chain(validators_range.clone().map(|x| (x, 7 + 1 + 100)))
+				.chain(nominators_range.clone().map(|x| (x, 7 + 1 + 100)))
 				.collect::<Vec<_>>(),
 		}
 		.assimilate_storage(&mut storage);
@@ -266,22 +268,19 @@ impl ExtBuilder {
 }
 
 pub(crate) fn run_to_block(n: u64, on_idle: bool) {
-	let current_block = System::block_number();
-	assert!(n > current_block);
-	while System::block_number() < n {
-		Balances::on_finalize(System::block_number());
-		Staking::on_finalize(System::block_number());
-		FastUnstake::on_finalize(System::block_number());
-
-		System::set_block_number(System::block_number() + 1);
-
-		Balances::on_initialize(System::block_number());
-		Staking::on_initialize(System::block_number());
-		FastUnstake::on_initialize(System::block_number());
-		if on_idle {
-			FastUnstake::on_idle(System::block_number(), BlockWeights::get().max_block);
-		}
-	}
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default()
+			.before_finalize(|_| {
+				// Satisfy the timestamp pallet.
+				Timestamp::set_timestamp(0);
+			})
+			.after_initialize(|bn| {
+				if on_idle {
+					FastUnstake::on_idle(bn, BlockWeights::get().max_block);
+				}
+			}),
+	);
 }
 
 pub(crate) fn next_block(on_idle: bool) {
diff --git a/substrate/frame/fast-unstake/src/tests.rs b/substrate/frame/fast-unstake/src/tests.rs
index 7c11f381ca102927e10a3b8241d87dfa28a07d5b..0fddb88e02b7bef231d5f9bb3961318c8656dc24 100644
--- a/substrate/frame/fast-unstake/src/tests.rs
+++ b/substrate/frame/fast-unstake/src/tests.rs
@@ -19,7 +19,15 @@
 
 use super::*;
 use crate::{mock::*, types::*, Event};
-use frame_support::{pallet_prelude::*, testing_prelude::*, traits::Currency};
+use frame_support::{
+	pallet_prelude::*,
+	testing_prelude::*,
+	traits::{
+		fungible::Inspect,
+		tokens::{Fortitude::Polite, Preservation::Expendable},
+		Currency,
+	},
+};
 use pallet_staking::{CurrentEra, RewardDestination};
 
 use sp_runtime::traits::BadOrigin;
@@ -146,7 +154,7 @@ fn deregister_works() {
 
 		// Controller then changes mind and deregisters.
 		assert_ok!(FastUnstake::deregister(RuntimeOrigin::signed(1)));
-		assert_eq!(<T as Config>::Currency::reserved_balance(&1) - pre_reserved, 0);
+		assert_eq!(<T as Config>::Currency::reserved_balance(&1), pre_reserved);
 
 		// Ensure stash no longer exists in the queue.
 		assert_eq!(Queue::<T>::get(1), None);
@@ -297,7 +305,7 @@ mod on_idle {
 			);
 			assert_eq!(Queue::<T>::count(), 3);
 
-			assert_eq!(<T as Config>::Currency::reserved_balance(&1) - pre_reserved, 0);
+			assert_eq!(<T as Config>::Currency::reserved_balance(&1), pre_reserved);
 
 			assert_eq!(
 				fast_unstake_events_since_last_call(),
@@ -793,6 +801,8 @@ mod on_idle {
 				RuntimeOrigin::signed(VALIDATOR_PREFIX),
 				vec![VALIDATOR_PREFIX]
 			));
+
+			assert_eq!(Balances::reducible_balance(&VALIDATOR_PREFIX, Expendable, Polite), 7);
 			assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(VALIDATOR_PREFIX)));
 
 			// but they indeed are exposed!
diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs
index 87369c23948ca0993fc986f555782c48397549b2..0a85d9ffd2b08c4668b874685d9547a8a20785ee 100644
--- a/substrate/frame/grandpa/src/mock.rs
+++ b/substrate/frame/grandpa/src/mock.rs
@@ -161,6 +161,7 @@ impl onchain::Config for OnChainSeqPhragmen {
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Test {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
 	type SessionsPerEra = SessionsPerEra;
diff --git a/substrate/frame/identity/src/tests.rs b/substrate/frame/identity/src/tests.rs
index 7bf5b2a727607e910bc7a2470c383aa236a18e35..01bc312723aa52f9d3bd6efcb9867d1777888f22 100644
--- a/substrate/frame/identity/src/tests.rs
+++ b/substrate/frame/identity/src/tests.rs
@@ -26,7 +26,7 @@ use crate::{
 use codec::{Decode, Encode};
 use frame_support::{
 	assert_err, assert_noop, assert_ok, derive_impl, parameter_types,
-	traits::{ConstU32, ConstU64, Get, OnFinalize, OnInitialize},
+	traits::{ConstU32, ConstU64, Get},
 	BoundedVec,
 };
 use frame_system::EnsureRoot;
@@ -114,18 +114,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 	ext
 }
 
-fn run_to_block(n: u64) {
-	while System::block_number() < n {
-		Identity::on_finalize(System::block_number());
-		Balances::on_finalize(System::block_number());
-		System::on_finalize(System::block_number());
-		System::set_block_number(System::block_number() + 1);
-		System::on_initialize(System::block_number());
-		Balances::on_initialize(System::block_number());
-		Identity::on_initialize(System::block_number());
-	}
-}
-
 fn account(id: u8) -> AccountIdOf<Test> {
 	[id; 32].into()
 }
@@ -1714,7 +1702,7 @@ fn unaccepted_usernames_through_grant_should_expire() {
 			Some((who.clone(), expiration, Provider::Allocation))
 		);
 
-		run_to_block(now + expiration - 1);
+		System::run_to_block::<AllPalletsWithSystem>(now + expiration - 1);
 
 		// Cannot be removed
 		assert_noop!(
@@ -1722,7 +1710,7 @@ fn unaccepted_usernames_through_grant_should_expire() {
 			Error::<Test>::NotExpired
 		);
 
-		run_to_block(now + expiration);
+		System::run_to_block::<AllPalletsWithSystem>(now + expiration);
 
 		// Anyone can remove
 		assert_ok!(Identity::remove_expired_approval(
@@ -1782,7 +1770,7 @@ fn unaccepted_usernames_through_deposit_should_expire() {
 			Some((who.clone(), expiration, Provider::AuthorityDeposit(username_deposit)))
 		);
 
-		run_to_block(now + expiration - 1);
+		System::run_to_block::<AllPalletsWithSystem>(now + expiration - 1);
 
 		// Cannot be removed
 		assert_noop!(
@@ -1790,7 +1778,7 @@ fn unaccepted_usernames_through_deposit_should_expire() {
 			Error::<Test>::NotExpired
 		);
 
-		run_to_block(now + expiration);
+		System::run_to_block::<AllPalletsWithSystem>(now + expiration);
 
 		// Anyone can remove
 		assert_eq!(
diff --git a/substrate/frame/lottery/src/mock.rs b/substrate/frame/lottery/src/mock.rs
index d2c442e2ac6e5acd0b096308ebed02ad9fe0f78e..b771ed0849f6929d9f9dba5bfb32e54fc400b76f 100644
--- a/substrate/frame/lottery/src/mock.rs
+++ b/substrate/frame/lottery/src/mock.rs
@@ -20,10 +20,7 @@
 use super::*;
 use crate as pallet_lottery;
 
-use frame_support::{
-	derive_impl, parameter_types,
-	traits::{ConstU32, OnFinalize, OnInitialize},
-};
+use frame_support::{derive_impl, parameter_types, traits::ConstU32};
 use frame_support_test::TestRandomness;
 use frame_system::EnsureRoot;
 use sp_runtime::{BuildStorage, Perbill};
@@ -83,16 +80,3 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 	.unwrap();
 	t.into()
 }
-
-/// Run until a particular block.
-pub fn run_to_block(n: u64) {
-	while System::block_number() < n {
-		if System::block_number() > 1 {
-			Lottery::on_finalize(System::block_number());
-			System::on_finalize(System::block_number());
-		}
-		System::set_block_number(System::block_number() + 1);
-		System::on_initialize(System::block_number());
-		Lottery::on_initialize(System::block_number());
-	}
-}
diff --git a/substrate/frame/lottery/src/tests.rs b/substrate/frame/lottery/src/tests.rs
index ae3a6c858f2426e2119f090a5c3e4f53b51a333b..119be5df49250989835a71081e280d31f8837c2b 100644
--- a/substrate/frame/lottery/src/tests.rs
+++ b/substrate/frame/lottery/src/tests.rs
@@ -17,12 +17,11 @@
 
 //! Tests for the module.
 
-use super::*;
-use frame_support::{assert_noop, assert_ok, assert_storage_noop};
-use mock::{
-	new_test_ext, run_to_block, Balances, BalancesCall, Lottery, RuntimeCall, RuntimeOrigin,
-	SystemCall, Test,
+use crate::{
+	mock::{Lottery, *},
+	*,
 };
+use frame_support::{assert_noop, assert_ok, assert_storage_noop};
 use sp_runtime::{traits::BadOrigin, TokenError};
 
 #[test]
@@ -74,13 +73,13 @@ fn basic_end_to_end_works() {
 		assert_eq!(TicketsCount::<Test>::get(), 4);
 
 		// Go to end
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 		assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(5), call.clone()));
 		// Ticket isn't bought
 		assert_eq!(TicketsCount::<Test>::get(), 4);
 
 		// Go to payout
-		run_to_block(25);
+		System::run_to_block::<AllPalletsWithSystem>(25);
 		// User 1 wins
 		assert_eq!(Balances::free_balance(&1), 70 + 40);
 		// Lottery is reset and restarted
@@ -115,11 +114,11 @@ fn stop_repeat_works() {
 		// Lottery still exists.
 		assert!(crate::Lottery::<Test>::get().is_some());
 		// End and pick a winner.
-		run_to_block(length + delay);
+		System::run_to_block::<AllPalletsWithSystem>(length + delay);
 
 		// Lottery stays dead and does not repeat.
 		assert!(crate::Lottery::<Test>::get().is_none());
-		run_to_block(length + delay + 1);
+		System::run_to_block::<AllPalletsWithSystem>(length + delay + 1);
 		assert!(crate::Lottery::<Test>::get().is_none());
 	});
 }
@@ -281,7 +280,7 @@ fn buy_ticket_works() {
 		assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 1, 20, 5, false));
 
 		// Go to start, buy ticket for transfer
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(1), call));
 		assert_eq!(TicketsCount::<Test>::get(), 1);
 
@@ -300,12 +299,12 @@ fn buy_ticket_works() {
 		assert_eq!(TicketsCount::<Test>::get(), 2);
 
 		// Go to end, can't buy tickets anymore
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 		assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), call.clone()));
 		assert_eq!(TicketsCount::<Test>::get(), 2);
 
 		// Go to payout, can't buy tickets when there is no lottery open
-		run_to_block(25);
+		System::run_to_block::<AllPalletsWithSystem>(25);
 		assert_ok!(Lottery::buy_ticket(RuntimeOrigin::signed(2), call.clone()));
 		assert_eq!(TicketsCount::<Test>::get(), 0);
 		assert_eq!(LotteryIndex::<Test>::get(), 1);
@@ -409,7 +408,7 @@ fn no_participants_works() {
 		assert_ok!(Lottery::start_lottery(RuntimeOrigin::root(), 10, length, delay, false));
 
 		// End the lottery, no one wins.
-		run_to_block(length + delay);
+		System::run_to_block::<AllPalletsWithSystem>(length + delay);
 	});
 }
 
diff --git a/substrate/frame/migrations/src/mock.rs b/substrate/frame/migrations/src/mock.rs
index 48ff175f8137860823589122060f158f53e4c4fd..ea86899cad83ce5dee779ee24aee088e60e17894 100644
--- a/substrate/frame/migrations/src/mock.rs
+++ b/substrate/frame/migrations/src/mock.rs
@@ -21,12 +21,7 @@
 
 use crate::{mock_helpers::*, Event, Historic};
 
-use frame_support::{
-	derive_impl,
-	migrations::*,
-	traits::{OnFinalize, OnInitialize},
-	weights::Weight,
-};
+use frame_support::{derive_impl, migrations::*, weights::Weight};
 use frame_system::EventRecord;
 use sp_core::H256;
 
@@ -113,18 +108,18 @@ pub fn test_closure<R>(f: impl FnOnce() -> R) -> R {
 	ext.execute_with(f)
 }
 
-pub fn run_to_block(n: u32) {
-	while System::block_number() < n as u64 {
-		log::debug!("Block {}", System::block_number());
-		System::set_block_number(System::block_number() + 1);
-		System::on_initialize(System::block_number());
-		Migrations::on_initialize(System::block_number());
-		// Executive calls this:
-		<Migrations as MultiStepMigrator>::step();
-
-		Migrations::on_finalize(System::block_number());
-		System::on_finalize(System::block_number());
-	}
+pub fn run_to_block(n: u64) {
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default()
+			.before_initialize(|bn| {
+				log::debug!("Block {bn}");
+			})
+			.after_initialize(|_| {
+				// Executive calls this:
+				<Migrations as MultiStepMigrator>::step();
+			}),
+	);
 }
 
 /// Returns the historic migrations, sorted by their identifier.
diff --git a/substrate/frame/mixnet/Cargo.toml b/substrate/frame/mixnet/Cargo.toml
index 0ae3b3938c608b6ad10a0b6a06f256b463c50564..33bf7146980d5e32a46093483494b73279f012e4 100644
--- a/substrate/frame/mixnet/Cargo.toml
+++ b/substrate/frame/mixnet/Cargo.toml
@@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { features = ["derive", "max-encoded-len"], workspace = true }
-frame = { workspace = true, features = ["experimental", "runtime"] }
+frame = { workspace = true, features = ["runtime"] }
 log = { workspace = true }
 scale-info = { features = ["derive"], workspace = true }
 serde = { features = ["derive"], workspace = true }
diff --git a/substrate/frame/multisig/Cargo.toml b/substrate/frame/multisig/Cargo.toml
index 0d175617c9c23dedd5f2e72e806f187dc1e5e119..e18e14f2626bfcae1ed509005e1108913e42568e 100644
--- a/substrate/frame/multisig/Cargo.toml
+++ b/substrate/frame/multisig/Cargo.toml
@@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { workspace = true }
-frame = { workspace = true, features = ["experimental", "runtime"] }
+frame = { workspace = true, features = ["runtime"] }
 scale-info = { features = ["derive"], workspace = true }
 
 # third party
diff --git a/substrate/frame/nis/src/mock.rs b/substrate/frame/nis/src/mock.rs
index 2b008f8ec2a41e3580e61d4bd1c28ba044368273..08e69ef0de0542ae755fbd762403147e802a1470 100644
--- a/substrate/frame/nis/src/mock.rs
+++ b/substrate/frame/nis/src/mock.rs
@@ -21,7 +21,7 @@ use crate::{self as pallet_nis, Perquintill, WithMaximumOf};
 
 use frame_support::{
 	derive_impl, ord_parameter_types, parameter_types,
-	traits::{fungible::Inspect, ConstU32, ConstU64, OnFinalize, OnInitialize, StorageMapShim},
+	traits::{fungible::Inspect, ConstU32, ConstU64, StorageMapShim},
 	weights::Weight,
 	PalletId,
 };
@@ -145,15 +145,3 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 pub fn new_test_ext_empty() -> sp_io::TestExternalities {
 	frame_system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
 }
-
-pub fn run_to_block(n: u64) {
-	while System::block_number() < n {
-		Nis::on_finalize(System::block_number());
-		Balances::on_finalize(System::block_number());
-		System::on_finalize(System::block_number());
-		System::set_block_number(System::block_number() + 1);
-		System::on_initialize(System::block_number());
-		Balances::on_initialize(System::block_number());
-		Nis::on_initialize(System::block_number());
-	}
-}
diff --git a/substrate/frame/nis/src/tests.rs b/substrate/frame/nis/src/tests.rs
index a17aaf421827f3aba79fa36065178922b0af6d93..10c39a0d48edb4f7c586d55d54e4ada7495aede4 100644
--- a/substrate/frame/nis/src/tests.rs
+++ b/substrate/frame/nis/src/tests.rs
@@ -55,7 +55,7 @@ fn enlarge(amount: Balance, max_bids: u32) {
 #[test]
 fn basic_setup_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 
 		for q in 0..3 {
 			assert!(Queues::<Test>::get(q).is_empty());
@@ -76,7 +76,7 @@ fn basic_setup_works() {
 #[test]
 fn place_bid_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_noop!(Nis::place_bid(signed(1), 1, 2), Error::<Test>::AmountTooSmall);
 		assert_noop!(Nis::place_bid(signed(1), 101, 2), FundsUnavailable);
 		assert_noop!(Nis::place_bid(signed(1), 10, 4), Error::<Test>::DurationTooBig);
@@ -90,7 +90,7 @@ fn place_bid_works() {
 #[test]
 fn place_bid_queuing_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 20, 2));
 		assert_ok!(Nis::place_bid(signed(1), 10, 2));
 		assert_ok!(Nis::place_bid(signed(1), 5, 2));
@@ -116,7 +116,7 @@ fn place_bid_queuing_works() {
 #[test]
 fn place_bid_fails_when_queue_full() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 10, 2));
 		assert_ok!(Nis::place_bid(signed(2), 10, 2));
 		assert_ok!(Nis::place_bid(signed(3), 10, 2));
@@ -128,7 +128,7 @@ fn place_bid_fails_when_queue_full() {
 #[test]
 fn multiple_place_bids_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 10, 1));
 		assert_ok!(Nis::place_bid(signed(1), 10, 2));
 		assert_ok!(Nis::place_bid(signed(1), 10, 2));
@@ -154,7 +154,7 @@ fn multiple_place_bids_works() {
 #[test]
 fn retract_single_item_queue_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 10, 1));
 		assert_ok!(Nis::place_bid(signed(1), 10, 2));
 		assert_ok!(Nis::retract_bid(signed(1), 10, 1));
@@ -169,7 +169,7 @@ fn retract_single_item_queue_works() {
 #[test]
 fn retract_with_other_and_duplicate_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 10, 1));
 		assert_ok!(Nis::place_bid(signed(1), 10, 2));
 		assert_ok!(Nis::place_bid(signed(1), 10, 2));
@@ -190,7 +190,7 @@ fn retract_with_other_and_duplicate_works() {
 #[test]
 fn retract_non_existent_item_fails() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_noop!(Nis::retract_bid(signed(1), 10, 1), Error::<Test>::UnknownBid);
 		assert_ok!(Nis::place_bid(signed(1), 10, 1));
 		assert_noop!(Nis::retract_bid(signed(1), 20, 1), Error::<Test>::UnknownBid);
@@ -202,7 +202,7 @@ fn retract_non_existent_item_fails() {
 #[test]
 fn basic_enlarge_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		assert_ok!(Nis::place_bid(signed(2), 40, 2));
 		enlarge(40, 2);
@@ -240,7 +240,7 @@ fn basic_enlarge_works() {
 #[test]
 fn enlarge_respects_bids_limit() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		assert_ok!(Nis::place_bid(signed(2), 40, 2));
 		assert_ok!(Nis::place_bid(signed(3), 40, 2));
@@ -285,7 +285,7 @@ fn enlarge_respects_bids_limit() {
 #[test]
 fn enlarge_respects_amount_limit_and_will_split() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 80, 1));
 		enlarge(40, 2);
 
@@ -317,7 +317,7 @@ fn enlarge_respects_amount_limit_and_will_split() {
 #[test]
 fn basic_thaw_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		assert_eq!(Nis::issuance().effective, 400);
 		assert_eq!(Balances::free_balance(1), 60);
@@ -330,9 +330,9 @@ fn basic_thaw_works() {
 		assert_eq!(Balances::reserved_balance(1), 40);
 		assert_eq!(holdings(), 40);
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::<Test>::NotExpired);
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_noop!(Nis::thaw_private(signed(1), 1, None), Error::<Test>::UnknownReceipt);
 		assert_noop!(Nis::thaw_private(signed(2), 0, None), Error::<Test>::NotOwner);
 
@@ -359,12 +359,12 @@ fn basic_thaw_works() {
 #[test]
 fn partial_thaw_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 80, 1));
 		enlarge(80, 1);
 		assert_eq!(holdings(), 80);
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		let prop = Perquintill::from_rational(4_100_000, 21_000_000u64);
 		assert_noop!(Nis::thaw_private(signed(1), 0, Some(prop)), Error::<Test>::MakesDust);
 		let prop = Perquintill::from_rational(1_050_000, 21_000_000u64);
@@ -402,10 +402,10 @@ fn partial_thaw_works() {
 #[test]
 fn thaw_respects_transfers() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		enlarge(40, 1);
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 
 		assert_eq!(Nis::owner(&0), Some(1));
 		assert_eq!(Balances::reserved_balance(&1), 40);
@@ -428,10 +428,10 @@ fn thaw_respects_transfers() {
 #[test]
 fn communify_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		enlarge(40, 1);
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 
 		assert_eq!(Nis::owner(&0), Some(1));
 		assert_eq!(Balances::reserved_balance(&1), 40);
@@ -479,10 +479,10 @@ fn communify_works() {
 #[test]
 fn privatize_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		enlarge(40, 1);
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_noop!(Nis::privatize(signed(2), 0), Error::<Test>::AlreadyPrivate);
 		assert_ok!(Nis::communify(signed(1), 0));
 
@@ -503,11 +503,11 @@ fn privatize_works() {
 #[test]
 fn privatize_and_thaw_with_another_receipt_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		assert_ok!(Nis::place_bid(signed(2), 40, 1));
 		enlarge(80, 2);
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 
 		assert_ok!(Nis::communify(signed(1), 0));
 		assert_ok!(Nis::communify(signed(2), 1));
@@ -535,7 +535,7 @@ fn privatize_and_thaw_with_another_receipt_works() {
 #[test]
 fn communal_thaw_when_issuance_higher_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1));
 		assert_ok!(Nis::place_bid(signed(1), 100, 1));
 		enlarge(100, 1);
@@ -552,7 +552,7 @@ fn communal_thaw_when_issuance_higher_works() {
 		assert_ok!(Balances::mint_into(&3, 50));
 		assert_ok!(Balances::mint_into(&4, 50));
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 
 		// Unfunded initially...
 		assert_noop!(Nis::thaw_communal(signed(1), 0), Error::<Test>::Unfunded);
@@ -581,7 +581,7 @@ fn communal_thaw_when_issuance_higher_works() {
 #[test]
 fn private_thaw_when_issuance_higher_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1));
 		assert_ok!(Nis::place_bid(signed(1), 100, 1));
 		enlarge(100, 1);
@@ -591,7 +591,7 @@ fn private_thaw_when_issuance_higher_works() {
 		assert_ok!(Balances::mint_into(&3, 50));
 		assert_ok!(Balances::mint_into(&4, 50));
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 
 		// Unfunded initially...
 		assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::<Test>::Unfunded);
@@ -609,7 +609,7 @@ fn private_thaw_when_issuance_higher_works() {
 #[test]
 fn thaw_with_ignored_issuance_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		// Give account zero some balance.
 		assert_ok!(Balances::mint_into(&0, 200));
 
@@ -622,7 +622,7 @@ fn thaw_with_ignored_issuance_works() {
 		assert_ok!(Balances::transfer_allow_death(signed(0), 3, 50));
 		assert_ok!(Balances::transfer_allow_death(signed(0), 4, 50));
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// Unfunded initially...
 		assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::<Test>::Unfunded);
 		// ...so we fund...
@@ -640,7 +640,7 @@ fn thaw_with_ignored_issuance_works() {
 #[test]
 fn thaw_when_issuance_lower_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Balances::transfer_allow_death(signed(2), 1, 1));
 		assert_ok!(Nis::place_bid(signed(1), 100, 1));
 		enlarge(100, 1);
@@ -650,7 +650,7 @@ fn thaw_when_issuance_lower_works() {
 		assert_ok!(Balances::burn_from(&3, 25, Expendable, Exact, Force));
 		assert_ok!(Balances::burn_from(&4, 25, Expendable, Exact, Force));
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_ok!(Nis::thaw_private(signed(1), 0, None));
 
 		assert_ok!(Balances::transfer_allow_death(signed(1), 2, 1));
@@ -662,7 +662,7 @@ fn thaw_when_issuance_lower_works() {
 #[test]
 fn multiple_thaws_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Balances::transfer_allow_death(signed(3), 1, 1));
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		assert_ok!(Nis::place_bid(signed(1), 60, 1));
@@ -675,11 +675,11 @@ fn multiple_thaws_works() {
 		assert_ok!(Balances::mint_into(&4, 100));
 		assert_ok!(Nis::fund_deficit(signed(1)));
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_ok!(Nis::thaw_private(signed(1), 0, None));
 		assert_ok!(Nis::thaw_private(signed(1), 1, None));
 		assert_noop!(Nis::thaw_private(signed(2), 2, None), Error::<Test>::Throttled);
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_ok!(Nis::thaw_private(signed(2), 2, None));
 
 		assert_ok!(Balances::transfer_allow_death(signed(1), 3, 1));
@@ -693,7 +693,7 @@ fn multiple_thaws_works() {
 #[test]
 fn multiple_thaws_works_in_alternative_thaw_order() {
 	new_test_ext().execute_with(|| {
-		run_to_block(1);
+		System::run_to_block::<AllPalletsWithSystem>(1);
 		assert_ok!(Balances::transfer_allow_death(signed(3), 1, 1));
 		assert_ok!(Nis::place_bid(signed(1), 40, 1));
 		assert_ok!(Nis::place_bid(signed(1), 60, 1));
@@ -706,12 +706,12 @@ fn multiple_thaws_works_in_alternative_thaw_order() {
 		assert_ok!(Balances::mint_into(&4, 100));
 		assert_ok!(Nis::fund_deficit(signed(1)));
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_ok!(Nis::thaw_private(signed(2), 2, None));
 		assert_noop!(Nis::thaw_private(signed(1), 1, None), Error::<Test>::Throttled);
 		assert_ok!(Nis::thaw_private(signed(1), 0, None));
 
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_ok!(Nis::thaw_private(signed(1), 1, None));
 
 		assert_ok!(Balances::transfer_allow_death(signed(1), 3, 1));
@@ -725,7 +725,7 @@ fn multiple_thaws_works_in_alternative_thaw_order() {
 #[test]
 fn enlargement_to_target_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(2);
+		System::run_to_block::<AllPalletsWithSystem>(2);
 		let w = <() as WeightInfo>::process_queues() +
 			<() as WeightInfo>::process_queue() +
 			(<() as WeightInfo>::process_bid() * 2);
@@ -737,7 +737,7 @@ fn enlargement_to_target_works() {
 		assert_ok!(Nis::place_bid(signed(3), 40, 3));
 		Target::set(Perquintill::from_percent(40));
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert_eq!(Queues::<Test>::get(1), vec![Bid { amount: 40, who: 1 },]);
 		assert_eq!(
 			Queues::<Test>::get(2),
@@ -749,7 +749,7 @@ fn enlargement_to_target_works() {
 		);
 		assert_eq!(QueueTotals::<Test>::get(), vec![(1, 40), (2, 80), (2, 80)]);
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// Two new items should have been issued to 2 & 3 for 40 each & duration of 3.
 		assert_eq!(
 			Receipts::<Test>::get(0).unwrap(),
@@ -778,7 +778,7 @@ fn enlargement_to_target_works() {
 			}
 		);
 
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		// No change
 		assert_eq!(
 			Summary::<Test>::get(),
@@ -791,7 +791,7 @@ fn enlargement_to_target_works() {
 			}
 		);
 
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		// Two new items should have been issued to 1 & 2 for 40 each & duration of 2.
 		assert_eq!(
 			Receipts::<Test>::get(2).unwrap(),
@@ -820,7 +820,7 @@ fn enlargement_to_target_works() {
 			}
 		);
 
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		// No change now.
 		assert_eq!(
 			Summary::<Test>::get(),
@@ -835,7 +835,7 @@ fn enlargement_to_target_works() {
 
 		// Set target a bit higher to use up the remaining bid.
 		Target::set(Perquintill::from_percent(60));
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 
 		// One new item should have been issued to 1 for 40 each & duration of 2.
 		assert_eq!(
diff --git a/substrate/frame/node-authorization/Cargo.toml b/substrate/frame/node-authorization/Cargo.toml
index 7e55ad178091ffc42d858c80d4729a6f6d86d362..86a78e6e361535ef4556527e6a84890e50cd3a4f 100644
--- a/substrate/frame/node-authorization/Cargo.toml
+++ b/substrate/frame/node-authorization/Cargo.toml
@@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { features = ["derive"], workspace = true }
-frame = { workspace = true, features = ["experimental", "runtime"] }
+frame = { workspace = true, features = ["runtime"] }
 log = { workspace = true }
 scale-info = { features = ["derive"], workspace = true }
 
diff --git a/substrate/frame/nomination-pools/benchmarking/src/inner.rs b/substrate/frame/nomination-pools/benchmarking/src/inner.rs
index 7ddb78cca3f9b8d78293f37c2e894ed805b84ed3..20c5eafbcfc59dee4d2460224e83a70458804d6d 100644
--- a/substrate/frame/nomination-pools/benchmarking/src/inner.rs
+++ b/substrate/frame/nomination-pools/benchmarking/src/inner.rs
@@ -132,6 +132,10 @@ fn migrate_to_transfer_stake<T: Config>(pool_id: PoolId) {
 			.expect("member should have enough balance to transfer");
 		});
 
+	// Pool needs to have ED balance free to stake so give it some.
+	// Note: we didn't require ED until pallet-staking migrated from locks to holds.
+	let _ = CurrencyOf::<T>::mint_into(&pool_acc, CurrencyOf::<T>::minimum_balance());
+
 	pallet_staking::Pallet::<T>::migrate_to_direct_staker(&pool_acc);
 }
 
@@ -141,14 +145,6 @@ fn vote_to_balance<T: pallet_nomination_pools::Config>(
 	vote.try_into().map_err(|_| "could not convert u64 to Balance")
 }
 
-/// `assertion` should strictly be true if the adapter is using `Delegate` strategy and strictly
-/// false if the adapter is not using `Delegate` strategy.
-fn assert_if_delegate<T: pallet_nomination_pools::Config>(assertion: bool) {
-	let legacy_adapter_used = T::StakeAdapter::strategy_type() != StakeStrategyType::Delegate;
-	// one and only one of the two should be true.
-	assert!(assertion ^ legacy_adapter_used);
-}
-
 #[allow(unused)]
 struct ListScenario<T: pallet_nomination_pools::Config> {
 	/// Stash/Controller that is expected to be moved.
@@ -981,9 +977,6 @@ mod benchmarks {
 
 	#[benchmark]
 	fn apply_slash() {
-		// Note: With older `TransferStake` strategy, slashing is greedy and apply_slash should
-		// always fail.
-
 		// We want to fill member's unbonding pools. So let's bond with big enough amount.
 		let deposit_amount =
 			Pools::<T>::depositor_min_bond() * T::MaxUnbonding::get().into() * 4u32.into();
@@ -993,7 +986,7 @@ mod benchmarks {
 		// verify user balance in the pool.
 		assert_eq!(PoolMembers::<T>::get(&depositor).unwrap().total_balance(), deposit_amount);
 		// verify delegated balance.
-		assert_if_delegate::<T>(
+		assert!(
 			T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) ==
 				Some(deposit_amount),
 		);
@@ -1017,7 +1010,7 @@ mod benchmarks {
 			deposit_amount / 2u32.into()
 		);
 		// verify delegated balance are not yet slashed.
-		assert_if_delegate::<T>(
+		assert!(
 			T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) ==
 				Some(deposit_amount),
 		);
@@ -1041,13 +1034,11 @@ mod benchmarks {
 
 		#[block]
 		{
-			assert_if_delegate::<T>(
-				Pools::<T>::apply_slash(
-					RuntimeOrigin::Signed(slash_reporter.clone()).into(),
-					depositor_lookup.clone(),
-				)
-				.is_ok(),
-			);
+			assert!(Pools::<T>::apply_slash(
+				RuntimeOrigin::Signed(slash_reporter.clone()).into(),
+				depositor_lookup.clone(),
+			)
+			.is_ok(),);
 		}
 
 		// verify balances are correct and slash applied.
@@ -1055,7 +1046,7 @@ mod benchmarks {
 			PoolMembers::<T>::get(&depositor).unwrap().total_balance(),
 			deposit_amount / 2u32.into()
 		);
-		assert_if_delegate::<T>(
+		assert!(
 			T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) ==
 				Some(deposit_amount / 2u32.into()),
 		);
@@ -1126,18 +1117,16 @@ mod benchmarks {
 		let _ = migrate_to_transfer_stake::<T>(1);
 		#[block]
 		{
-			assert_if_delegate::<T>(
-				Pools::<T>::migrate_pool_to_delegate_stake(
-					RuntimeOrigin::Signed(depositor.clone()).into(),
-					1u32.into(),
-				)
-				.is_ok(),
-			);
+			assert!(Pools::<T>::migrate_pool_to_delegate_stake(
+				RuntimeOrigin::Signed(depositor.clone()).into(),
+				1u32.into(),
+			)
+			.is_ok(),);
 		}
-		// this queries agent balance if `DelegateStake` strategy.
+		// this queries agent balance.
 		assert_eq!(
 			T::StakeAdapter::total_balance(Pool::from(pool_account.clone())),
-			Some(deposit_amount)
+			Some(deposit_amount + CurrencyOf::<T>::minimum_balance())
 		);
 	}
 
@@ -1152,13 +1141,11 @@ mod benchmarks {
 		let _ = migrate_to_transfer_stake::<T>(1);
 
 		// Now migrate pool to delegate stake keeping delegators unmigrated.
-		assert_if_delegate::<T>(
-			Pools::<T>::migrate_pool_to_delegate_stake(
-				RuntimeOrigin::Signed(depositor.clone()).into(),
-				1u32.into(),
-			)
-			.is_ok(),
-		);
+		assert!(Pools::<T>::migrate_pool_to_delegate_stake(
+			RuntimeOrigin::Signed(depositor.clone()).into(),
+			1u32.into(),
+		)
+		.is_ok(),);
 
 		// delegation does not exist.
 		assert!(
@@ -1171,16 +1158,14 @@ mod benchmarks {
 
 		#[block]
 		{
-			assert_if_delegate::<T>(
-				Pools::<T>::migrate_delegation(
-					RuntimeOrigin::Signed(depositor.clone()).into(),
-					depositor_lookup.clone(),
-				)
-				.is_ok(),
-			);
+			assert!(Pools::<T>::migrate_delegation(
+				RuntimeOrigin::Signed(depositor.clone()).into(),
+				depositor_lookup.clone(),
+			)
+			.is_ok(),);
 		}
 		// verify balances once more.
-		assert_if_delegate::<T>(
+		assert!(
 			T::StakeAdapter::member_delegation_balance(Member::from(depositor.clone())) ==
 				Some(deposit_amount),
 		);
diff --git a/substrate/frame/nomination-pools/benchmarking/src/mock.rs b/substrate/frame/nomination-pools/benchmarking/src/mock.rs
index 15d9e2c56031fe1e47b7b0cef3139ca177ddeb67..7c09cf22ad51e9575780525718d4fdd830bb6ecd 100644
--- a/substrate/frame/nomination-pools/benchmarking/src/mock.rs
+++ b/substrate/frame/nomination-pools/benchmarking/src/mock.rs
@@ -78,6 +78,7 @@ parameter_types! {
 }
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Runtime {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = Balance;
 	type UnixTime = pallet_timestamp::Pallet<Self>;
diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs
index f125919dabfa6bce68dea67dd4fd60ed26ec0e3c..f1c68af4ea6ad170bbd54af728cdfd68dcef3688 100644
--- a/substrate/frame/nomination-pools/src/adapter.rs
+++ b/substrate/frame/nomination-pools/src/adapter.rs
@@ -16,6 +16,7 @@
 // limitations under the License.
 
 use crate::*;
+use frame_support::traits::tokens::{Fortitude::Polite, Preservation::Expendable};
 use sp_staking::{Agent, DelegationInterface, DelegationMigrator, Delegator};
 
 /// Types of stake strategies.
@@ -245,8 +246,10 @@ pub trait StakeStrategy {
 /// strategy in an existing runtime, storage migration is required. See
 /// [`migration::unversioned::DelegationStakeMigration`]. For new runtimes, it is highly recommended
 /// to use the [`DelegateStake`] strategy.
+#[deprecated = "consider migrating to DelegateStake"]
 pub struct TransferStake<T: Config, Staking: StakingInterface>(PhantomData<(T, Staking)>);
 
+#[allow(deprecated)]
 impl<T: Config, Staking: StakingInterface<Balance = BalanceOf<T>, AccountId = T::AccountId>>
 	StakeStrategy for TransferStake<T, Staking>
 {
@@ -262,7 +265,8 @@ impl<T: Config, Staking: StakingInterface<Balance = BalanceOf<T>, AccountId = T:
 		pool_account: Pool<Self::AccountId>,
 		_: Member<Self::AccountId>,
 	) -> BalanceOf<T> {
-		T::Currency::balance(&pool_account.0).saturating_sub(Self::active_stake(pool_account))
+		// free/liquid balance of the pool account.
+		T::Currency::reducible_balance(&pool_account.get(), Expendable, Polite)
 	}
 
 	fn total_balance(pool_account: Pool<Self::AccountId>) -> Option<BalanceOf<T>> {
diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs
index cc942039760c0208776fc2f045310a9cb501c73a..f4552389a267abaacb459ed3b4fea0a82daa1a80 100644
--- a/substrate/frame/nomination-pools/src/mock.rs
+++ b/substrate/frame/nomination-pools/src/mock.rs
@@ -23,8 +23,10 @@ use frame_support::{
 	PalletId,
 };
 use frame_system::{EnsureSignedBy, RawOrigin};
-use sp_runtime::{BuildStorage, FixedU128};
-use sp_staking::{OnStakingUpdate, Stake};
+use sp_runtime::{BuildStorage, DispatchResult, FixedU128};
+use sp_staking::{
+	Agent, DelegationInterface, DelegationMigrator, Delegator, OnStakingUpdate, Stake,
+};
 
 pub type BlockNumber = u64;
 pub type AccountId = u128;
@@ -76,6 +78,7 @@ impl StakingMock {
 		let bonded = BondedBalanceMap::get();
 		let pre_total = bonded.get(&acc).unwrap();
 		Self::set_bonded_balance(acc, pre_total - amount);
+		DelegateMock::on_slash(acc, amount);
 		Pools::on_slash(&acc, pre_total - amount, &Default::default(), amount);
 	}
 }
@@ -112,8 +115,8 @@ impl sp_staking::StakingInterface for StakingMock {
 			.ok_or(DispatchError::Other("NotStash"))
 	}
 
-	fn is_virtual_staker(_who: &Self::AccountId) -> bool {
-		false
+	fn is_virtual_staker(who: &Self::AccountId) -> bool {
+		AgentBalanceMap::get().contains_key(who)
 	}
 
 	fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
@@ -162,7 +165,9 @@ impl sp_staking::StakingInterface for StakingMock {
 		staker_map.retain(|(unlocking_at, _amount)| *unlocking_at > current_era);
 
 		// if there was a withdrawal, notify the pallet.
-		Pools::on_withdraw(&who, unlocking_before.saturating_sub(unlocking(&staker_map)));
+		let withdraw_amount = unlocking_before.saturating_sub(unlocking(&staker_map));
+		Pools::on_withdraw(&who, withdraw_amount);
+		DelegateMock::on_withdraw(who, withdraw_amount);
 
 		UnbondingBalanceMap::set(&unbonding_map);
 		Ok(UnbondingBalanceMap::get().get(&who).unwrap().is_empty() &&
@@ -239,6 +244,176 @@ impl sp_staking::StakingInterface for StakingMock {
 	}
 }
 
+parameter_types! {
+	// Map of agent to their (delegated balance, unclaimed withdrawal, pending slash).
+	pub storage AgentBalanceMap: BTreeMap<AccountId, (Balance, Balance, Balance)> = Default::default();
+	pub storage DelegatorBalanceMap: BTreeMap<AccountId, Balance> = Default::default();
+}
+pub struct DelegateMock;
+impl DelegationInterface for DelegateMock {
+	type Balance = Balance;
+	type AccountId = AccountId;
+	fn agent_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance> {
+		AgentBalanceMap::get()
+			.get(&agent.get())
+			.copied()
+			.map(|(delegated, _, pending)| delegated - pending)
+	}
+
+	fn agent_transferable_balance(agent: Agent<Self::AccountId>) -> Option<Self::Balance> {
+		AgentBalanceMap::get()
+			.get(&agent.get())
+			.copied()
+			.map(|(_, unclaimed_withdrawals, _)| unclaimed_withdrawals)
+	}
+
+	fn delegator_balance(delegator: Delegator<Self::AccountId>) -> Option<Self::Balance> {
+		DelegatorBalanceMap::get().get(&delegator.get()).copied()
+	}
+
+	fn register_agent(
+		agent: Agent<Self::AccountId>,
+		_reward_account: &Self::AccountId,
+	) -> DispatchResult {
+		let mut agents = AgentBalanceMap::get();
+		agents.insert(agent.get(), (0, 0, 0));
+		AgentBalanceMap::set(&agents);
+		Ok(())
+	}
+
+	fn remove_agent(agent: Agent<Self::AccountId>) -> DispatchResult {
+		let mut agents = AgentBalanceMap::get();
+		let agent = agent.get();
+		assert!(agents.contains_key(&agent));
+		agents.remove(&agent);
+		AgentBalanceMap::set(&agents);
+		Ok(())
+	}
+
+	fn delegate(
+		delegator: Delegator<Self::AccountId>,
+		agent: Agent<Self::AccountId>,
+		amount: Self::Balance,
+	) -> DispatchResult {
+		let delegator = delegator.get();
+		let mut delegators = DelegatorBalanceMap::get();
+		delegators.entry(delegator).and_modify(|b| *b += amount).or_insert(amount);
+		DelegatorBalanceMap::set(&delegators);
+
+		let agent = agent.get();
+		let mut agents = AgentBalanceMap::get();
+		agents
+			.get_mut(&agent)
+			.map(|(d, _, _)| *d += amount)
+			.ok_or(DispatchError::Other("agent not registered"))?;
+		AgentBalanceMap::set(&agents);
+
+		if BondedBalanceMap::get().contains_key(&agent) {
+			StakingMock::bond_extra(&agent, amount)
+		} else {
+			// reward account does not matter in this context.
+			StakingMock::bond(&agent, amount, &999)
+		}
+	}
+
+	fn withdraw_delegation(
+		delegator: Delegator<Self::AccountId>,
+		agent: Agent<Self::AccountId>,
+		amount: Self::Balance,
+		_num_slashing_spans: u32,
+	) -> DispatchResult {
+		let mut delegators = DelegatorBalanceMap::get();
+		delegators.get_mut(&delegator.get()).map(|b| *b -= amount);
+		DelegatorBalanceMap::set(&delegators);
+
+		let mut agents = AgentBalanceMap::get();
+		agents.get_mut(&agent.get()).map(|(d, u, _)| {
+			*d -= amount;
+			*u -= amount;
+		});
+		AgentBalanceMap::set(&agents);
+
+		Ok(())
+	}
+
+	fn pending_slash(agent: Agent<Self::AccountId>) -> Option<Self::Balance> {
+		AgentBalanceMap::get()
+			.get(&agent.get())
+			.copied()
+			.map(|(_, _, pending_slash)| pending_slash)
+	}
+
+	fn delegator_slash(
+		agent: Agent<Self::AccountId>,
+		delegator: Delegator<Self::AccountId>,
+		value: Self::Balance,
+		_maybe_reporter: Option<Self::AccountId>,
+	) -> DispatchResult {
+		let mut delegators = DelegatorBalanceMap::get();
+		delegators.get_mut(&delegator.get()).map(|b| *b -= value);
+		DelegatorBalanceMap::set(&delegators);
+
+		let mut agents = AgentBalanceMap::get();
+		agents.get_mut(&agent.get()).map(|(_, _, p)| {
+			p.saturating_reduce(value);
+		});
+		AgentBalanceMap::set(&agents);
+
+		Ok(())
+	}
+}
+
+impl DelegateMock {
+	pub fn set_agent_balance(who: AccountId, delegated: Balance) {
+		let mut agents = AgentBalanceMap::get();
+		agents.insert(who, (delegated, 0, 0));
+		AgentBalanceMap::set(&agents);
+	}
+
+	pub fn set_delegator_balance(who: AccountId, amount: Balance) {
+		let mut delegators = DelegatorBalanceMap::get();
+		delegators.insert(who, amount);
+		DelegatorBalanceMap::set(&delegators);
+	}
+
+	pub fn on_slash(agent: AccountId, amount: Balance) {
+		let mut agents = AgentBalanceMap::get();
+		agents.get_mut(&agent).map(|(_, _, p)| *p += amount);
+		AgentBalanceMap::set(&agents);
+	}
+
+	fn on_withdraw(agent: AccountId, amount: Balance) {
+		let mut agents = AgentBalanceMap::get();
+		// if agent exists, add the amount to unclaimed withdrawals.
+		agents.get_mut(&agent).map(|(_, u, _)| *u += amount);
+		AgentBalanceMap::set(&agents);
+	}
+}
+
+impl DelegationMigrator for DelegateMock {
+	type Balance = Balance;
+	type AccountId = AccountId;
+	fn migrate_nominator_to_agent(
+		_agent: Agent<Self::AccountId>,
+		_reward_account: &Self::AccountId,
+	) -> DispatchResult {
+		unimplemented!("not used in current unit tests")
+	}
+
+	fn migrate_delegation(
+		_agent: Agent<Self::AccountId>,
+		_delegator: Delegator<Self::AccountId>,
+		_value: Self::Balance,
+	) -> DispatchResult {
+		unimplemented!("not used in current unit tests")
+	}
+
+	#[cfg(feature = "runtime-benchmarks")]
+	fn force_kill_agent(_agent: Agent<Self::AccountId>) {
+		unimplemented!("not used in current unit tests")
+	}
+}
+
 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
 impl frame_system::Config for Runtime {
 	type Nonce = u64;
@@ -295,7 +470,7 @@ impl pools::Config for Runtime {
 	type RewardCounter = RewardCounter;
 	type BalanceToU256 = BalanceToU256;
 	type U256ToBalance = U256ToBalance;
-	type StakeAdapter = adapter::TransferStake<Self, StakingMock>;
+	type StakeAdapter = adapter::DelegateStake<Self, StakingMock, DelegateMock>;
 	type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
 	type PalletId = PoolsPalletId;
 	type MaxMetadataLen = MaxMetadataLen;
@@ -435,18 +610,7 @@ parameter_types! {
 /// Helper to run a specified amount of blocks.
 pub fn run_blocks(n: u64) {
 	let current_block = System::block_number();
-	run_to_block(n + current_block);
-}
-
-/// Helper to run to a specific block.
-pub fn run_to_block(n: u64) {
-	let current_block = System::block_number();
-	assert!(n > current_block);
-	while System::block_number() < n {
-		Pools::on_finalize(System::block_number());
-		System::set_block_number(System::block_number() + 1);
-		Pools::on_initialize(System::block_number());
-	}
+	System::run_to_block::<AllPalletsWithSystem>(n + current_block);
 }
 
 /// All events of this pallet.
@@ -533,6 +697,21 @@ pub fn reward_imbalance(pool: PoolId) -> RewardImbalance {
 	}
 }
 
+pub fn set_pool_balance(who: AccountId, amount: Balance) {
+	StakingMock::set_bonded_balance(who, amount);
+	DelegateMock::set_agent_balance(who, amount);
+}
+
+pub fn member_delegation(who: AccountId) -> Balance {
+	<T as Config>::StakeAdapter::member_delegation_balance(Member::from(who))
+		.expect("who must be a pool member")
+}
+
+pub fn pool_balance(id: PoolId) -> Balance {
+	<T as Config>::StakeAdapter::total_balance(Pool::from(Pools::generate_bonded_account(id)))
+		.expect("who must be a bonded pool account")
+}
+
 #[cfg(test)]
 mod test {
 	use super::*;
diff --git a/substrate/frame/nomination-pools/src/tests.rs b/substrate/frame/nomination-pools/src/tests.rs
index 06261699a5b23ff58c227875b143a1eaba08e439..c46638d2f8f7bb474088b967e491c54d12bc3105 100644
--- a/substrate/frame/nomination-pools/src/tests.rs
+++ b/substrate/frame/nomination-pools/src/tests.rs
@@ -24,6 +24,7 @@ use sp_runtime::{
 	traits::{BadOrigin, Dispatchable},
 	FixedU128,
 };
+use sp_staking::{Agent, DelegationInterface};
 
 macro_rules! unbonding_pools_with_era {
 	($($k:expr => $v:expr),* $(,)?) => {{
@@ -127,41 +128,41 @@ mod bonded_pool {
 			};
 
 			// 1 points : 1 balance ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100);
+			set_pool_balance(bonded_pool.bonded_account(), 100);
 			assert_eq!(bonded_pool.balance_to_point(10), 10);
 			assert_eq!(bonded_pool.balance_to_point(0), 0);
 
 			// 2 points : 1 balance ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 50);
+			set_pool_balance(bonded_pool.bonded_account(), 50);
 			assert_eq!(bonded_pool.balance_to_point(10), 20);
 
 			// 1 points : 2 balance ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100);
+			set_pool_balance(bonded_pool.bonded_account(), 100);
 			bonded_pool.points = 50;
 			assert_eq!(bonded_pool.balance_to_point(10), 5);
 
 			// 100 points : 0 balance ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 0);
+			set_pool_balance(bonded_pool.bonded_account(), 0);
 			bonded_pool.points = 100;
 			assert_eq!(bonded_pool.balance_to_point(10), 100 * 10);
 
 			// 0 points : 100 balance
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100);
+			set_pool_balance(bonded_pool.bonded_account(), 100);
 			bonded_pool.points = 0;
 			assert_eq!(bonded_pool.balance_to_point(10), 10);
 
 			// 10 points : 3 balance ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 30);
+			set_pool_balance(bonded_pool.bonded_account(), 30);
 			bonded_pool.points = 100;
 			assert_eq!(bonded_pool.balance_to_point(10), 33);
 
 			// 2 points : 3 balance ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 300);
+			set_pool_balance(bonded_pool.bonded_account(), 300);
 			bonded_pool.points = 200;
 			assert_eq!(bonded_pool.balance_to_point(10), 6);
 
 			// 4 points : 9 balance ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 900);
+			set_pool_balance(bonded_pool.bonded_account(), 900);
 			bonded_pool.points = 400;
 			assert_eq!(bonded_pool.balance_to_point(90), 40);
 		})
@@ -182,7 +183,7 @@ mod bonded_pool {
 				},
 			};
 
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100);
+			set_pool_balance(bonded_pool.bonded_account(), 100);
 			assert_eq!(bonded_pool.points_to_balance(10), 10);
 			assert_eq!(bonded_pool.points_to_balance(0), 0);
 
@@ -191,27 +192,27 @@ mod bonded_pool {
 			assert_eq!(bonded_pool.points_to_balance(10), 20);
 
 			// 100 balance : 0 points ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100);
+			set_pool_balance(bonded_pool.bonded_account(), 100);
 			bonded_pool.points = 0;
 			assert_eq!(bonded_pool.points_to_balance(10), 0);
 
 			// 0 balance : 100 points ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 0);
+			set_pool_balance(bonded_pool.bonded_account(), 0);
 			bonded_pool.points = 100;
 			assert_eq!(bonded_pool.points_to_balance(10), 0);
 
 			// 10 balance : 3 points ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 100);
+			set_pool_balance(bonded_pool.bonded_account(), 100);
 			bonded_pool.points = 30;
 			assert_eq!(bonded_pool.points_to_balance(10), 33);
 
 			// 2 balance : 3 points ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 200);
+			set_pool_balance(bonded_pool.bonded_account(), 200);
 			bonded_pool.points = 300;
 			assert_eq!(bonded_pool.points_to_balance(10), 6);
 
 			// 4 balance : 9 points ratio
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), 400);
+			set_pool_balance(bonded_pool.bonded_account(), 400);
 			bonded_pool.points = 900;
 			assert_eq!(bonded_pool.points_to_balance(90), 40);
 		})
@@ -269,30 +270,21 @@ mod bonded_pool {
 				<<Runtime as Config>::MaxPointsToBalance as Get<u8>>::get().into();
 
 			// Simulate a 100% slashed pool
-			StakingMock::set_bonded_balance(pool.bonded_account(), 0);
+			set_pool_balance(pool.bonded_account(), 0);
 			assert_noop!(pool.ok_to_join(), Error::<Runtime>::OverflowRisk);
 
 			// Simulate a slashed pool at `MaxPointsToBalance` + 1 slashed pool
-			StakingMock::set_bonded_balance(
-				pool.bonded_account(),
-				max_points_to_balance.saturating_add(1),
-			);
+			set_pool_balance(pool.bonded_account(), max_points_to_balance.saturating_add(1));
 			assert_ok!(pool.ok_to_join());
 
 			// Simulate a slashed pool at `MaxPointsToBalance`
-			StakingMock::set_bonded_balance(pool.bonded_account(), max_points_to_balance);
+			set_pool_balance(pool.bonded_account(), max_points_to_balance);
 			assert_noop!(pool.ok_to_join(), Error::<Runtime>::OverflowRisk);
 
-			StakingMock::set_bonded_balance(
-				pool.bonded_account(),
-				Balance::MAX / max_points_to_balance,
-			);
+			set_pool_balance(pool.bonded_account(), Balance::MAX / max_points_to_balance);
 
 			// and a sanity check
-			StakingMock::set_bonded_balance(
-				pool.bonded_account(),
-				Balance::MAX / max_points_to_balance - 1,
-			);
+			set_pool_balance(pool.bonded_account(), Balance::MAX / max_points_to_balance - 1);
 			assert_ok!(pool.ok_to_join());
 		});
 	}
@@ -310,7 +302,7 @@ mod bonded_pool {
 					state: PoolState::Open,
 				},
 			};
-			StakingMock::set_bonded_balance(bonded_pool.bonded_account(), u128::MAX);
+			set_pool_balance(bonded_pool.bonded_account(), u128::MAX);
 
 			// Max out the points and balance of the pool and make sure the conversion works as
 			// expected and does not overflow.
@@ -640,8 +632,6 @@ mod sub_pools {
 }
 
 mod join {
-	use sp_runtime::TokenError;
-
 	use super::*;
 
 	#[test]
@@ -728,7 +718,7 @@ mod join {
 			);
 
 			// Force the pools bonded balance to 0, simulating a 100% slash
-			StakingMock::set_bonded_balance(Pools::generate_bonded_account(1), 0);
+			set_pool_balance(Pools::generate_bonded_account(1), 0);
 			assert_noop!(
 				Pools::join(RuntimeOrigin::signed(11), 420, 1),
 				Error::<Runtime>::OverflowRisk
@@ -754,29 +744,13 @@ mod join {
 			let max_points_to_balance: u128 =
 				<<Runtime as Config>::MaxPointsToBalance as Get<u8>>::get().into();
 
-			StakingMock::set_bonded_balance(
-				Pools::generate_bonded_account(123),
-				max_points_to_balance,
-			);
+			set_pool_balance(Pools::generate_bonded_account(123), max_points_to_balance);
 			assert_noop!(
 				Pools::join(RuntimeOrigin::signed(11), 420, 123),
 				Error::<Runtime>::OverflowRisk
 			);
 
-			StakingMock::set_bonded_balance(
-				Pools::generate_bonded_account(123),
-				Balance::MAX / max_points_to_balance,
-			);
-			// Balance needs to be gt Balance::MAX / `MaxPointsToBalance`
-			assert_noop!(
-				Pools::join(RuntimeOrigin::signed(11), 5, 123),
-				TokenError::FundsUnavailable,
-			);
-
-			StakingMock::set_bonded_balance(
-				Pools::generate_bonded_account(1),
-				max_points_to_balance,
-			);
+			set_pool_balance(Pools::generate_bonded_account(1), max_points_to_balance);
 
 			// Cannot join a pool that isn't open
 			unsafe_set_state(123, PoolState::Blocked);
@@ -807,7 +781,7 @@ mod join {
 	#[cfg_attr(not(debug_assertions), should_panic)]
 	fn join_panics_when_reward_pool_not_found() {
 		ExtBuilder::default().build_and_execute(|| {
-			StakingMock::set_bonded_balance(Pools::generate_bonded_account(123), 100);
+			set_pool_balance(Pools::generate_bonded_account(123), 100);
 			BondedPool::<Runtime> {
 				id: 123,
 				inner: BondedPoolInner {
@@ -2321,8 +2295,8 @@ mod claim_payout {
 	fn rewards_are_rounded_down_depositor_collects_them() {
 		ExtBuilder::default().add_members(vec![(20, 20)]).build_and_execute(|| {
 			// initial balance of 10.
-
-			assert_eq!(Currency::free_balance(&10), 35);
+			let init_balance_10 = Currency::free_balance(&10);
+			assert_eq!(member_delegation(10), 10);
 			assert_eq!(
 				Currency::free_balance(&default_reward_account()),
 				Currency::minimum_balance()
@@ -2373,8 +2347,10 @@ mod claim_payout {
 			);
 
 			assert!(!Metadata::<T>::contains_key(1));
-			// original ed + ed put into reward account + reward + bond + dust.
-			assert_eq!(Currency::free_balance(&10), 35 + 5 + 13 + 10 + 1);
+			// original ed + ed put into reward account + reward + dust.
+			assert_eq!(Currency::free_balance(&10), init_balance_10 + 5 + 13 + 1);
+			// delegation reduced from 10 to 0.
+			assert_eq!(member_delegation(10), 0);
 		})
 	}
 
@@ -2444,9 +2420,10 @@ mod claim_payout {
 			let claimable_reward = 8 - ExistentialDeposit::get();
 			// NOTE: easier to read if we use 3, so let's use the number instead of variable.
 			assert_eq!(claimable_reward, 3, "test is correct if rewards are divisible by 3");
+			let init_balance = Currency::free_balance(&10);
 
 			// given
-			assert_eq!(Currency::free_balance(&10), 35);
+			assert_eq!(member_delegation(10), 10);
 
 			// when
 
@@ -2455,7 +2432,10 @@ mod claim_payout {
 			assert_ok!(Pools::claim_payout_other(RuntimeOrigin::signed(80), 10));
 
 			// then
-			assert_eq!(Currency::free_balance(&10), 36);
+			// delegated balance does not change.
+			assert_eq!(member_delegation(10), 10);
+			// reward of 1 is paid out to 10.
+			assert_eq!(Currency::free_balance(&10), init_balance + 1);
 			assert_eq!(Currency::free_balance(&default_reward_account()), 7);
 		})
 	}
@@ -2818,6 +2798,8 @@ mod unbond {
 		ExtBuilder::default()
 			.add_members(vec![(40, 40), (550, 550)])
 			.build_and_execute(|| {
+				let init_balance_40 = Currency::free_balance(&40);
+				let init_balance_550 = Currency::free_balance(&550);
 				let ed = Currency::minimum_balance();
 				// Given a slash from 600 -> 500
 				StakingMock::slash_by(1, 500);
@@ -2864,7 +2846,9 @@ mod unbond {
 					PoolMembers::<Runtime>::get(40).unwrap().unbonding_eras,
 					member_unbonding_eras!(3 => 6)
 				);
-				assert_eq!(Currency::free_balance(&40), 40 + 40); // We claim rewards when unbonding
+				assert_eq!(member_delegation(40), 40);
+				// We claim rewards when unbonding
+				assert_eq!(Currency::free_balance(&40), init_balance_40 + 40);
 
 				// When
 				unsafe_set_state(1, PoolState::Destroying);
@@ -2893,7 +2877,8 @@ mod unbond {
 					PoolMembers::<Runtime>::get(550).unwrap().unbonding_eras,
 					member_unbonding_eras!(3 => 92)
 				);
-				assert_eq!(Currency::free_balance(&550), 550 + 550);
+				assert_eq!(member_delegation(550), 550);
+				assert_eq!(Currency::free_balance(&550), init_balance_550 + 550);
 				assert_eq!(
 					pool_events_since_last_call(),
 					vec![
@@ -2934,7 +2919,8 @@ mod unbond {
 				);
 				assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 0);
 
-				assert_eq!(Currency::free_balance(&550), 550 + 550 + 92);
+				// 550 is removed from pool.
+				assert_eq!(member_delegation(550), 0);
 				assert_eq!(
 					pool_events_since_last_call(),
 					vec![
@@ -3532,7 +3518,7 @@ mod pool_withdraw_unbonded {
 
 			assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(15));
 			assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(20));
-			assert_eq!(Balances::free_balance(&default_bonded_account()), 20);
+			assert_eq!(pool_balance(1), 20);
 
 			// When
 			CurrentEra::set(StakingMock::current_era() + StakingMock::bonding_duration() + 1);
@@ -3541,7 +3527,7 @@ mod pool_withdraw_unbonded {
 			// Then their unbonding balance is no longer locked
 			assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(15));
 			assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(15));
-			assert_eq!(Balances::free_balance(&default_bonded_account()), 20);
+			assert_eq!(pool_balance(1), 20);
 		});
 	}
 	#[test]
@@ -3552,7 +3538,7 @@ mod pool_withdraw_unbonded {
 
 			assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(15));
 			assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(20));
-			assert_eq!(Balances::free_balance(&default_bonded_account()), 20);
+			assert_eq!(pool_balance(1), 20);
 			assert_eq!(TotalValueLocked::<T>::get(), 20);
 
 			// When
@@ -3568,14 +3554,14 @@ mod pool_withdraw_unbonded {
 			// Then their unbonding balance is no longer locked
 			assert_eq!(StakingMock::active_stake(&default_bonded_account()), Ok(15));
 			assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(15));
-			assert_eq!(Currency::free_balance(&default_bonded_account()), 20);
+			assert_eq!(pool_balance(1), 20);
 
 			// The difference between TVL and member_balance is exactly the difference between
-			// `total_stake` and the `free_balance`.
-			// This relation is not guaranteed in the wild as arbitrary transfers towards
-			// `free_balance` can be made to the pool that are not accounted for.
-			let non_locked_balance = Balances::free_balance(&default_bonded_account()) -
-				StakingMock::total_stake(&default_bonded_account()).unwrap();
+			// `pool balance` (sum of all balance delegated to pool) and the `staked balance`.
+			// This is the withdrawn funds from the pool stake that has not yet been claimed by the
+			// respective members.
+			let non_locked_balance =
+				pool_balance(1) - StakingMock::total_stake(&default_bonded_account()).unwrap();
 			assert_eq!(member_balance, TotalValueLocked::<T>::get() + non_locked_balance);
 		});
 	}
@@ -3597,7 +3583,7 @@ mod withdraw_unbonded {
 				assert_eq!(StakingMock::bonding_duration(), 3);
 				assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(550), 550));
 				assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(40), 40));
-				assert_eq!(Currency::free_balance(&default_bonded_account()), 600);
+				assert_eq!(pool_balance(1), 600);
 
 				let mut current_era = 1;
 				CurrentEra::set(current_era);
@@ -3626,10 +3612,7 @@ mod withdraw_unbonded {
 						.1 /= 2;
 					UnbondingBalanceMap::set(&x);
 
-					Currency::set_balance(
-						&default_bonded_account(),
-						Currency::free_balance(&default_bonded_account()) / 2, // 300
-					);
+					set_pool_balance(1, pool_balance(1) / 2);
 					assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 10);
 					StakingMock::slash_by(1, 5);
 					assert_eq!(StakingMock::active_stake(&default_bonded_account()).unwrap(), 5);
@@ -3671,11 +3654,6 @@ mod withdraw_unbonded {
 						Event::PoolSlashed { pool_id: 1, balance: 5 }
 					]
 				);
-				assert_eq!(
-					balances_events_since_last_call(),
-					vec![BEvent::Burned { who: default_bonded_account(), amount: 300 }]
-				);
-
 				// When
 				assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(550), 550, 0));
 
@@ -3691,10 +3669,9 @@ mod withdraw_unbonded {
 						Event::MemberRemoved { pool_id: 1, member: 550, released_balance: 0 }
 					]
 				);
-				assert_eq!(
-					balances_events_since_last_call(),
-					vec![BEvent::Transfer { from: default_bonded_account(), to: 550, amount: 275 }]
-				);
+
+				// member has 40 tokens in delegation, but only 20 can be withdrawan.
+				assert_eq!(member_delegation(40), 40);
 
 				// When
 				assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(40), 40, 0));
@@ -3708,18 +3685,18 @@ mod withdraw_unbonded {
 				assert_eq!(
 					pool_events_since_last_call(),
 					vec![
+						// out of 40, 20 is withdrawn.
 						Event::Withdrawn { member: 40, pool_id: 1, balance: 20, points: 40 },
-						Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 0 }
+						// member is removed and the dangling delegation of 20 tokens left in their
+						// account is released.
+						Event::MemberRemoved { pool_id: 1, member: 40, released_balance: 20 }
 					]
 				);
-				assert_eq!(
-					balances_events_since_last_call(),
-					vec![BEvent::Transfer { from: default_bonded_account(), to: 40, amount: 20 }]
-				);
 
 				// now, finally, the depositor can take out its share.
 				unsafe_set_state(1, PoolState::Destroying);
 				assert_ok!(fully_unbond_permissioned(10));
+				assert_eq!(member_delegation(10), 10);
 
 				current_era += 3;
 				CurrentEra::set(current_era);
@@ -3731,7 +3708,9 @@ mod withdraw_unbonded {
 					vec![
 						Event::Unbonded { member: 10, pool_id: 1, balance: 5, points: 5, era: 9 },
 						Event::Withdrawn { member: 10, pool_id: 1, balance: 5, points: 5 },
-						Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 },
+						// when member is removed, any leftover delegation is released.
+						Event::MemberRemoved { pool_id: 1, member: 10, released_balance: 5 },
+						// when the last member leaves, the pool is destroyed.
 						Event::Destroyed { pool_id: 1 }
 					]
 				);
@@ -3739,7 +3718,6 @@ mod withdraw_unbonded {
 				assert_eq!(
 					balances_events_since_last_call(),
 					vec![
-						BEvent::Transfer { from: default_bonded_account(), to: 10, amount: 5 },
 						BEvent::Thawed { who: default_reward_account(), amount: 5 },
 						BEvent::Transfer { from: default_reward_account(), to: 10, amount: 5 }
 					]
@@ -3753,11 +3731,9 @@ mod withdraw_unbonded {
 			.add_members(vec![(40, 40), (550, 550)])
 			.build_and_execute(|| {
 				let _ = balances_events_since_last_call();
-
 				// Given
 				// current bond is 600, we slash it all to 300.
 				StakingMock::slash_by(1, 300);
-				Currency::set_balance(&default_bonded_account(), 300);
 				assert_eq!(StakingMock::total_stake(&default_bonded_account()), Ok(300));
 
 				assert_ok!(fully_unbond_permissioned(40));
@@ -3787,10 +3763,6 @@ mod withdraw_unbonded {
 						}
 					]
 				);
-				assert_eq!(
-					balances_events_since_last_call(),
-					vec![BEvent::Burned { who: default_bonded_account(), amount: 300 },]
-				);
 
 				CurrentEra::set(StakingMock::bonding_duration());
 
@@ -3798,10 +3770,6 @@ mod withdraw_unbonded {
 				assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(40), 40, 0));
 
 				// Then
-				assert_eq!(
-					balances_events_since_last_call(),
-					vec![BEvent::Transfer { from: default_bonded_account(), to: 40, amount: 20 },]
-				);
 				assert_eq!(
 					pool_events_since_last_call(),
 					vec![
@@ -3819,10 +3787,6 @@ mod withdraw_unbonded {
 				assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(550), 550, 0));
 
 				// Then
-				assert_eq!(
-					balances_events_since_last_call(),
-					vec![BEvent::Transfer { from: default_bonded_account(), to: 550, amount: 275 },]
-				);
 				assert_eq!(
 					pool_events_since_last_call(),
 					vec![
@@ -3852,9 +3816,11 @@ mod withdraw_unbonded {
 				assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0));
 
 				// then
-				assert_eq!(Currency::free_balance(&10), 10 + 35);
-				assert_eq!(Currency::free_balance(&default_bonded_account()), 0);
-
+				assert_eq!(
+					DelegateMock::agent_balance(Agent::from(default_bonded_account())),
+					None
+				);
+				assert_eq!(StakingMock::stake(&default_bonded_account()).unwrap().total, 0);
 				// in this test 10 also gets a fair share of the slash, because the slash was
 				// applied to the bonded account.
 				assert_eq!(
@@ -3870,7 +3836,6 @@ mod withdraw_unbonded {
 				assert_eq!(
 					balances_events_since_last_call(),
 					vec![
-						BEvent::Transfer { from: default_bonded_account(), to: 10, amount: 5 },
 						BEvent::Thawed { who: default_reward_account(), amount: 5 },
 						BEvent::Transfer { from: default_reward_account(), to: 10, amount: 5 }
 					]
@@ -3878,35 +3843,6 @@ mod withdraw_unbonded {
 			});
 	}
 
-	#[test]
-	fn withdraw_unbonded_handles_faulty_sub_pool_accounting() {
-		ExtBuilder::default().build_and_execute(|| {
-			// Given
-			assert_eq!(Currency::minimum_balance(), 5);
-			assert_eq!(Currency::free_balance(&10), 35);
-			assert_eq!(Currency::free_balance(&default_bonded_account()), 10);
-			unsafe_set_state(1, PoolState::Destroying);
-			assert_ok!(Pools::fully_unbond(RuntimeOrigin::signed(10), 10));
-
-			// Simulate a slash that is not accounted for in the sub pools.
-			Currency::set_balance(&default_bonded_account(), 5);
-			assert_eq!(
-				SubPoolsStorage::<Runtime>::get(1).unwrap().with_era,
-				//------------------------------balance decrease is not account for
-				unbonding_pools_with_era! { 3 => UnbondPool { points: 10, balance: 10 } }
-			);
-
-			CurrentEra::set(3);
-
-			// When
-			assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0));
-
-			// Then
-			assert_eq!(Currency::free_balance(&10), 10 + 35);
-			assert_eq!(Currency::free_balance(&default_bonded_account()), 0);
-		});
-	}
-
 	#[test]
 	fn withdraw_unbonded_errors_correctly() {
 		ExtBuilder::default().with_check(0).build_and_execute(|| {
@@ -3925,6 +3861,10 @@ mod withdraw_unbonded {
 			let mut member = PoolMember { pool_id: 1, points: 10, ..Default::default() };
 			PoolMembers::<Runtime>::insert(11, member.clone());
 
+			// set agent and delegator balance
+			DelegateMock::set_agent_balance(Pools::generate_bonded_account(1), 10);
+			DelegateMock::set_delegator_balance(11, 10);
+
 			// Simulate calling `unbond`
 			member.unbonding_eras = member_unbonding_eras!(3 => 10);
 			PoolMembers::<Runtime>::insert(11, member.clone());
@@ -4045,7 +3985,7 @@ mod withdraw_unbonded {
 				}
 			);
 			CurrentEra::set(StakingMock::bonding_duration());
-			assert_eq!(Currency::free_balance(&100), 100);
+			assert_eq!(member_delegation(100), 100);
 
 			// Cannot permissionlessly withdraw
 			assert_noop!(
@@ -4061,6 +4001,7 @@ mod withdraw_unbonded {
 
 			assert_eq!(SubPoolsStorage::<Runtime>::get(1).unwrap(), Default::default(),);
 			assert_eq!(Currency::free_balance(&100), 100 + 100);
+			assert_eq!(member_delegation(100), 0);
 			assert!(!PoolMembers::<Runtime>::contains_key(100));
 			assert_eq!(
 				pool_events_since_last_call(),
@@ -4662,10 +4603,6 @@ mod withdraw_unbonded {
 
 			// move to era when unbonded funds can be withdrawn.
 			CurrentEra::set(4);
-
-			// increment consumer by 1 reproducing the erroneous consumer bug.
-			// refer https://github.com/paritytech/polkadot-sdk/issues/4440.
-			assert_ok!(frame_system::Pallet::<T>::inc_consumers(&pool_one));
 			assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0));
 
 			assert_eq!(
@@ -4712,7 +4649,7 @@ mod create {
 			));
 			assert_eq!(TotalValueLocked::<T>::get(), 10 + StakingMock::minimum_nominator_bond());
 
-			assert_eq!(Currency::free_balance(&11), 0);
+			assert_eq!(member_delegation(11), StakingMock::minimum_nominator_bond());
 			assert_eq!(
 				PoolMembers::<Runtime>::get(11).unwrap(),
 				PoolMember {
@@ -4851,7 +4788,7 @@ mod create {
 				789
 			));
 
-			assert_eq!(Currency::free_balance(&11), 0);
+			assert_eq!(member_delegation(11), StakingMock::minimum_nominator_bond());
 			// delete the initial pool created, then pool_Id `1` will be free
 
 			assert_noop!(
@@ -5014,16 +4951,9 @@ mod set_state {
 			// surpassed. Making this pool destroyable by anyone.
 			StakingMock::slash_by(1, 10);
 
-			// in mock we are using transfer stake which implies slash is greedy. Extrinsic to
-			// apply pending slash should fail.
-			assert_noop!(
-				Pools::apply_slash(RuntimeOrigin::signed(11), 10),
-				Error::<Runtime>::NotSupported
-			);
-
-			// pending slash api should return zero as well.
-			assert_eq!(Pools::api_pool_pending_slash(1), 0);
-			assert_eq!(Pools::api_member_pending_slash(10), 0);
+			// pending slash is correct.
+			assert_eq!(Pools::api_pool_pending_slash(1), 10);
+			assert_eq!(Pools::api_member_pending_slash(10), 10);
 
 			// When
 			assert_ok!(Pools::set_state(RuntimeOrigin::signed(11), 1, PoolState::Destroying));
@@ -5175,13 +5105,13 @@ mod bond_extra {
 			// given
 			assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 10);
-			assert_eq!(Currency::free_balance(&10), 100);
+			assert_eq!(member_delegation(10), 10);
 
 			// when
 			assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10)));
 
 			// then
-			assert_eq!(Currency::free_balance(&10), 90);
+			assert_eq!(member_delegation(10), 10 + 10);
 			assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 20);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 20);
 
@@ -5198,7 +5128,7 @@ mod bond_extra {
 			assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(20)));
 
 			// then
-			assert_eq!(Currency::free_balance(&10), 70);
+			assert_eq!(member_delegation(10), 20 + 20);
 			assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 40);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 40);
 
@@ -5221,13 +5151,15 @@ mod bond_extra {
 			// given
 			assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 10);
-			assert_eq!(Currency::free_balance(&10), 35);
+			// 10 has delegated 10 tokens to the pool.
+			assert_eq!(member_delegation(10), 10);
 
 			// when
 			assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::Rewards));
 
 			// then
-			assert_eq!(Currency::free_balance(&10), 35);
+			// delegator balance is increased by the claimable reward.
+			assert_eq!(member_delegation(10), 10 + claimable_reward);
 			assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10 + claimable_reward);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 10 + claimable_reward);
 
@@ -5264,8 +5196,8 @@ mod bond_extra {
 			assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 20);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30);
 
-			assert_eq!(Currency::free_balance(&10), 35);
-			assert_eq!(Currency::free_balance(&20), 20);
+			assert_eq!(member_delegation(10), 10);
+			assert_eq!(member_delegation(20), 20);
 			assert_eq!(TotalValueLocked::<T>::get(), 30);
 
 			// when
@@ -5273,7 +5205,7 @@ mod bond_extra {
 			assert_eq!(Currency::free_balance(&default_reward_account()), 7);
 
 			// then
-			assert_eq!(Currency::free_balance(&10), 35);
+			assert_eq!(member_delegation(10), 10 + 1);
 			assert_eq!(TotalValueLocked::<T>::get(), 31);
 
 			// 10's share of the reward is 1/3, since they gave 10/30 of the total shares.
@@ -5284,11 +5216,11 @@ mod bond_extra {
 			assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(20), BondExtra::Rewards));
 
 			// then
-			assert_eq!(Currency::free_balance(&20), 20);
 			assert_eq!(TotalValueLocked::<T>::get(), 33);
 
 			// 20's share of the rewards is the other 2/3 of the rewards, since they have 20/30 of
 			// the shares
+			assert_eq!(member_delegation(20), 20 + 2);
 			assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 20 + 2);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30 + 3);
 
@@ -5320,8 +5252,8 @@ mod bond_extra {
 			assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10);
 			assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 20);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30);
-			assert_eq!(Currency::free_balance(&10), 35);
-			assert_eq!(Currency::free_balance(&20), 20);
+			assert_eq!(member_delegation(10), 10);
+			assert_eq!(member_delegation(20), 20);
 
 			// Permissioned by default
 			assert_noop!(
@@ -5337,7 +5269,7 @@ mod bond_extra {
 			assert_eq!(Currency::free_balance(&default_reward_account()), 7);
 
 			// then
-			assert_eq!(Currency::free_balance(&10), 35);
+			assert_eq!(member_delegation(10), 10 + 1);
 			assert_eq!(PoolMembers::<Runtime>::get(10).unwrap().points, 10 + 1);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 30 + 1);
 
@@ -5355,7 +5287,7 @@ mod bond_extra {
 			));
 
 			// then
-			assert_eq!(Currency::free_balance(&20), 12);
+			assert_eq!(member_delegation(20), 20 + 10);
 			assert_eq!(Currency::free_balance(&default_reward_account()), 5);
 			assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 30);
 			assert_eq!(BondedPools::<Runtime>::get(1).unwrap().points, 41);
@@ -7487,63 +7419,3 @@ mod chill {
 		})
 	}
 }
-
-// the test mock is using `TransferStake` and so `DelegateStake` is not tested here. Extrinsics
-// meant for `DelegateStake` should be gated.
-//
-// `DelegateStake` tests are in `pallet-nomination-pools-test-delegate-stake`. Since we support both
-// strategies currently, we keep these tests as it is but in future we may remove `TransferStake`
-// completely.
-mod delegate_stake {
-	use super::*;
-	#[test]
-	fn delegation_specific_calls_are_gated() {
-		ExtBuilder::default().with_check(0).build_and_execute(|| {
-			// Given
-			Currency::set_balance(&11, ExistentialDeposit::get() + 2);
-			assert!(!PoolMembers::<Runtime>::contains_key(11));
-
-			// When
-			assert_ok!(Pools::join(RuntimeOrigin::signed(11), 2, 1));
-
-			// Then
-			assert_eq!(
-				pool_events_since_last_call(),
-				vec![
-					Event::Created { depositor: 10, pool_id: 1 },
-					Event::Bonded { member: 10, pool_id: 1, bonded: 10, joined: true },
-					Event::Bonded { member: 11, pool_id: 1, bonded: 2, joined: true },
-				]
-			);
-
-			assert_eq!(
-				PoolMembers::<Runtime>::get(11).unwrap(),
-				PoolMember::<Runtime> { pool_id: 1, points: 2, ..Default::default() }
-			);
-
-			// ensure pool 1 cannot be migrated.
-			assert!(!Pools::api_pool_needs_delegate_migration(1));
-			assert_noop!(
-				Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1),
-				Error::<Runtime>::NotSupported
-			);
-
-			// members cannot be migrated either.
-			assert!(!Pools::api_member_needs_delegate_migration(10));
-			assert_noop!(
-				Pools::migrate_delegation(RuntimeOrigin::signed(10), 11),
-				Error::<Runtime>::NotSupported
-			);
-
-			// Given
-			// The bonded balance is slashed in half
-			StakingMock::slash_by(1, 6);
-
-			// since slash is greedy with `TransferStake`, `apply_slash` should not work either.
-			assert_noop!(
-				Pools::apply_slash(RuntimeOrigin::signed(10), 11),
-				Error::<Runtime>::NotSupported
-			);
-		});
-	}
-}
diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs
index cc6335959ab738115c88c4878bc3dc2d8bb6db81..54783332aa3ef245bced9e737ed5c8a89265e7d8 100644
--- a/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs
+++ b/substrate/frame/nomination-pools/test-delegate-stake/src/lib.rs
@@ -21,7 +21,10 @@ mod mock;
 
 use frame_support::{
 	assert_noop, assert_ok, hypothetically,
-	traits::{fungible::InspectHold, Currency},
+	traits::{
+		fungible::{InspectHold, Mutate},
+		Currency,
+	},
 };
 use mock::*;
 use pallet_nomination_pools::{
@@ -942,9 +945,13 @@ fn pool_slash_non_proportional_bonded_pool_and_chunks() {
 fn pool_migration_e2e() {
 	new_test_ext().execute_with(|| {
 		LegacyAdapter::set(true);
-		assert_eq!(Balances::minimum_balance(), 5);
 		assert_eq!(CurrentEra::<T>::get(), None);
 
+		// hack: mint ED to pool so that the deprecated `TransferStake` works correctly with
+		// staking.
+		assert_eq!(Balances::minimum_balance(), 5);
+		assert_ok!(Balances::mint_into(&POOL1_BONDED, 5));
+
 		// create the pool with TransferStake strategy.
 		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
 		assert_eq!(LastPoolId::<Runtime>::get(), 1);
@@ -1050,10 +1057,11 @@ fn pool_migration_e2e() {
 
 		assert_eq!(
 			delegated_staking_events_since_last_call(),
+			// delegated also contains the extra ED that we minted when pool was `TransferStake` .
 			vec![DelegatedStakingEvent::Delegated {
 				agent: POOL1_BONDED,
 				delegator: proxy_delegator_1,
-				amount: 50 + 10 * 3
+				amount: 50 + 10 * 3 + 5
 			}]
 		);
 
@@ -1223,6 +1231,11 @@ fn disable_pool_operations_on_non_migrated() {
 		assert_eq!(Balances::minimum_balance(), 5);
 		assert_eq!(CurrentEra::<T>::get(), None);
 
+		// hack: mint ED to pool so that the deprecated `TransferStake` works correctly with
+		// staking.
+		assert_eq!(Balances::minimum_balance(), 5);
+		assert_ok!(Balances::mint_into(&POOL1_BONDED, 5));
+
 		// create the pool with TransferStake strategy.
 		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
 		assert_eq!(LastPoolId::<Runtime>::get(), 1);
@@ -1331,11 +1344,12 @@ fn disable_pool_operations_on_non_migrated() {
 		assert_ok!(Pools::migrate_pool_to_delegate_stake(RuntimeOrigin::signed(10), 1));
 		assert_eq!(
 			delegated_staking_events_since_last_call(),
+			// delegated also contains the extra ED that we minted when pool was `TransferStake` .
 			vec![DelegatedStakingEvent::Delegated {
 				agent: POOL1_BONDED,
 				delegator: DelegatedStaking::generate_proxy_delegator(Agent::from(POOL1_BONDED))
 					.get(),
-				amount: 50 + 10
+				amount: 50 + 10 + 5
 			},]
 		);
 
diff --git a/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs b/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs
index d1bc4ef8ff281986532d069292b2fed7785e6ca4..d943ba6f533330453b8961886005b4f20be0804b 100644
--- a/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs
+++ b/substrate/frame/nomination-pools/test-delegate-stake/src/mock.rs
@@ -15,6 +15,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Disable warnings for `TransferStake` being deprecated.
+#![allow(deprecated)]
+
 use frame_election_provider_support::VoteWeight;
 use frame_support::{
 	assert_ok, derive_impl,
@@ -92,6 +95,7 @@ parameter_types! {
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Runtime {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type UnixTime = pallet_timestamp::Pallet<Self>;
 	type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
diff --git a/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml b/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml
deleted file mode 100644
index 0b21d5f4e8cf1e3a98e3220a17bfe7eeb0b44cc0..0000000000000000000000000000000000000000
--- a/substrate/frame/nomination-pools/test-transfer-stake/Cargo.toml
+++ /dev/null
@@ -1,39 +0,0 @@
-[package]
-name = "pallet-nomination-pools-test-transfer-stake"
-version = "1.0.0"
-authors.workspace = true
-edition.workspace = true
-license = "Apache-2.0"
-homepage.workspace = true
-repository.workspace = true
-description = "FRAME nomination pools pallet tests with the staking pallet"
-publish = false
-
-[lints]
-workspace = true
-
-[package.metadata.docs.rs]
-targets = ["x86_64-unknown-linux-gnu"]
-
-[dev-dependencies]
-codec = { features = ["derive"], workspace = true, default-features = true }
-scale-info = { features = ["derive"], workspace = true, default-features = true }
-
-sp-core = { workspace = true, default-features = true }
-sp-io = { workspace = true, default-features = true }
-sp-runtime = { workspace = true, default-features = true }
-sp-staking = { workspace = true, default-features = true }
-
-frame-election-provider-support = { workspace = true, default-features = true }
-frame-support = { workspace = true, default-features = true }
-frame-system = { workspace = true, default-features = true }
-
-pallet-bags-list = { workspace = true, default-features = true }
-pallet-balances = { workspace = true, default-features = true }
-pallet-nomination-pools = { workspace = true, default-features = true }
-pallet-staking = { workspace = true, default-features = true }
-pallet-staking-reward-curve = { workspace = true, default-features = true }
-pallet-timestamp = { workspace = true, default-features = true }
-
-log = { workspace = true, default-features = true }
-sp-tracing = { workspace = true, default-features = true }
diff --git a/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs b/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs
deleted file mode 100644
index cc39cfee91c80d95db2b069023991f7647eb3b79..0000000000000000000000000000000000000000
--- a/substrate/frame/nomination-pools/test-transfer-stake/src/lib.rs
+++ /dev/null
@@ -1,912 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#![cfg(test)]
-
-mod mock;
-
-use frame_support::{assert_noop, assert_ok, traits::Currency};
-use mock::*;
-use pallet_nomination_pools::{
-	BondExtra, BondedPools, Error as PoolsError, Event as PoolsEvent, LastPoolId, PoolMember,
-	PoolMembers, PoolState,
-};
-use pallet_staking::{
-	CurrentEra, Error as StakingError, Event as StakingEvent, Payee, RewardDestination,
-};
-use sp_runtime::{bounded_btree_map, traits::Zero};
-
-#[test]
-fn pool_lifecycle_e2e() {
-	new_test_ext().execute_with(|| {
-		assert_eq!(Balances::minimum_balance(), 5);
-		assert_eq!(CurrentEra::<T>::get(), None);
-
-		// create the pool, we know this has id 1.
-		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
-		assert_eq!(LastPoolId::<Runtime>::get(), 1);
-
-		// have the pool nominate.
-		assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Created { depositor: 10, pool_id: 1 },
-				PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true },
-			]
-		);
-
-		// have two members join
-		assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1));
-		assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
-			]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },
-				PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true },
-			]
-		);
-
-		// pool goes into destroying
-		assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying));
-
-		// depositor cannot unbond yet.
-		assert_noop!(
-			Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
-			PoolsError::<Runtime>::MinimumBondNotMet,
-		);
-
-		// now the members want to unbond.
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
-
-		assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras.len(), 1);
-		assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 0);
-		assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().unbonding_eras.len(), 1);
-		assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().points, 0);
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
-			]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying },
-				PoolsEvent::Unbonded { member: 20, pool_id: 1, points: 10, balance: 10, era: 3 },
-				PoolsEvent::Unbonded { member: 21, pool_id: 1, points: 10, balance: 10, era: 3 },
-			]
-		);
-
-		// depositor cannot still unbond
-		assert_noop!(
-			Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
-			PoolsError::<Runtime>::MinimumBondNotMet,
-		);
-
-		for e in 1..BondingDuration::get() {
-			CurrentEra::<Runtime>::set(Some(e));
-			assert_noop!(
-				Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0),
-				PoolsError::<Runtime>::CannotWithdrawAny
-			);
-		}
-
-		// members are now unlocked.
-		CurrentEra::<Runtime>::set(Some(BondingDuration::get()));
-
-		// depositor cannot still unbond
-		assert_noop!(
-			Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
-			PoolsError::<Runtime>::MinimumBondNotMet,
-		);
-
-		// but members can now withdraw.
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0));
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0));
-		assert!(PoolMembers::<Runtime>::get(20).is_none());
-		assert!(PoolMembers::<Runtime>::get(21).is_none());
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 },]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Withdrawn { member: 20, pool_id: 1, points: 10, balance: 10 },
-				PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 },
-				PoolsEvent::Withdrawn { member: 21, pool_id: 1, points: 10, balance: 10 },
-				PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 },
-			]
-		);
-
-		// as soon as all members have left, the depositor can try to unbond, but since the
-		// min-nominator intention is set, they must chill first.
-		assert_noop!(
-			Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
-			pallet_staking::Error::<Runtime>::InsufficientBond
-		);
-
-		assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 50));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Chilled { stash: POOL1_BONDED },
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 50 },
-			]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 50, balance: 50, era: 6 }]
-		);
-
-		// waiting another bonding duration:
-		CurrentEra::<Runtime>::set(Some(BondingDuration::get() * 2));
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 1));
-
-		// pools is fully destroyed now.
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 50 },]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 },
-				PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 },
-				PoolsEvent::Destroyed { pool_id: 1 }
-			]
-		);
-	})
-}
-
-#[test]
-fn destroy_pool_with_erroneous_consumer() {
-	new_test_ext().execute_with(|| {
-		// create the pool, we know this has id 1.
-		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
-		assert_eq!(LastPoolId::<Runtime>::get(), 1);
-
-		// expect consumers on pool account to be 2 (staking lock and an explicit inc by staking).
-		assert_eq!(frame_system::Pallet::<T>::consumers(&POOL1_BONDED), 2);
-
-		// increment consumer by 1 reproducing the erroneous consumer bug.
-		// refer https://github.com/paritytech/polkadot-sdk/issues/4440.
-		assert_ok!(frame_system::Pallet::<T>::inc_consumers(&POOL1_BONDED));
-		assert_eq!(frame_system::Pallet::<T>::consumers(&POOL1_BONDED), 3);
-
-		// have the pool nominate.
-		assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Created { depositor: 10, pool_id: 1 },
-				PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true },
-			]
-		);
-
-		// pool goes into destroying
-		assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying));
-
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying },]
-		);
-
-		// move to era 1
-		CurrentEra::<Runtime>::set(Some(1));
-
-		// depositor need to chill before unbonding
-		assert_noop!(
-			Pools::unbond(RuntimeOrigin::signed(10), 10, 50),
-			pallet_staking::Error::<Runtime>::InsufficientBond
-		);
-
-		assert_ok!(Pools::chill(RuntimeOrigin::signed(10), 1));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 50));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Chilled { stash: POOL1_BONDED },
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 50 },
-			]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Unbonded {
-				member: 10,
-				pool_id: 1,
-				points: 50,
-				balance: 50,
-				era: 1 + 3
-			}]
-		);
-
-		// waiting bonding duration:
-		CurrentEra::<Runtime>::set(Some(1 + 3));
-		// this should work even with an extra consumer count on pool account.
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 1));
-
-		// pools is fully destroyed now.
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 50 },]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Withdrawn { member: 10, pool_id: 1, points: 50, balance: 50 },
-				PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 },
-				PoolsEvent::Destroyed { pool_id: 1 }
-			]
-		);
-	})
-}
-
-#[test]
-fn pool_chill_e2e() {
-	new_test_ext().execute_with(|| {
-		assert_eq!(Balances::minimum_balance(), 5);
-		assert_eq!(CurrentEra::<T>::get(), None);
-
-		// create the pool, we know this has id 1.
-		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 50, 10, 10, 10));
-		assert_eq!(LastPoolId::<Runtime>::get(), 1);
-
-		// have the pool nominate.
-		assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 50 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Created { depositor: 10, pool_id: 1 },
-				PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 50, joined: true },
-			]
-		);
-
-		// have two members join
-		assert_ok!(Pools::join(RuntimeOrigin::signed(20), 10, 1));
-		assert_ok!(Pools::join(RuntimeOrigin::signed(21), 10, 1));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 },
-			]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 10, joined: true },
-				PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 10, joined: true },
-			]
-		);
-
-		// in case depositor does not have more than `MinNominatorBond` staked, we can end up in
-		// situation where a member unbonding would cause pool balance to drop below
-		// `MinNominatorBond` and hence not allowed. This can happen if the `MinNominatorBond` is
-		// increased after the pool is created.
-		assert_ok!(Staking::set_staking_configs(
-			RuntimeOrigin::root(),
-			pallet_staking::ConfigOp::Set(55), // minimum nominator bond
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-		));
-
-		// members can unbond as long as total stake of the pool is above min nominator bond
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10),);
-		assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().unbonding_eras.len(), 1);
-		assert_eq!(PoolMembers::<Runtime>::get(20).unwrap().points, 0);
-
-		// this member cannot unbond since it will cause `pool stake < MinNominatorBond`
-		assert_noop!(
-			Pools::unbond(RuntimeOrigin::signed(21), 21, 10),
-			StakingError::<Runtime>::InsufficientBond,
-		);
-
-		// members can call `chill` permissionlessly now
-		assert_ok!(Pools::chill(RuntimeOrigin::signed(20), 1));
-
-		// now another member can unbond.
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
-		assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().unbonding_eras.len(), 1);
-		assert_eq!(PoolMembers::<Runtime>::get(21).unwrap().points, 0);
-
-		// nominator can not resume nomination until depositor have enough stake
-		assert_noop!(
-			Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]),
-			PoolsError::<Runtime>::MinimumBondNotMet,
-		);
-
-		// other members joining pool does not affect the depositor's ability to resume nomination
-		assert_ok!(Pools::join(RuntimeOrigin::signed(22), 10, 1));
-
-		assert_noop!(
-			Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]),
-			PoolsError::<Runtime>::MinimumBondNotMet,
-		);
-
-		// depositor can bond extra stake
-		assert_ok!(Pools::bond_extra(RuntimeOrigin::signed(10), BondExtra::FreeBalance(10)));
-
-		// `chill` can not be called permissionlessly anymore
-		assert_noop!(
-			Pools::chill(RuntimeOrigin::signed(20), 1),
-			PoolsError::<Runtime>::NotNominator,
-		);
-
-		// now nominator can resume nomination
-		assert_ok!(Pools::nominate(RuntimeOrigin::signed(10), 1, vec![1, 2, 3]));
-
-		// skip to make the unbonding period end.
-		CurrentEra::<Runtime>::set(Some(BondingDuration::get()));
-
-		// members can now withdraw.
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0));
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
-				StakingEvent::Chilled { stash: POOL1_BONDED },
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // other member bonding
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: 10 }, // depositor bond extra
-				StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 20 },
-			]
-		);
-	})
-}
-
-#[test]
-fn pool_slash_e2e() {
-	new_test_ext().execute_with(|| {
-		ExistentialDeposit::set(1);
-		assert_eq!(Balances::minimum_balance(), 1);
-		assert_eq!(CurrentEra::<T>::get(), None);
-
-		// create the pool, we know this has id 1.
-		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10));
-		assert_eq!(LastPoolId::<Runtime>::get(), 1);
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Created { depositor: 10, pool_id: 1 },
-				PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
-			]
-		);
-
-		assert_eq!(
-			Payee::<Runtime>::get(POOL1_BONDED),
-			Some(RewardDestination::Account(POOL1_REWARD))
-		);
-
-		// have two members join
-		assert_ok!(Pools::join(RuntimeOrigin::signed(20), 20, 1));
-		assert_ok!(Pools::join(RuntimeOrigin::signed(21), 20, 1));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 },
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: 20 }
-			]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: 20, joined: true },
-				PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: 20, joined: true },
-			]
-		);
-
-		// now let's progress a bit.
-		CurrentEra::<Runtime>::set(Some(1));
-
-		// 20 / 80 of the total funds are unlocked, and safe from any further slash.
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 10));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }
-			]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 4 },
-				PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 4 }
-			]
-		);
-
-		CurrentEra::<Runtime>::set(Some(2));
-
-		// note: depositor cannot fully unbond at this point.
-		// these funds will still get slashed.
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 10));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, 10));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
-				StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 },
-			]
-		);
-
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Unbonded { member: 10, pool_id: 1, balance: 10, points: 10, era: 5 },
-				PoolsEvent::Unbonded { member: 20, pool_id: 1, balance: 10, points: 10, era: 5 },
-				PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 10, points: 10, era: 5 },
-			]
-		);
-
-		// At this point, 20 are safe from slash, 30 are unlocking but vulnerable to slash, and
-		// another 30 are active and vulnerable to slash. Let's slash half of them.
-		pallet_staking::slashing::do_slash::<Runtime>(
-			&POOL1_BONDED,
-			30,
-			&mut Default::default(),
-			&mut Default::default(),
-			2, // slash era 2, affects chunks at era 5 onwards.
-		);
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				// 30 has been slashed to 15 (15 slash)
-				PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 5, balance: 15 },
-				// 30 has been slashed to 15 (15 slash)
-				PoolsEvent::PoolSlashed { pool_id: 1, balance: 15 }
-			]
-		);
-
-		CurrentEra::<Runtime>::set(Some(3));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, 10));
-
-		assert_eq!(
-			PoolMembers::<Runtime>::get(21).unwrap(),
-			PoolMember {
-				pool_id: 1,
-				points: 0,
-				last_recorded_reward_counter: Zero::zero(),
-				// the 10 points unlocked just now correspond to 5 points in the unbond pool.
-				unbonding_eras: bounded_btree_map!(5 => 10, 6 => 5)
-			}
-		);
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 5 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Unbonded { member: 21, pool_id: 1, balance: 5, points: 5, era: 6 }]
-		);
-
-		// now we start withdrawing. we do it all at once, at era 6 where 20 and 21 are fully free.
-		CurrentEra::<Runtime>::set(Some(6));
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(20), 20, 0));
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(21), 21, 0));
-
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				// 20 had unbonded 10 safely, and 10 got slashed by half.
-				PoolsEvent::Withdrawn { member: 20, pool_id: 1, balance: 10 + 5, points: 20 },
-				PoolsEvent::MemberRemoved { pool_id: 1, member: 20, released_balance: 0 },
-				// 21 unbonded all of it after the slash
-				PoolsEvent::Withdrawn { member: 21, pool_id: 1, balance: 5 + 5, points: 15 },
-				PoolsEvent::MemberRemoved { pool_id: 1, member: 21, released_balance: 0 }
-			]
-		);
-		assert_eq!(
-			staking_events_since_last_call(),
-			// a 10 (un-slashed) + 10/2 (slashed) balance from 10 has also been unlocked
-			vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 15 + 10 + 15 }]
-		);
-
-		// now, finally, we can unbond the depositor further than their current limit.
-		assert_ok!(Pools::set_state(RuntimeOrigin::signed(10), 1, PoolState::Destroying));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(10), 10, 20));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: 10 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::StateChanged { pool_id: 1, new_state: PoolState::Destroying },
-				PoolsEvent::Unbonded { member: 10, pool_id: 1, points: 10, balance: 10, era: 9 }
-			]
-		);
-
-		CurrentEra::<Runtime>::set(Some(9));
-		assert_eq!(
-			PoolMembers::<Runtime>::get(10).unwrap(),
-			PoolMember {
-				pool_id: 1,
-				points: 0,
-				last_recorded_reward_counter: Zero::zero(),
-				unbonding_eras: bounded_btree_map!(4 => 10, 5 => 10, 9 => 10)
-			}
-		);
-		// withdraw the depositor, they should lose 12 balance in total due to slash.
-		assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(10), 10, 0));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Withdrawn { stash: POOL1_BONDED, amount: 10 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Withdrawn { member: 10, pool_id: 1, balance: 10 + 15, points: 30 },
-				PoolsEvent::MemberRemoved { pool_id: 1, member: 10, released_balance: 0 },
-				PoolsEvent::Destroyed { pool_id: 1 }
-			]
-		);
-	});
-}
-
-#[test]
-fn pool_slash_proportional() {
-	// a typical example where 3 pool members unbond in era 99, 100, and 101, and a slash that
-	// happened in era 100 should only affect the latter two.
-	new_test_ext().execute_with(|| {
-		ExistentialDeposit::set(1);
-		BondingDuration::set(28);
-		assert_eq!(Balances::minimum_balance(), 1);
-		assert_eq!(CurrentEra::<T>::get(), None);
-
-		// create the pool, we know this has id 1.
-		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10));
-		assert_eq!(LastPoolId::<T>::get(), 1);
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Created { depositor: 10, pool_id: 1 },
-				PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
-			]
-		);
-
-		// have two members join
-		let bond = 20;
-		assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1));
-		assert_ok!(Pools::join(RuntimeOrigin::signed(21), bond, 1));
-		assert_ok!(Pools::join(RuntimeOrigin::signed(22), bond, 1));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond },
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond },
-				StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond },
-			]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true },
-				PoolsEvent::Bonded { member: 21, pool_id: 1, bonded: bond, joined: true },
-				PoolsEvent::Bonded { member: 22, pool_id: 1, bonded: bond, joined: true },
-			]
-		);
-
-		// now let's progress a lot.
-		CurrentEra::<T>::set(Some(99));
-
-		// and unbond
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond));
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Unbonded {
-				member: 20,
-				pool_id: 1,
-				balance: bond,
-				points: bond,
-				era: 127
-			}]
-		);
-
-		CurrentEra::<T>::set(Some(100));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(21), 21, bond));
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Unbonded {
-				member: 21,
-				pool_id: 1,
-				balance: bond,
-				points: bond,
-				era: 128
-			}]
-		);
-
-		CurrentEra::<T>::set(Some(101));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(22), 22, bond));
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond },]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Unbonded {
-				member: 22,
-				pool_id: 1,
-				balance: bond,
-				points: bond,
-				era: 129
-			}]
-		);
-
-		// Apply a slash that happened in era 100. This is typically applied with a delay.
-		// Of the total 100, 50 is slashed.
-		assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40);
-		pallet_staking::slashing::do_slash::<Runtime>(
-			&POOL1_BONDED,
-			50,
-			&mut Default::default(),
-			&mut Default::default(),
-			100,
-		);
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 50 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				// This era got slashed 12.5, which rounded up to 13.
-				PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 128, balance: 7 },
-				// This era got slashed 12 instead of 12.5 because an earlier chunk got 0.5 more
-				// slashed, and 12 is all the remaining slash
-				PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 129, balance: 8 },
-				// Bonded pool got slashed for 25, remaining 15 in it.
-				PoolsEvent::PoolSlashed { pool_id: 1, balance: 15 }
-			]
-		);
-	});
-}
-
-#[test]
-fn pool_slash_non_proportional_only_bonded_pool() {
-	// A typical example where a pool member unbonds in era 99, and they can get away with a slash
-	// that happened in era 100, as long as the pool has enough active bond to cover the slash. If
-	// everything else in the slashing/staking system works, this should always be the case.
-	// Nonetheless, `ledger.slash` has been written such that it will slash greedily from any chunk
-	// if it runs out of chunks that it thinks should be affected by the slash.
-	new_test_ext().execute_with(|| {
-		ExistentialDeposit::set(1);
-		BondingDuration::set(28);
-		assert_eq!(Balances::minimum_balance(), 1);
-		assert_eq!(CurrentEra::<T>::get(), None);
-
-		// create the pool, we know this has id 1.
-		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10));
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Created { depositor: 10, pool_id: 1 },
-				PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
-			]
-		);
-
-		// have two members join
-		let bond = 20;
-		assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1));
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true }]
-		);
-
-		// progress and unbond.
-		CurrentEra::<T>::set(Some(99));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond));
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Unbonded {
-				member: 20,
-				pool_id: 1,
-				balance: bond,
-				points: bond,
-				era: 127
-			}]
-		);
-
-		// slash for 30. This will be deducted only from the bonded pool.
-		CurrentEra::<T>::set(Some(100));
-		assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40);
-		pallet_staking::slashing::do_slash::<Runtime>(
-			&POOL1_BONDED,
-			30,
-			&mut Default::default(),
-			&mut Default::default(),
-			100,
-		);
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 30 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::PoolSlashed { pool_id: 1, balance: 10 }]
-		);
-	});
-}
-
-#[test]
-fn pool_slash_non_proportional_bonded_pool_and_chunks() {
-	// An uncommon example where even though some funds are unlocked such that they should not be
-	// affected by a slash, we still slash out of them. This should not happen at all. If a
-	// nomination has unbonded, from the next era onwards, their exposure will drop, so if an era
-	// happens in that era, then their share of that slash should naturally be less, such that only
-	// their active ledger stake is enough to compensate it.
-	new_test_ext().execute_with(|| {
-		ExistentialDeposit::set(1);
-		BondingDuration::set(28);
-		assert_eq!(Balances::minimum_balance(), 1);
-		assert_eq!(CurrentEra::<T>::get(), None);
-
-		// create the pool, we know this has id 1.
-		assert_ok!(Pools::create(RuntimeOrigin::signed(10), 40, 10, 10, 10));
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: 40 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				PoolsEvent::Created { depositor: 10, pool_id: 1 },
-				PoolsEvent::Bonded { member: 10, pool_id: 1, bonded: 40, joined: true },
-			]
-		);
-
-		// have two members join
-		let bond = 20;
-		assert_ok!(Pools::join(RuntimeOrigin::signed(20), bond, 1));
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Bonded { stash: POOL1_BONDED, amount: bond }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Bonded { member: 20, pool_id: 1, bonded: bond, joined: true }]
-		);
-
-		// progress and unbond.
-		CurrentEra::<T>::set(Some(99));
-		assert_ok!(Pools::unbond(RuntimeOrigin::signed(20), 20, bond));
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Unbonded { stash: POOL1_BONDED, amount: bond }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![PoolsEvent::Unbonded {
-				member: 20,
-				pool_id: 1,
-				balance: bond,
-				points: bond,
-				era: 127
-			}]
-		);
-
-		// slash 50. This will be deducted only from the bonded pool and one of the unbonding pools.
-		CurrentEra::<T>::set(Some(100));
-		assert_eq!(BondedPools::<T>::get(1).unwrap().points, 40);
-		pallet_staking::slashing::do_slash::<Runtime>(
-			&POOL1_BONDED,
-			50,
-			&mut Default::default(),
-			&mut Default::default(),
-			100,
-		);
-
-		assert_eq!(
-			staking_events_since_last_call(),
-			vec![StakingEvent::Slashed { staker: POOL1_BONDED, amount: 50 }]
-		);
-		assert_eq!(
-			pool_events_since_last_call(),
-			vec![
-				// out of 20, 10 was taken.
-				PoolsEvent::UnbondingPoolSlashed { pool_id: 1, era: 127, balance: 10 },
-				// out of 40, all was taken.
-				PoolsEvent::PoolSlashed { pool_id: 1, balance: 0 }
-			]
-		);
-	});
-}
diff --git a/substrate/frame/nomination-pools/test-transfer-stake/src/mock.rs b/substrate/frame/nomination-pools/test-transfer-stake/src/mock.rs
deleted file mode 100644
index d913c5fe6948cb5a0fae1d5ccf8e533e5ddb8cc7..0000000000000000000000000000000000000000
--- a/substrate/frame/nomination-pools/test-transfer-stake/src/mock.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use frame_election_provider_support::VoteWeight;
-use frame_support::{
-	assert_ok, derive_impl,
-	pallet_prelude::*,
-	parameter_types,
-	traits::{ConstU64, ConstU8, VariantCountOf},
-	PalletId,
-};
-use sp_runtime::{
-	traits::{Convert, IdentityLookup},
-	BuildStorage, FixedU128, Perbill,
-};
-
-type AccountId = u128;
-type BlockNumber = u64;
-type Balance = u128;
-
-pub(crate) type T = Runtime;
-
-pub(crate) const POOL1_BONDED: AccountId = 20318131474730217858575332831085u128;
-pub(crate) const POOL1_REWARD: AccountId = 20397359637244482196168876781421u128;
-
-#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
-impl frame_system::Config for Runtime {
-	type AccountId = AccountId;
-	type Lookup = IdentityLookup<Self::AccountId>;
-	type Block = Block;
-	type AccountData = pallet_balances::AccountData<Balance>;
-}
-
-impl pallet_timestamp::Config for Runtime {
-	type Moment = u64;
-	type OnTimestampSet = ();
-	type MinimumPeriod = ConstU64<5>;
-	type WeightInfo = ();
-}
-
-parameter_types! {
-	pub static ExistentialDeposit: Balance = 5;
-}
-
-#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
-impl pallet_balances::Config for Runtime {
-	type Balance = Balance;
-	type ExistentialDeposit = ExistentialDeposit;
-	type AccountStore = System;
-	type FreezeIdentifier = RuntimeFreezeReason;
-	type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
-	type RuntimeFreezeReason = RuntimeFreezeReason;
-}
-
-pallet_staking_reward_curve::build! {
-	const I_NPOS: sp_runtime::curve::PiecewiseLinear<'static> = curve!(
-		min_inflation: 0_025_000,
-		max_inflation: 0_100_000,
-		ideal_stake: 0_500_000,
-		falloff: 0_050_000,
-		max_piece_count: 40,
-		test_precision: 0_005_000,
-	);
-}
-
-parameter_types! {
-	pub const RewardCurve: &'static sp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS;
-	pub static BondingDuration: u32 = 3;
-}
-
-#[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
-impl pallet_staking::Config for Runtime {
-	type Currency = Balances;
-	type UnixTime = pallet_timestamp::Pallet<Self>;
-	type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
-	type BondingDuration = BondingDuration;
-	type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
-	type ElectionProvider =
-		frame_election_provider_support::NoElection<(AccountId, BlockNumber, Staking, ())>;
-	type GenesisElectionProvider = Self::ElectionProvider;
-	type VoterList = VoterList;
-	type TargetList = pallet_staking::UseValidatorsMap<Self>;
-	type EventListeners = Pools;
-	type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
-}
-
-parameter_types! {
-	pub static BagThresholds: &'static [VoteWeight] = &[10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000];
-}
-
-type VoterBagsListInstance = pallet_bags_list::Instance1;
-impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime {
-	type RuntimeEvent = RuntimeEvent;
-	type WeightInfo = ();
-	type BagThresholds = BagThresholds;
-	type ScoreProvider = Staking;
-	type Score = VoteWeight;
-}
-
-pub struct BalanceToU256;
-impl Convert<Balance, sp_core::U256> for BalanceToU256 {
-	fn convert(n: Balance) -> sp_core::U256 {
-		n.into()
-	}
-}
-
-pub struct U256ToBalance;
-impl Convert<sp_core::U256, Balance> for U256ToBalance {
-	fn convert(n: sp_core::U256) -> Balance {
-		n.try_into().unwrap()
-	}
-}
-
-parameter_types! {
-	pub const PostUnbondingPoolsWindow: u32 = 10;
-	pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
-}
-
-impl pallet_nomination_pools::Config for Runtime {
-	type RuntimeEvent = RuntimeEvent;
-	type WeightInfo = ();
-	type Currency = Balances;
-	type RuntimeFreezeReason = RuntimeFreezeReason;
-	type RewardCounter = FixedU128;
-	type BalanceToU256 = BalanceToU256;
-	type U256ToBalance = U256ToBalance;
-	type StakeAdapter = pallet_nomination_pools::adapter::TransferStake<Self, Staking>;
-	type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
-	type MaxMetadataLen = ConstU32<256>;
-	type MaxUnbonding = ConstU32<8>;
-	type MaxPointsToBalance = ConstU8<10>;
-	type PalletId = PoolsPalletId;
-	type AdminOrigin = frame_system::EnsureRoot<Self::AccountId>;
-}
-
-type Block = frame_system::mocking::MockBlock<Runtime>;
-
-frame_support::construct_runtime!(
-	pub enum Runtime {
-		System: frame_system,
-		Timestamp: pallet_timestamp,
-		Balances: pallet_balances,
-		Staking: pallet_staking,
-		VoterList: pallet_bags_list::<Instance1>,
-		Pools: pallet_nomination_pools,
-	}
-);
-
-pub fn new_test_ext() -> sp_io::TestExternalities {
-	sp_tracing::try_init_simple();
-	let mut storage = frame_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
-	let _ = pallet_nomination_pools::GenesisConfig::<Runtime> {
-		min_join_bond: 2,
-		min_create_bond: 2,
-		max_pools: Some(3),
-		max_members_per_pool: Some(5),
-		max_members: Some(3 * 5),
-		global_max_commission: Some(Perbill::from_percent(90)),
-	}
-	.assimilate_storage(&mut storage)
-	.unwrap();
-
-	let _ = pallet_balances::GenesisConfig::<Runtime> {
-		balances: vec![(10, 100), (20, 100), (21, 100), (22, 100)],
-	}
-	.assimilate_storage(&mut storage)
-	.unwrap();
-
-	let mut ext = sp_io::TestExternalities::from(storage);
-
-	ext.execute_with(|| {
-		// for events to be deposited.
-		frame_system::Pallet::<Runtime>::set_block_number(1);
-
-		// set some limit for nominations.
-		assert_ok!(Staking::set_staking_configs(
-			RuntimeOrigin::root(),
-			pallet_staking::ConfigOp::Set(10), // minimum nominator bond
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-			pallet_staking::ConfigOp::Noop,
-		));
-	});
-
-	ext
-}
-
-parameter_types! {
-	static ObservedEventsPools: usize = 0;
-	static ObservedEventsStaking: usize = 0;
-	static ObservedEventsBalances: usize = 0;
-}
-
-pub(crate) fn pool_events_since_last_call() -> Vec<pallet_nomination_pools::Event<Runtime>> {
-	let events = System::events()
-		.into_iter()
-		.map(|r| r.event)
-		.filter_map(|e| if let RuntimeEvent::Pools(inner) = e { Some(inner) } else { None })
-		.collect::<Vec<_>>();
-	let already_seen = ObservedEventsPools::get();
-	ObservedEventsPools::set(events.len());
-	events.into_iter().skip(already_seen).collect()
-}
-
-pub(crate) fn staking_events_since_last_call() -> Vec<pallet_staking::Event<Runtime>> {
-	let events = System::events()
-		.into_iter()
-		.map(|r| r.event)
-		.filter_map(|e| if let RuntimeEvent::Staking(inner) = e { Some(inner) } else { None })
-		.collect::<Vec<_>>();
-	let already_seen = ObservedEventsStaking::get();
-	ObservedEventsStaking::set(events.len());
-	events.into_iter().skip(already_seen).collect()
-}
diff --git a/substrate/frame/offences/benchmarking/src/inner.rs b/substrate/frame/offences/benchmarking/src/inner.rs
index 75f3e9931e34c3d8827049d76d0e5510086ccdba..3d3cd470bc24cc3fd63eaf09a52b99ee5eb7b33e 100644
--- a/substrate/frame/offences/benchmarking/src/inner.rs
+++ b/substrate/frame/offences/benchmarking/src/inner.rs
@@ -180,16 +180,12 @@ where
 	<T as frame_system::Config>::RuntimeEvent: TryInto<frame_system::Event<T>>,
 {
 	// make sure that all slashes have been applied
-	// (n nominators + one validator) * (slashed + unlocked) + deposit to reporter +
-	// reporter account endowed + some funds rescinded from issuance.
-	assert_eq!(
-		System::<T>::read_events_for_pallet::<pallet_balances::Event<T>>().len(),
-		2 * (offender_count + 1) + 3
-	);
+	// deposit to reporter + reporter account endowed.
+	assert_eq!(System::<T>::read_events_for_pallet::<pallet_balances::Event<T>>().len(), 2);
 	// (n nominators + one validator) * slashed + Slash Reported
 	assert_eq!(
 		System::<T>::read_events_for_pallet::<pallet_staking::Event<T>>().len(),
-		1 * (offender_count + 1) + 1
+		1 * (offender_count + 1) as usize + 1
 	);
 	// offence
 	assert_eq!(System::<T>::read_events_for_pallet::<pallet_offences::Event>().len(), 1);
diff --git a/substrate/frame/offences/benchmarking/src/mock.rs b/substrate/frame/offences/benchmarking/src/mock.rs
index c5c178aa4443dbd44c38b56e212fcfa1599cbfb6..3c81f2a664e32138f5fee5f43aab30e1f7ce6819 100644
--- a/substrate/frame/offences/benchmarking/src/mock.rs
+++ b/substrate/frame/offences/benchmarking/src/mock.rs
@@ -125,6 +125,7 @@ impl onchain::Config for OnChainSeqPhragmen {
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Test {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
 	type UnixTime = pallet_timestamp::Pallet<Self>;
diff --git a/substrate/frame/proxy/Cargo.toml b/substrate/frame/proxy/Cargo.toml
index a36b2c1cb9c3af5dc545f35d4788d8a043f1a77e..3f2565abac88d2653781046d111c1f50fb757393 100644
--- a/substrate/frame/proxy/Cargo.toml
+++ b/substrate/frame/proxy/Cargo.toml
@@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { features = ["max-encoded-len"], workspace = true }
-frame = { workspace = true, features = ["experimental", "runtime"] }
+frame = { workspace = true, features = ["runtime"] }
 scale-info = { features = ["derive"], workspace = true }
 
 [dev-dependencies]
diff --git a/substrate/frame/recovery/src/mock.rs b/substrate/frame/recovery/src/mock.rs
index 3930db82d6c77ad0133e0726e904c0ac0efb4bc7..86f13b0da4f7632cc67a49a899a670f592030d2e 100644
--- a/substrate/frame/recovery/src/mock.rs
+++ b/substrate/frame/recovery/src/mock.rs
@@ -20,10 +20,7 @@
 use super::*;
 
 use crate as recovery;
-use frame_support::{
-	derive_impl, parameter_types,
-	traits::{OnFinalize, OnInitialize},
-};
+use frame_support::{derive_impl, parameter_types};
 use sp_runtime::BuildStorage;
 
 type Block = frame_system::mocking::MockBlock<Test>;
@@ -86,14 +83,3 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 	.unwrap();
 	t.into()
 }
-
-/// Run until a particular block.
-pub fn run_to_block(n: u64) {
-	while System::block_number() < n {
-		if System::block_number() > 1 {
-			System::on_finalize(System::block_number());
-		}
-		System::set_block_number(System::block_number() + 1);
-		System::on_initialize(System::block_number());
-	}
-}
diff --git a/substrate/frame/recovery/src/tests.rs b/substrate/frame/recovery/src/tests.rs
index 93df07015852e689db0ec02114642baa303ee349..97085df2ae788f34ee43a1c705563e5dd0a46cea 100644
--- a/substrate/frame/recovery/src/tests.rs
+++ b/substrate/frame/recovery/src/tests.rs
@@ -17,12 +17,8 @@
 
 //! Tests for the module.
 
-use super::*;
+use crate::{mock::*, *};
 use frame_support::{assert_noop, assert_ok, traits::Currency};
-use mock::{
-	new_test_ext, run_to_block, Balances, BalancesCall, MaxFriends, Recovery, RecoveryCall,
-	RuntimeCall, RuntimeOrigin, Test,
-};
 use sp_runtime::{bounded_vec, traits::BadOrigin};
 
 #[test]
@@ -70,7 +66,7 @@ fn recovery_life_cycle_works() {
 			delay_period
 		));
 		// Some time has passed, and the user lost their keys!
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		// Using account 1, the user begins the recovery process to recover the lost account
 		assert_ok!(Recovery::initiate_recovery(RuntimeOrigin::signed(1), 5));
 		// Off chain, the user contacts their friends and asks them to vouch for the recovery
@@ -84,7 +80,7 @@ fn recovery_life_cycle_works() {
 			Error::<Test>::DelayPeriod
 		);
 		// We need to wait at least the delay_period number of blocks before we can recover
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 		assert_ok!(Recovery::claim_recovery(RuntimeOrigin::signed(1), 5));
 		// Account 1 can use account 5 to close the active recovery process, claiming the deposited
 		// funds used to initiate the recovery process into account 5.
@@ -128,7 +124,7 @@ fn malicious_recovery_fails() {
 			delay_period
 		));
 		// Some time has passed, and account 1 wants to try and attack this account!
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		// Using account 1, the malicious user begins the recovery process on account 5
 		assert_ok!(Recovery::initiate_recovery(RuntimeOrigin::signed(1), 5));
 		// Off chain, the user **tricks** their friends and asks them to vouch for the recovery
@@ -144,7 +140,7 @@ fn malicious_recovery_fails() {
 			Error::<Test>::DelayPeriod
 		);
 		// Account 1 needs to wait...
-		run_to_block(19);
+		System::run_to_block::<AllPalletsWithSystem>(19);
 		// One more block to wait!
 		assert_noop!(
 			Recovery::claim_recovery(RuntimeOrigin::signed(1), 5),
@@ -158,7 +154,7 @@ fn malicious_recovery_fails() {
 		// Thanks for the free money!
 		assert_eq!(Balances::total_balance(&5), 110);
 		// The recovery process has been closed, so account 1 can't make the claim
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 		assert_noop!(
 			Recovery::claim_recovery(RuntimeOrigin::signed(1), 5),
 			Error::<Test>::NotStarted
@@ -397,7 +393,7 @@ fn claim_recovery_handles_basic_errors() {
 			Recovery::claim_recovery(RuntimeOrigin::signed(1), 5),
 			Error::<Test>::DelayPeriod
 		);
-		run_to_block(11);
+		System::run_to_block::<AllPalletsWithSystem>(11);
 		// Cannot claim an account which has not passed the threshold number of votes
 		assert_ok!(Recovery::vouch_recovery(RuntimeOrigin::signed(2), 5, 1));
 		assert_ok!(Recovery::vouch_recovery(RuntimeOrigin::signed(3), 5, 1));
@@ -427,7 +423,7 @@ fn claim_recovery_works() {
 		assert_ok!(Recovery::vouch_recovery(RuntimeOrigin::signed(3), 5, 1));
 		assert_ok!(Recovery::vouch_recovery(RuntimeOrigin::signed(4), 5, 1));
 
-		run_to_block(11);
+		System::run_to_block::<AllPalletsWithSystem>(11);
 
 		// Account can be recovered.
 		assert_ok!(Recovery::claim_recovery(RuntimeOrigin::signed(1), 5));
@@ -439,7 +435,7 @@ fn claim_recovery_works() {
 		assert_ok!(Recovery::vouch_recovery(RuntimeOrigin::signed(3), 5, 4));
 		assert_ok!(Recovery::vouch_recovery(RuntimeOrigin::signed(4), 5, 4));
 
-		run_to_block(21);
+		System::run_to_block::<AllPalletsWithSystem>(21);
 
 		// Account is re-recovered.
 		assert_ok!(Recovery::claim_recovery(RuntimeOrigin::signed(4), 5));
diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml
index 1284f5ee8947b6ff8d0d6990ce45ba8710ab2711..49a27cfdaab2ac49130687ffc1f48dd5b8ef4794 100644
--- a/substrate/frame/revive/Cargo.toml
+++ b/substrate/frame/revive/Cargo.toml
@@ -25,7 +25,7 @@ hex = { workspace = true }
 impl-trait-for-tuples = { workspace = true }
 log = { workspace = true }
 paste = { workspace = true }
-polkavm = { version = "0.18.0", default-features = false }
+polkavm = { version = "0.19.0", default-features = false }
 rlp = { workspace = true }
 scale-info = { features = ["derive"], workspace = true }
 serde = { features = [
diff --git a/substrate/frame/revive/README.md b/substrate/frame/revive/README.md
index 575920dfaac79f6188b0b81c8f747dd364f13a12..7538f77d10bc07cbed79dbc6eff82869f1a73673 100644
--- a/substrate/frame/revive/README.md
+++ b/substrate/frame/revive/README.md
@@ -49,29 +49,6 @@ This module executes PolkaVM smart contracts. These can potentially be written i
 RISC-V. For now, the only officially supported languages are Solidity (via [`revive`](https://github.com/xermicus/revive))
 and Rust (check the `fixtures` directory for Rust examples).
 
-## Debugging
-
-Contracts can emit messages to the client when called as RPC through the
-[`debug_message`](https://paritytech.github.io/substrate/master/pallet_revive/trait.SyscallDocs.html#tymethod.debug_message)
-API.
-
-Those messages are gathered into an internal buffer and sent to the RPC client. It is up to the individual client if
-and how those messages are presented to the user.
-
-This buffer is also printed as a debug message. In order to see these messages on the node console the log level for the
-`runtime::revive` target needs to be raised to at least the `debug` level. However, those messages are easy to
-overlook because of the noise generated by block production. A good starting point for observing them on the console is
-using this command line in the root directory of the Substrate repository:
-
-```bash
-cargo run --release -- --dev -lerror,runtime::revive=debug
-```
-
-This raises the log level of `runtime::revive` to `debug` and all other targets to `error` in order to prevent them
-from spamming the console.
-
-`--dev`: Use a dev chain spec `--tmp`: Use temporary storage for chain data (the chain state is deleted on exit)
-
 ## Host function tracing
 
 For contract authors, it can be a helpful debugging tool to see which host functions are called, with which arguments,
diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml
index e17bc88a3847a660500671d85c234dfb6d5e0b80..a6f25cc26f3c040ed9db6b56b9b402d8319ee3b1 100644
--- a/substrate/frame/revive/fixtures/Cargo.toml
+++ b/substrate/frame/revive/fixtures/Cargo.toml
@@ -21,7 +21,7 @@ sp-io = { workspace = true, default-features = true, optional = true }
 
 [build-dependencies]
 anyhow = { workspace = true, default-features = true }
-polkavm-linker = { version = "0.18.0" }
+polkavm-linker = { version = "0.19.0" }
 toml = { workspace = true }
 
 [features]
diff --git a/substrate/frame/revive/fixtures/build/_Cargo.toml b/substrate/frame/revive/fixtures/build/_Cargo.toml
index bfb9aaedd6f5cfa2535aa023f4ca68ba5f835cee..483d9775b12a6cbf6443cb4615abc55ca4f6e36f 100644
--- a/substrate/frame/revive/fixtures/build/_Cargo.toml
+++ b/substrate/frame/revive/fixtures/build/_Cargo.toml
@@ -14,7 +14,7 @@ edition = "2021"
 [dependencies]
 uapi = { package = 'pallet-revive-uapi', path = "", features = ["unstable-hostfn"], default-features = false }
 common = { package = 'pallet-revive-fixtures-common', path = "" }
-polkavm-derive = { version = "0.18.0" }
+polkavm-derive = { version = "0.19.0" }
 
 [profile.release]
 opt-level = 3
diff --git a/substrate/frame/revive/fixtures/contracts/call.rs b/substrate/frame/revive/fixtures/contracts/call.rs
index ee51548879d9de9479d5d9f1c695f6c6d4bc5889..7c4c0882c6b87c526b4e90d4d00c399f584fab49 100644
--- a/substrate/frame/revive/fixtures/contracts/call.rs
+++ b/substrate/frame/revive/fixtures/contracts/call.rs
@@ -38,10 +38,10 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::empty(),
 		callee_addr,
-		0u64,       // How much ref_time to devote for the execution. 0 = all.
-		0u64,       // How much proof_size to devote for the execution. 0 = all.
-		None,       // No deposit limit.
-		&[0u8; 32], // Value transferred to the contract.
+		u64::MAX,       // How much ref_time to devote for the execution. u64::MAX = use all.
+		u64::MAX,       // How much proof_size to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
+		&[0u8; 32],     // Value transferred to the contract.
 		callee_input,
 		None,
 	)
diff --git a/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs b/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs
index 129adde2cec915adbf2f3327ab6b536466b607e9..9a8fe5f5f6cc536d39a255db7a9d803e3b4bd70e 100644
--- a/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs
+++ b/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs
@@ -42,9 +42,9 @@ fn assert_call<const N: usize>(callee_address: &[u8; 20], expected_output: [u8;
 	api::call(
 		uapi::CallFlags::ALLOW_REENTRY,
 		callee_address,
-		0u64,
-		0u64,
-		None,
+		u64::MAX,
+		u64::MAX,
+		&[u8::MAX; 32],
 		&[0u8; 32],
 		&[],
 		Some(output_buf_capped),
@@ -67,9 +67,9 @@ fn assert_instantiate<const N: usize>(expected_output: [u8; BUF_SIZE]) {
 
 	api::instantiate(
 		&code_hash,
-		0u64,
-		0u64,
-		None,
+		u64::MAX,
+		u64::MAX,
+		&[u8::MAX; 32],
 		&[0; 32],
 		&[0; 32],
 		None,
diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs
index 2d13b9f7095638a29f523d15b7d1c94dd2012946..19b3ae3fdb2624f341df5c781bf03d7fa77123f1 100644
--- a/substrate/frame/revive/fixtures/contracts/call_return_code.rs
+++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs
@@ -42,10 +42,10 @@ pub extern "C" fn call() {
 	let err_code = match api::call(
 		uapi::CallFlags::empty(),
 		callee_addr,
-		0u64,                // How much ref_time to devote for the execution. 0 = all.
-		0u64,                // How much proof_size to devote for the execution. 0 = all.
-		None,                // No deposit limit.
-		value, 				 // Value transferred to the contract.
+		u64::MAX,                 // How much ref_time to devote for the execution. u64::MAX = use all.
+		u64::MAX,                 // How much proof_size to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
+		value,                    // Value transferred to the contract.
 		input,
 		None,
 	) {
diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs
index 8c8aee9628498e4cbc67e8e9f304106c717a486f..78b275459f0e005841ee5f96d0b03693a137efb3 100644
--- a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs
+++ b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs
@@ -42,10 +42,10 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::empty(),
 		callee_addr,
-		0u64,       // How much ref_time to devote for the execution. 0 = all.
-		0u64,       // How much proof_size to devote for the execution. 0 = all.
-		None,       // No deposit limit.
-		&[0u8; 32], // Value transferred to the contract.
+		u64::MAX,                 // How much ref_time to devote for the execution. u64::MAX = use all.
+		u64::MAX,                 // How much proof_size to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32],           // No deposit limit.
+		&[0u8; 32],               // Value transferred to the contract.
 		callee_input,
 		None,
 	)
diff --git a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs
index 330393e706e98fea7f7e6b04816885f37ac21073..155a4b41bd95f330be6f3af261fc6db7f3acb044 100644
--- a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs
+++ b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs
@@ -40,10 +40,10 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::from_bits(flags).unwrap(),
 		callee_addr,
-		0u64,               // How much ref_time to devote for the execution. 0 = all.
-		0u64,               // How much proof_size to devote for the execution. 0 = all.
-		None,               // No deposit limit.
-		&u256_bytes(value), // Value transferred to the contract.
+		u64::MAX,                 // How much ref_time to devote for the execution. u64::MAX = use all.
+		u64::MAX,                 // How much proof_size to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32],           // No deposit limit.
+		&u256_bytes(value),       // Value transferred to the contract.
 		forwarded_input,
 		None,
 	)
diff --git a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs
index 6ab892a6b7ae8416d923215df0a13b9ee13faf0c..af5c301a353c94cc240c39a73c9ffeffed906152 100644
--- a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs
+++ b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs
@@ -43,8 +43,8 @@ pub extern "C" fn call() {
 		callee_addr,
 		ref_time,
 		proof_size,
-		None,       // No deposit limit.
-		&[0u8; 32], // value transferred to the contract.
+		&[u8::MAX; 32],   // No deposit limit.
+		&[0u8; 32],       // value transferred to the contract.
 		forwarded_input,
 		None,
 	)
diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs
index edad43fae251918ed61f43283cd97c3319348f28..d042dc2c22a2508fafdeecf75e2003006b75a117 100644
--- a/substrate/frame/revive/fixtures/contracts/caller_contract.rs
+++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs
@@ -42,9 +42,9 @@ pub extern "C" fn call() {
 	// Fail to deploy the contract since it returns a non-zero exit status.
 	let res = api::instantiate(
 		code_hash,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&value,
 		&reverted_input,
 		None,
@@ -56,9 +56,9 @@ pub extern "C" fn call() {
 	// Fail to deploy the contract due to insufficient ref_time weight.
 	let res = api::instantiate(
 		code_hash,
-		1u64, // too little ref_time weight
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		1u64,           // too little ref_time weight
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&value,
 		&input,
 		None,
@@ -70,9 +70,9 @@ pub extern "C" fn call() {
 	// Fail to deploy the contract due to insufficient proof_size weight.
 	let res = api::instantiate(
 		code_hash,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		1u64, // Too little proof_size weight
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		1u64,           // Too little proof_size weight
+		&[u8::MAX; 32], // No deposit limit.
 		&value,
 		&input,
 		None,
@@ -86,9 +86,9 @@ pub extern "C" fn call() {
 
 	api::instantiate(
 		code_hash,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&value,
 		&input,
 		Some(&mut callee),
@@ -101,9 +101,9 @@ pub extern "C" fn call() {
 	let res = api::call(
 		uapi::CallFlags::empty(),
 		&callee,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&value,
 		&reverted_input,
 		None,
@@ -114,9 +114,9 @@ pub extern "C" fn call() {
 	let res = api::call(
 		uapi::CallFlags::empty(),
 		&callee,
-		1u64, // Too little ref_time weight.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		1u64,           // Too little ref_time weight.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&value,
 		&input,
 		None,
@@ -127,9 +127,9 @@ pub extern "C" fn call() {
 	let res = api::call(
 		uapi::CallFlags::empty(),
 		&callee,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		1u64, // too little proof_size weight
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		1u64,           // too little proof_size weight
+		&[u8::MAX; 32], // No deposit limit.
 		&value,
 		&input,
 		None,
@@ -141,9 +141,9 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::empty(),
 		&callee,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&value,
 		&input,
 		Some(&mut &mut output[..]),
diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs
index 22d6c5b548d87acd2bffc17401f42a682d328dd7..9b76b9d39ee946b20d07c9ff2458123c9b0c9968 100644
--- a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs
+++ b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs
@@ -54,10 +54,10 @@ pub extern "C" fn call() {
 		api::call(
 			uapi::CallFlags::ALLOW_REENTRY,
 			&addr,
-			0u64,       // How much ref_time to devote for the execution. 0 = all.
-			0u64,       // How much proof_size to devote for the execution. 0 = all.
-			None,       // No deposit limit.
-			&[0u8; 32], // Value transferred to the contract.
+			u64::MAX,       // How much ref_time to devote for the execution. u64::MAX = use all.
+			u64::MAX,       // How much proof_size to devote for the execution. u64::MAX = use all.
+			&[u8::MAX; 32], // No deposit limit.
+			&[0u8; 32],     // Value transferred to the contract.
 			input,
 			None,
 		)
diff --git a/substrate/frame/revive/fixtures/contracts/create1_with_value.rs b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs
index c6adab82886076484b57972c943d676c443d41e9..3554f8f620a29f08e4855ef5b77a2be3a3f6c887 100644
--- a/substrate/frame/revive/fixtures/contracts/create1_with_value.rs
+++ b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs
@@ -34,6 +34,16 @@ pub extern "C" fn call() {
 	api::value_transferred(&mut value);
 
 	// Deploy the contract with no salt (equivalent to create1).
-	let ret = api::instantiate(code_hash, 0u64, 0u64, None, &value, &[], None, None, None);
+	let ret = api::instantiate(
+		code_hash,
+		u64::MAX,
+		u64::MAX,
+		&[u8::MAX; 32],
+		&value,
+		&[],
+		None,
+		None,
+		None
+	);
 	assert!(ret.is_ok());
 }
diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs
index a12c36af856a9018e18630ab5476246bd51392f9..5bb11e27903e7ff15eebc0f6abad80319cd83677 100644
--- a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs
+++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs
@@ -43,10 +43,10 @@ pub extern "C" fn call() {
 	let ret = api::call(
 		uapi::CallFlags::empty(),
 		callee,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		Some(deposit_limit),
-		&[0u8; 32], // Value transferred to the contract.
+		u64::MAX,      // How much ref_time weight to devote for the execution. u64::MAX = use all resources.
+		u64::MAX,      // How much proof_size weight to devote for the execution. u64::MAX = use all resources.
+		deposit_limit,
+		&[0u8; 32],    // Value transferred to the contract.
 		input,
 		None,
 	);
diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs
index ecc0fc79e6fdae77ccb132ea6fc2c6be594ad8a9..f627bc8ba6c418b4d36a42eda2ea49daf382e82e 100644
--- a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs
+++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs
@@ -41,9 +41,9 @@ pub extern "C" fn call() {
 
 	let ret = api::instantiate(
 		code_hash,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		Some(deposit_limit),
+		u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		deposit_limit,
 		&value,
 		input,
 		Some(&mut address),
diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs
index cf12fed27563a960840cd2e31c1a7a28979d4738..660db84028dbdd5b7c990beaa26b5a82dfe4f8dc 100644
--- a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs
+++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs
@@ -49,10 +49,10 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::empty(),
 		callee,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None,
-		&[0u8; 32], // Value transferred to the contract.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = all.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = all.
+		&[u8::MAX; 32], // No deposit limit.
+		&[0u8; 32],     // Value transferred to the contract.
 		input,
 		None,
 	)
diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs b/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs
deleted file mode 100644
index 6c850a9ec66312a65e183ce04e7acdc6166093fb..0000000000000000000000000000000000000000
--- a/substrate/frame/revive/fixtures/contracts/debug_message_invalid_utf8.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Emit a debug message with an invalid utf-8 code.
-#![no_std]
-#![no_main]
-
-extern crate common;
-use uapi::{HostFn, HostFnImpl as api};
-
-#[no_mangle]
-#[polkavm_derive::polkavm_export]
-pub extern "C" fn deploy() {}
-
-#[no_mangle]
-#[polkavm_derive::polkavm_export]
-pub extern "C" fn call() {
-	api::debug_message(b"\xFC").unwrap();
-}
diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs b/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs
deleted file mode 100644
index 0ce2b6b5628da348415e5a39161576161b39146b..0000000000000000000000000000000000000000
--- a/substrate/frame/revive/fixtures/contracts/debug_message_logging_disabled.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-// This file is part of Substrate.
-
-// Copyright (C) Parity Technologies (UK) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// 	http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Emit a "Hello World!" debug message but assume that logging is disabled.
-#![no_std]
-#![no_main]
-
-extern crate common;
-use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode};
-
-#[no_mangle]
-#[polkavm_derive::polkavm_export]
-pub extern "C" fn deploy() {}
-
-#[no_mangle]
-#[polkavm_derive::polkavm_export]
-pub extern "C" fn call() {
-	assert_eq!(api::debug_message(b"Hello World!"), Err(ReturnErrorCode::LoggingDisabled));
-}
diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call.rs b/substrate/frame/revive/fixtures/contracts/delegate_call.rs
index 3cf74acf1321a390e437edf497495881b1a8012c..0dedd5f704cb9a84d8d2387a8b407d91047ac34b 100644
--- a/substrate/frame/revive/fixtures/contracts/delegate_call.rs
+++ b/substrate/frame/revive/fixtures/contracts/delegate_call.rs
@@ -46,7 +46,15 @@ pub extern "C" fn call() {
 	assert!(value[0] == 2u8);
 
 	let input = [0u8; 0];
-	api::delegate_call(uapi::CallFlags::empty(), address, ref_time, proof_size, None, &input, None).unwrap();
+	api::delegate_call(
+		uapi::CallFlags::empty(),
+		address,
+		ref_time,
+		proof_size,
+		&[u8::MAX; 32],
+		&input,
+		None
+	).unwrap();
 
 	api::get_storage(StorageFlags::empty(), &key, value).unwrap();
 	assert!(value[0] == 1u8);
diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_deposit_limit.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_deposit_limit.rs
index 0f157f5a18ac20c64994548aa9b1570c47a1e162..0c503aa93c565bc5c6b593a77a93d0f51ace4498 100644
--- a/substrate/frame/revive/fixtures/contracts/delegate_call_deposit_limit.rs
+++ b/substrate/frame/revive/fixtures/contracts/delegate_call_deposit_limit.rs
@@ -34,7 +34,15 @@ pub extern "C" fn call() {
 	);
 
 	let input = [0u8; 0];
-	let ret = api::delegate_call(uapi::CallFlags::empty(), address, 0, 0, Some(&u256_bytes(deposit_limit)), &input, None);
+	let ret = api::delegate_call(
+		uapi::CallFlags::empty(),
+		address,
+		u64::MAX,
+		u64::MAX,
+		&u256_bytes(deposit_limit),
+		&input,
+		None
+	);
 
 	if let Err(code) = ret {
 		api::return_value(uapi::ReturnFlags::REVERT, &(code as u32).to_le_bytes());
diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs
index a8501dad4692d51477db7a5027107249393be0b2..b7bdb792c76c5c30d39fe1d62daa426a837fb945 100644
--- a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs
+++ b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs
@@ -32,5 +32,13 @@ pub extern "C" fn call() {
 
 	// Delegate call into passed address.
 	let input = [0u8; 0];
-	api::delegate_call(uapi::CallFlags::empty(), address, 0, 0, None, &input, None).unwrap();
+	api::delegate_call(
+		uapi::CallFlags::empty(),
+		address,
+		u64::MAX,
+		u64::MAX,
+		&[u8::MAX; 32],
+		&input,
+		None
+	).unwrap();
 }
diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs
index 8342f4acf95296d621c855408aacc5b8457a4234..c2c7da528ba7c92939facce5dfcb9cb2b82b6ffd 100644
--- a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs
+++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs
@@ -35,9 +35,9 @@ pub extern "C" fn deploy() {
 
 	api::instantiate(
 		code_hash,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&VALUE,
 		&input,
 		Some(&mut address),
@@ -62,9 +62,9 @@ pub extern "C" fn call() {
 	let res = api::call(
 		uapi::CallFlags::empty(),
 		&callee_addr,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&VALUE,
 		&[0u8; 1],
 		None,
@@ -75,9 +75,9 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::empty(),
 		&callee_addr,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, // How much proof_size weight to devote for the execution. 0 = all.
-		None, // No deposit limit.
+		u64::MAX,       // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX,       // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32], // No deposit limit.
 		&VALUE,
 		&[0u8; 0],
 		None,
diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs
index 6e3e708a6b3d82917e7b2cf09b195fd4c80c1743..53fb213143c489e96b59058292030135ed3a7db8 100644
--- a/substrate/frame/revive/fixtures/contracts/drain.rs
+++ b/substrate/frame/revive/fixtures/contracts/drain.rs
@@ -41,7 +41,7 @@ pub extern "C" fn call() {
 		&[0u8; 20],
 		0,
 		0,
-		None,
+		&[u8::MAX; 32],
 		&u256_bytes(balance),
 		&[],
 		None,
diff --git a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs
index 9764859c619b90452eeacbb337a7b94a2cd7d86c..f7cbd75be5aaaf3fcf0ac01bfd33fdd2e83bc387 100644
--- a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs
+++ b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs
@@ -33,10 +33,9 @@ pub extern "C" fn call() {
 
 	let err_code = match api::instantiate(
 		code_hash,
-		0u64, // How much ref_time weight to devote for the execution. 0 = all.
-		0u64, /* How much proof_size weight to devote for the execution. 0 =
-		       * all. */
-		None,                   // No deposit limit.
+		u64::MAX,               // How much ref_time weight to devote for the execution. u64::MAX = use all.
+		u64::MAX,               // How much proof_size weight to devote for the execution. u64::MAX = use all.
+		&[u8::MAX; 32],         // No deposit limit.
 		&u256_bytes(10_000u64), // Value to transfer.
 		input,
 		None,
diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs
index 3d7702c6537adc3528eeb23443347185f144c172..6be5d5c72f9ac7b94cbb191419796c68be4c52cc 100644
--- a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs
+++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs
@@ -52,7 +52,15 @@ fn load_input(delegate_call: bool) {
 	}
 
 	if delegate_call {
-		api::delegate_call(uapi::CallFlags::empty(), address, 0, 0, None, &[], None).unwrap();
+		api::delegate_call(
+			uapi::CallFlags::empty(),
+			address,
+			u64::MAX,
+			u64::MAX,
+			&[u8::MAX; 32],
+			&[],
+			None
+		).unwrap();
 	}
 }
 
diff --git a/substrate/frame/revive/fixtures/contracts/origin.rs b/substrate/frame/revive/fixtures/contracts/origin.rs
index 8e9afd8e80526b8ba272c8cbe8007d19b76afe5b..151ca3da77cd05349ef03db4f0579f73a2230499 100644
--- a/substrate/frame/revive/fixtures/contracts/origin.rs
+++ b/substrate/frame/revive/fixtures/contracts/origin.rs
@@ -49,9 +49,9 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::ALLOW_REENTRY,
 		&addr,
-		0u64,
-		0u64,
-		None,
+		u64::MAX,
+		u64::MAX,
+		&[u8::MAX; 32],
 		&[0; 32],
 		&[],
 		Some(&mut &mut buf[..]),
diff --git a/substrate/frame/revive/fixtures/contracts/read_only_call.rs b/substrate/frame/revive/fixtures/contracts/read_only_call.rs
index ea74d56867f5efd1dd2f671a7b015418f7b1d7b7..0a87ecbb9b140a6e4d797a83a174e565b6954d7d 100644
--- a/substrate/frame/revive/fixtures/contracts/read_only_call.rs
+++ b/substrate/frame/revive/fixtures/contracts/read_only_call.rs
@@ -39,10 +39,10 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::READ_ONLY,
 		callee_addr,
-		0u64,       // How much ref_time to devote for the execution. 0 = all.
-		0u64,       // How much proof_size to devote for the execution. 0 = all.
-		None,       // No deposit limit.
-		&[0u8; 32], // Value transferred to the contract.
+		u64::MAX,                 // How much ref_time to devote for the execution. u64::MAX = all.
+		u64::MAX,                 // How much proof_size to devote for the execution. u64::MAX = all.
+		&[u8::MAX; 32],           // No deposit limit.
+		&[0u8; 32],               // Value transferred to the contract.
 		callee_input,
 		None,
 	)
diff --git a/substrate/frame/revive/fixtures/contracts/recurse.rs b/substrate/frame/revive/fixtures/contracts/recurse.rs
index 2e70d67d8c738c700f35d281a51605e7b9c76170..ead565c01459e6848c846611abe1c6132b751940 100644
--- a/substrate/frame/revive/fixtures/contracts/recurse.rs
+++ b/substrate/frame/revive/fixtures/contracts/recurse.rs
@@ -43,10 +43,10 @@ pub extern "C" fn call() {
 	api::call(
 		uapi::CallFlags::ALLOW_REENTRY,
 		&addr,
-		0u64,       // How much ref_time to devote for the execution. 0 = all.
-		0u64,       // How much deposit_limit to devote for the execution. 0 = all.
-		None,       // No deposit limit.
-		&[0u8; 32], // Value transferred to the contract.
+		u64::MAX,       // How much ref_time to devote for the execution. u64::MAX = use all resources.
+		u64::MAX,       // How much proof_size to devote for the execution. u64::MAX = use all resources.
+		&[u8::MAX; 32], // No deposit limit.
+		&[0u8; 32],     // Value transferred to the contract.
 		&(calls_left - 1).to_le_bytes(),
 		None,
 	)
diff --git a/substrate/frame/revive/fixtures/contracts/return_data_api.rs b/substrate/frame/revive/fixtures/contracts/return_data_api.rs
index 1d483373cffdd8a31123c25148c1469deee06805..1407e5323ea18989bcf5efec8867e48d75ff9710 100644
--- a/substrate/frame/revive/fixtures/contracts/return_data_api.rs
+++ b/substrate/frame/revive/fixtures/contracts/return_data_api.rs
@@ -80,8 +80,16 @@ fn assert_return_data_size_of(expected: u64) {
 
 /// Assert the return data to be reset after a balance transfer.
 fn assert_balance_transfer_does_reset() {
-	api::call(uapi::CallFlags::empty(), &[0u8; 20], 0, 0, None, &u256_bytes(128), &[], None)
-		.unwrap();
+	api::call(
+		uapi::CallFlags::empty(),
+		&[0u8; 20],
+		u64::MAX,
+		u64::MAX,
+		&[u8::MAX; 32],
+		&u256_bytes(128),
+		&[],
+		None
+	).unwrap();
 	assert_return_data_size_of(0);
 }
 
@@ -111,9 +119,9 @@ pub extern "C" fn call() {
 	let mut instantiate = |exit_flag| {
 		api::instantiate(
 			code_hash,
-			0u64,
-			0u64,
-			None,
+			u64::MAX,
+			u64::MAX,
+			&[u8::MAX; 32],
 			&[0; 32],
 			&construct_input(exit_flag),
 			Some(&mut address_buf),
@@ -125,9 +133,9 @@ pub extern "C" fn call() {
 		api::call(
 			uapi::CallFlags::empty(),
 			address_buf,
-			0u64,
-			0u64,
-			None,
+			u64::MAX,
+			u64::MAX,
+			&[u8::MAX; 32],
 			&[0; 32],
 			&construct_input(exit_flag),
 			None,
diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs
index 2f37706634bd126f465ce6ea579cc0878d951bdd..053e545deb19e0c9ecb322ec38f023d566f3b726 100644
--- a/substrate/frame/revive/fixtures/contracts/self_destruct.rs
+++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs
@@ -42,10 +42,10 @@ pub extern "C" fn call() {
 		api::call(
 			uapi::CallFlags::ALLOW_REENTRY,
 			&addr,
-			0u64,       // How much ref_time to devote for the execution. 0 = all.
-			0u64,       // How much proof_size to devote for the execution. 0 = all.
-			None,       // No deposit limit.
-			&[0u8; 32], // Value to transfer.
+			u64::MAX,                 // How much ref_time to devote for the execution. u64 = all.
+			u64::MAX,                 // How much proof_size to devote for the execution. u64 = all.
+			&[u8::MAX; 32],           // No deposit limit.
+			&[0u8; 32],               // Value to transfer.
 			&[0u8; 0],
 			None,
 		)
diff --git a/substrate/frame/revive/fixtures/contracts/debug_message_works.rs b/substrate/frame/revive/fixtures/contracts/to_account_id.rs
similarity index 78%
rename from substrate/frame/revive/fixtures/contracts/debug_message_works.rs
rename to substrate/frame/revive/fixtures/contracts/to_account_id.rs
index 3a2509509d8f156300a256d1a5b0494ea961f5ef..c2a8fce3ec995c9cd28aa31584f6dd66948d6a25 100644
--- a/substrate/frame/revive/fixtures/contracts/debug_message_works.rs
+++ b/substrate/frame/revive/fixtures/contracts/to_account_id.rs
@@ -15,11 +15,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Emit a "Hello World!" debug message.
 #![no_std]
 #![no_main]
 
-extern crate common;
+use common::input;
 use uapi::{HostFn, HostFnImpl as api};
 
 #[no_mangle]
@@ -29,5 +28,13 @@ pub extern "C" fn deploy() {}
 #[no_mangle]
 #[polkavm_derive::polkavm_export]
 pub extern "C" fn call() {
-	api::debug_message(b"Hello World!").unwrap();
+    input!(
+        address: &[u8; 20],
+        expected_account_id: &[u8; 32],
+    );
+
+    let mut account_id = [0u8; 32];
+    api::to_account_id(address, &mut account_id);
+
+    assert!(&account_id == expected_account_id);
 }
diff --git a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs
index 09d45d0a8411a3a9fca37a8034aa880317ad1834..053f97feda4a8dcc8dbd302aed6dbc952d743874 100644
--- a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs
+++ b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs
@@ -33,7 +33,7 @@ pub extern "C" fn call() {
 		&[0u8; 20],
 		0,
 		0,
-		None,
+		&[u8::MAX; 32],
 		&u256_bytes(100u64),
 		&[],
 		None,
diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs
index 38171edf11524614341fe02e41072209886801d6..7685253d1ea2cc227a6ef13f20330c711ada4502 100644
--- a/substrate/frame/revive/fixtures/src/lib.rs
+++ b/substrate/frame/revive/fixtures/src/lib.rs
@@ -22,7 +22,7 @@ extern crate alloc;
 // generated file that tells us where to find the fixtures
 include!(concat!(env!("OUT_DIR"), "/fixture_location.rs"));
 
-/// Load a given wasm module and returns a wasm binary contents along with it's hash.
+/// Load a given wasm module and returns a wasm binary contents along with its hash.
 #[cfg(feature = "std")]
 pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec<u8>, sp_core::H256)> {
 	let out_dir: std::path::PathBuf = FIXTURE_DIR.into();
diff --git a/substrate/frame/revive/proc-macro/src/lib.rs b/substrate/frame/revive/proc-macro/src/lib.rs
index b09bdef14632b937a351cea6b2968e889f2202a0..6e38063d20a674c6c567835ced35def5c0858ac2 100644
--- a/substrate/frame/revive/proc-macro/src/lib.rs
+++ b/substrate/frame/revive/proc-macro/src/lib.rs
@@ -510,12 +510,7 @@ fn expand_functions(def: &EnvDef) -> TokenStream2 {
 			quote! {
 				// wrap body in closure to make sure the tracing is always executed
 				let result = (|| #body)();
-				if ::log::log_enabled!(target: "runtime::revive::strace", ::log::Level::Trace) {
-						use core::fmt::Write;
-						let mut msg = alloc::string::String::default();
-						let _ = core::write!(&mut msg, #trace_fmt_str, #( #trace_fmt_args, )* result);
-						self.ext().append_debug_buffer(&msg);
-				}
+				::log::trace!(target: "runtime::revive::strace", #trace_fmt_str, #( #trace_fmt_args, )* result);
 				result
 			}
 		};
diff --git a/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm
index 77de4ff3b1b3fe1f378ae31bbba24ddb38cc6300..48de6e0aa0c6cc1604008ba4e65c237dd557675b 100644
Binary files a/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/Errors.polkavm differ
diff --git a/substrate/frame/revive/rpc/examples/js/pvm/EventExample.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/EventExample.polkavm
index 6dbc5ca8b108c1ad04cc248b735b2d7d4f43f2a4..cea22e46adcad0dc9bf6375e676dd4923c624c89 100644
Binary files a/substrate/frame/revive/rpc/examples/js/pvm/EventExample.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/EventExample.polkavm differ
diff --git a/substrate/frame/revive/rpc/examples/js/pvm/Flipper.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/Flipper.polkavm
index 488ee684f0c4aee5d64f8b691d048ebefdd51044..67f11e68f117309169a317d415ba547020c1f568 100644
Binary files a/substrate/frame/revive/rpc/examples/js/pvm/Flipper.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/Flipper.polkavm differ
diff --git a/substrate/frame/revive/rpc/examples/js/pvm/FlipperCaller.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/FlipperCaller.polkavm
index 585fbb392a314c15a35e7e529106738bde3be02a..29efafd8722db556b949b04c47c88eeec07535a6 100644
Binary files a/substrate/frame/revive/rpc/examples/js/pvm/FlipperCaller.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/FlipperCaller.polkavm differ
diff --git a/substrate/frame/revive/rpc/examples/js/pvm/PiggyBank.polkavm b/substrate/frame/revive/rpc/examples/js/pvm/PiggyBank.polkavm
index 3f96fdfc21d8d8fc5bf68a34f0c9f7b055afa2a4..78455fcdd7c64a3a1f5e93b6d62cd03b46eb5953 100644
Binary files a/substrate/frame/revive/rpc/examples/js/pvm/PiggyBank.polkavm and b/substrate/frame/revive/rpc/examples/js/pvm/PiggyBank.polkavm differ
diff --git a/substrate/frame/revive/rpc/revive_chain.metadata b/substrate/frame/revive/rpc/revive_chain.metadata
index 402e8c2d22b21471929e9c61acd2cc968af614cf..a03c95b4944f663225642b1678ef66aaccec3fb5 100644
Binary files a/substrate/frame/revive/rpc/revive_chain.metadata and b/substrate/frame/revive/rpc/revive_chain.metadata differ
diff --git a/substrate/frame/revive/src/benchmarking/call_builder.rs b/substrate/frame/revive/src/benchmarking/call_builder.rs
index 1177d47aadc3f2303533ef1e73258883c8e2fb1e..077e18ff5f0b805654d91c86f040e5269819eb67 100644
--- a/substrate/frame/revive/src/benchmarking/call_builder.rs
+++ b/substrate/frame/revive/src/benchmarking/call_builder.rs
@@ -22,7 +22,7 @@ use crate::{
 	storage::meter::Meter,
 	transient_storage::MeterEntry,
 	wasm::{PreparedCall, Runtime},
-	BalanceOf, Config, DebugBuffer, Error, GasMeter, MomentOf, Origin, WasmBlob, Weight,
+	BalanceOf, Config, Error, GasMeter, MomentOf, Origin, WasmBlob, Weight,
 };
 use alloc::{vec, vec::Vec};
 use frame_benchmarking::benchmarking;
@@ -38,7 +38,6 @@ pub struct CallSetup<T: Config> {
 	gas_meter: GasMeter<T>,
 	storage_meter: Meter<T>,
 	value: BalanceOf<T>,
-	debug_message: Option<DebugBuffer>,
 	data: Vec<u8>,
 	transient_storage_size: u32,
 }
@@ -91,7 +90,6 @@ where
 			gas_meter: GasMeter::new(Weight::MAX),
 			storage_meter,
 			value: 0u32.into(),
-			debug_message: None,
 			data: vec![],
 			transient_storage_size: 0,
 		}
@@ -122,16 +120,6 @@ where
 		self.transient_storage_size = size;
 	}
 
-	/// Set the debug message.
-	pub fn enable_debug_message(&mut self) {
-		self.debug_message = Some(Default::default());
-	}
-
-	/// Get the debug message.
-	pub fn debug_message(&self) -> Option<DebugBuffer> {
-		self.debug_message.clone()
-	}
-
 	/// Get the call's input data.
 	pub fn data(&self) -> Vec<u8> {
 		self.data.clone()
@@ -150,7 +138,6 @@ where
 			&mut self.gas_meter,
 			&mut self.storage_meter,
 			self.value,
-			self.debug_message.as_mut(),
 		);
 		if self.transient_storage_size > 0 {
 			Self::with_transient_storage(&mut ext.0, self.transient_storage_size).unwrap();
diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs
index e67c39ec08991636fe26b032a87ab8b94e24a822..18d7bb0afc31ab56c71115078ec84b1f34a0a8ef 100644
--- a/substrate/frame/revive/src/benchmarking/mod.rs
+++ b/substrate/frame/revive/src/benchmarking/mod.rs
@@ -107,8 +107,6 @@ where
 			Code::Upload(module.code),
 			data,
 			salt,
-			DebugInfo::Skip,
-			CollectEvents::Skip,
 		);
 
 		let address = outcome.result?.addr;
@@ -558,6 +556,38 @@ mod benchmarks {
 		assert_eq!(result.unwrap(), 1);
 	}
 
+	#[benchmark(pov_mode = Measured)]
+	fn seal_to_account_id() {
+		// use a mapped address for the benchmark, to ensure that we bench the worst
+		// case (and not the fallback case).
+		let address = {
+			let caller = account("seal_to_account_id", 0, 0);
+			T::Currency::set_balance(&caller, caller_funding::<T>());
+			T::AddressMapper::map(&caller).unwrap();
+			T::AddressMapper::to_address(&caller)
+		};
+
+		let len = <T::AccountId as MaxEncodedLen>::max_encoded_len();
+		build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]);
+
+		let result;
+		#[block]
+		{
+			result = runtime.bench_to_account_id(memory.as_mut_slice(), len as u32, 0);
+		}
+
+		assert_ok!(result);
+		assert_ne!(
+			memory.as_slice()[20..32],
+			[0xEE; 12],
+			"fallback suffix found where none should be"
+		);
+		assert_eq!(
+			T::AccountId::decode(&mut memory.as_slice()),
+			Ok(runtime.ext().to_account_id(&address))
+		);
+	}
+
 	#[benchmark(pov_mode = Measured)]
 	fn seal_code_hash() {
 		let contract = Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
@@ -1047,32 +1077,6 @@ mod benchmarks {
 		);
 	}
 
-	// Benchmark debug_message call
-	// Whereas this function is used in RPC mode only, it still should be secured
-	// against an excessive use.
-	//
-	// i: size of input in bytes up to maximum allowed contract memory or maximum allowed debug
-	// buffer size, whichever is less.
-	#[benchmark]
-	fn seal_debug_message(
-		i: Linear<0, { (limits::code::BLOB_BYTES).min(limits::DEBUG_BUFFER_BYTES) }>,
-	) {
-		let mut setup = CallSetup::<T>::default();
-		setup.enable_debug_message();
-		let (mut ext, _) = setup.ext();
-		let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
-		// Fill memory with printable ASCII bytes.
-		let mut memory = (0..i).zip((32..127).cycle()).map(|i| i.1).collect::<Vec<_>>();
-
-		let result;
-		#[block]
-		{
-			result = runtime.bench_debug_message(memory.as_mut_slice(), 0, i);
-		}
-		assert_ok!(result);
-		assert_eq!(setup.debug_message().unwrap().len() as u32, i);
-	}
-
 	#[benchmark(skip_meta, pov_mode = Measured)]
 	fn get_storage_empty() -> Result<(), BenchmarkError> {
 		let max_key_len = limits::STORAGE_KEY_BYTES;
@@ -1648,8 +1652,8 @@ mod benchmarks {
 				memory.as_mut_slice(),
 				CallFlags::CLONE_INPUT.bits(), // flags
 				0,                             // callee_ptr
-				0,                             // ref_time_limit
-				0,                             // proof_size_limit
+				u64::MAX,                      // ref_time_limit
+				u64::MAX,                      // proof_size_limit
 				callee_len,                    // deposit_ptr
 				callee_len + deposit_len,      // value_ptr
 				0,                             // input_data_ptr
@@ -1688,8 +1692,8 @@ mod benchmarks {
 				memory.as_mut_slice(),
 				0,           // flags
 				0,           // address_ptr
-				0,           // ref_time_limit
-				0,           // proof_size_limit
+				u64::MAX,    // ref_time_limit
+				u64::MAX,    // proof_size_limit
 				address_len, // deposit_ptr
 				0,           // input_data_ptr
 				0,           // input_data_len
@@ -1715,7 +1719,7 @@ mod benchmarks {
 		let value_bytes = Into::<U256>::into(value).encode();
 		let value_len = value_bytes.len() as u32;
 
-		let deposit: BalanceOf<T> = 0u32.into();
+		let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
 		let deposit_bytes = Into::<U256>::into(deposit).encode();
 		let deposit_len = deposit_bytes.len() as u32;
 
@@ -1750,8 +1754,8 @@ mod benchmarks {
 			result = runtime.bench_instantiate(
 				memory.as_mut_slice(),
 				0,                   // code_hash_ptr
-				0,                   // ref_time_limit
-				0,                   // proof_size_limit
+				u64::MAX,            // ref_time_limit
+				u64::MAX,            // proof_size_limit
 				offset(hash_len),    // deposit_ptr
 				offset(deposit_len), // value_ptr
 				offset(value_len),   // input_data_ptr
diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs
index a6a2591497683af65cfa6ed1a4b1a7de2802bb2e..f696f75a4a138fe27255e9e334d309b3db3f72fe 100644
--- a/substrate/frame/revive/src/exec.rs
+++ b/substrate/frame/revive/src/exec.rs
@@ -24,8 +24,8 @@ use crate::{
 	runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo},
 	storage::{self, meter::Diff, WriteOutcome},
 	transient_storage::TransientStorage,
-	BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, Error,
-	Event, ImmutableData, ImmutableDataOf, Pallet as Contracts, LOG_TARGET,
+	BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, Error, Event,
+	ImmutableData, ImmutableDataOf, Pallet as Contracts,
 };
 use alloc::vec::Vec;
 use core::{fmt::Debug, marker::PhantomData, mem};
@@ -53,7 +53,7 @@ use sp_core::{
 };
 use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
 use sp_runtime::{
-	traits::{BadOrigin, Convert, Dispatchable, Saturating, Zero},
+	traits::{BadOrigin, Bounded, Convert, Dispatchable, Saturating, Zero},
 	DispatchError, SaturatedConversion,
 };
 
@@ -293,6 +293,9 @@ pub trait Ext: sealing::Sealed {
 	/// Check if a contract lives at the specified `address`.
 	fn is_contract(&self, address: &H160) -> bool;
 
+	/// Returns the account id for the given `address`.
+	fn to_account_id(&self, address: &H160) -> AccountIdOf<Self::T>;
+
 	/// Returns the code hash of the contract for the given `address`.
 	/// If not a contract but account exists then `keccak_256([])` is returned, otherwise `zero`.
 	fn code_hash(&self, address: &H160) -> H256;
@@ -325,7 +328,7 @@ pub trait Ext: sealing::Sealed {
 	/// Returns `Err(InvalidImmutableAccess)` if called from a constructor.
 	fn get_immutable_data(&mut self) -> Result<ImmutableData, DispatchError>;
 
-	/// Set the the immutable data of the current contract.
+	/// Set the immutable data of the current contract.
 	///
 	/// Returns `Err(InvalidImmutableAccess)` if not called from a constructor.
 	///
@@ -378,19 +381,6 @@ pub trait Ext: sealing::Sealed {
 	/// Charges `diff` from the meter.
 	fn charge_storage(&mut self, diff: &Diff);
 
-	/// Append a string to the debug buffer.
-	///
-	/// It is added as-is without any additional new line.
-	///
-	/// This is a no-op if debug message recording is disabled which is always the case
-	/// when the code is executing on-chain.
-	///
-	/// Returns `true` if debug message recording is enabled. Otherwise `false` is returned.
-	fn append_debug_buffer(&mut self, msg: &str) -> bool;
-
-	/// Returns `true` if debug message recording is enabled. Otherwise `false` is returned.
-	fn debug_buffer_enabled(&self) -> bool;
-
 	/// Call some dispatchable and return the result.
 	fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo;
 
@@ -555,11 +545,6 @@ pub struct Stack<'a, T: Config, E> {
 	frames: BoundedVec<Frame<T>, ConstU32<{ limits::CALL_STACK_DEPTH }>>,
 	/// Statically guarantee that each call stack has at least one frame.
 	first_frame: Frame<T>,
-	/// A text buffer used to output human readable information.
-	///
-	/// All the bytes added to this field should be valid UTF-8. The buffer has no defined
-	/// structure and is intended to be shown to users as-is for debugging purposes.
-	debug_message: Option<&'a mut DebugBuffer>,
 	/// Transient storage used to store data, which is kept for the duration of a transaction.
 	transient_storage: TransientStorage<T>,
 	/// Whether or not actual transfer of funds should be performed.
@@ -765,11 +750,6 @@ where
 {
 	/// Create and run a new call stack by calling into `dest`.
 	///
-	/// # Note
-	///
-	/// `debug_message` should only ever be set to `Some` when executing as an RPC because
-	/// it adds allocations and could be abused to drive the runtime into an OOM panic.
-	///
 	/// # Return Value
 	///
 	/// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)>
@@ -781,7 +761,6 @@ where
 		value: U256,
 		input_data: Vec<u8>,
 		skip_transfer: bool,
-		debug_message: Option<&'a mut DebugBuffer>,
 	) -> ExecResult {
 		let dest = T::AddressMapper::to_account_id(&dest);
 		if let Some((mut stack, executable)) = Self::new(
@@ -791,7 +770,6 @@ where
 			storage_meter,
 			value,
 			skip_transfer,
-			debug_message,
 		)? {
 			stack.run(executable, input_data).map(|_| stack.first_frame.last_frame_output)
 		} else {
@@ -801,11 +779,6 @@ where
 
 	/// Create and run a new call stack by instantiating a new contract.
 	///
-	/// # Note
-	///
-	/// `debug_message` should only ever be set to `Some` when executing as an RPC because
-	/// it adds allocations and could be abused to drive the runtime into an OOM panic.
-	///
 	/// # Return Value
 	///
 	/// Result<(NewContractAccountId, ExecReturnValue), ExecError)>
@@ -818,7 +791,6 @@ where
 		input_data: Vec<u8>,
 		salt: Option<&[u8; 32]>,
 		skip_transfer: bool,
-		debug_message: Option<&'a mut DebugBuffer>,
 	) -> Result<(H160, ExecReturnValue), ExecError> {
 		let (mut stack, executable) = Self::new(
 			FrameArgs::Instantiate {
@@ -832,7 +804,6 @@ where
 			storage_meter,
 			value,
 			skip_transfer,
-			debug_message,
 		)?
 		.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE);
 		let address = T::AddressMapper::to_address(&stack.top_frame().account_id);
@@ -848,7 +819,6 @@ where
 		gas_meter: &'a mut GasMeter<T>,
 		storage_meter: &'a mut storage::meter::Meter<T>,
 		value: BalanceOf<T>,
-		debug_message: Option<&'a mut DebugBuffer>,
 	) -> (Self, E) {
 		Self::new(
 			FrameArgs::Call {
@@ -861,7 +831,6 @@ where
 			storage_meter,
 			value.into(),
 			false,
-			debug_message,
 		)
 		.unwrap()
 		.unwrap()
@@ -878,16 +847,15 @@ where
 		storage_meter: &'a mut storage::meter::Meter<T>,
 		value: U256,
 		skip_transfer: bool,
-		debug_message: Option<&'a mut DebugBuffer>,
 	) -> Result<Option<(Self, E)>, ExecError> {
 		origin.ensure_mapped()?;
 		let Some((first_frame, executable)) = Self::new_frame(
 			args,
 			value,
 			gas_meter,
-			Weight::zero(),
+			Weight::max_value(),
 			storage_meter,
-			BalanceOf::<T>::zero(),
+			BalanceOf::<T>::max_value(),
 			false,
 			true,
 		)?
@@ -903,7 +871,6 @@ where
 			block_number: <frame_system::Pallet<T>>::block_number(),
 			first_frame,
 			frames: Default::default(),
-			debug_message,
 			transient_storage: TransientStorage::new(limits::TRANSIENT_STORAGE_BYTES),
 			skip_transfer,
 			_phantom: Default::default(),
@@ -1117,55 +1084,22 @@ where
 				return Ok(output);
 			}
 
-			// Storage limit is normally enforced as late as possible (when the last frame returns)
-			// so that the ordering of storage accesses does not matter.
-			// (However, if a special limit was set for a sub-call, it should be enforced right
-			// after the sub-call returned. See below for this case of enforcement).
-			if self.frames.is_empty() {
-				let frame = &mut self.first_frame;
-				frame.contract_info.load(&frame.account_id);
-				let contract = frame.contract_info.as_contract();
-				frame.nested_storage.enforce_limit(contract)?;
-			}
-
 			let frame = self.top_frame_mut();
 
-			// If a special limit was set for the sub-call, we enforce it here.
-			// The sub-call will be rolled back in case the limit is exhausted.
+			// The storage deposit is only charged at the end of every call stack.
+			// To make sure that no sub call uses more than it is allowed to,
+			// the limit is manually enforced here.
 			let contract = frame.contract_info.as_contract();
 			frame
 				.nested_storage
-				.enforce_subcall_limit(contract)
+				.enforce_limit(contract)
 				.map_err(|e| ExecError { error: e, origin: ErrorOrigin::Callee })?;
 
-			let account_id = T::AddressMapper::to_address(&frame.account_id);
-			match (entry_point, delegated_code_hash) {
-				(ExportedFunction::Constructor, _) => {
-					// It is not allowed to terminate a contract inside its constructor.
-					if matches!(frame.contract_info, CachedContract::Terminated) {
-						return Err(Error::<T>::TerminatedInConstructor.into());
-					}
-
-					let caller = T::AddressMapper::to_address(self.caller().account_id()?);
-					// Deposit an instantiation event.
-					Contracts::<T>::deposit_event(Event::Instantiated {
-						deployer: caller,
-						contract: account_id,
-					});
-				},
-				(ExportedFunction::Call, Some(code_hash)) => {
-					Contracts::<T>::deposit_event(Event::DelegateCalled {
-						contract: account_id,
-						code_hash,
-					});
-				},
-				(ExportedFunction::Call, None) => {
-					let caller = self.caller();
-					Contracts::<T>::deposit_event(Event::Called {
-						caller: caller.clone(),
-						contract: account_id,
-					});
-				},
+			// It is not allowed to terminate a contract inside its constructor.
+			if entry_point == ExportedFunction::Constructor &&
+				matches!(frame.contract_info, CachedContract::Terminated)
+			{
+				return Err(Error::<T>::TerminatedInConstructor.into());
 			}
 
 			Ok(output)
@@ -1260,13 +1194,6 @@ where
 				}
 			}
 		} else {
-			if let Some((msg, false)) = self.debug_message.as_ref().map(|m| (m, m.is_empty())) {
-				log::debug!(
-					target: LOG_TARGET,
-					"Execution finished with debug buffer: {}",
-					core::str::from_utf8(msg).unwrap_or("<Invalid UTF8>"),
-				);
-			}
 			self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas));
 			if !persist {
 				return;
@@ -1463,7 +1390,7 @@ where
 				FrameArgs::Call { dest: dest.clone(), cached_info, delegated_call: None },
 				value,
 				gas_limit,
-				deposit_limit.try_into().map_err(|_| Error::<T>::BalanceConversionFailed)?,
+				deposit_limit.saturated_into::<BalanceOf<T>>(),
 				// Enable read-only access if requested; cannot disable it if already set.
 				read_only || self.is_read_only(),
 			)? {
@@ -1519,7 +1446,7 @@ where
 			},
 			value,
 			gas_limit,
-			deposit_limit.try_into().map_err(|_| Error::<T>::BalanceConversionFailed)?,
+			deposit_limit.saturated_into::<BalanceOf<T>>(),
 			self.is_read_only(),
 		)?;
 		self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data)
@@ -1549,7 +1476,7 @@ where
 			},
 			value.try_into().map_err(|_| Error::<T>::BalanceConversionFailed)?,
 			gas_limit,
-			deposit_limit.try_into().map_err(|_| Error::<T>::BalanceConversionFailed)?,
+			deposit_limit.saturated_into::<BalanceOf<T>>(),
 			self.is_read_only(),
 		)?;
 		let address = T::AddressMapper::to_address(&self.top_frame().account_id);
@@ -1579,10 +1506,6 @@ where
 				.charge_deposit(frame.account_id.clone(), StorageDeposit::Refund(*deposit));
 		}
 
-		Contracts::<T>::deposit_event(Event::Terminated {
-			contract: account_address,
-			beneficiary: *beneficiary,
-		});
 		Ok(())
 	}
 
@@ -1652,6 +1575,10 @@ where
 		ContractInfoOf::<T>::contains_key(&address)
 	}
 
+	fn to_account_id(&self, address: &H160) -> T::AccountId {
+		T::AddressMapper::to_account_id(address)
+	}
+
 	fn code_hash(&self, address: &H160) -> H256 {
 		<ContractInfoOf<T>>::get(&address)
 			.map(|contract| contract.code_hash)
@@ -1769,28 +1696,6 @@ where
 		self.top_frame_mut().nested_storage.charge(diff)
 	}
 
-	fn debug_buffer_enabled(&self) -> bool {
-		self.debug_message.is_some()
-	}
-
-	fn append_debug_buffer(&mut self, msg: &str) -> bool {
-		if let Some(buffer) = &mut self.debug_message {
-			buffer
-				.try_extend(&mut msg.bytes())
-				.map_err(|_| {
-					log::debug!(
-						target: LOG_TARGET,
-						"Debug buffer (of {} bytes) exhausted!",
-						limits::DEBUG_BUFFER_BYTES,
-					)
-				})
-				.ok();
-			true
-		} else {
-			false
-		}
-	}
-
 	fn call_runtime(&self, call: <Self::T as Config>::RuntimeCall) -> DispatchResultWithPostInfo {
 		let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.account_id().clone()).into();
 		origin.add_filter(T::CallFilter::contains);
@@ -1857,11 +1762,6 @@ where
 
 		Self::increment_refcount(hash)?;
 		Self::decrement_refcount(prev_hash);
-		Contracts::<Self::T>::deposit_event(Event::ContractCodeUpdated {
-			contract: T::AddressMapper::to_address(&frame.account_id),
-			new_code_hash: hash,
-			old_code_hash: prev_hash,
-		});
 		Ok(())
 	}
 
@@ -2113,7 +2013,6 @@ mod tests {
 					value.into(),
 					vec![],
 					false,
-					None,
 				),
 				Ok(_)
 			);
@@ -2206,7 +2105,6 @@ mod tests {
 				value.into(),
 				vec![],
 				false,
-				None,
 			)
 			.unwrap();
 
@@ -2247,7 +2145,6 @@ mod tests {
 				value.into(),
 				vec![],
 				false,
-				None,
 			));
 
 			assert_eq!(get_balance(&ALICE), 100 - value);
@@ -2284,7 +2181,6 @@ mod tests {
 					U256::zero(),
 					vec![],
 					false,
-					None,
 				),
 				ExecError {
 					error: Error::<Test>::CodeNotFound.into(),
@@ -2302,7 +2198,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -2331,7 +2226,6 @@ mod tests {
 				55u64.into(),
 				vec![],
 				false,
-				None,
 			)
 			.unwrap();
 
@@ -2381,7 +2275,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			);
 
 			let output = result.unwrap();
@@ -2411,7 +2304,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			);
 
 			let output = result.unwrap();
@@ -2441,7 +2333,6 @@ mod tests {
 				U256::zero(),
 				vec![1, 2, 3, 4],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -2478,7 +2369,6 @@ mod tests {
 					vec![1, 2, 3, 4],
 					Some(&[0; 32]),
 					false,
-					None,
 				);
 				assert_matches!(result, Ok(_));
 			});
@@ -2533,7 +2423,6 @@ mod tests {
 				value.into(),
 				vec![],
 				false,
-				None,
 			);
 
 			assert_matches!(result, Ok(_));
@@ -2598,7 +2487,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			);
 
 			assert_matches!(result, Ok(_));
@@ -2664,7 +2552,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			);
 
 			assert_matches!(result, Ok(_));
@@ -2697,7 +2584,40 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
+			);
+			assert_matches!(result, Ok(_));
+		});
+	}
+
+	#[test]
+	fn to_account_id_returns_proper_values() {
+		let bob_code_hash = MockLoader::insert(Call, |ctx, _| {
+			let alice_account_id = <Test as Config>::AddressMapper::to_account_id(&ALICE_ADDR);
+			assert_eq!(ctx.ext.to_account_id(&ALICE_ADDR), alice_account_id);
+
+			const UNMAPPED_ADDR: H160 = H160([99u8; 20]);
+			let mut unmapped_fallback_account_id = [0xEE; 32];
+			unmapped_fallback_account_id[..20].copy_from_slice(UNMAPPED_ADDR.as_bytes());
+			assert_eq!(
+				ctx.ext.to_account_id(&UNMAPPED_ADDR),
+				AccountId32::new(unmapped_fallback_account_id)
+			);
+
+			exec_success()
+		});
+
+		ExtBuilder::default().build().execute_with(|| {
+			place_contract(&BOB, bob_code_hash);
+			let origin = Origin::from_account_id(ALICE);
+			let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap();
+			let result = MockStack::run_call(
+				origin,
+				BOB_ADDR,
+				&mut GasMeter::<Test>::new(GAS_LIMIT),
+				&mut storage_meter,
+				U256::zero(),
+				vec![0],
+				false,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -2735,7 +2655,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -2762,7 +2681,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -2807,7 +2725,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -2834,7 +2751,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -2861,7 +2777,6 @@ mod tests {
 				1u64.into(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Err(_));
 		});
@@ -2906,7 +2821,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -2952,7 +2866,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			);
 
 			assert_matches!(result, Ok(_));
@@ -2979,7 +2892,6 @@ mod tests {
 					vec![],
 					Some(&[0; 32]),
 					false,
-					None,
 				),
 				Err(_)
 			);
@@ -3015,7 +2927,6 @@ mod tests {
 						vec![],
 						Some(&[0 ;32]),
 						false,
-						None,
 					),
 					Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address
 				);
@@ -3031,13 +2942,6 @@ mod tests {
 					ContractInfo::<Test>::load_code_hash(&instantiated_contract_id).unwrap(),
 					dummy_ch
 				);
-				assert_eq!(
-					&events(),
-					&[Event::Instantiated {
-						deployer: ALICE_ADDR,
-						contract: instantiated_contract_address
-					}]
-				);
 			});
 	}
 
@@ -3070,7 +2974,6 @@ mod tests {
 						vec![],
 						Some(&[0; 32]),
 						false,
-						None,
 					),
 					Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address
 				);
@@ -3098,8 +3001,8 @@ mod tests {
 				let (address, output) = ctx
 					.ext
 					.instantiate(
-						Weight::zero(),
-						U256::zero(),
+						Weight::MAX,
+						U256::MAX,
 						dummy_ch,
 						<Test as Config>::Currency::minimum_balance().into(),
 						vec![],
@@ -3135,7 +3038,6 @@ mod tests {
 						(min_balance * 10).into(),
 						vec![],
 						false,
-						None,
 					),
 					Ok(_)
 				);
@@ -3155,19 +3057,6 @@ mod tests {
 					ContractInfo::<Test>::load_code_hash(&instantiated_contract_id).unwrap(),
 					dummy_ch
 				);
-				assert_eq!(
-					&events(),
-					&[
-						Event::Instantiated {
-							deployer: BOB_ADDR,
-							contract: instantiated_contract_address
-						},
-						Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: BOB_ADDR
-						},
-					]
-				);
 			});
 	}
 
@@ -3216,17 +3105,9 @@ mod tests {
 						U256::zero(),
 						vec![],
 						false,
-						None,
 					),
 					Ok(_)
 				);
-
-				// The contract wasn't instantiated so we don't expect to see an instantiation
-				// event here.
-				assert_eq!(
-					&events(),
-					&[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB_ADDR },]
-				);
 			});
 	}
 
@@ -3260,7 +3141,6 @@ mod tests {
 						vec![],
 						Some(&[0; 32]),
 						false,
-						None,
 					),
 					Err(Error::<Test>::TerminatedInConstructor.into())
 				);
@@ -3325,7 +3205,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -3388,7 +3267,6 @@ mod tests {
 					vec![],
 					Some(&[0; 32]),
 					false,
-					None,
 				);
 				assert_matches!(result, Ok(_));
 			});
@@ -3435,113 +3313,11 @@ mod tests {
 					U256::zero(),
 					vec![],
 					false,
-					None,
 				)
 				.unwrap();
 			});
 	}
 
-	#[test]
-	fn printing_works() {
-		let code_hash = MockLoader::insert(Call, |ctx, _| {
-			ctx.ext.append_debug_buffer("This is a test");
-			ctx.ext.append_debug_buffer("More text");
-			exec_success()
-		});
-
-		let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap();
-
-		ExtBuilder::default().build().execute_with(|| {
-			let min_balance = <Test as Config>::Currency::minimum_balance();
-
-			let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
-			set_balance(&ALICE, min_balance * 10);
-			place_contract(&BOB, code_hash);
-			let origin = Origin::from_account_id(ALICE);
-			let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap();
-			MockStack::run_call(
-				origin,
-				BOB_ADDR,
-				&mut gas_meter,
-				&mut storage_meter,
-				U256::zero(),
-				vec![],
-				false,
-				Some(&mut debug_buffer),
-			)
-			.unwrap();
-		});
-
-		assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text");
-	}
-
-	#[test]
-	fn printing_works_on_fail() {
-		let code_hash = MockLoader::insert(Call, |ctx, _| {
-			ctx.ext.append_debug_buffer("This is a test");
-			ctx.ext.append_debug_buffer("More text");
-			exec_trapped()
-		});
-
-		let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap();
-
-		ExtBuilder::default().build().execute_with(|| {
-			let min_balance = <Test as Config>::Currency::minimum_balance();
-
-			let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
-			set_balance(&ALICE, min_balance * 10);
-			place_contract(&BOB, code_hash);
-			let origin = Origin::from_account_id(ALICE);
-			let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap();
-			let result = MockStack::run_call(
-				origin,
-				BOB_ADDR,
-				&mut gas_meter,
-				&mut storage_meter,
-				U256::zero(),
-				vec![],
-				false,
-				Some(&mut debug_buffer),
-			);
-			assert!(result.is_err());
-		});
-
-		assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text");
-	}
-
-	#[test]
-	fn debug_buffer_is_limited() {
-		let code_hash = MockLoader::insert(Call, move |ctx, _| {
-			ctx.ext.append_debug_buffer("overflowing bytes");
-			exec_success()
-		});
-
-		// Pre-fill the buffer almost up to its limit, leaving not enough space to the message
-		let debug_buf_before = DebugBuffer::try_from(vec![0u8; DebugBuffer::bound() - 5]).unwrap();
-		let mut debug_buf_after = debug_buf_before.clone();
-
-		ExtBuilder::default().build().execute_with(|| {
-			let min_balance = <Test as Config>::Currency::minimum_balance();
-			let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
-			set_balance(&ALICE, min_balance * 10);
-			place_contract(&BOB, code_hash);
-			let origin = Origin::from_account_id(ALICE);
-			let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap();
-			MockStack::run_call(
-				origin,
-				BOB_ADDR,
-				&mut gas_meter,
-				&mut storage_meter,
-				U256::zero(),
-				vec![],
-				false,
-				Some(&mut debug_buf_after),
-			)
-			.unwrap();
-			assert_eq!(debug_buf_before, debug_buf_after);
-		});
-	}
-
 	#[test]
 	fn call_reentry_direct_recursion() {
 		// call the contract passed as input with disabled reentry
@@ -3569,7 +3345,6 @@ mod tests {
 				U256::zero(),
 				CHARLIE_ADDR.as_bytes().to_vec(),
 				false,
-				None,
 			));
 
 			// Calling into oneself fails
@@ -3582,7 +3357,6 @@ mod tests {
 					U256::zero(),
 					BOB_ADDR.as_bytes().to_vec(),
 					false,
-					None,
 				)
 				.map_err(|e| e.error),
 				<Error<Test>>::ReentranceDenied,
@@ -3633,7 +3407,6 @@ mod tests {
 					U256::zero(),
 					vec![0],
 					false,
-					None,
 				)
 				.map_err(|e| e.error),
 				<Error<Test>>::ReentranceDenied,
@@ -3668,31 +3441,20 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			)
 			.unwrap();
 
 			let remark_hash = <Test as frame_system::Config>::Hashing::hash(b"Hello World");
 			assert_eq!(
 				System::events(),
-				vec![
-					EventRecord {
-						phase: Phase::Initialization,
-						event: MetaEvent::System(frame_system::Event::Remarked {
-							sender: BOB_FALLBACK,
-							hash: remark_hash
-						}),
-						topics: vec![],
-					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: MetaEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: BOB_ADDR,
-						}),
-						topics: vec![],
-					},
-				]
+				vec![EventRecord {
+					phase: Phase::Initialization,
+					event: MetaEvent::System(frame_system::Event::Remarked {
+						sender: BOB_FALLBACK,
+						hash: remark_hash
+					}),
+					topics: vec![],
+				},]
 			);
 		});
 	}
@@ -3753,7 +3515,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			)
 			.unwrap();
 
@@ -3782,14 +3543,6 @@ mod tests {
 						},),
 						topics: vec![],
 					},
-					EventRecord {
-						phase: Phase::Initialization,
-						event: MetaEvent::Contracts(crate::Event::Called {
-							caller: Origin::from_account_id(ALICE),
-							contract: BOB_ADDR,
-						}),
-						topics: vec![],
-					},
 				]
 			);
 		});
@@ -3802,8 +3555,8 @@ mod tests {
 		let succ_fail_code = MockLoader::insert(Constructor, move |ctx, _| {
 			ctx.ext
 				.instantiate(
-					Weight::zero(),
-					U256::zero(),
+					Weight::MAX,
+					U256::MAX,
 					fail_code,
 					ctx.ext.minimum_balance() * 100,
 					vec![],
@@ -3819,8 +3572,8 @@ mod tests {
 			let addr = ctx
 				.ext
 				.instantiate(
-					Weight::zero(),
-					U256::zero(),
+					Weight::MAX,
+					U256::MAX,
 					success_code,
 					ctx.ext.minimum_balance() * 100,
 					vec![],
@@ -3880,7 +3633,6 @@ mod tests {
 					vec![],
 					Some(&[0; 32]),
 					false,
-					None,
 				)
 				.ok();
 				assert_eq!(System::account_nonce(&ALICE), 0);
@@ -3894,7 +3646,6 @@ mod tests {
 					vec![],
 					Some(&[0; 32]),
 					false,
-					None,
 				));
 				assert_eq!(System::account_nonce(&ALICE), 1);
 
@@ -3907,7 +3658,6 @@ mod tests {
 					vec![],
 					Some(&[0; 32]),
 					false,
-					None,
 				));
 				assert_eq!(System::account_nonce(&ALICE), 2);
 
@@ -3920,7 +3670,6 @@ mod tests {
 					vec![],
 					Some(&[0; 32]),
 					false,
-					None,
 				));
 				assert_eq!(System::account_nonce(&ALICE), 3);
 			});
@@ -3989,7 +3738,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -4101,7 +3849,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -4141,7 +3888,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -4181,7 +3927,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -4235,7 +3980,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -4292,7 +4036,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -4368,7 +4111,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -4439,7 +4181,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -4478,7 +4219,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			));
 		});
 	}
@@ -4541,7 +4281,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -4575,7 +4314,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -4597,7 +4335,7 @@ mod tests {
 				// Successful instantiation should set the output
 				let address = ctx
 					.ext
-					.instantiate(Weight::zero(), U256::zero(), ok_ch, value, vec![], None)
+					.instantiate(Weight::MAX, U256::MAX, ok_ch, value, vec![], None)
 					.unwrap();
 				assert_eq!(
 					ctx.ext.last_frame_output(),
@@ -4606,15 +4344,7 @@ mod tests {
 
 				// Balance transfers should reset the output
 				ctx.ext
-					.call(
-						Weight::zero(),
-						U256::zero(),
-						&address,
-						U256::from(1),
-						vec![],
-						true,
-						false,
-					)
+					.call(Weight::MAX, U256::MAX, &address, U256::from(1), vec![], true, false)
 					.unwrap();
 				assert_eq!(ctx.ext.last_frame_output(), &Default::default());
 
@@ -4659,7 +4389,6 @@ mod tests {
 					U256::zero(),
 					vec![],
 					false,
-					None,
 				)
 				.unwrap()
 			});
@@ -4728,7 +4457,6 @@ mod tests {
 				U256::zero(),
 				vec![0],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -4800,7 +4528,6 @@ mod tests {
 				U256::zero(),
 				vec![],
 				false,
-				None,
 			);
 			assert_matches!(result, Ok(_));
 		});
@@ -4827,7 +4554,7 @@ mod tests {
 
 				// Constructors can not access the immutable data
 				ctx.ext
-					.instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None)
+					.instantiate(Weight::MAX, U256::MAX, dummy_ch, value, vec![], None)
 					.unwrap();
 
 				exec_success()
@@ -4852,7 +4579,6 @@ mod tests {
 					U256::zero(),
 					vec![],
 					false,
-					None,
 				)
 				.unwrap()
 			});
@@ -4922,7 +4648,6 @@ mod tests {
 					U256::zero(),
 					vec![],
 					false,
-					None,
 				)
 				.unwrap()
 			});
@@ -4944,7 +4669,7 @@ mod tests {
 			move |ctx, _| {
 				let value = <Test as Config>::Currency::minimum_balance().into();
 				ctx.ext
-					.instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None)
+					.instantiate(Weight::MAX, U256::MAX, dummy_ch, value, vec![], None)
 					.unwrap();
 
 				exec_success()
@@ -4969,7 +4694,6 @@ mod tests {
 					U256::zero(),
 					vec![],
 					false,
-					None,
 				)
 				.unwrap()
 			});
@@ -4989,7 +4713,7 @@ mod tests {
 			move |ctx, _| {
 				let value = <Test as Config>::Currency::minimum_balance().into();
 				ctx.ext
-					.instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None)
+					.instantiate(Weight::MAX, U256::MAX, dummy_ch, value, vec![], None)
 					.unwrap();
 
 				exec_success()
@@ -5014,7 +4738,6 @@ mod tests {
 					U256::zero(),
 					vec![],
 					false,
-					None,
 				)
 				.unwrap()
 			});
@@ -5070,7 +4793,6 @@ mod tests {
 					U256::zero(),
 					vec![0],
 					false,
-					None,
 				),
 				Ok(_)
 			);
diff --git a/substrate/frame/revive/src/gas.rs b/substrate/frame/revive/src/gas.rs
index 9aad84e69201e4991679f7b1e57590598760415e..e8338db12192bfe5ba698f191b3469be12d5a511 100644
--- a/substrate/frame/revive/src/gas.rs
+++ b/substrate/frame/revive/src/gas.rs
@@ -22,7 +22,7 @@ use frame_support::{
 	weights::Weight,
 	DefaultNoBound,
 };
-use sp_runtime::{traits::Zero, DispatchError};
+use sp_runtime::DispatchError;
 
 #[cfg(test)]
 use std::{any::Any, fmt::Debug};
@@ -89,7 +89,7 @@ pub struct RefTimeLeft(u64);
 
 /// Resource that needs to be synced to the executor.
 ///
-/// Wrapped to make sure that the resource will be synced back the the executor.
+/// Wrapped to make sure that the resource will be synced back to the executor.
 #[must_use]
 pub struct Syncable(polkavm::Gas);
 
@@ -168,25 +168,19 @@ impl<T: Config> GasMeter<T> {
 		}
 	}
 
-	/// Create a new gas meter by removing gas from the current meter.
+	/// Create a new gas meter by removing *all* the gas from the current meter.
 	///
-	/// # Note
-	///
-	/// Passing `0` as amount is interpreted as "all remaining gas".
+	/// This should only be used by the primordial frame in a sequence of calls - every subsequent
+	/// frame should use [`nested`](Self::nested).
+	pub fn nested_take_all(&mut self) -> Self {
+		let gas_left = self.gas_left;
+		self.gas_left -= gas_left;
+		GasMeter::new(gas_left)
+	}
+
+	/// Create a new gas meter for a nested call by removing gas from the current meter.
 	pub fn nested(&mut self, amount: Weight) -> Self {
-		let amount = Weight::from_parts(
-			if amount.ref_time().is_zero() {
-				self.gas_left().ref_time()
-			} else {
-				amount.ref_time()
-			},
-			if amount.proof_size().is_zero() {
-				self.gas_left().proof_size()
-			} else {
-				amount.proof_size()
-			},
-		)
-		.min(self.gas_left);
+		let amount = amount.min(self.gas_left);
 		self.gas_left -= amount;
 		GasMeter::new(amount)
 	}
@@ -392,6 +386,50 @@ mod tests {
 		assert!(gas_meter.charge(SimpleToken(1)).is_err());
 	}
 
+	/// Previously, passing a `Weight` of 0 to `nested` would consume all of the meter's current
+	/// gas.
+	///
+	/// Now, a `Weight` of 0 means no gas for the nested call.
+	#[test]
+	fn nested_zero_gas_requested() {
+		let test_weight = 50000.into();
+		let mut gas_meter = GasMeter::<Test>::new(test_weight);
+		let gas_for_nested_call = gas_meter.nested(0.into());
+
+		assert_eq!(gas_meter.gas_left(), 50000.into());
+		assert_eq!(gas_for_nested_call.gas_left(), 0.into())
+	}
+
+	#[test]
+	fn nested_some_gas_requested() {
+		let test_weight = 50000.into();
+		let mut gas_meter = GasMeter::<Test>::new(test_weight);
+		let gas_for_nested_call = gas_meter.nested(10000.into());
+
+		assert_eq!(gas_meter.gas_left(), 40000.into());
+		assert_eq!(gas_for_nested_call.gas_left(), 10000.into())
+	}
+
+	#[test]
+	fn nested_all_gas_requested() {
+		let test_weight = Weight::from_parts(50000, 50000);
+		let mut gas_meter = GasMeter::<Test>::new(test_weight);
+		let gas_for_nested_call = gas_meter.nested(test_weight);
+
+		assert_eq!(gas_meter.gas_left(), Weight::from_parts(0, 0));
+		assert_eq!(gas_for_nested_call.gas_left(), 50_000.into())
+	}
+
+	#[test]
+	fn nested_excess_gas_requested() {
+		let test_weight = Weight::from_parts(50000, 50000);
+		let mut gas_meter = GasMeter::<Test>::new(test_weight);
+		let gas_for_nested_call = gas_meter.nested(test_weight + 10000.into());
+
+		assert_eq!(gas_meter.gas_left(), Weight::from_parts(0, 0));
+		assert_eq!(gas_for_nested_call.gas_left(), 50_000.into())
+	}
+
 	// Make sure that the gas meter does not charge in case of overcharge
 	#[test]
 	fn overcharge_does_not_charge() {
diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs
index 04bce264a188c1b55f1a9c53398903a9a9e55545..a9f2842c35f6a2fc3fe151879f70f99993865a14 100644
--- a/substrate/frame/revive/src/lib.rs
+++ b/substrate/frame/revive/src/lib.rs
@@ -45,7 +45,7 @@ use crate::{
 		runtime::{gas_from_fee, GAS_PRICE},
 		GasEncoder, GenericTransaction,
 	},
-	exec::{AccountIdOf, ExecError, Executable, Ext, Key, Origin, Stack as ExecStack},
+	exec::{AccountIdOf, ExecError, Executable, Ext, Key, Stack as ExecStack},
 	gas::GasMeter,
 	storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
 	wasm::{CodeInfo, RuntimeCosts, WasmBlob},
@@ -71,7 +71,7 @@ use frame_support::{
 use frame_system::{
 	ensure_signed,
 	pallet_prelude::{BlockNumberFor, OriginFor},
-	EventRecord, Pallet as System,
+	Pallet as System,
 };
 use pallet_transaction_payment::OnChargeTransaction;
 use scale_info::TypeInfo;
@@ -84,7 +84,7 @@ use sp_runtime::{
 pub use crate::{
 	address::{create1, create2, AccountId32Mapper, AddressMapper},
 	debug::Tracing,
-	exec::MomentOf,
+	exec::{MomentOf, Origin},
 	pallet::*,
 };
 pub use primitives::*;
@@ -98,9 +98,6 @@ type BalanceOf<T> =
 	<<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
 type OnChargeTransactionBalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<T>>::Balance;
 type CodeVec = BoundedVec<u8, ConstU32<{ limits::code::BLOB_BYTES }>>;
-type EventRecordOf<T> =
-	EventRecord<<T as frame_system::Config>::RuntimeEvent, <T as frame_system::Config>::Hash>;
-type DebugBuffer = BoundedVec<u8, ConstU32<{ limits::DEBUG_BUFFER_BYTES }>>;
 type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
 
 /// Used as a sentinel value when reading and writing contract memory.
@@ -258,9 +255,9 @@ pub mod pallet {
 		#[pallet::no_default_bounds]
 		type InstantiateOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
 
-		/// For most production chains, it's recommended to use the `()` implementation of this
-		/// trait. This implementation offers additional logging when the log target
-		/// "runtime::revive" is set to trace.
+		/// Debugging utilities for contracts.
+		/// For production chains, it's recommended to use the `()` implementation of this
+		/// trait.
 		#[pallet::no_default_bounds]
 		type Debug: Debugger<Self>;
 
@@ -382,25 +379,6 @@ pub mod pallet {
 
 	#[pallet::event]
 	pub enum Event<T: Config> {
-		/// Contract deployed by address at the specified address.
-		Instantiated { deployer: H160, contract: H160 },
-
-		/// Contract has been removed.
-		///
-		/// # Note
-		///
-		/// The only way for a contract to be removed and emitting this event is by calling
-		/// `seal_terminate`.
-		Terminated {
-			/// The contract that was terminated.
-			contract: H160,
-			/// The account that received the contracts remaining balance
-			beneficiary: H160,
-		},
-
-		/// Code with the specified hash has been stored.
-		CodeStored { code_hash: H256, deposit_held: BalanceOf<T>, uploader: H160 },
-
 		/// A custom event emitted by the contract.
 		ContractEmitted {
 			/// The contract that emitted the event.
@@ -412,54 +390,6 @@ pub mod pallet {
 			/// Number of topics is capped by [`limits::NUM_EVENT_TOPICS`].
 			topics: Vec<H256>,
 		},
-
-		/// A code with the specified hash was removed.
-		CodeRemoved { code_hash: H256, deposit_released: BalanceOf<T>, remover: H160 },
-
-		/// A contract's code was updated.
-		ContractCodeUpdated {
-			/// The contract that has been updated.
-			contract: H160,
-			/// New code hash that was set for the contract.
-			new_code_hash: H256,
-			/// Previous code hash of the contract.
-			old_code_hash: H256,
-		},
-
-		/// A contract was called either by a plain account or another contract.
-		///
-		/// # Note
-		///
-		/// Please keep in mind that like all events this is only emitted for successful
-		/// calls. This is because on failure all storage changes including events are
-		/// rolled back.
-		Called {
-			/// The caller of the `contract`.
-			caller: Origin<T>,
-			/// The contract that was called.
-			contract: H160,
-		},
-
-		/// A contract delegate called a code hash.
-		///
-		/// # Note
-		///
-		/// Please keep in mind that like all events this is only emitted for successful
-		/// calls. This is because on failure all storage changes including events are
-		/// rolled back.
-		DelegateCalled {
-			/// The contract that performed the delegate call and hence in whose context
-			/// the `code_hash` is executed.
-			contract: H160,
-			/// The code hash that was delegate called.
-			code_hash: H256,
-		},
-
-		/// Some funds have been transferred and held as storage deposit.
-		StorageDepositTransferredAndHeld { from: H160, to: H160, amount: BalanceOf<T> },
-
-		/// Some storage deposit funds have been transferred and released.
-		StorageDepositTransferredAndReleased { from: H160, to: H160, amount: BalanceOf<T> },
 	}
 
 	#[pallet::error]
@@ -810,9 +740,8 @@ pub mod pallet {
 				gas_limit,
 				DepositLimit::Balance(storage_deposit_limit),
 				data,
-				DebugInfo::Skip,
-				CollectEvents::Skip,
 			);
+
 			if let Ok(return_value) = &output.result {
 				if return_value.did_revert() {
 					output.result = Err(<Error<T>>::ContractReverted.into());
@@ -848,8 +777,6 @@ pub mod pallet {
 				Code::Existing(code_hash),
 				data,
 				salt,
-				DebugInfo::Skip,
-				CollectEvents::Skip,
 			);
 			if let Ok(retval) = &output.result {
 				if retval.result.did_revert() {
@@ -914,8 +841,6 @@ pub mod pallet {
 				Code::Upload(code),
 				data,
 				salt,
-				DebugInfo::Skip,
-				CollectEvents::Skip,
 			);
 			if let Ok(retval) = &output.result {
 				if retval.result.did_revert() {
@@ -993,11 +918,6 @@ pub mod pallet {
 				};
 				<ExecStack<T, WasmBlob<T>>>::increment_refcount(code_hash)?;
 				<ExecStack<T, WasmBlob<T>>>::decrement_refcount(contract.code_hash);
-				Self::deposit_event(Event::ContractCodeUpdated {
-					contract: dest,
-					new_code_hash: code_hash,
-					old_code_hash: contract.code_hash,
-				});
 				contract.code_hash = code_hash;
 				Ok(())
 			})
@@ -1085,16 +1005,10 @@ where
 		gas_limit: Weight,
 		storage_deposit_limit: DepositLimit<BalanceOf<T>>,
 		data: Vec<u8>,
-		debug: DebugInfo,
-		collect_events: CollectEvents,
-	) -> ContractResult<ExecReturnValue, BalanceOf<T>, EventRecordOf<T>> {
+	) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
 		let mut gas_meter = GasMeter::new(gas_limit);
 		let mut storage_deposit = Default::default();
-		let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) {
-			Some(DebugBuffer::default())
-		} else {
-			None
-		};
+
 		let try_call = || {
 			let origin = Origin::from_runtime_origin(origin)?;
 			let mut storage_meter = match storage_deposit_limit {
@@ -1109,7 +1023,6 @@ where
 				Self::convert_native_to_evm(value),
 				data,
 				storage_deposit_limit.is_unchecked(),
-				debug_message.as_mut(),
 			)?;
 			storage_deposit = storage_meter
 				.try_into_deposit(&origin, storage_deposit_limit.is_unchecked())
@@ -1119,18 +1032,11 @@ where
 			Ok(result)
 		};
 		let result = Self::run_guarded(try_call);
-		let events = if matches!(collect_events, CollectEvents::UnsafeCollect) {
-			Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
-		} else {
-			None
-		};
 		ContractResult {
 			result: result.map_err(|r| r.error),
 			gas_consumed: gas_meter.gas_consumed(),
 			gas_required: gas_meter.gas_required(),
 			storage_deposit,
-			debug_message: debug_message.unwrap_or_default().to_vec(),
-			events,
 		}
 	}
 
@@ -1138,8 +1044,7 @@ where
 	///
 	/// Identical to [`Self::instantiate`] or [`Self::instantiate_with_code`] but tailored towards
 	/// being called by other code within the runtime as opposed to from an extrinsic. It returns
-	/// more information and allows the enablement of features that are not suitable for an
-	/// extrinsic (debugging, event collection).
+	/// more information to the caller useful to estimate the cost of the operation.
 	pub fn bare_instantiate(
 		origin: OriginFor<T>,
 		value: BalanceOf<T>,
@@ -1148,14 +1053,9 @@ where
 		code: Code,
 		data: Vec<u8>,
 		salt: Option<[u8; 32]>,
-		debug: DebugInfo,
-		collect_events: CollectEvents,
-	) -> ContractResult<InstantiateReturnValue, BalanceOf<T>, EventRecordOf<T>> {
+	) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
 		let mut gas_meter = GasMeter::new(gas_limit);
 		let mut storage_deposit = Default::default();
-		let mut debug_message =
-			if debug == DebugInfo::UnsafeDebug { Some(DebugBuffer::default()) } else { None };
-
 		let unchecked_deposit_limit = storage_deposit_limit.is_unchecked();
 		let mut storage_deposit_limit = match storage_deposit_limit {
 			DepositLimit::Balance(limit) => limit,
@@ -1195,7 +1095,6 @@ where
 				data,
 				salt.as_ref(),
 				unchecked_deposit_limit,
-				debug_message.as_mut(),
 			);
 			storage_deposit = storage_meter
 				.try_into_deposit(&instantiate_origin, unchecked_deposit_limit)?
@@ -1203,11 +1102,6 @@ where
 			result
 		};
 		let output = Self::run_guarded(try_instantiate);
-		let events = if matches!(collect_events, CollectEvents::UnsafeCollect) {
-			Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
-		} else {
-			None
-		};
 		ContractResult {
 			result: output
 				.map(|(addr, result)| InstantiateReturnValue { result, addr })
@@ -1215,8 +1109,6 @@ where
 			gas_consumed: gas_meter.gas_consumed(),
 			gas_required: gas_meter.gas_required(),
 			storage_deposit,
-			debug_message: debug_message.unwrap_or_default().to_vec(),
-			events,
 		}
 	}
 
@@ -1273,8 +1165,6 @@ where
 		};
 
 		let input = tx.input.clone().unwrap_or_default().0;
-		let debug = DebugInfo::Skip;
-		let collect_events = CollectEvents::Skip;
 
 		let extract_error = |err| {
 			if err == Error::<T>::TransferFailed.into() ||
@@ -1305,8 +1195,6 @@ where
 					gas_limit,
 					storage_deposit_limit,
 					input.clone(),
-					debug,
-					collect_events,
 				);
 
 				let data = match result.result {
@@ -1363,8 +1251,6 @@ where
 					Code::Upload(code.to_vec()),
 					data.to_vec(),
 					None,
-					debug,
-					collect_events,
 				);
 
 				let returned_data = match result.result {
@@ -1535,12 +1421,11 @@ environmental!(executing_contract: bool);
 sp_api::decl_runtime_apis! {
 	/// The API used to dry-run contract interactions.
 	#[api_version(1)]
-	pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, EventRecord> where
+	pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber> where
 		AccountId: Codec,
 		Balance: Codec,
 		Nonce: Codec,
 		BlockNumber: Codec,
-		EventRecord: Codec,
 	{
 		/// Returns the free balance of the given `[H160]` address, using EVM decimals.
 		fn balance(address: H160) -> U256;
@@ -1558,7 +1443,7 @@ sp_api::decl_runtime_apis! {
 			gas_limit: Option<Weight>,
 			storage_deposit_limit: Option<Balance>,
 			input_data: Vec<u8>,
-		) -> ContractResult<ExecReturnValue, Balance, EventRecord>;
+		) -> ContractResult<ExecReturnValue, Balance>;
 
 		/// Instantiate a new contract.
 		///
@@ -1571,7 +1456,7 @@ sp_api::decl_runtime_apis! {
 			code: Code,
 			data: Vec<u8>,
 			salt: Option<[u8; 32]>,
-		) -> ContractResult<InstantiateReturnValue, Balance, EventRecord>;
+		) -> ContractResult<InstantiateReturnValue, Balance>;
 
 
 		/// Perform an Ethereum call.
diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs
index 3b55106c67d8ca45ebe6dcf6ce6b33c7e4d0fc68..f101abf0ea7e9478be157c693b4f251d80af4df7 100644
--- a/substrate/frame/revive/src/limits.rs
+++ b/substrate/frame/revive/src/limits.rs
@@ -57,11 +57,6 @@ pub const TRANSIENT_STORAGE_BYTES: u32 = 4 * 1024;
 /// The maximum allowable length in bytes for (transient) storage keys.
 pub const STORAGE_KEY_BYTES: u32 = 128;
 
-/// The maximum size of the debug buffer contracts can write messages to.
-///
-/// The buffer will always be disabled for on-chain execution.
-pub const DEBUG_BUFFER_BYTES: u32 = 2 * 1024 * 1024;
-
 /// The page size in which PolkaVM should allocate memory chunks.
 pub const PAGE_SIZE: u32 = 4 * 1024;
 
diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs
index a7127f812b4b9726c4b7b191956c4fb1975b8a83..9c149c7cc38905bc75d57ce9d3af1cc6bf2e86a4 100644
--- a/substrate/frame/revive/src/primitives.rs
+++ b/substrate/frame/revive/src/primitives.rs
@@ -63,7 +63,7 @@ impl<T> From<T> for DepositLimit<T> {
 /// `ContractsApi` version. Therefore when SCALE decoding a `ContractResult` its trailing data
 /// should be ignored to avoid any potential compatibility issues.
 #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
-pub struct ContractResult<R, Balance, EventRecord> {
+pub struct ContractResult<R, Balance> {
 	/// How much weight was consumed during execution.
 	pub gas_consumed: Weight,
 	/// How much weight is required as gas limit in order to execute this call.
@@ -72,7 +72,7 @@ pub struct ContractResult<R, Balance, EventRecord> {
 	///
 	/// # Note
 	///
-	/// This can only different from [`Self::gas_consumed`] when weight pre charging
+	/// This can only be different from [`Self::gas_consumed`] when weight pre charging
 	/// is used. Currently, only `seal_call_runtime` makes use of pre charging.
 	/// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging
 	/// when a non-zero `gas_limit` argument is supplied.
@@ -84,26 +84,8 @@ pub struct ContractResult<R, Balance, EventRecord> {
 	/// is `Err`. This is because on error all storage changes are rolled back including the
 	/// payment of the deposit.
 	pub storage_deposit: StorageDeposit<Balance>,
-	/// An optional debug message. This message is only filled when explicitly requested
-	/// by the code that calls into the contract. Otherwise it is empty.
-	///
-	/// The contained bytes are valid UTF-8. This is not declared as `String` because
-	/// this type is not allowed within the runtime.
-	///
-	/// Clients should not make any assumptions about the format of the buffer.
-	/// They should just display it as-is. It is **not** only a collection of log lines
-	/// provided by a contract but a formatted buffer with different sections.
-	///
-	/// # Note
-	///
-	/// The debug message is never generated during on-chain execution. It is reserved for
-	/// RPC calls.
-	pub debug_message: Vec<u8>,
 	/// The execution result of the wasm code.
 	pub result: Result<R, DispatchError>,
-	/// The events that were emitted during execution. It is an option as event collection is
-	/// optional.
-	pub events: Option<Vec<EventRecord>>,
 }
 
 /// The result of the execution of a `eth_transact` call.
@@ -284,36 +266,3 @@ where
 		}
 	}
 }
-
-/// Determines whether events should be collected during execution.
-#[derive(
-	Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo,
-)]
-pub enum CollectEvents {
-	/// Collect events.
-	///
-	/// # Note
-	///
-	/// Events should only be collected when called off-chain, as this would otherwise
-	/// collect all the Events emitted in the block so far and put them into the PoV.
-	///
-	/// **Never** use this mode for on-chain execution.
-	UnsafeCollect,
-	/// Skip event collection.
-	Skip,
-}
-
-/// Determines whether debug messages will be collected.
-#[derive(
-	Copy, Clone, PartialEq, Eq, RuntimeDebug, Decode, Encode, MaxEncodedLen, scale_info::TypeInfo,
-)]
-pub enum DebugInfo {
-	/// Collect debug messages.
-	/// # Note
-	///
-	/// This should only ever be set to `UnsafeDebug` when executing as an RPC because
-	/// it adds allocations and could be abused to drive the runtime into an OOM panic.
-	UnsafeDebug,
-	/// Skip collection of debug messages.
-	Skip,
-}
diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs
index 6eddf048be98fc0470650fdb1c90197ea03db91e..cd390c86f63ae94a30d065def82e628a0091b653 100644
--- a/substrate/frame/revive/src/storage/meter.rs
+++ b/substrate/frame/revive/src/storage/meter.rs
@@ -18,8 +18,8 @@
 //! This module contains functions to meter the storage deposit.
 
 use crate::{
-	address::AddressMapper, storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error,
-	Event, HoldReason, Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET,
+	storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, HoldReason, Inspect,
+	Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET,
 };
 use alloc::vec::Vec;
 use core::{fmt::Debug, marker::PhantomData};
@@ -101,12 +101,8 @@ pub struct Root;
 
 /// State parameter that constitutes a meter that is in its nested state.
 /// Its value indicates whether the nested meter has its own limit.
-#[derive(DefaultNoBound, RuntimeDebugNoBound)]
-pub enum Nested {
-	#[default]
-	DerivedLimit,
-	OwnLimit,
-}
+#[derive(Default, Debug)]
+pub struct Nested;
 
 impl State for Root {}
 impl State for Nested {}
@@ -125,10 +121,8 @@ pub struct RawMeter<T: Config, E, S: State + Default + Debug> {
 	/// We only have one charge per contract hence the size of this vector is
 	/// limited by the maximum call depth.
 	charges: Vec<Charge<T>>,
-	/// We store the nested state to determine if it has a special limit for sub-call.
-	nested: S,
 	/// Type parameter only used in impls.
-	_phantom: PhantomData<E>,
+	_phantom: PhantomData<(E, S)>,
 }
 
 /// This type is used to describe a storage change when charging from the meter.
@@ -281,21 +275,14 @@ where
 	S: State + Default + Debug,
 {
 	/// Create a new child that has its `limit`.
-	/// Passing `0` as the limit is interpreted as to take whatever is remaining from its parent.
 	///
 	/// This is called whenever a new subcall is initiated in order to track the storage
 	/// usage for this sub call separately. This is necessary because we want to exchange balance
 	/// with the current contract we are interacting with.
 	pub fn nested(&self, limit: BalanceOf<T>) -> RawMeter<T, E, Nested> {
 		debug_assert!(matches!(self.contract_state(), ContractState::Alive));
-		// If a special limit is specified higher than it is available,
-		// we want to enforce the lesser limit to the nested meter, to fail in the sub-call.
-		let limit = self.available().min(limit);
-		if limit.is_zero() {
-			RawMeter { limit: self.available(), ..Default::default() }
-		} else {
-			RawMeter { limit, nested: Nested::OwnLimit, ..Default::default() }
-		}
+
+		RawMeter { limit: self.available().min(limit), ..Default::default() }
 	}
 
 	/// Absorb a child that was spawned to handle a sub call.
@@ -477,13 +464,6 @@ impl<T: Config, E: Ext<T>> RawMeter<T, E, Nested> {
 
 	/// [`Self::charge`] does not enforce the storage limit since we want to do this check as late
 	/// as possible to allow later refunds to offset earlier charges.
-	///
-	/// # Note
-	///
-	/// We normally need to call this **once** for every call stack and not for every cross contract
-	/// call. However, if a dedicated limit is specified for a sub-call, this needs to be called
-	/// once the sub-call has returned. For this, the [`Self::enforce_subcall_limit`] wrapper is
-	/// used.
 	pub fn enforce_limit(
 		&mut self,
 		info: Option<&mut ContractInfo<T>>,
@@ -502,18 +482,6 @@ impl<T: Config, E: Ext<T>> RawMeter<T, E, Nested> {
 		}
 		Ok(())
 	}
-
-	/// This is a wrapper around [`Self::enforce_limit`] to use on the exit from a sub-call to
-	/// enforce its special limit if needed.
-	pub fn enforce_subcall_limit(
-		&mut self,
-		info: Option<&mut ContractInfo<T>>,
-	) -> Result<(), DispatchError> {
-		match self.nested {
-			Nested::OwnLimit => self.enforce_limit(info),
-			Nested::DerivedLimit => Ok(()),
-		}
-	}
 }
 
 impl<T: Config> Ext<T> for ReservingExt {
@@ -548,12 +516,6 @@ impl<T: Config> Ext<T> for ReservingExt {
 					Preservation::Preserve,
 					Fortitude::Polite,
 				)?;
-
-				Pallet::<T>::deposit_event(Event::StorageDepositTransferredAndHeld {
-					from: T::AddressMapper::to_address(origin),
-					to: T::AddressMapper::to_address(contract),
-					amount: *amount,
-				});
 			},
 			Deposit::Refund(amount) => {
 				let transferred = T::Currency::transfer_on_hold(
@@ -566,12 +528,6 @@ impl<T: Config> Ext<T> for ReservingExt {
 					Fortitude::Polite,
 				)?;
 
-				Pallet::<T>::deposit_event(Event::StorageDepositTransferredAndReleased {
-					from: T::AddressMapper::to_address(contract),
-					to: T::AddressMapper::to_address(origin),
-					amount: transferred,
-				});
-
 				if transferred < *amount {
 					// This should never happen, if it does it means that there is a bug in the
 					// runtime logic. In the rare case this happens we try to refund as much as we
@@ -724,6 +680,49 @@ mod tests {
 		)
 	}
 
+	/// Previously, passing a limit of 0 meant unlimited storage for a nested call.
+	///
+	/// Now, a limit of 0 means the subcall will not be able to use any storage.
+	#[test]
+	fn nested_zero_limit_requested() {
+		clear_ext();
+
+		let meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap();
+		assert_eq!(meter.available(), 1_000);
+		let nested0 = meter.nested(BalanceOf::<Test>::zero());
+		assert_eq!(nested0.available(), 0);
+	}
+
+	#[test]
+	fn nested_some_limit_requested() {
+		clear_ext();
+
+		let meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap();
+		assert_eq!(meter.available(), 1_000);
+		let nested0 = meter.nested(500);
+		assert_eq!(nested0.available(), 500);
+	}
+
+	#[test]
+	fn nested_all_limit_requested() {
+		clear_ext();
+
+		let meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap();
+		assert_eq!(meter.available(), 1_000);
+		let nested0 = meter.nested(1_000);
+		assert_eq!(nested0.available(), 1_000);
+	}
+
+	#[test]
+	fn nested_over_limit_requested() {
+		clear_ext();
+
+		let meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap();
+		assert_eq!(meter.available(), 1_000);
+		let nested0 = meter.nested(2_000);
+		assert_eq!(nested0.available(), 1_000);
+	}
+
 	#[test]
 	fn empty_charge_works() {
 		clear_ext();
@@ -879,7 +878,7 @@ mod tests {
 			let mut meter = TestMeter::new(&test_case.origin, 1_000, 0).unwrap();
 			assert_eq!(meter.available(), 1_000);
 
-			let mut nested0 = meter.nested(BalanceOf::<Test>::zero());
+			let mut nested0 = meter.nested(BalanceOf::<Test>::max_value());
 			nested0.charge(&Diff {
 				bytes_added: 5,
 				bytes_removed: 1,
@@ -895,7 +894,7 @@ mod tests {
 				items_deposit: 20,
 				immutable_data_len: 0,
 			});
-			let mut nested1 = nested0.nested(BalanceOf::<Test>::zero());
+			let mut nested1 = nested0.nested(BalanceOf::<Test>::max_value());
 			nested1.charge(&Diff { items_removed: 5, ..Default::default() });
 			nested1.charge(&Diff { bytes_added: 20, ..Default::default() });
 			nested1.terminate(&nested1_info, CHARLIE);
diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs
index 8ba5e7384070289617c667c8e95b0608a2846a6d..7fbb5b676439e6c378e3b51683bac449513faca5 100644
--- a/substrate/frame/revive/src/test_utils/builder.rs
+++ b/substrate/frame/revive/src/test_utils/builder.rs
@@ -17,9 +17,8 @@
 
 use super::{deposit_limit, GAS_LIMIT};
 use crate::{
-	address::AddressMapper, AccountIdOf, BalanceOf, Code, CollectEvents, Config, ContractResult,
-	DebugInfo, DepositLimit, EventRecordOf, ExecReturnValue, InstantiateReturnValue, OriginFor,
-	Pallet, Weight,
+	address::AddressMapper, AccountIdOf, BalanceOf, Code, Config, ContractResult, DepositLimit,
+	ExecReturnValue, InstantiateReturnValue, OriginFor, Pallet, Weight,
 };
 use frame_support::pallet_prelude::DispatchResultWithPostInfo;
 use paste::paste;
@@ -138,9 +137,7 @@ builder!(
 		code: Code,
 		data: Vec<u8>,
 		salt: Option<[u8; 32]>,
-		debug: DebugInfo,
-		collect_events: CollectEvents,
-	) -> ContractResult<InstantiateReturnValue, BalanceOf<T>, EventRecordOf<T>>;
+	) -> ContractResult<InstantiateReturnValue, BalanceOf<T>>;
 
 	/// Build the instantiate call and unwrap the result.
 	pub fn build_and_unwrap_result(self) -> InstantiateReturnValue {
@@ -164,8 +161,6 @@ builder!(
 			code,
 			data: vec![],
 			salt: Some([0; 32]),
-			debug: DebugInfo::UnsafeDebug,
-			collect_events: CollectEvents::Skip,
 		}
 	}
 );
@@ -201,9 +196,7 @@ builder!(
 		gas_limit: Weight,
 		storage_deposit_limit: DepositLimit<BalanceOf<T>>,
 		data: Vec<u8>,
-		debug: DebugInfo,
-		collect_events: CollectEvents,
-	) -> ContractResult<ExecReturnValue, BalanceOf<T>, EventRecordOf<T>>;
+	) -> ContractResult<ExecReturnValue, BalanceOf<T>>;
 
 	/// Build the call and unwrap the result.
 	pub fn build_and_unwrap_result(self) -> ExecReturnValue {
@@ -219,8 +212,6 @@ builder!(
 			gas_limit: GAS_LIMIT,
 			storage_deposit_limit: DepositLimit::Balance(deposit_limit::<T>()),
 			data: vec![],
-			debug: DebugInfo::UnsafeDebug,
-			collect_events: CollectEvents::Skip,
 		}
 	}
 );
diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs
index 664578bf7672e3aed56223c229ded0636952306a..8398bc2cb66ffbdcfbb8cf14207e4b61a5de8f26 100644
--- a/substrate/frame/revive/src/tests.rs
+++ b/substrate/frame/revive/src/tests.rs
@@ -38,9 +38,9 @@ use crate::{
 	tests::test_utils::{get_contract, get_contract_checked},
 	wasm::Memory,
 	weights::WeightInfo,
-	AccountId32Mapper, BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo,
-	ContractInfoOf, DebugInfo, DeletionQueueCounter, DepositLimit, Error, EthTransactError,
-	HoldReason, Origin, Pallet, PristineCode, H160,
+	AccountId32Mapper, BalanceOf, Code, CodeInfoOf, Config, ContractInfo, ContractInfoOf,
+	DeletionQueueCounter, DepositLimit, Error, EthTransactError, HoldReason, Origin, Pallet,
+	PristineCode, H160,
 };
 
 use crate::test_utils::builder::Contract;
@@ -713,25 +713,6 @@ fn instantiate_and_call_and_deposit_event() {
 					}),
 					topics: vec![],
 				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Instantiated {
-						deployer: ALICE_ADDR,
-						contract: addr
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(
-						pallet_revive::Event::StorageDepositTransferredAndHeld {
-							from: ALICE_ADDR,
-							to: addr,
-							amount: test_utils::contract_info_storage_deposit(&addr),
-						}
-					),
-					topics: vec![],
-				},
 			]
 		);
 	});
@@ -1078,14 +1059,6 @@ fn deploy_and_call_other_contract() {
 					}),
 					topics: vec![],
 				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Instantiated {
-						deployer: caller_addr,
-						contract: callee_addr,
-					}),
-					topics: vec![],
-				},
 				EventRecord {
 					phase: Phase::Initialization,
 					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
@@ -1095,33 +1068,6 @@ fn deploy_and_call_other_contract() {
 					}),
 					topics: vec![],
 				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Called {
-						caller: Origin::from_account_id(caller_account.clone()),
-						contract: callee_addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Called {
-						caller: Origin::from_account_id(ALICE),
-						contract: caller_addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(
-						pallet_revive::Event::StorageDepositTransferredAndHeld {
-							from: ALICE_ADDR,
-							to: callee_addr,
-							amount: test_utils::contract_info_storage_deposit(&callee_addr),
-						}
-					),
-					topics: vec![],
-				},
 			]
 		);
 	});
@@ -1149,7 +1095,7 @@ fn delegate_call() {
 
 		assert_ok!(builder::call(caller_addr)
 			.value(1337)
-			.data((callee_addr, 0u64, 0u64).encode())
+			.data((callee_addr, u64::MAX, u64::MAX).encode())
 			.build());
 	});
 }
@@ -1373,8 +1319,6 @@ fn self_destruct_works() {
 		// Check that the BOB contract has been instantiated.
 		let _ = get_contract(&contract.addr);
 
-		let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr);
-
 		// Drop all previous events
 		initialize_block(2);
 
@@ -1404,33 +1348,6 @@ fn self_destruct_works() {
 		pretty_assertions::assert_eq!(
 			System::events(),
 			vec![
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Terminated {
-						contract: contract.addr,
-						beneficiary: DJANGO_ADDR,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Called {
-						caller: Origin::from_account_id(ALICE),
-						contract: contract.addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(
-						pallet_revive::Event::StorageDepositTransferredAndReleased {
-							from: contract.addr,
-							to: ALICE_ADDR,
-							amount: info_deposit,
-						}
-					),
-					topics: vec![],
-				},
 				EventRecord {
 					phase: Phase::Initialization,
 					event: RuntimeEvent::System(frame_system::Event::KilledAccount {
@@ -2184,58 +2101,6 @@ fn refcounter() {
 	});
 }
 
-#[test]
-fn debug_message_works() {
-	let (wasm, _code_hash) = compile_module("debug_message_works").unwrap();
-
-	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-			.value(30_000)
-			.build_and_unwrap_contract();
-		let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build();
-
-		assert_matches!(result.result, Ok(_));
-		assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!");
-	});
-}
-
-#[test]
-fn debug_message_logging_disabled() {
-	let (wasm, _code_hash) = compile_module("debug_message_logging_disabled").unwrap();
-
-	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-			.value(30_000)
-			.build_and_unwrap_contract();
-		// the dispatchables always run without debugging
-		assert_ok!(Contracts::call(
-			RuntimeOrigin::signed(ALICE),
-			addr,
-			0,
-			GAS_LIMIT,
-			deposit_limit::<Test>(),
-			vec![]
-		));
-	});
-}
-
-#[test]
-fn debug_message_invalid_utf8() {
-	let (wasm, _code_hash) = compile_module("debug_message_invalid_utf8").unwrap();
-
-	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
-		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-			.value(30_000)
-			.build_and_unwrap_contract();
-		let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build();
-		assert_ok!(result.result);
-		assert!(result.debug_message.is_empty());
-	});
-}
-
 #[test]
 fn gas_estimation_for_subcalls() {
 	let (caller_code, _caller_hash) = compile_module("call_with_limit").unwrap();
@@ -2261,12 +2126,12 @@ fn gas_estimation_for_subcalls() {
 
 		// Run the test for all of those weight limits for the subcall
 		let weights = [
-			Weight::zero(),
+			Weight::MAX,
 			GAS_LIMIT,
 			GAS_LIMIT * 2,
 			GAS_LIMIT / 5,
-			Weight::from_parts(0, GAS_LIMIT.proof_size()),
-			Weight::from_parts(GAS_LIMIT.ref_time(), 0),
+			Weight::from_parts(u64::MAX, GAS_LIMIT.proof_size()),
+			Weight::from_parts(GAS_LIMIT.ref_time(), u64::MAX),
 		];
 
 		// This call is passed to the sub call in order to create a large `required_weight`
@@ -2451,79 +2316,6 @@ fn ecdsa_recover() {
 	})
 }
 
-#[test]
-fn bare_instantiate_returns_events() {
-	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-		let min_balance = Contracts::min_balance();
-		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-
-		let result = builder::bare_instantiate(Code::Upload(wasm))
-			.value(min_balance * 100)
-			.collect_events(CollectEvents::UnsafeCollect)
-			.build();
-
-		let events = result.events.unwrap();
-		assert!(!events.is_empty());
-		assert_eq!(events, System::events());
-	});
-}
-
-#[test]
-fn bare_instantiate_does_not_return_events() {
-	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-		let min_balance = Contracts::min_balance();
-		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-
-		let result = builder::bare_instantiate(Code::Upload(wasm)).value(min_balance * 100).build();
-
-		let events = result.events;
-		assert!(!System::events().is_empty());
-		assert!(events.is_none());
-	});
-}
-
-#[test]
-fn bare_call_returns_events() {
-	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-		let min_balance = Contracts::min_balance();
-		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-
-		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-			.value(min_balance * 100)
-			.build_and_unwrap_contract();
-
-		let result = builder::bare_call(addr).collect_events(CollectEvents::UnsafeCollect).build();
-
-		let events = result.events.unwrap();
-		assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success);
-		assert!(!events.is_empty());
-		assert_eq!(events, System::events());
-	});
-}
-
-#[test]
-fn bare_call_does_not_return_events() {
-	let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap();
-	ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
-		let min_balance = Contracts::min_balance();
-		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1000 * min_balance);
-
-		let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm))
-			.value(min_balance * 100)
-			.build_and_unwrap_contract();
-
-		let result = builder::bare_call(addr).build();
-
-		let events = result.events;
-		assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success);
-		assert!(!System::events().is_empty());
-		assert!(events.is_none());
-	});
-}
-
 #[test]
 fn sr25519_verify() {
 	let (wasm, _code_hash) = compile_module("sr25519_verify").unwrap();
@@ -2637,23 +2429,9 @@ fn upload_code_works() {
 		initialize_block(2);
 
 		assert!(!PristineCode::<Test>::contains_key(&code_hash));
-
 		assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
 		// Ensure the contract was stored and get expected deposit amount to be reserved.
-		let deposit_expected = expected_deposit(ensure_stored(code_hash));
-
-		assert_eq!(
-			System::events(),
-			vec![EventRecord {
-				phase: Phase::Initialization,
-				event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-					code_hash,
-					deposit_held: deposit_expected,
-					uploader: ALICE_ADDR
-				}),
-				topics: vec![],
-			},]
-		);
+		expected_deposit(ensure_stored(code_hash));
 	});
 }
 
@@ -2711,32 +2489,8 @@ fn remove_code_works() {
 
 		assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
 		// Ensure the contract was stored and get expected deposit amount to be reserved.
-		let deposit_expected = expected_deposit(ensure_stored(code_hash));
-
+		expected_deposit(ensure_stored(code_hash));
 		assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash));
-		assert_eq!(
-			System::events(),
-			vec![
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-						code_hash,
-						deposit_held: deposit_expected,
-						uploader: ALICE_ADDR
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::CodeRemoved {
-						code_hash,
-						deposit_released: deposit_expected,
-						remover: ALICE_ADDR
-					}),
-					topics: vec![],
-				},
-			]
-		);
 	});
 }
 
@@ -2752,25 +2506,12 @@ fn remove_code_wrong_origin() {
 
 		assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, 1_000,));
 		// Ensure the contract was stored and get expected deposit amount to be reserved.
-		let deposit_expected = expected_deposit(ensure_stored(code_hash));
+		expected_deposit(ensure_stored(code_hash));
 
 		assert_noop!(
 			Contracts::remove_code(RuntimeOrigin::signed(BOB), code_hash),
 			sp_runtime::traits::BadOrigin,
 		);
-
-		assert_eq!(
-			System::events(),
-			vec![EventRecord {
-				phase: Phase::Initialization,
-				event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-					code_hash,
-					deposit_held: deposit_expected,
-					uploader: ALICE_ADDR
-				}),
-				topics: vec![],
-			},]
-		);
 	});
 }
 
@@ -2829,7 +2570,7 @@ fn instantiate_with_zero_balance_works() {
 			builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract();
 
 		// Ensure the contract was stored and get expected deposit amount to be reserved.
-		let deposit_expected = expected_deposit(ensure_stored(code_hash));
+		expected_deposit(ensure_stored(code_hash));
 
 		// Make sure the account exists even though no free balance was send
 		assert_eq!(<Test as Config>::Currency::free_balance(&account_id), min_balance);
@@ -2841,15 +2582,6 @@ fn instantiate_with_zero_balance_works() {
 		assert_eq!(
 			System::events(),
 			vec![
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-						code_hash,
-						deposit_held: deposit_expected,
-						uploader: ALICE_ADDR
-					}),
-					topics: vec![],
-				},
 				EventRecord {
 					phase: Phase::Initialization,
 					event: RuntimeEvent::System(frame_system::Event::NewAccount {
@@ -2874,25 +2606,6 @@ fn instantiate_with_zero_balance_works() {
 					}),
 					topics: vec![],
 				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Instantiated {
-						deployer: ALICE_ADDR,
-						contract: addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(
-						pallet_revive::Event::StorageDepositTransferredAndHeld {
-							from: ALICE_ADDR,
-							to: addr,
-							amount: test_utils::contract_info_storage_deposit(&addr),
-						}
-					),
-					topics: vec![],
-				},
 			]
 		);
 	});
@@ -2915,7 +2628,7 @@ fn instantiate_with_below_existential_deposit_works() {
 			.build_and_unwrap_contract();
 
 		// Ensure the contract was stored and get expected deposit amount to be reserved.
-		let deposit_expected = expected_deposit(ensure_stored(code_hash));
+		expected_deposit(ensure_stored(code_hash));
 		// Make sure the account exists even though not enough free balance was send
 		assert_eq!(<Test as Config>::Currency::free_balance(&account_id), min_balance + value);
 		assert_eq!(
@@ -2926,15 +2639,6 @@ fn instantiate_with_below_existential_deposit_works() {
 		assert_eq!(
 			System::events(),
 			vec![
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::CodeStored {
-						code_hash,
-						deposit_held: deposit_expected,
-						uploader: ALICE_ADDR
-					}),
-					topics: vec![],
-				},
 				EventRecord {
 					phase: Phase::Initialization,
 					event: RuntimeEvent::System(frame_system::Event::NewAccount {
@@ -2968,25 +2672,6 @@ fn instantiate_with_below_existential_deposit_works() {
 					}),
 					topics: vec![],
 				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Instantiated {
-						deployer: ALICE_ADDR,
-						contract: addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(
-						pallet_revive::Event::StorageDepositTransferredAndHeld {
-							from: ALICE_ADDR,
-							to: addr,
-							amount: test_utils::contract_info_storage_deposit(&addr),
-						}
-					),
-					topics: vec![],
-				},
 			]
 		);
 	});
@@ -3028,74 +2713,15 @@ fn storage_deposit_works() {
 
 		assert_eq!(
 			System::events(),
-			vec![
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
-						from: ALICE,
-						to: account_id.clone(),
-						amount: 42,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Called {
-						caller: Origin::from_account_id(ALICE),
-						contract: addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(
-						pallet_revive::Event::StorageDepositTransferredAndHeld {
-							from: ALICE_ADDR,
-							to: addr,
-							amount: charged0,
-						}
-					),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Called {
-						caller: Origin::from_account_id(ALICE),
-						contract: addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(
-						pallet_revive::Event::StorageDepositTransferredAndHeld {
-							from: ALICE_ADDR,
-							to: addr,
-							amount: charged1,
-						}
-					),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Called {
-						caller: Origin::from_account_id(ALICE),
-						contract: addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(
-						pallet_revive::Event::StorageDepositTransferredAndReleased {
-							from: addr,
-							to: ALICE_ADDR,
-							amount: refunded0,
-						}
-					),
-					topics: vec![],
-				},
-			]
+			vec![EventRecord {
+				phase: Phase::Initialization,
+				event: RuntimeEvent::Balances(pallet_balances::Event::Transfer {
+					from: ALICE,
+					to: account_id.clone(),
+					amount: 42,
+				}),
+				topics: vec![],
+			},]
 		);
 	});
 }
@@ -3188,18 +2814,6 @@ fn set_code_extrinsic() {
 		assert_eq!(get_contract(&addr).code_hash, new_code_hash);
 		assert_refcount!(&code_hash, 0);
 		assert_refcount!(&new_code_hash, 1);
-		assert_eq!(
-			System::events(),
-			vec![EventRecord {
-				phase: Phase::Initialization,
-				event: RuntimeEvent::Contracts(pallet_revive::Event::ContractCodeUpdated {
-					contract: addr,
-					new_code_hash,
-					old_code_hash: code_hash,
-				}),
-				topics: vec![],
-			},]
-		);
 	});
 }
 
@@ -3305,7 +2919,7 @@ fn contract_reverted() {
 
 #[test]
 fn set_code_hash() {
-	let (wasm, code_hash) = compile_module("set_code_hash").unwrap();
+	let (wasm, _) = compile_module("set_code_hash").unwrap();
 	let (new_wasm, new_code_hash) = compile_module("new_set_code_hash_contract").unwrap();
 
 	ExtBuilder::default().existential_deposit(100).build().execute_with(|| {
@@ -3327,47 +2941,12 @@ fn set_code_hash() {
 		// First call sets new code_hash and returns 1
 		let result = builder::bare_call(contract_addr)
 			.data(new_code_hash.as_ref().to_vec())
-			.debug(DebugInfo::UnsafeDebug)
 			.build_and_unwrap_result();
 		assert_return_code!(result, 1);
 
 		// Second calls new contract code that returns 2
-		let result = builder::bare_call(contract_addr)
-			.debug(DebugInfo::UnsafeDebug)
-			.build_and_unwrap_result();
+		let result = builder::bare_call(contract_addr).build_and_unwrap_result();
 		assert_return_code!(result, 2);
-
-		// Checking for the last event only
-		assert_eq!(
-			&System::events(),
-			&[
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::ContractCodeUpdated {
-						contract: contract_addr,
-						new_code_hash,
-						old_code_hash: code_hash,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Called {
-						caller: Origin::from_account_id(ALICE),
-						contract: contract_addr,
-					}),
-					topics: vec![],
-				},
-				EventRecord {
-					phase: Phase::Initialization,
-					event: RuntimeEvent::Contracts(crate::Event::Called {
-						caller: Origin::from_account_id(ALICE),
-						contract: contract_addr,
-					}),
-					topics: vec![],
-				},
-			],
-		);
 	});
 }
 
@@ -3453,13 +3032,13 @@ fn deposit_limit_in_nested_calls() {
 
 		// We do not remove any storage but add a storage item of 12 bytes in the caller
 		// contract. This would cost 12 + 2 = 14 Balance.
-		// The nested call doesn't get a special limit, which is set by passing 0 to it.
+		// The nested call doesn't get a special limit, which is set by passing `u64::MAX` to it.
 		// This should fail as the specified parent's limit is less than the cost: 13 <
 		// 14.
 		assert_err_ignore_postinfo!(
 			builder::call(addr_caller)
 				.storage_deposit_limit(13)
-				.data((100u32, &addr_callee, U256::from(0u64)).encode())
+				.data((100u32, &addr_callee, U256::MAX).encode())
 				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
@@ -3467,13 +3046,13 @@ fn deposit_limit_in_nested_calls() {
 		// Now we specify the parent's limit high enough to cover the caller's storage
 		// additions. However, we use a single byte more in the callee, hence the storage
 		// deposit should be 15 Balance.
-		// The nested call doesn't get a special limit, which is set by passing 0 to it.
+		// The nested call doesn't get a special limit, which is set by passing `u64::MAX` to it.
 		// This should fail as the specified parent's limit is less than the cost: 14
 		// < 15.
 		assert_err_ignore_postinfo!(
 			builder::call(addr_caller)
 				.storage_deposit_limit(14)
-				.data((101u32, &addr_callee, U256::from(0u64)).encode())
+				.data((101u32, &addr_callee, &U256::MAX).encode())
 				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
@@ -3495,7 +3074,7 @@ fn deposit_limit_in_nested_calls() {
 		assert_err_ignore_postinfo!(
 			builder::call(addr_caller)
 				.storage_deposit_limit(0)
-				.data((87u32, &addr_callee, U256::from(0u64)).encode())
+				.data((87u32, &addr_callee, &U256::MAX.to_little_endian()).encode())
 				.build(),
 			<Error<Test>>::StorageDepositLimitExhausted,
 		);
@@ -3551,28 +3130,24 @@ fn deposit_limit_in_nested_instantiate() {
 		//
 		// Provided the limit is set to be 1 Balance less,
 		// this call should fail on the return from the caller contract.
-		assert_err_ignore_postinfo!(
-			builder::call(addr_caller)
-				.origin(RuntimeOrigin::signed(BOB))
-				.storage_deposit_limit(callee_info_len + 2 + ED + 1)
-				.data((0u32, &code_hash_callee, U256::from(0u64)).encode())
-				.build(),
-			<Error<Test>>::StorageDepositLimitExhausted,
-		);
+		let ret = builder::bare_call(addr_caller)
+			.origin(RuntimeOrigin::signed(BOB))
+			.storage_deposit_limit(DepositLimit::Balance(callee_info_len + 2 + ED + 1))
+			.data((0u32, &code_hash_callee, &U256::MAX.to_little_endian()).encode())
+			.build_and_unwrap_result();
+		assert_return_code!(ret, RuntimeReturnCode::OutOfResources);
 		// The charges made on instantiation should be rolled back.
 		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
 
 		// Now we give enough limit for the instantiation itself, but require for 1 more storage
 		// byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on
 		// the return from constructor.
-		assert_err_ignore_postinfo!(
-			builder::call(addr_caller)
-				.origin(RuntimeOrigin::signed(BOB))
-				.storage_deposit_limit(callee_info_len + 2 + ED + 2)
-				.data((1u32, &code_hash_callee, U256::from(0u64)).encode())
-				.build(),
-			<Error<Test>>::StorageDepositLimitExhausted,
-		);
+		let ret = builder::bare_call(addr_caller)
+			.origin(RuntimeOrigin::signed(BOB))
+			.storage_deposit_limit(DepositLimit::Balance(callee_info_len + 2 + ED + 2))
+			.data((1u32, &code_hash_callee, U256::from(0u64)).encode())
+			.build_and_unwrap_result();
+		assert_return_code!(ret, RuntimeReturnCode::OutOfResources);
 		// The charges made on the instantiation should be rolled back.
 		assert_eq!(<Test as Config>::Currency::free_balance(&BOB), 1_000_000);
 
@@ -4664,6 +4239,43 @@ fn origin_api_works() {
 	});
 }
 
+#[test]
+fn to_account_id_works() {
+	let (code_hash_code, _) = compile_module("to_account_id").unwrap();
+
+	ExtBuilder::default().existential_deposit(1).build().execute_with(|| {
+		let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
+		let _ = <Test as Config>::Currency::set_balance(&EVE, 1_000_000);
+
+		let Contract { addr, .. } =
+			builder::bare_instantiate(Code::Upload(code_hash_code)).build_and_unwrap_contract();
+
+		// mapped account
+		<Pallet<Test>>::map_account(RuntimeOrigin::signed(EVE)).unwrap();
+		let expected_mapped_account_id = &<Test as Config>::AddressMapper::to_account_id(&EVE_ADDR);
+		assert_ne!(
+			expected_mapped_account_id.encode()[20..32],
+			[0xEE; 12],
+			"fallback suffix found where none should be"
+		);
+		assert_ok!(builder::call(addr)
+			.data((EVE_ADDR, expected_mapped_account_id).encode())
+			.build());
+
+		// fallback for unmapped accounts
+		let expected_fallback_account_id =
+			&<Test as Config>::AddressMapper::to_account_id(&BOB_ADDR);
+		assert_eq!(
+			expected_fallback_account_id.encode()[20..32],
+			[0xEE; 12],
+			"no fallback suffix found where one should be"
+		);
+		assert_ok!(builder::call(addr)
+			.data((BOB_ADDR, expected_fallback_account_id).encode())
+			.build());
+	});
+}
+
 #[test]
 fn code_hash_works() {
 	let (code_hash_code, self_code_hash) = compile_module("code_hash").unwrap();
@@ -4814,7 +4426,7 @@ fn skip_transfer_works() {
 					..Default::default()
 				},
 				Weight::MAX,
-				|_| 0u32
+				|_| 0u32,
 			),
 			EthTransactError::Message(format!(
 				"insufficient funds for gas * price + value: address {BOB_ADDR:?} have 0 (supplied gas 1)"
@@ -4829,7 +4441,7 @@ fn skip_transfer_works() {
 				..Default::default()
 			},
 			Weight::MAX,
-			|_| 0u32
+			|_| 0u32,
 		));
 
 		let Contract { addr, .. } =
@@ -4848,7 +4460,7 @@ fn skip_transfer_works() {
 					..Default::default()
 				},
 				Weight::MAX,
-				|_| 0u32
+				|_| 0u32,
 			),
 			EthTransactError::Message(format!(
 				"insufficient funds for gas * price + value: address {BOB_ADDR:?} have 0 (supplied gas 1)"
@@ -4856,26 +4468,24 @@ fn skip_transfer_works() {
 		);
 
 		// fails when calling from a contract when gas is specified.
-		assert_err!(
-			Pallet::<Test>::bare_eth_transact(
-				GenericTransaction {
-					from: Some(BOB_ADDR),
-					to: Some(caller_addr),
-					input: Some((0u32, &addr).encode().into()),
-					gas: Some(1u32.into()),
-					..Default::default()
-				},
-				Weight::MAX,
-				|_| 0u32
-			),
-			EthTransactError::Message(format!("insufficient funds for gas * price + value: address {BOB_ADDR:?} have 0 (supplied gas 1)"))
-		);
+		assert!(Pallet::<Test>::bare_eth_transact(
+			GenericTransaction {
+				from: Some(BOB_ADDR),
+				to: Some(caller_addr),
+				input: Some((0u32, &addr).encode().into()),
+				gas: Some(1u32.into()),
+				..Default::default()
+			},
+			Weight::MAX,
+			|_| 0u32
+		)
+		.is_err(),);
 
 		// works when no gas is specified.
 		assert_ok!(Pallet::<Test>::bare_eth_transact(
 			GenericTransaction { from: Some(BOB_ADDR), to: Some(addr), ..Default::default() },
 			Weight::MAX,
-			|_| 0u32
+			|_| 0u32,
 		));
 
 		// works when calling from a contract when no gas is specified.
@@ -4887,7 +4497,7 @@ fn skip_transfer_works() {
 				..Default::default()
 			},
 			Weight::MAX,
-			|_| 0u32
+			|_| 0u32,
 		));
 	});
 }
diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs
index c9e19e52ace1372aecf6129c22f03b11f845f66c..b1fdb2d47441eff1335a4f9b800e04c9489bd94b 100644
--- a/substrate/frame/revive/src/tests/test_debug.rs
+++ b/substrate/frame/revive/src/tests/test_debug.rs
@@ -119,8 +119,6 @@ fn debugging_works() {
 			Code::Upload(wasm),
 			vec![],
 			Some([0u8; 32]),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
 		)
 		.result
 		.unwrap()
@@ -204,8 +202,6 @@ fn call_interception_works() {
 			vec![],
 			// some salt to ensure that the address of this contract is unique among all tests
 			Some([0x41; 32]),
-			DebugInfo::Skip,
-			CollectEvents::Skip,
 		)
 		.result
 		.unwrap()
diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs
index 3bd4bde5679f24ba89db49032f44909b851071c2..527cf16309540c3edacf626bb7e222f82677f3d9 100644
--- a/substrate/frame/revive/src/wasm/mod.rs
+++ b/substrate/frame/revive/src/wasm/mod.rs
@@ -29,14 +29,13 @@ pub use crate::wasm::runtime::{ReturnData, TrapReason};
 pub use crate::wasm::runtime::{Memory, Runtime, RuntimeCosts};
 
 use crate::{
-	address::AddressMapper,
 	exec::{ExecResult, Executable, ExportedFunction, Ext},
 	gas::{GasMeter, Token},
 	limits,
 	storage::meter::Diff,
 	weights::WeightInfo,
-	AccountIdOf, BadOrigin, BalanceOf, CodeInfoOf, CodeVec, Config, Error, Event, ExecError,
-	HoldReason, Pallet, PristineCode, Weight, LOG_TARGET,
+	AccountIdOf, BadOrigin, BalanceOf, CodeInfoOf, CodeVec, Config, Error, ExecError, HoldReason,
+	PristineCode, Weight, LOG_TARGET,
 };
 use alloc::vec::Vec;
 use codec::{Decode, Encode, MaxEncodedLen};
@@ -157,16 +156,9 @@ where
 					code_info.deposit,
 					BestEffort,
 				);
-				let deposit_released = code_info.deposit;
-				let remover = T::AddressMapper::to_address(&code_info.owner);
 
 				*existing = None;
 				<PristineCode<T>>::remove(&code_hash);
-				<Pallet<T>>::deposit_event(Event::CodeRemoved {
-					code_hash,
-					deposit_released,
-					remover,
-				});
 				Ok(())
 			} else {
 				Err(<Error<T>>::CodeNotFound.into())
@@ -202,12 +194,6 @@ where
 					self.code_info.refcount = 0;
 					<PristineCode<T>>::insert(code_hash, &self.code);
 					*stored_code_info = Some(self.code_info.clone());
-					let uploader = T::AddressMapper::to_address(&self.code_info.owner);
-					<Pallet<T>>::deposit_event(Event::CodeStored {
-						code_hash,
-						deposit_held: deposit,
-						uploader,
-					});
 					Ok(deposit)
 				},
 			}
@@ -288,7 +274,7 @@ impl<T: Config> WasmBlob<T> {
 		}
 		let engine = polkavm::Engine::new(&config).expect(
 			"on-chain (no_std) use of interpreter is hard coded.
-				interpreter is available on all plattforms; qed",
+				interpreter is available on all platforms; qed",
 		);
 
 		let mut module_config = polkavm::ModuleConfig::new();
diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs
index 52f79f2eb55a5ca8d3f67824d81ca8b1857d168e..4fbcfe1b47f5b1866e745ab11e599de6e0ce5f99 100644
--- a/substrate/frame/revive/src/wasm/runtime.rs
+++ b/substrate/frame/revive/src/wasm/runtime.rs
@@ -293,6 +293,8 @@ pub enum RuntimeCosts {
 	CallDataSize,
 	/// Weight of calling `seal_return_data_size`.
 	ReturnDataSize,
+	/// Weight of calling `seal_to_account_id`.
+	ToAccountId,
 	/// Weight of calling `seal_origin`.
 	Origin,
 	/// Weight of calling `seal_is_contract`.
@@ -339,8 +341,6 @@ pub enum RuntimeCosts {
 	Terminate(u32),
 	/// Weight of calling `seal_deposit_event` with the given number of topics and event size.
 	DepositEvent { num_topic: u32, len: u32 },
-	/// Weight of calling `seal_debug_message` per byte of passed message.
-	DebugMessage(u32),
 	/// Weight of calling `seal_set_storage` for the given storage item sizes.
 	SetStorage { old_bytes: u32, new_bytes: u32 },
 	/// Weight of calling `seal_clear_storage` per cleared byte.
@@ -468,6 +468,7 @@ impl<T: Config> Token<T> for RuntimeCosts {
 			Caller => T::WeightInfo::seal_caller(),
 			Origin => T::WeightInfo::seal_origin(),
 			IsContract => T::WeightInfo::seal_is_contract(),
+			ToAccountId => T::WeightInfo::seal_to_account_id(),
 			CodeHash => T::WeightInfo::seal_code_hash(),
 			CodeSize => T::WeightInfo::seal_code_size(),
 			OwnCodeHash => T::WeightInfo::seal_own_code_hash(),
@@ -489,7 +490,6 @@ impl<T: Config> Token<T> for RuntimeCosts {
 			WeightToFee => T::WeightInfo::seal_weight_to_fee(),
 			Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies),
 			DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len),
-			DebugMessage(len) => T::WeightInfo::seal_debug_message(len),
 			SetStorage { new_bytes, old_bytes } => {
 				cost_storage!(write, seal_set_storage, new_bytes, old_bytes)
 			},
@@ -669,10 +669,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
 		match result {
 			Ok(_) => Ok(ReturnErrorCode::Success),
 			Err(e) => {
-				if self.ext.debug_buffer_enabled() {
-					self.ext.append_debug_buffer("call failed with: ");
-					self.ext.append_debug_buffer(e.into());
-				};
+				log::debug!(target: LOG_TARGET, "call failed with: {e:?}");
 				Ok(ErrorReturnCode::get())
 			},
 		}
@@ -1004,8 +1001,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
 		self.charge_gas(call_type.cost())?;
 
 		let callee = memory.read_h160(callee_ptr)?;
-		let deposit_limit =
-			if deposit_ptr == SENTINEL { U256::zero() } else { memory.read_u256(deposit_ptr)? };
+		let deposit_limit = memory.read_u256(deposit_ptr)?;
 
 		let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
 			let input = self.input_data.as_ref().ok_or(Error::<E::T>::InputForwarded)?;
@@ -1091,8 +1087,7 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
 		salt_ptr: u32,
 	) -> Result<ReturnErrorCode, TrapReason> {
 		self.charge_gas(RuntimeCosts::Instantiate { input_data_len })?;
-		let deposit_limit: U256 =
-			if deposit_ptr == SENTINEL { U256::zero() } else { memory.read_u256(deposit_ptr)? };
+		let deposit_limit: U256 = memory.read_u256(deposit_ptr)?;
 		let value = memory.read_u256(value_ptr)?;
 		let code_hash = memory.read_h256(code_hash_ptr)?;
 		let input_data = memory.read(input_data_ptr, input_data_len)?;
@@ -1834,27 +1829,6 @@ pub mod env {
 		self.contains_storage(memory, flags, key_ptr, key_len)
 	}
 
-	/// Emit a custom debug message.
-	/// See [`pallet_revive_uapi::HostFn::debug_message`].
-	fn debug_message(
-		&mut self,
-		memory: &mut M,
-		str_ptr: u32,
-		str_len: u32,
-	) -> Result<ReturnErrorCode, TrapReason> {
-		let str_len = str_len.min(limits::DEBUG_BUFFER_BYTES);
-		self.charge_gas(RuntimeCosts::DebugMessage(str_len))?;
-		if self.ext.append_debug_buffer("") {
-			let data = memory.read(str_ptr, str_len)?;
-			if let Some(msg) = core::str::from_utf8(&data).ok() {
-				self.ext.append_debug_buffer(msg);
-			}
-			Ok(ReturnErrorCode::Success)
-		} else {
-			Ok(ReturnErrorCode::LoggingDisabled)
-		}
-	}
-
 	/// Recovers the ECDSA public key from the given message hash and signature.
 	/// See [`pallet_revive_uapi::HostFn::ecdsa_recover`].
 	fn ecdsa_recover(
@@ -2164,12 +2138,30 @@ pub mod env {
 				Ok(ReturnErrorCode::Success)
 			},
 			Err(e) => {
-				if self.ext.append_debug_buffer("") {
-					self.ext.append_debug_buffer("seal0::xcm_send failed with: ");
-					self.ext.append_debug_buffer(e.into());
-				};
+				log::debug!(target: LOG_TARGET, "seal0::xcm_send failed with: {e:?}");
 				Ok(ReturnErrorCode::XcmSendFailed)
 			},
 		}
 	}
+
+	/// Retrieves the account id for a specified contract address.
+	///
+	/// See [`pallet_revive_uapi::HostFn::to_account_id`].
+	fn to_account_id(
+		&mut self,
+		memory: &mut M,
+		addr_ptr: u32,
+		out_ptr: u32,
+	) -> Result<(), TrapReason> {
+		self.charge_gas(RuntimeCosts::ToAccountId)?;
+		let address = memory.read_h160(addr_ptr)?;
+		let account_id = self.ext.to_account_id(&address);
+		Ok(self.write_fixed_sandbox_output(
+			memory,
+			out_ptr,
+			&account_id.encode(),
+			false,
+			already_charged,
+		)?)
+	}
 }
diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs
index e35ba5ca0766458427ed0f73927eea6bb4f1cc66..52153d74ca758c429a4982e393b7d211d79ab5b4 100644
--- a/substrate/frame/revive/src/weights.rs
+++ b/substrate/frame/revive/src/weights.rs
@@ -67,6 +67,7 @@ pub trait WeightInfo {
 	fn seal_caller() -> Weight;
 	fn seal_origin() -> Weight;
 	fn seal_is_contract() -> Weight;
+	fn seal_to_account_id() -> Weight;
 	fn seal_code_hash() -> Weight;
 	fn seal_own_code_hash() -> Weight;
 	fn seal_code_size() -> Weight;
@@ -96,7 +97,6 @@ pub trait WeightInfo {
 	fn seal_return(n: u32, ) -> Weight;
 	fn seal_terminate(n: u32, ) -> Weight;
 	fn seal_deposit_event(t: u32, n: u32, ) -> Weight;
-	fn seal_debug_message(i: u32, ) -> Weight;
 	fn get_storage_empty() -> Weight;
 	fn get_storage_full() -> Weight;
 	fn set_storage_empty() -> Weight;
@@ -378,6 +378,16 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		Weight::from_parts(10_336_000, 3771)
 			.saturating_add(T::DbWeight::get().reads(1_u64))
 	}
+	/// Storage: `Revive::AddressSuffix` (r:1 w:0)
+	/// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`)
+	fn seal_to_account_id() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `212`
+		//  Estimated: `3677`
+		// Minimum execution time: 4_000_000 picoseconds.
+		Weight::from_parts(4_000_000, 3677)
+			.saturating_add(T::DbWeight::get().reads(1_u64))
+	}
 	/// Storage: `Revive::ContractInfoOf` (r:1 w:0)
 	/// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`)
 	fn seal_code_hash() -> Weight {
@@ -643,16 +653,6 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 			// Standard Error: 34
 			.saturating_add(Weight::from_parts(774, 0).saturating_mul(n.into()))
 	}
-	/// The range of component `i` is `[0, 262144]`.
-	fn seal_debug_message(i: u32, ) -> Weight {
-		// Proof Size summary in bytes:
-		//  Measured:  `0`
-		//  Estimated: `0`
-		// Minimum execution time: 340_000 picoseconds.
-		Weight::from_parts(306_527, 0)
-			// Standard Error: 1
-			.saturating_add(Weight::from_parts(728, 0).saturating_mul(i.into()))
-	}
 	/// Storage: `Skipped::Metadata` (r:0 w:0)
 	/// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	fn get_storage_empty() -> Weight {
@@ -1274,6 +1274,16 @@ impl WeightInfo for () {
 		Weight::from_parts(10_336_000, 3771)
 			.saturating_add(RocksDbWeight::get().reads(1_u64))
 	}
+	/// Storage: `Revive::AddressSuffix` (r:1 w:0)
+	/// Proof: `Revive::AddressSuffix` (`max_values`: None, `max_size`: Some(32), added: 2507, mode: `Measured`)
+	fn seal_to_account_id() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `212`
+		//  Estimated: `3677`
+		// Minimum execution time: 4_000_000 picoseconds.
+		Weight::from_parts(4_000_000, 3677)
+			.saturating_add(RocksDbWeight::get().reads(1_u64))
+	}
 	/// Storage: `Revive::ContractInfoOf` (r:1 w:0)
 	/// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`)
 	fn seal_code_hash() -> Weight {
@@ -1539,16 +1549,6 @@ impl WeightInfo for () {
 			// Standard Error: 34
 			.saturating_add(Weight::from_parts(774, 0).saturating_mul(n.into()))
 	}
-	/// The range of component `i` is `[0, 262144]`.
-	fn seal_debug_message(i: u32, ) -> Weight {
-		// Proof Size summary in bytes:
-		//  Measured:  `0`
-		//  Estimated: `0`
-		// Minimum execution time: 340_000 picoseconds.
-		Weight::from_parts(306_527, 0)
-			// Standard Error: 1
-			.saturating_add(Weight::from_parts(728, 0).saturating_mul(i.into()))
-	}
 	/// Storage: `Skipped::Metadata` (r:0 w:0)
 	/// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	fn get_storage_empty() -> Weight {
diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml
index 7241d667fcdc75c4dbd6e2c5f3a6a02efd1835aa..cf006941cfd0a6477d35b9b0d992060105f6f3c1 100644
--- a/substrate/frame/revive/uapi/Cargo.toml
+++ b/substrate/frame/revive/uapi/Cargo.toml
@@ -22,7 +22,7 @@ paste = { workspace = true }
 scale-info = { features = ["derive"], optional = true, workspace = true }
 
 [target.'cfg(target_arch = "riscv64")'.dependencies]
-polkavm-derive = { version = "0.18.0" }
+polkavm-derive = { version = "0.19.0" }
 
 [package.metadata.docs.rs]
 features = ["unstable-hostfn"]
diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs
index eced4843b5522a6d0cbf98f18ee9ba06abbfb4b6..3e5cf0eb0c243d44c19368bd9cf7305fb418b829 100644
--- a/substrate/frame/revive/uapi/src/host.rs
+++ b/substrate/frame/revive/uapi/src/host.rs
@@ -113,7 +113,7 @@ pub trait HostFn: private::Sealed {
 		callee: &[u8; 20],
 		ref_time_limit: u64,
 		proof_size_limit: u64,
-		deposit: Option<&[u8; 32]>,
+		deposit: &[u8; 32],
 		value: &[u8; 32],
 		input_data: &[u8],
 		output: Option<&mut &mut [u8]>,
@@ -144,6 +144,18 @@ pub trait HostFn: private::Sealed {
 	/// - `output`: A reference to the output data buffer to write the origin's address.
 	fn origin(output: &mut [u8; 20]);
 
+	/// Retrieve the account id for a specified address.
+	///
+	/// # Parameters
+	///
+	/// - `addr`: A `H160` address.
+	/// - `output`: A reference to the output data buffer to write the account id.
+	///
+	/// # Note
+	///
+	/// If no mapping exists for `addr`, the fallback account id will be returned.
+	fn to_account_id(addr: &[u8; 20], output: &mut [u8]);
+
 	/// Retrieve the code hash for a specified contract address.
 	///
 	/// # Parameters
@@ -202,7 +214,7 @@ pub trait HostFn: private::Sealed {
 		address: &[u8; 20],
 		ref_time_limit: u64,
 		proof_size_limit: u64,
-		deposit_limit: Option<&[u8; 32]>,
+		deposit_limit: &[u8; 32],
 		input_data: &[u8],
 		output: Option<&mut &mut [u8]>,
 	) -> Result;
@@ -318,7 +330,7 @@ pub trait HostFn: private::Sealed {
 		code_hash: &[u8; 32],
 		ref_time_limit: u64,
 		proof_size_limit: u64,
-		deposit: Option<&[u8; 32]>,
+		deposit: &[u8; 32],
 		value: &[u8; 32],
 		input: &[u8],
 		address: Option<&mut [u8; 20]>,
@@ -488,7 +500,7 @@ pub trait HostFn: private::Sealed {
 	/// A return value of `true` indicates that this contract is being called by a root origin,
 	/// and `false` indicates that the caller is a signed origin.
 	#[unstable_hostfn]
-	fn caller_is_root() -> u32;
+	fn caller_is_root() -> bool;
 
 	/// Clear the value at the given key in the contract storage.
 	///
@@ -515,26 +527,6 @@ pub trait HostFn: private::Sealed {
 	#[unstable_hostfn]
 	fn contains_storage(flags: StorageFlags, key: &[u8]) -> Option<u32>;
 
-	/// Emit a custom debug message.
-	///
-	/// No newlines are added to the supplied message.
-	/// Specifying invalid UTF-8 just drops the message with no trap.
-	///
-	/// This is a no-op if debug message recording is disabled which is always the case
-	/// when the code is executing on-chain. The message is interpreted as UTF-8 and
-	/// appended to the debug buffer which is then supplied to the calling RPC client.
-	///
-	/// # Note
-	///
-	/// Even though no action is taken when debug message recording is disabled there is still
-	/// a non trivial overhead (and weight cost) associated with calling this function. Contract
-	/// languages should remove calls to this function (either at runtime or compile time) when
-	/// not being executed as an RPC. For example, they could allow users to disable logging
-	/// through compile time flags (cargo features) for on-chain deployment. Additionally, the
-	/// return value of this function can be cached in order to prevent further calls at runtime.
-	#[unstable_hostfn]
-	fn debug_message(str: &[u8]) -> Result;
-
 	/// Recovers the ECDSA public key from the given message hash and signature.
 	///
 	/// Writes the public key into the given output buffer.
diff --git a/substrate/frame/revive/uapi/src/host/riscv64.rs b/substrate/frame/revive/uapi/src/host/riscv64.rs
index 6fdda86892d53000e50c3f5c9dfb7be56081e062..3726564e26ebaea2982912e63ad08924774790ed 100644
--- a/substrate/frame/revive/uapi/src/host/riscv64.rs
+++ b/substrate/frame/revive/uapi/src/host/riscv64.rs
@@ -69,6 +69,7 @@ mod sys {
 		pub fn caller(out_ptr: *mut u8);
 		pub fn origin(out_ptr: *mut u8);
 		pub fn is_contract(account_ptr: *const u8) -> ReturnCode;
+		pub fn to_account_id(address_ptr: *const u8, out_ptr: *mut u8);
 		pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8);
 		pub fn code_size(address_ptr: *const u8) -> u64;
 		pub fn own_code_hash(out_ptr: *mut u8);
@@ -109,7 +110,6 @@ mod sys {
 			out_ptr: *mut u8,
 			out_len_ptr: *mut u32,
 		) -> ReturnCode;
-		pub fn debug_message(str_ptr: *const u8, str_len: u32) -> ReturnCode;
 		pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode;
 		pub fn ecdsa_recover(
 			signature_ptr: *const u8,
@@ -168,7 +168,7 @@ impl HostFn for HostFnImpl {
 		code_hash: &[u8; 32],
 		ref_time_limit: u64,
 		proof_size_limit: u64,
-		deposit_limit: Option<&[u8; 32]>,
+		deposit_limit: &[u8; 32],
 		value: &[u8; 32],
 		input: &[u8],
 		mut address: Option<&mut [u8; 20]>,
@@ -180,7 +180,7 @@ impl HostFn for HostFnImpl {
 			None => crate::SENTINEL as _,
 		};
 		let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
-		let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit);
+		let deposit_limit_ptr = deposit_limit.as_ptr();
 		let salt_ptr = ptr_or_sentinel(&salt);
 		#[repr(C)]
 		#[allow(dead_code)]
@@ -225,13 +225,13 @@ impl HostFn for HostFnImpl {
 		callee: &[u8; 20],
 		ref_time_limit: u64,
 		proof_size_limit: u64,
-		deposit_limit: Option<&[u8; 32]>,
+		deposit_limit: &[u8; 32],
 		value: &[u8; 32],
 		input: &[u8],
 		mut output: Option<&mut &mut [u8]>,
 	) -> Result {
 		let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
-		let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit);
+		let deposit_limit_ptr = deposit_limit.as_ptr();
 		#[repr(C)]
 		#[allow(dead_code)]
 		struct Args {
@@ -273,12 +273,12 @@ impl HostFn for HostFnImpl {
 		address: &[u8; 20],
 		ref_time_limit: u64,
 		proof_size_limit: u64,
-		deposit_limit: Option<&[u8; 32]>,
+		deposit_limit: &[u8; 32],
 		input: &[u8],
 		mut output: Option<&mut &mut [u8]>,
 	) -> Result {
 		let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
-		let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit);
+		let deposit_limit_ptr = deposit_limit.as_ptr();
 		#[repr(C)]
 		#[allow(dead_code)]
 		struct Args {
@@ -457,6 +457,11 @@ impl HostFn for HostFnImpl {
 		unsafe { sys::ref_time_left() }
 	}
 
+	#[unstable_hostfn]
+	fn to_account_id(address: &[u8; 20], output: &mut [u8]) {
+		unsafe { sys::to_account_id(address.as_ptr(), output.as_mut_ptr()) }
+	}
+
 	#[unstable_hostfn]
 	fn block_hash(block_number_ptr: &[u8; 32], output: &mut [u8; 32]) {
 		unsafe { sys::block_hash(block_number_ptr.as_ptr(), output.as_mut_ptr()) };
@@ -501,8 +506,9 @@ impl HostFn for HostFnImpl {
 	}
 
 	#[unstable_hostfn]
-	fn caller_is_root() -> u32 {
-		unsafe { sys::caller_is_root() }.into_u32()
+	fn caller_is_root() -> bool {
+		let ret_val = unsafe { sys::caller_is_root() };
+		ret_val.into_bool()
 	}
 
 	#[unstable_hostfn]
@@ -518,12 +524,6 @@ impl HostFn for HostFnImpl {
 		ret_code.into()
 	}
 
-	#[unstable_hostfn]
-	fn debug_message(str: &[u8]) -> Result {
-		let ret_code = unsafe { sys::debug_message(str.as_ptr(), str.len() as u32) };
-		ret_code.into()
-	}
-
 	#[unstable_hostfn]
 	fn ecdsa_recover(
 		signature: &[u8; 65],
diff --git a/substrate/frame/revive/uapi/src/lib.rs b/substrate/frame/revive/uapi/src/lib.rs
index ef1798b4bf615a7e50054954546c963094746042..867f356339876a433f01248780c9cdab6c8ebb7e 100644
--- a/substrate/frame/revive/uapi/src/lib.rs
+++ b/substrate/frame/revive/uapi/src/lib.rs
@@ -86,9 +86,8 @@ define_error_codes! {
 	/// Transfer failed for other not further specified reason. Most probably
 	/// reserved or locked balance of the sender that was preventing the transfer.
 	TransferFailed = 4,
-	/// The call to `debug_message` had no effect because debug message
-	/// recording was disabled.
-	LoggingDisabled = 5,
+	/// The subcall ran out of weight or storage deposit.
+	OutOfResources = 5,
 	/// The call dispatched by `call_runtime` was executed but returned an error.
 	CallRuntimeFailed = 6,
 	/// ECDSA public key recovery failed. Most probably wrong recovery id or signature.
@@ -99,8 +98,6 @@ define_error_codes! {
 	XcmExecutionFailed = 9,
 	/// The `xcm_send` call failed.
 	XcmSendFailed = 10,
-	/// The subcall ran out of weight or storage deposit.
-	OutOfResources = 11,
 }
 
 /// The raw return code returned by the host side.
diff --git a/substrate/frame/root-offences/src/mock.rs b/substrate/frame/root-offences/src/mock.rs
index a27fb36f64a6478f00a1379160bbcd7d63c59d6b..3f14dc00b56068f8be244d24e4cb011748c0aa7f 100644
--- a/substrate/frame/root-offences/src/mock.rs
+++ b/substrate/frame/root-offences/src/mock.rs
@@ -25,7 +25,7 @@ use frame_election_provider_support::{
 };
 use frame_support::{
 	derive_impl, parameter_types,
-	traits::{ConstU32, ConstU64, Hooks, OneSessionHandler},
+	traits::{ConstU32, ConstU64, OneSessionHandler},
 };
 use pallet_staking::StakerStatus;
 use sp_runtime::{curve::PiecewiseLinear, testing::UintAuthorityId, traits::Zero, BuildStorage};
@@ -126,6 +126,7 @@ parameter_types! {
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Test {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
 	type UnixTime = Timestamp;
@@ -206,10 +207,10 @@ impl ExtBuilder {
 				(30, self.balance_factor * 50),
 				(40, self.balance_factor * 50),
 				// stashes
-				(11, self.balance_factor * 1000),
-				(21, self.balance_factor * 1000),
-				(31, self.balance_factor * 500),
-				(41, self.balance_factor * 1000),
+				(11, self.balance_factor * 1500),
+				(21, self.balance_factor * 1500),
+				(31, self.balance_factor * 1000),
+				(41, self.balance_factor * 2000),
 			],
 		}
 		.assimilate_storage(&mut storage)
@@ -283,16 +284,12 @@ pub(crate) fn start_session(session_index: SessionIndex) {
 /// a block import/propose process where we first initialize the block, then execute some stuff (not
 /// in the function), and then finalize the block.
 pub(crate) fn run_to_block(n: BlockNumber) {
-	Staking::on_finalize(System::block_number());
-	for b in (System::block_number() + 1)..=n {
-		System::set_block_number(b);
-		Session::on_initialize(b);
-		<Staking as Hooks<u64>>::on_initialize(b);
-		Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP);
-		if b != n {
-			Staking::on_finalize(System::block_number());
-		}
-	}
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default().after_initialize(|bn| {
+			Timestamp::set_timestamp(bn * BLOCK_TIME + INIT_TIMESTAMP);
+		}),
+	);
 }
 
 pub(crate) fn active_era() -> EraIndex {
diff --git a/substrate/frame/salary/Cargo.toml b/substrate/frame/salary/Cargo.toml
index 626993a0547b58bee5a125edcec1ffce9ed5b779..84c55b110c8c2b3ebc1b948791311e551094dadf 100644
--- a/substrate/frame/salary/Cargo.toml
+++ b/substrate/frame/salary/Cargo.toml
@@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { features = ["derive"], workspace = true }
-frame = { workspace = true, features = ["experimental", "runtime"] }
+frame = { workspace = true, features = ["runtime"] }
 log = { workspace = true }
 pallet-ranked-collective = { optional = true, workspace = true }
 scale-info = { features = ["derive"], workspace = true }
diff --git a/substrate/frame/scheduler/src/mock.rs b/substrate/frame/scheduler/src/mock.rs
index 8d36ca1c42e3ad916a5cd38e2ef3fa7bcfea5483..43a964bcf149731d7df03ad80d6247a82b2a8ec0 100644
--- a/substrate/frame/scheduler/src/mock.rs
+++ b/substrate/frame/scheduler/src/mock.rs
@@ -22,7 +22,7 @@ use super::*;
 use crate as scheduler;
 use frame_support::{
 	derive_impl, ord_parameter_types, parameter_types,
-	traits::{ConstU32, Contains, EitherOfDiverse, EqualPrivilegeOnly, OnFinalize, OnInitialize},
+	traits::{ConstU32, Contains, EitherOfDiverse, EqualPrivilegeOnly},
 };
 use frame_system::{EnsureRoot, EnsureSignedBy};
 use sp_runtime::{BuildStorage, Perbill};
@@ -236,14 +236,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 	t.into()
 }
 
-pub fn run_to_block(n: u64) {
-	while System::block_number() < n {
-		Scheduler::on_finalize(System::block_number());
-		System::set_block_number(System::block_number() + 1);
-		Scheduler::on_initialize(System::block_number());
-	}
-}
-
 pub fn root() -> OriginCaller {
 	system::RawOrigin::Root.into()
 }
diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs
index 3023a370a4b6034e4ec9366fcaa065035edccec3..7552239341088d7ad46c287edc23f1dd5b417088 100644
--- a/substrate/frame/scheduler/src/tests.rs
+++ b/substrate/frame/scheduler/src/tests.rs
@@ -20,7 +20,7 @@
 use super::*;
 use crate::mock::{
 	logger::{self, Threshold},
-	new_test_ext, root, run_to_block, LoggerCall, RuntimeCall, Scheduler, Test, *,
+	new_test_ext, root, LoggerCall, RuntimeCall, Scheduler, Test, *,
 };
 use frame_support::{
 	assert_err, assert_noop, assert_ok,
@@ -52,14 +52,14 @@ fn basic_scheduling_works() {
 		));
 
 		// `log` runtime call should not have executed yet
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// `log` runtime call should have executed at block 4
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -87,17 +87,17 @@ fn scheduling_with_preimages_works() {
 		assert!(Preimage::is_requested(&hash));
 
 		// `log` runtime call should not have executed yet
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// preimage should not have been removed when executed by the scheduler
 		assert!(!Preimage::len(&hash).is_some());
 		assert!(!Preimage::is_requested(&hash));
 		// `log` runtime call should have executed at block 4
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -105,7 +105,7 @@ fn scheduling_with_preimages_works() {
 #[test]
 fn schedule_after_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(2);
+		System::run_to_block::<AllPalletsWithSystem>(2);
 		let call =
 			RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) });
 		assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call));
@@ -117,11 +117,11 @@ fn schedule_after_works() {
 			root(),
 			Preimage::bound(call).unwrap()
 		));
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert!(logger::log().is_empty());
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -129,7 +129,7 @@ fn schedule_after_works() {
 #[test]
 fn schedule_after_zero_works() {
 	new_test_ext().execute_with(|| {
-		run_to_block(2);
+		System::run_to_block::<AllPalletsWithSystem>(2);
 		let call =
 			RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_parts(10, 0) });
 		assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call));
@@ -141,9 +141,9 @@ fn schedule_after_zero_works() {
 			Preimage::bound(call).unwrap()
 		));
 		// Will trigger on the next block.
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -163,19 +163,19 @@ fn periodic_scheduling_works() {
 			}))
 			.unwrap()
 		));
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
 	});
 }
@@ -201,37 +201,37 @@ fn retry_scheduling_works() {
 		// retry 10 times every 3 blocks
 		assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 3));
 		assert_eq!(Retries::<Test>::iter().count(), 1);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert!(Agenda::<Test>::get(4)[0].is_some());
 		// task should be retried in block 7
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(Agenda::<Test>::get(4).is_empty());
 		assert!(Agenda::<Test>::get(7)[0].is_some());
 		assert!(logger::log().is_empty());
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert!(Agenda::<Test>::get(7)[0].is_some());
 		assert!(logger::log().is_empty());
 		// task still fails, should be retried in block 10
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert!(Agenda::<Test>::get(7).is_empty());
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		assert!(logger::log().is_empty());
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		assert!(logger::log().is_empty());
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert!(logger::log().is_empty());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		// finally it should succeed
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
-		run_to_block(11);
+		System::run_to_block::<AllPalletsWithSystem>(11);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -262,37 +262,37 @@ fn named_retry_scheduling_works() {
 		// retry 10 times every 3 blocks
 		assert_ok!(Scheduler::set_retry_named(root().into(), [1u8; 32], 10, 3));
 		assert_eq!(Retries::<Test>::iter().count(), 1);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert!(Agenda::<Test>::get(4)[0].is_some());
 		// task should be retried in block 7
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(Agenda::<Test>::get(4).is_empty());
 		assert!(Agenda::<Test>::get(7)[0].is_some());
 		assert!(logger::log().is_empty());
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert!(Agenda::<Test>::get(7)[0].is_some());
 		assert!(logger::log().is_empty());
 		// task still fails, should be retried in block 10
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert!(Agenda::<Test>::get(7).is_empty());
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		assert!(logger::log().is_empty());
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		assert!(logger::log().is_empty());
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert!(logger::log().is_empty());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		// finally it should succeed
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
-		run_to_block(11);
+		System::run_to_block::<AllPalletsWithSystem>(11);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -333,11 +333,11 @@ fn retry_scheduling_multiple_tasks_works() {
 		// task 42 will be retried 10 times every 3 blocks
 		assert_ok!(Scheduler::set_retry(root().into(), (4, 1), 10, 3));
 		assert_eq!(Retries::<Test>::iter().count(), 2);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert_eq!(Agenda::<Test>::get(4).len(), 2);
 		// both tasks fail
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(Agenda::<Test>::get(4).is_empty());
 		// 20 is rescheduled for next block
 		assert_eq!(Agenda::<Test>::get(5).len(), 1);
@@ -345,41 +345,41 @@ fn retry_scheduling_multiple_tasks_works() {
 		assert_eq!(Agenda::<Test>::get(7).len(), 1);
 		assert!(logger::log().is_empty());
 		// 20 still fails
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		// 20 rescheduled for next block
 		assert_eq!(Agenda::<Test>::get(6).len(), 1);
 		assert_eq!(Agenda::<Test>::get(7).len(), 1);
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert!(logger::log().is_empty());
 		// 20 still fails
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		// rescheduled for next block together with 42
 		assert_eq!(Agenda::<Test>::get(7).len(), 2);
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert!(logger::log().is_empty());
 		// both tasks will fail, for 20 it was the last retry so it's dropped
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert!(Agenda::<Test>::get(7).is_empty());
 		assert!(Agenda::<Test>::get(8).is_empty());
 		// 42 is rescheduled for block 10
 		assert_eq!(Agenda::<Test>::get(10).len(), 1);
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert!(logger::log().is_empty());
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		assert_eq!(Agenda::<Test>::get(10).len(), 1);
 		assert!(logger::log().is_empty());
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert!(logger::log().is_empty());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		// 42 runs successfully
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
-		run_to_block(11);
+		System::run_to_block::<AllPalletsWithSystem>(11);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -422,11 +422,11 @@ fn retry_scheduling_multiple_named_tasks_works() {
 		// task 42 will be retried 10 times every 3 block
 		assert_ok!(Scheduler::set_retry_named(root().into(), [42u8; 32], 10, 3));
 		assert_eq!(Retries::<Test>::iter().count(), 2);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert_eq!(Agenda::<Test>::get(4).len(), 2);
 		// both tasks fail
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(Agenda::<Test>::get(4).is_empty());
 		// 42 is rescheduled for block 7
 		assert_eq!(Agenda::<Test>::get(7).len(), 1);
@@ -434,41 +434,41 @@ fn retry_scheduling_multiple_named_tasks_works() {
 		assert_eq!(Agenda::<Test>::get(5).len(), 1);
 		assert!(logger::log().is_empty());
 		// 20 still fails
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		// 20 rescheduled for next block
 		assert_eq!(Agenda::<Test>::get(6).len(), 1);
 		assert_eq!(Agenda::<Test>::get(7).len(), 1);
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert!(logger::log().is_empty());
 		// 20 still fails
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		// 20 rescheduled for next block together with 42
 		assert_eq!(Agenda::<Test>::get(7).len(), 2);
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert!(logger::log().is_empty());
 		// both tasks will fail, for 20 it was the last retry so it's dropped
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert!(Agenda::<Test>::get(7).is_empty());
 		assert!(Agenda::<Test>::get(8).is_empty());
 		// 42 is rescheduled for block 10
 		assert_eq!(Agenda::<Test>::get(10).len(), 1);
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert!(logger::log().is_empty());
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		assert_eq!(Agenda::<Test>::get(10).len(), 1);
 		assert!(logger::log().is_empty());
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert!(logger::log().is_empty());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		// 42 runs successfully
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
-		run_to_block(11);
+		System::run_to_block::<AllPalletsWithSystem>(11);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -495,33 +495,33 @@ fn retry_scheduling_with_period_works() {
 		// 42 will be retried 10 times every 2 blocks
 		assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 2));
 		assert_eq!(Retries::<Test>::iter().count(), 1);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert!(Agenda::<Test>::get(4)[0].is_some());
 		// 42 runs successfully once, it will run again at block 7
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(Agenda::<Test>::get(4).is_empty());
 		assert!(Agenda::<Test>::get(7)[0].is_some());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// nothing changed
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert!(Agenda::<Test>::get(7)[0].is_some());
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// 42 runs successfully again, it will run again at block 10
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert!(Agenda::<Test>::get(7).is_empty());
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
 		// 42 has 10 retries left out of a total of 10
 		assert_eq!(Retries::<Test>::get((10, 0)).unwrap().remaining, 10);
 		// 42 will fail because we're outside the set threshold (block number in `4..8`), so it
 		// should be retried in 2 blocks (at block 12)
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		// should be queued for the normal period of 3 blocks
 		assert!(Agenda::<Test>::get(13)[0].is_some());
 		// should also be queued to be retried in 2 blocks
@@ -532,7 +532,7 @@ fn retry_scheduling_with_period_works() {
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
 		// 42 will fail again
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		// should still be queued for the normal period
 		assert!(Agenda::<Test>::get(13)[0].is_some());
 		// should be queued to be retried in 2 blocks
@@ -543,7 +543,7 @@ fn retry_scheduling_with_period_works() {
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
 		// 42 will fail for the regular periodic run
-		run_to_block(13);
+		System::run_to_block::<AllPalletsWithSystem>(13);
 		// should still be queued for the normal period
 		assert!(Agenda::<Test>::get(16)[0].is_some());
 		// should still be queued to be retried next block
@@ -560,7 +560,7 @@ fn retry_scheduling_with_period_works() {
 		// change the threshold to allow the task to succeed
 		Threshold::<Test>::put((14, 100));
 		// first retry should now succeed
-		run_to_block(14);
+		System::run_to_block::<AllPalletsWithSystem>(14);
 		assert!(Agenda::<Test>::get(15)[0].as_ref().unwrap().maybe_periodic.is_none());
 		assert_eq!(Agenda::<Test>::get(16).iter().filter(|entry| entry.is_some()).count(), 1);
 		assert!(Agenda::<Test>::get(16)[0].is_some());
@@ -569,7 +569,7 @@ fn retry_scheduling_with_period_works() {
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
 		// second retry should also succeed
-		run_to_block(15);
+		System::run_to_block::<AllPalletsWithSystem>(15);
 		assert_eq!(Agenda::<Test>::get(16).iter().filter(|entry| entry.is_some()).count(), 1);
 		assert!(Agenda::<Test>::get(16)[0].is_some());
 		assert!(Agenda::<Test>::get(17).is_empty());
@@ -580,7 +580,7 @@ fn retry_scheduling_with_period_works() {
 			vec![(root(), 42u32), (root(), 42u32), (root(), 42u32), (root(), 42u32)]
 		);
 		// normal periodic run on block 16 will succeed
-		run_to_block(16);
+		System::run_to_block::<AllPalletsWithSystem>(16);
 		// next periodic run at block 19
 		assert!(Agenda::<Test>::get(19)[0].is_some());
 		assert!(Agenda::<Test>::get(18).is_empty());
@@ -598,7 +598,7 @@ fn retry_scheduling_with_period_works() {
 			]
 		);
 		// final periodic run on block 19 will succeed
-		run_to_block(19);
+		System::run_to_block::<AllPalletsWithSystem>(19);
 		// next periodic run at block 19
 		assert_eq!(Agenda::<Test>::iter().count(), 0);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
@@ -639,33 +639,33 @@ fn named_retry_scheduling_with_period_works() {
 		// 42 will be retried 10 times every 2 blocks
 		assert_ok!(Scheduler::set_retry_named(root().into(), [42u8; 32], 10, 2));
 		assert_eq!(Retries::<Test>::iter().count(), 1);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert!(Agenda::<Test>::get(4)[0].is_some());
 		// 42 runs successfully once, it will run again at block 7
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(Agenda::<Test>::get(4).is_empty());
 		assert!(Agenda::<Test>::get(7)[0].is_some());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// nothing changed
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert!(Agenda::<Test>::get(7)[0].is_some());
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// 42 runs successfully again, it will run again at block 10
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert!(Agenda::<Test>::get(7).is_empty());
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
 		// 42 has 10 retries left out of a total of 10
 		assert_eq!(Retries::<Test>::get((10, 0)).unwrap().remaining, 10);
 		// 42 will fail because we're outside the set threshold (block number in `4..8`), so it
 		// should be retried in 2 blocks (at block 12)
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		// should be queued for the normal period of 3 blocks
 		assert!(Agenda::<Test>::get(13)[0].is_some());
 		// should also be queued to be retried in 2 blocks
@@ -677,7 +677,7 @@ fn named_retry_scheduling_with_period_works() {
 		assert_eq!(Lookup::<Test>::get([42u8; 32]).unwrap(), (13, 0));
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
 		// 42 will fail again
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		// should still be queued for the normal period
 		assert!(Agenda::<Test>::get(13)[0].is_some());
 		// should be queued to be retried in 2 blocks
@@ -688,7 +688,7 @@ fn named_retry_scheduling_with_period_works() {
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
 		// 42 will fail for the regular periodic run
-		run_to_block(13);
+		System::run_to_block::<AllPalletsWithSystem>(13);
 		// should still be queued for the normal period
 		assert!(Agenda::<Test>::get(16)[0].is_some());
 		// should still be queued to be retried next block
@@ -706,7 +706,7 @@ fn named_retry_scheduling_with_period_works() {
 		// change the threshold to allow the task to succeed
 		Threshold::<Test>::put((14, 100));
 		// first retry should now succeed
-		run_to_block(14);
+		System::run_to_block::<AllPalletsWithSystem>(14);
 		assert!(Agenda::<Test>::get(15)[0].as_ref().unwrap().maybe_periodic.is_none());
 		assert_eq!(Agenda::<Test>::get(16).iter().filter(|entry| entry.is_some()).count(), 1);
 		assert!(Agenda::<Test>::get(16)[0].is_some());
@@ -715,7 +715,7 @@ fn named_retry_scheduling_with_period_works() {
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
 		// second retry should also succeed
-		run_to_block(15);
+		System::run_to_block::<AllPalletsWithSystem>(15);
 		assert_eq!(Agenda::<Test>::get(16).iter().filter(|entry| entry.is_some()).count(), 1);
 		assert!(Agenda::<Test>::get(16)[0].is_some());
 		assert!(Agenda::<Test>::get(17).is_empty());
@@ -727,7 +727,7 @@ fn named_retry_scheduling_with_period_works() {
 			vec![(root(), 42u32), (root(), 42u32), (root(), 42u32), (root(), 42u32)]
 		);
 		// normal periodic run on block 16 will succeed
-		run_to_block(16);
+		System::run_to_block::<AllPalletsWithSystem>(16);
 		// next periodic run at block 19
 		assert!(Agenda::<Test>::get(19)[0].is_some());
 		assert!(Agenda::<Test>::get(18).is_empty());
@@ -746,7 +746,7 @@ fn named_retry_scheduling_with_period_works() {
 			]
 		);
 		// final periodic run on block 19 will succeed
-		run_to_block(19);
+		System::run_to_block::<AllPalletsWithSystem>(19);
 		// next periodic run at block 19
 		assert_eq!(Agenda::<Test>::iter().count(), 0);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
@@ -786,12 +786,12 @@ fn retry_scheduling_expires() {
 		// task 42 will be retried 3 times every block
 		assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 3, 1));
 		assert_eq!(Retries::<Test>::iter().count(), 1);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		// task 42 is scheduled for next block
 		assert!(Agenda::<Test>::get(4)[0].is_some());
 		// task fails because we're past block 3
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// task is scheduled for next block
 		assert!(Agenda::<Test>::get(4).is_empty());
 		assert!(Agenda::<Test>::get(5)[0].is_some());
@@ -799,7 +799,7 @@ fn retry_scheduling_expires() {
 		assert_eq!(Retries::<Test>::get((5, 0)).unwrap().remaining, 2);
 		assert!(logger::log().is_empty());
 		// task fails again
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		// task is scheduled for next block
 		assert!(Agenda::<Test>::get(5).is_empty());
 		assert!(Agenda::<Test>::get(6)[0].is_some());
@@ -807,7 +807,7 @@ fn retry_scheduling_expires() {
 		assert_eq!(Retries::<Test>::get((6, 0)).unwrap().remaining, 1);
 		assert!(logger::log().is_empty());
 		// task fails again
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		// task is scheduled for next block
 		assert!(Agenda::<Test>::get(6).is_empty());
 		assert!(Agenda::<Test>::get(7)[0].is_some());
@@ -815,7 +815,7 @@ fn retry_scheduling_expires() {
 		assert_eq!(Retries::<Test>::get((7, 0)).unwrap().remaining, 0);
 		assert!(logger::log().is_empty());
 		// task fails again
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		// task ran out of retries so it gets dropped
 		assert_eq!(Agenda::<Test>::iter().count(), 0);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
@@ -949,17 +949,17 @@ fn retry_periodic_full_cycle() {
 		// 42 will be retried 2 times every block
 		assert_ok!(Scheduler::set_retry_named(root().into(), [42u8; 32], 2, 1));
 		assert_eq!(Retries::<Test>::iter().count(), 1);
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert!(logger::log().is_empty());
 		assert!(Agenda::<Test>::get(10)[0].is_some());
 		// 42 runs successfully once, it will run again at block 110
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert!(Agenda::<Test>::get(10).is_empty());
 		assert!(Agenda::<Test>::get(110)[0].is_some());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// nothing changed
-		run_to_block(109);
+		System::run_to_block::<AllPalletsWithSystem>(109);
 		assert!(Agenda::<Test>::get(110)[0].is_some());
 		// original task still has 2 remaining retries
 		assert_eq!(Retries::<Test>::get((110, 0)).unwrap().remaining, 2);
@@ -968,7 +968,7 @@ fn retry_periodic_full_cycle() {
 		Threshold::<Test>::put((1, 2));
 		// 42 will fail because we're outside the set threshold (block number in `1..2`), so it
 		// should be retried next block (at block 111)
-		run_to_block(110);
+		System::run_to_block::<AllPalletsWithSystem>(110);
 		// should be queued for the normal period of 100 blocks
 		assert!(Agenda::<Test>::get(210)[0].is_some());
 		// should also be queued to be retried next block
@@ -980,7 +980,7 @@ fn retry_periodic_full_cycle() {
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// 42 retry will fail again
-		run_to_block(111);
+		System::run_to_block::<AllPalletsWithSystem>(111);
 		// should still be queued for the normal period
 		assert!(Agenda::<Test>::get(210)[0].is_some());
 		// should be queued to be retried next block
@@ -991,20 +991,20 @@ fn retry_periodic_full_cycle() {
 		assert_eq!(Retries::<Test>::iter().count(), 2);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// 42 retry will fail again
-		run_to_block(112);
+		System::run_to_block::<AllPalletsWithSystem>(112);
 		// should still be queued for the normal period
 		assert!(Agenda::<Test>::get(210)[0].is_some());
 		// 42 retry clone ran out of retries, must have been evicted
 		assert_eq!(Agenda::<Test>::iter().count(), 1);
 
 		// advance
-		run_to_block(209);
+		System::run_to_block::<AllPalletsWithSystem>(209);
 		// should still be queued for the normal period
 		assert!(Agenda::<Test>::get(210)[0].is_some());
 		// 42 retry clone ran out of retries, must have been evicted
 		assert_eq!(Agenda::<Test>::iter().count(), 1);
 		// 42 should fail again and should spawn another retry clone
-		run_to_block(210);
+		System::run_to_block::<AllPalletsWithSystem>(210);
 		// should be queued for the normal period of 100 blocks
 		assert!(Agenda::<Test>::get(310)[0].is_some());
 		// should also be queued to be retried next block
@@ -1018,7 +1018,7 @@ fn retry_periodic_full_cycle() {
 		// make 42 run successfully again
 		Threshold::<Test>::put((1, 1000));
 		// 42 retry clone should now succeed
-		run_to_block(211);
+		System::run_to_block::<AllPalletsWithSystem>(211);
 		// should be queued for the normal period of 100 blocks
 		assert!(Agenda::<Test>::get(310)[0].is_some());
 		// retry was successful, retry task should have been discarded
@@ -1029,7 +1029,7 @@ fn retry_periodic_full_cycle() {
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
 
 		// fast forward to the last periodic run of 42
-		run_to_block(310);
+		System::run_to_block::<AllPalletsWithSystem>(310);
 		// 42 was successful, the period ended as this was the 4th scheduled periodic run so 42 must
 		// have been discarded
 		assert_eq!(Agenda::<Test>::iter().count(), 0);
@@ -1057,7 +1057,7 @@ fn reschedule_works() {
 			(4, 0)
 		);
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 
 		assert_eq!(Scheduler::do_reschedule((4, 0), DispatchTime::At(6)).unwrap(), (6, 0));
@@ -1067,13 +1067,13 @@ fn reschedule_works() {
 			Error::<Test>::RescheduleNoChange
 		);
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(logger::log().is_empty());
 
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -1097,7 +1097,7 @@ fn reschedule_named_works() {
 			(4, 0)
 		);
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 
 		assert_eq!(Scheduler::do_reschedule_named([1u8; 32], DispatchTime::At(6)).unwrap(), (6, 0));
@@ -1107,13 +1107,13 @@ fn reschedule_named_works() {
 			Error::<Test>::RescheduleNoChange
 		);
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(logger::log().is_empty());
 
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -1137,16 +1137,16 @@ fn reschedule_named_periodic_works() {
 			(4, 0)
 		);
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 
 		assert_eq!(Scheduler::do_reschedule_named([1u8; 32], DispatchTime::At(5)).unwrap(), (5, 0));
 		assert_eq!(Scheduler::do_reschedule_named([1u8; 32], DispatchTime::At(6)).unwrap(), (6, 0));
 
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert!(logger::log().is_empty());
 
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 
 		assert_eq!(
@@ -1154,16 +1154,16 @@ fn reschedule_named_periodic_works() {
 			(10, 0)
 		);
 
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32)]);
 
-		run_to_block(13);
+		System::run_to_block::<AllPalletsWithSystem>(13);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
 
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 42u32), (root(), 42u32)]);
 	});
 }
@@ -1197,11 +1197,11 @@ fn cancel_named_scheduling_works_with_normal_cancel() {
 			.unwrap(),
 		)
 		.unwrap();
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert_ok!(Scheduler::do_cancel_named(None, [1u8; 32]));
 		assert_ok!(Scheduler::do_cancel(None, i));
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert!(logger::log().is_empty());
 	});
 }
@@ -1251,13 +1251,13 @@ fn cancel_named_periodic_scheduling_works() {
 			.unwrap(),
 		)
 		.unwrap();
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		assert_ok!(Scheduler::do_cancel_named(None, [1u8; 32]));
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);
 	});
 }
@@ -1283,9 +1283,9 @@ fn scheduler_respects_weight_limits() {
 			Preimage::bound(call).unwrap(),
 		));
 		// 69 and 42 do not fit together
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]);
 	});
 }
@@ -1316,26 +1316,26 @@ fn retry_respects_weight_limits() {
 		// set a retry config for 20 for 10 retries every block
 		assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 1));
 		// 20 should fail and be retried later
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(Agenda::<Test>::get(5)[0].is_some());
 		assert!(Agenda::<Test>::get(8)[0].is_some());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert!(logger::log().is_empty());
 		// 20 still fails but is scheduled next block together with 42
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert_eq!(Agenda::<Test>::get(8).len(), 2);
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert!(logger::log().is_empty());
 		// 20 and 42 do not fit together
 		// 42 is executed as it was first in the queue
 		// 20 is still on the 8th block's agenda
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		assert!(Agenda::<Test>::get(8)[0].is_none());
 		assert!(Agenda::<Test>::get(8)[1].is_some());
 		assert_eq!(Retries::<Test>::iter().count(), 1);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// 20 is executed and the schedule is cleared
-		run_to_block(9);
+		System::run_to_block::<AllPalletsWithSystem>(9);
 		assert_eq!(Agenda::<Test>::iter().count(), 0);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
 		assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 20u32)]);
@@ -1386,7 +1386,7 @@ fn try_schedule_retry_respects_weight_limits() {
 		// set a retry config for 20 for 10 retries every block
 		assert_ok!(Scheduler::set_retry(root().into(), (4, 0), 10, 1));
 		// 20 should fail and, because of insufficient weight, it should not be scheduled again
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// nothing else should be scheduled
 		assert_eq!(Agenda::<Test>::iter().count(), 0);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
@@ -1415,7 +1415,7 @@ fn scheduler_does_not_delete_permanently_overweight_call() {
 			Preimage::bound(call).unwrap(),
 		));
 		// Never executes.
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![]);
 
 		// Assert the `PermanentlyOverweight` event.
@@ -1445,7 +1445,7 @@ fn scheduler_handles_periodic_failure() {
 			bound.clone(),
 		));
 		// Executes 5 times till block 20.
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 		assert_eq!(logger::log().len(), 5);
 
 		// Block 28 will already be full.
@@ -1460,7 +1460,7 @@ fn scheduler_handles_periodic_failure() {
 		}
 
 		// Going to block 24 will emit a `PeriodicFailed` event.
-		run_to_block(24);
+		System::run_to_block::<AllPalletsWithSystem>(24);
 		assert_eq!(logger::log().len(), 6);
 
 		assert_eq!(
@@ -1498,7 +1498,7 @@ fn scheduler_handles_periodic_unavailable_preimage() {
 		assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), call.encode()));
 
 		// Executes 1 times till block 4.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log().len(), 1);
 
 		// As the public api doesn't support to remove a noted preimage, we need to first unnote it
@@ -1508,7 +1508,7 @@ fn scheduler_handles_periodic_unavailable_preimage() {
 		Preimage::request(&hash);
 
 		// Does not ever execute again.
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log().len(), 1);
 
 		// The preimage is not requested anymore.
@@ -1536,7 +1536,7 @@ fn scheduler_respects_priority_ordering() {
 			root(),
 			Preimage::bound(call).unwrap(),
 		));
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 69u32), (root(), 42u32)]);
 	});
 }
@@ -1571,10 +1571,10 @@ fn scheduler_respects_priority_ordering_with_soft_deadlines() {
 		));
 
 		// 2600 does not fit with 69 or 42, but has higher priority, so will go through
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 2600u32)]);
 		// 69 and 42 fit together
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]);
 	});
 }
@@ -1701,14 +1701,14 @@ fn root_calls_works() {
 			Scheduler::schedule_named(RuntimeOrigin::root(), [1u8; 32], 4, None, 127, call,)
 		);
 		assert_ok!(Scheduler::schedule(RuntimeOrigin::root(), 4, None, 127, call2));
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Scheduled calls are in the agenda.
 		assert_eq!(Agenda::<Test>::get(4).len(), 2);
 		assert!(logger::log().is_empty());
 		assert_ok!(Scheduler::cancel_named(RuntimeOrigin::root(), [1u8; 32]));
 		assert_ok!(Scheduler::cancel(RuntimeOrigin::root(), 4, 1));
 		// Scheduled calls are made NONE, so should not effect state
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert!(logger::log().is_empty());
 	});
 }
@@ -1716,7 +1716,7 @@ fn root_calls_works() {
 #[test]
 fn fails_to_schedule_task_in_the_past() {
 	new_test_ext().execute_with(|| {
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 
 		let call1 = Box::new(RuntimeCall::Logger(LoggerCall::log {
 			i: 69,
@@ -1768,14 +1768,14 @@ fn should_use_origin() {
 			call,
 		));
 		assert_ok!(Scheduler::schedule(system::RawOrigin::Signed(1).into(), 4, None, 127, call2,));
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Scheduled calls are in the agenda.
 		assert_eq!(Agenda::<Test>::get(4).len(), 2);
 		assert!(logger::log().is_empty());
 		assert_ok!(Scheduler::cancel_named(system::RawOrigin::Signed(1).into(), [1u8; 32]));
 		assert_ok!(Scheduler::cancel(system::RawOrigin::Signed(1).into(), 4, 1));
 		// Scheduled calls are made NONE, so should not effect state
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert!(logger::log().is_empty());
 	});
 }
@@ -1829,7 +1829,7 @@ fn should_check_origin_for_cancel() {
 			call,
 		));
 		assert_ok!(Scheduler::schedule(system::RawOrigin::Signed(1).into(), 4, None, 127, call2,));
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Scheduled calls are in the agenda.
 		assert_eq!(Agenda::<Test>::get(4).len(), 2);
 		assert!(logger::log().is_empty());
@@ -1840,7 +1840,7 @@ fn should_check_origin_for_cancel() {
 		assert_noop!(Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1), BadOrigin);
 		assert_noop!(Scheduler::cancel_named(system::RawOrigin::Root.into(), [1u8; 32]), BadOrigin);
 		assert_noop!(Scheduler::cancel(system::RawOrigin::Root.into(), 4, 1), BadOrigin);
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_eq!(
 			logger::log(),
 			vec![
@@ -1888,17 +1888,17 @@ fn cancel_removes_retry_entry() {
 		// task 42 will be retried 10 times every 3 blocks
 		assert_ok!(Scheduler::set_retry_named(root().into(), [1u8; 32], 10, 1));
 		assert_eq!(Retries::<Test>::iter().count(), 2);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert_eq!(Agenda::<Test>::get(4).len(), 2);
 		// both tasks fail
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(Agenda::<Test>::get(4).is_empty());
 		// 42 and 20 are rescheduled for next block
 		assert_eq!(Agenda::<Test>::get(5).len(), 2);
 		assert!(logger::log().is_empty());
 		// 42 and 20 still fail
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		// 42 and 20 rescheduled for next block
 		assert_eq!(Agenda::<Test>::get(6).len(), 2);
 		assert_eq!(Retries::<Test>::iter().count(), 2);
@@ -1909,7 +1909,7 @@ fn cancel_removes_retry_entry() {
 		assert!(Scheduler::cancel(root().into(), 6, 0).is_ok());
 
 		// 20 is removed, 42 still fails
-		run_to_block(6);
+		System::run_to_block::<AllPalletsWithSystem>(6);
 		// 42 rescheduled for next block
 		assert_eq!(Agenda::<Test>::get(7).len(), 1);
 		// 20's retry entry is removed
@@ -1920,7 +1920,7 @@ fn cancel_removes_retry_entry() {
 		assert!(Scheduler::cancel(root().into(), 7, 0).is_ok());
 
 		// both tasks are canceled, everything is removed now
-		run_to_block(7);
+		System::run_to_block::<AllPalletsWithSystem>(7);
 		assert!(Agenda::<Test>::get(8).is_empty());
 		assert_eq!(Retries::<Test>::iter().count(), 0);
 	});
@@ -1963,7 +1963,7 @@ fn cancel_retries_works() {
 		// task 42 will be retried 10 times every 3 blocks
 		assert_ok!(Scheduler::set_retry_named(root().into(), [1u8; 32], 10, 1));
 		assert_eq!(Retries::<Test>::iter().count(), 2);
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		assert!(logger::log().is_empty());
 		assert_eq!(Agenda::<Test>::get(4).len(), 2);
 		// cancel the retry config for 20
@@ -1972,7 +1972,7 @@ fn cancel_retries_works() {
 		// cancel the retry config for 42
 		assert_ok!(Scheduler::cancel_retry_named(root().into(), [1u8; 32]));
 		assert_eq!(Retries::<Test>::iter().count(), 0);
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// both tasks failed and there are no more retries, so they are evicted
 		assert_eq!(Agenda::<Test>::get(4).len(), 0);
 		assert_eq!(Retries::<Test>::iter().count(), 0);
@@ -2287,7 +2287,7 @@ fn postponed_named_task_cannot_be_rescheduled() {
 		assert!(Lookup::<Test>::contains_key(name));
 
 		// Run to a very large block.
-		run_to_block(10);
+		System::run_to_block::<AllPalletsWithSystem>(10);
 
 		// It was not executed.
 		assert!(logger::log().is_empty());
@@ -2321,7 +2321,7 @@ fn postponed_named_task_cannot_be_rescheduled() {
 		// Finally add the preimage.
 		assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(0), call.encode()));
 
-		run_to_block(1000);
+		System::run_to_block::<AllPalletsWithSystem>(1000);
 		// It did not execute.
 		assert!(logger::log().is_empty());
 		assert!(!Preimage::is_requested(&hash));
@@ -2357,14 +2357,14 @@ fn scheduler_v3_anon_basic_works() {
 		)
 		.unwrap();
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Did not execute till block 3.
 		assert!(logger::log().is_empty());
 		// Executes in block 4.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// ... but not again.
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -2389,7 +2389,7 @@ fn scheduler_v3_anon_cancel_works() {
 		// Cancel the call.
 		assert_ok!(<Scheduler as Anon<_, _, _>>::cancel(address));
 		// It did not get executed.
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert!(logger::log().is_empty());
 		// Cannot cancel again.
 		assert_err!(<Scheduler as Anon<_, _, _>>::cancel(address), DispatchError::Unavailable);
@@ -2413,7 +2413,7 @@ fn scheduler_v3_anon_reschedule_works() {
 		)
 		.unwrap();
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Did not execute till block 3.
 		assert!(logger::log().is_empty());
 
@@ -2430,9 +2430,9 @@ fn scheduler_v3_anon_reschedule_works() {
 		// Re-schedule to block 5.
 		assert_ok!(<Scheduler as Anon<_, _, _>>::reschedule(address, DispatchTime::At(5)));
 		// Scheduled for block 5.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(logger::log().is_empty());
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		// Does execute in block 5.
 		assert_eq!(logger::log(), vec![(root(), 42)]);
 		// Cannot re-schedule executed task.
@@ -2461,14 +2461,14 @@ fn scheduler_v3_anon_next_schedule_time_works() {
 		)
 		.unwrap();
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Did not execute till block 3.
 		assert!(logger::log().is_empty());
 
 		// Scheduled for block 4.
 		assert_eq!(<Scheduler as Anon<_, _, _>>::next_dispatch_time(address), Ok(4));
 		// Block 4 executes it.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 42)]);
 
 		// It has no dispatch time anymore.
@@ -2498,7 +2498,7 @@ fn scheduler_v3_anon_reschedule_and_next_schedule_time_work() {
 		)
 		.unwrap();
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Did not execute till block 3.
 		assert!(logger::log().is_empty());
 
@@ -2512,10 +2512,10 @@ fn scheduler_v3_anon_reschedule_and_next_schedule_time_work() {
 		assert_eq!(<Scheduler as Anon<_, _, _>>::next_dispatch_time(address), Ok(5));
 
 		// Block 4 does nothing.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(logger::log().is_empty());
 		// Block 5 executes it.
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		assert_eq!(logger::log(), vec![(root(), 42)]);
 	});
 }
@@ -2548,7 +2548,7 @@ fn scheduler_v3_anon_schedule_agenda_overflows() {
 			DispatchError::Exhausted
 		);
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// All scheduled calls are executed.
 		assert_eq!(logger::log().len() as u32, max);
 	});
@@ -2597,7 +2597,7 @@ fn scheduler_v3_anon_cancel_and_schedule_fills_holes() {
 			assert_eq!(i, index);
 		}
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// Maximum number of calls are executed.
 		assert_eq!(logger::log().len() as u32, max);
 	});
@@ -2643,7 +2643,7 @@ fn scheduler_v3_anon_reschedule_fills_holes() {
 			assert_eq!(new, want);
 		}
 
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		// Maximum number of calls are executed.
 		assert_eq!(logger::log().len() as u32, max);
 	});
@@ -2670,14 +2670,14 @@ fn scheduler_v3_named_basic_works() {
 		)
 		.unwrap();
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Did not execute till block 3.
 		assert!(logger::log().is_empty());
 		// Executes in block 4.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 		// ... but not again.
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert_eq!(logger::log(), vec![(root(), 42u32)]);
 	});
 }
@@ -2705,7 +2705,7 @@ fn scheduler_v3_named_cancel_named_works() {
 		// Cancel the call by name.
 		assert_ok!(<Scheduler as Named<_, _, _>>::cancel_named(name));
 		// It did not get executed.
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert!(logger::log().is_empty());
 		// Cannot cancel again.
 		assert_noop!(<Scheduler as Named<_, _, _>>::cancel_named(name), DispatchError::Unavailable);
@@ -2735,7 +2735,7 @@ fn scheduler_v3_named_cancel_without_name_works() {
 		// Cancel the call by address.
 		assert_ok!(<Scheduler as Anon<_, _, _>>::cancel(address));
 		// It did not get executed.
-		run_to_block(100);
+		System::run_to_block::<AllPalletsWithSystem>(100);
 		assert!(logger::log().is_empty());
 		// Cannot cancel again.
 		assert_err!(<Scheduler as Anon<_, _, _>>::cancel(address), DispatchError::Unavailable);
@@ -2762,7 +2762,7 @@ fn scheduler_v3_named_reschedule_named_works() {
 		)
 		.unwrap();
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Did not execute till block 3.
 		assert!(logger::log().is_empty());
 
@@ -2784,9 +2784,9 @@ fn scheduler_v3_named_reschedule_named_works() {
 		// Re-schedule to block 5.
 		assert_ok!(<Scheduler as Named<_, _, _>>::reschedule_named(name, DispatchTime::At(5)));
 		// Scheduled for block 5.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert!(logger::log().is_empty());
-		run_to_block(5);
+		System::run_to_block::<AllPalletsWithSystem>(5);
 		// Does execute in block 5.
 		assert_eq!(logger::log(), vec![(root(), 42)]);
 		// Cannot re-schedule executed task.
@@ -2822,7 +2822,7 @@ fn scheduler_v3_named_next_schedule_time_works() {
 		)
 		.unwrap();
 
-		run_to_block(3);
+		System::run_to_block::<AllPalletsWithSystem>(3);
 		// Did not execute till block 3.
 		assert!(logger::log().is_empty());
 
@@ -2831,7 +2831,7 @@ fn scheduler_v3_named_next_schedule_time_works() {
 		// Also works by address.
 		assert_eq!(<Scheduler as Anon<_, _, _>>::next_dispatch_time(address), Ok(4));
 		// Block 4 executes it.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 		assert_eq!(logger::log(), vec![(root(), 42)]);
 
 		// It has no dispatch time anymore.
@@ -3025,7 +3025,7 @@ fn unavailable_call_is_detected() {
 		assert!(Preimage::is_requested(&hash));
 
 		// Executes in block 4.
-		run_to_block(4);
+		System::run_to_block::<AllPalletsWithSystem>(4);
 
 		assert_eq!(
 			System::events().last().unwrap().event,
diff --git a/substrate/frame/session/benchmarking/src/mock.rs b/substrate/frame/session/benchmarking/src/mock.rs
index 346cd04c0fa9eb5f917557aa050e759762f9a341..74201da3d2f310f58593d8566eb650086245c76f 100644
--- a/substrate/frame/session/benchmarking/src/mock.rs
+++ b/substrate/frame/session/benchmarking/src/mock.rs
@@ -133,6 +133,7 @@ impl onchain::Config for OnChainSeqPhragmen {
 
 #[derive_impl(pallet_staking::config_preludes::TestDefaultConfig)]
 impl pallet_staking::Config for Test {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
 	type UnixTime = pallet_timestamp::Pallet<Self>;
diff --git a/substrate/frame/society/src/mock.rs b/substrate/frame/society/src/mock.rs
index 3c27c08a10610f44ebaf2fc72678f52be2f06507..8cb5dc823753b3b15351a98ba782fa696ed91ca5 100644
--- a/substrate/frame/society/src/mock.rs
+++ b/substrate/frame/society/src/mock.rs
@@ -138,18 +138,6 @@ impl EnvBuilder {
 	}
 }
 
-/// Run until a particular block.
-pub fn run_to_block(n: u64) {
-	while System::block_number() < n {
-		if System::block_number() > 1 {
-			System::on_finalize(System::block_number());
-		}
-		System::set_block_number(System::block_number() + 1);
-		System::on_initialize(System::block_number());
-		Society::on_initialize(System::block_number());
-	}
-}
-
 /// Creates a bid struct using input parameters.
 pub fn bid<AccountId, Balance>(
 	who: AccountId,
@@ -173,12 +161,12 @@ pub fn candidacy<AccountId, Balance>(
 pub fn next_challenge() {
 	let challenge_period: u64 = <Test as Config>::ChallengePeriod::get();
 	let now = System::block_number();
-	run_to_block(now + challenge_period - now % challenge_period);
+	System::run_to_block::<AllPalletsWithSystem>(now + challenge_period - now % challenge_period);
 }
 
 pub fn next_voting() {
 	if let Period::Voting { more, .. } = Society::period() {
-		run_to_block(System::block_number() + more);
+		System::run_to_block::<AllPalletsWithSystem>(System::block_number() + more);
 	}
 }
 
@@ -235,8 +223,11 @@ pub fn conclude_intake(allow_resignation: bool, judge_intake: Option<bool>) {
 pub fn next_intake() {
 	let claim_period: u64 = <Test as Config>::ClaimPeriod::get();
 	match Society::period() {
-		Period::Voting { more, .. } => run_to_block(System::block_number() + more + claim_period),
-		Period::Claim { more, .. } => run_to_block(System::block_number() + more),
+		Period::Voting { more, .. } => System::run_to_block::<AllPalletsWithSystem>(
+			System::block_number() + more + claim_period,
+		),
+		Period::Claim { more, .. } =>
+			System::run_to_block::<AllPalletsWithSystem>(System::block_number() + more),
 	}
 }
 
diff --git a/substrate/frame/society/src/tests.rs b/substrate/frame/society/src/tests.rs
index 2a13f99855b59f66764ffc2fd93fe3479df47c46..22832f18b6fe0305e3a4fa025776ce9d36730b22 100644
--- a/substrate/frame/society/src/tests.rs
+++ b/substrate/frame/society/src/tests.rs
@@ -272,7 +272,7 @@ fn bidding_works() {
 		// 40, now a member, can vote for 50
 		assert_ok!(Society::vote(Origin::signed(40), 50, true));
 		conclude_intake(true, None);
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		// 50 is now a member
 		assert_eq!(members(), vec![10, 30, 40, 50]);
 		// Pot is increased by 1000, and 500 is paid out. Total payout so far is 1200.
@@ -282,7 +282,7 @@ fn bidding_works() {
 		assert_eq!(candidacies(), vec![]);
 		assert_ok!(Society::defender_vote(Origin::signed(10), true)); // Keep defender around
 																// Next period
-		run_to_block(16);
+		System::run_to_block::<AllPalletsWithSystem>(16);
 		// Same members
 		assert_eq!(members(), vec![10, 30, 40, 50]);
 		// Pot is increased by 1000 again
@@ -294,7 +294,7 @@ fn bidding_works() {
 		// Candidate 60 is voted in.
 		assert_ok!(Society::vote(Origin::signed(50), 60, true));
 		conclude_intake(true, None);
-		run_to_block(20);
+		System::run_to_block::<AllPalletsWithSystem>(20);
 		// 60 joins as a member
 		assert_eq!(members(), vec![10, 30, 40, 50, 60]);
 		// Pay them
@@ -368,7 +368,7 @@ fn rejecting_skeptic_on_approved_is_punished() {
 		}
 		conclude_intake(true, None);
 		assert_eq!(Members::<Test>::get(10).unwrap().strikes, 0);
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		assert_eq!(members(), vec![10, 20, 30, 40]);
 		assert_eq!(Members::<Test>::get(skeptic).unwrap().strikes, 1);
 	});
@@ -418,7 +418,7 @@ fn slash_payout_works() {
 			Payouts::<Test>::get(20),
 			PayoutRecord { paid: 0, payouts: vec![(8, 500)].try_into().unwrap() }
 		);
-		run_to_block(8);
+		System::run_to_block::<AllPalletsWithSystem>(8);
 		// payout should be here, but 500 less
 		assert_ok!(Society::payout(RuntimeOrigin::signed(20)));
 		assert_eq!(Balances::free_balance(20), 550);
@@ -1315,7 +1315,7 @@ fn drop_candidate_works() {
 		assert_ok!(Society::vote(Origin::signed(10), 40, false));
 		assert_ok!(Society::vote(Origin::signed(20), 40, false));
 		assert_ok!(Society::vote(Origin::signed(30), 40, false));
-		run_to_block(12);
+		System::run_to_block::<AllPalletsWithSystem>(12);
 		assert_ok!(Society::drop_candidate(Origin::signed(50), 40));
 		// 40 candidacy has gone.
 		assert_eq!(candidates(), vec![]);
diff --git a/substrate/frame/src/lib.rs b/substrate/frame/src/lib.rs
index f79a52bc6c5bf8343a6a4a6d022be5090f71873c..18c7bd1239443643a9f86762c542f25f23086a75 100644
--- a/substrate/frame/src/lib.rs
+++ b/substrate/frame/src/lib.rs
@@ -106,7 +106,7 @@
 //! [dependencies]
 //! codec = { features = ["max-encoded-len"], workspace = true }
 //! scale-info = { features = ["derive"], workspace = true }
-//! frame = { workspace = true, features = ["experimental", "runtime"] }
+//! frame = { workspace = true, features = ["runtime"] }
 //!
 //! [features]
 //! default = ["std"]
@@ -150,7 +150,6 @@
 //! * `runtime::apis` should expose all common runtime APIs that all FRAME-based runtimes need.
 
 #![cfg_attr(not(feature = "std"), no_std)]
-#![cfg(feature = "experimental")]
 
 #[doc(no_inline)]
 pub use frame_support::pallet;
@@ -325,7 +324,7 @@ pub mod testing_prelude {
 		assert_storage_noop, hypothetically, storage_alias,
 	};
 
-	pub use frame_system::{self, mocking::*};
+	pub use frame_system::{self, mocking::*, RunToBlockHooks};
 
 	#[deprecated(note = "Use `frame::testing_prelude::TestState` instead.")]
 	pub use sp_io::TestExternalities;
diff --git a/substrate/frame/staking/Cargo.toml b/substrate/frame/staking/Cargo.toml
index 22176b6d720b201583905f59780c19aa7c33d2f4..74b1c78e9cbee9661978ee2111ec9d3410a7f56c 100644
--- a/substrate/frame/staking/Cargo.toml
+++ b/substrate/frame/staking/Cargo.toml
@@ -41,6 +41,7 @@ rand_chacha = { optional = true, workspace = true }
 [dev-dependencies]
 frame-benchmarking = { workspace = true, default-features = true }
 frame-election-provider-support = { workspace = true, default-features = true }
+frame-support = { features = ["experimental"], workspace = true, default-features = true }
 pallet-bags-list = { workspace = true, default-features = true }
 pallet-balances = { workspace = true, default-features = true }
 pallet-staking-reward-curve = { workspace = true, default-features = true }
diff --git a/substrate/frame/staking/src/asset.rs b/substrate/frame/staking/src/asset.rs
index 23368b1f8fca713f78405165c00ec09d54a8d257..a1140d317c204957e5f7cc9178f704cffe5965c1 100644
--- a/substrate/frame/staking/src/asset.rs
+++ b/substrate/frame/staking/src/asset.rs
@@ -18,9 +18,15 @@
 //! Contains all the interactions with [`Config::Currency`] to manipulate the underlying staking
 //! asset.
 
-use frame_support::traits::{Currency, InspectLockableCurrency, LockableCurrency};
-
-use crate::{BalanceOf, Config, NegativeImbalanceOf, PositiveImbalanceOf};
+use crate::{BalanceOf, Config, HoldReason, NegativeImbalanceOf, PositiveImbalanceOf};
+use frame_support::traits::{
+	fungible::{
+		hold::{Balanced as FunHoldBalanced, Inspect as FunHoldInspect, Mutate as FunHoldMutate},
+		Balanced, Inspect as FunInspect,
+	},
+	tokens::{Fortitude, Precision, Preservation},
+};
+use sp_runtime::{DispatchResult, Saturating};
 
 /// Existential deposit for the chain.
 pub fn existential_deposit<T: Config>() -> BalanceOf<T> {
@@ -32,7 +38,7 @@ pub fn total_issuance<T: Config>() -> BalanceOf<T> {
 	T::Currency::total_issuance()
 }
 
-/// Total balance of `who`. Includes both, free and reserved.
+/// Total balance of `who`. Includes both free and staked.
 pub fn total_balance<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
 	T::Currency::total_balance(who)
 }
@@ -41,42 +47,65 @@ pub fn total_balance<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
 ///
 /// This includes balance free to stake along with any balance that is already staked.
 pub fn stakeable_balance<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
-	T::Currency::free_balance(who)
+	free_to_stake::<T>(who).saturating_add(staked::<T>(who))
 }
 
 /// Balance of `who` that is currently at stake.
 ///
-/// The staked amount is locked and cannot be transferred out of `who`s account.
+/// The staked amount is on hold and cannot be transferred out of `who`s account.
 pub fn staked<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
-	T::Currency::balance_locked(crate::STAKING_ID, who)
+	T::Currency::balance_on_hold(&HoldReason::Staking.into(), who)
+}
+
+/// Balance of who that can be staked additionally.
+///
+/// Does not include the current stake.
+pub fn free_to_stake<T: Config>(who: &T::AccountId) -> BalanceOf<T> {
+	// since we want to be able to use frozen funds for staking, we force the reduction.
+	T::Currency::reducible_balance(who, Preservation::Preserve, Fortitude::Force)
 }
 
 /// Set balance that can be staked for `who`.
 ///
-/// This includes any balance that is already staked.
+/// If `Value` is lower than the current staked balance, the difference is unlocked.
+///
+/// Should only be used with test.
 #[cfg(any(test, feature = "runtime-benchmarks"))]
 pub fn set_stakeable_balance<T: Config>(who: &T::AccountId, value: BalanceOf<T>) {
-	T::Currency::make_free_balance_be(who, value);
+	use frame_support::traits::fungible::Mutate;
+
+	// minimum free balance (non-staked) required to keep the account alive.
+	let ed = existential_deposit::<T>();
+	// currently on stake
+	let staked_balance = staked::<T>(who);
+
+	// if new value is greater than staked balance, mint some free balance.
+	if value > staked_balance {
+		let _ = T::Currency::set_balance(who, value - staked_balance + ed);
+	} else {
+		// else reduce the staked balance.
+		update_stake::<T>(who, value).expect("can remove from what is staked");
+		// burn all free, only leaving ED.
+		let _ = T::Currency::set_balance(who, ed);
+	}
+
+	// ensure new stakeable balance same as desired `value`.
+	assert_eq!(stakeable_balance::<T>(who), value);
 }
 
 /// Update `amount` at stake for `who`.
 ///
 /// Overwrites the existing stake amount. If passed amount is lower than the existing stake, the
 /// difference is unlocked.
-pub fn update_stake<T: Config>(who: &T::AccountId, amount: BalanceOf<T>) {
-	T::Currency::set_lock(
-		crate::STAKING_ID,
-		who,
-		amount,
-		frame_support::traits::WithdrawReasons::all(),
-	);
+pub fn update_stake<T: Config>(who: &T::AccountId, amount: BalanceOf<T>) -> DispatchResult {
+	T::Currency::set_on_hold(&HoldReason::Staking.into(), who, amount)
 }
 
-/// Kill the stake of `who`.
+/// Release all staked amount to `who`.
 ///
-/// All locked amount is unlocked.
-pub fn kill_stake<T: Config>(who: &T::AccountId) {
-	T::Currency::remove_lock(crate::STAKING_ID, who);
+/// Fails if there are consumers left on `who` that restricts it from being reaped.
+pub fn kill_stake<T: Config>(who: &T::AccountId) -> DispatchResult {
+	T::Currency::release_all(&HoldReason::Staking.into(), who, Precision::BestEffort).map(|_| ())
 }
 
 /// Slash the value from `who`.
@@ -86,29 +115,32 @@ pub fn slash<T: Config>(
 	who: &T::AccountId,
 	value: BalanceOf<T>,
 ) -> (NegativeImbalanceOf<T>, BalanceOf<T>) {
-	T::Currency::slash(who, value)
+	T::Currency::slash(&HoldReason::Staking.into(), who, value)
 }
 
 /// Mint `value` into an existing account `who`.
 ///
 /// This does not increase the total issuance.
-pub fn mint_existing<T: Config>(
+pub fn mint_into_existing<T: Config>(
 	who: &T::AccountId,
 	value: BalanceOf<T>,
 ) -> Option<PositiveImbalanceOf<T>> {
-	T::Currency::deposit_into_existing(who, value).ok()
+	// since the account already exists, we mint exact value even if value is below ED.
+	T::Currency::deposit(who, value, Precision::Exact).ok()
 }
 
-/// Mint reward and create account for `who` if it does not exist.
+/// Mint `value` and create account for `who` if it does not exist.
 ///
-/// This does not increase the total issuance.
+/// If value is below existential deposit, the account is not created.
+///
+/// Note: This does not increase the total issuance.
 pub fn mint_creating<T: Config>(who: &T::AccountId, value: BalanceOf<T>) -> PositiveImbalanceOf<T> {
-	T::Currency::deposit_creating(who, value)
+	T::Currency::deposit(who, value, Precision::BestEffort).unwrap_or_default()
 }
 
 /// Deposit newly issued or slashed `value` into `who`.
 pub fn deposit_slashed<T: Config>(who: &T::AccountId, value: NegativeImbalanceOf<T>) {
-	T::Currency::resolve_creating(who, value)
+	let _ = T::Currency::resolve(who, value);
 }
 
 /// Issue `value` increasing total issuance.
@@ -121,5 +153,5 @@ pub fn issue<T: Config>(value: BalanceOf<T>) -> NegativeImbalanceOf<T> {
 /// Burn the amount from the total issuance.
 #[cfg(feature = "runtime-benchmarks")]
 pub fn burn<T: Config>(amount: BalanceOf<T>) -> PositiveImbalanceOf<T> {
-	T::Currency::burn(amount)
+	T::Currency::rescind(amount)
 }
diff --git a/substrate/frame/staking/src/benchmarking.rs b/substrate/frame/staking/src/benchmarking.rs
index 79d8dd3fbc30365e4a81a2f708383754f67a49cc..59d272168d68bc06f73e4e85ce7fc5a407ca3daf 100644
--- a/substrate/frame/staking/src/benchmarking.rs
+++ b/substrate/frame/staking/src/benchmarking.rs
@@ -257,7 +257,11 @@ mod benchmarks {
 			.map(|l| l.active)
 			.ok_or("ledger not created after")?;
 
-		let _ = asset::mint_existing::<T>(&stash, max_additional).unwrap();
+		let _ = asset::mint_into_existing::<T>(
+			&stash,
+			max_additional + asset::existential_deposit::<T>(),
+		)
+		.unwrap();
 
 		whitelist_account!(stash);
 
@@ -1133,6 +1137,23 @@ mod benchmarks {
 		Ok(())
 	}
 
+	#[benchmark]
+	fn migrate_currency() -> Result<(), BenchmarkError> {
+		let (stash, _ctrl) =
+			create_stash_controller::<T>(USER_SEED, 100, RewardDestination::Staked)?;
+		let stake = asset::staked::<T>(&stash);
+		migrate_to_old_currency::<T>(stash.clone());
+		// no holds
+		assert!(asset::staked::<T>(&stash).is_zero());
+		whitelist_account!(stash);
+
+		#[extrinsic_call]
+		_(RawOrigin::Signed(stash.clone()), stash.clone());
+
+		assert_eq!(asset::staked::<T>(&stash), stake);
+		Ok(())
+	}
+
 	impl_benchmark_test_suite!(
 		Staking,
 		crate::mock::ExtBuilder::default().has_stakers(true),
diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs
index ac3be04cf607176d6aee437a7b49bc3f4afffd55..1d66ebd27e9f729e8a42a93960435ccf78b79769 100644
--- a/substrate/frame/staking/src/ledger.rs
+++ b/substrate/frame/staking/src/ledger.rs
@@ -32,6 +32,7 @@
 //! state consistency.
 
 use frame_support::{defensive, ensure, traits::Defensive};
+use sp_runtime::DispatchResult;
 use sp_staking::{StakingAccount, StakingInterface};
 
 use crate::{
@@ -187,7 +188,8 @@ impl<T: Config> StakingLedger<T> {
 		// We skip locking virtual stakers.
 		if !Pallet::<T>::is_virtual_staker(&self.stash) {
 			// for direct stakers, update lock on stash based on ledger.
-			asset::update_stake::<T>(&self.stash, self.total);
+			asset::update_stake::<T>(&self.stash, self.total)
+				.map_err(|_| Error::<T>::NotEnoughFunds)?;
 		}
 
 		Ledger::<T>::insert(
@@ -250,7 +252,7 @@ impl<T: Config> StakingLedger<T> {
 
 	/// Clears all data related to a staking ledger and its bond in both [`Ledger`] and [`Bonded`]
 	/// storage items and updates the stash staking lock.
-	pub(crate) fn kill(stash: &T::AccountId) -> Result<(), Error<T>> {
+	pub(crate) fn kill(stash: &T::AccountId) -> DispatchResult {
 		let controller = <Bonded<T>>::get(stash).ok_or(Error::<T>::NotStash)?;
 
 		<Ledger<T>>::get(&controller).ok_or(Error::<T>::NotController).map(|ledger| {
@@ -259,9 +261,9 @@ impl<T: Config> StakingLedger<T> {
 			<Payee<T>>::remove(&stash);
 
 			// kill virtual staker if it exists.
-			if <VirtualStakers<T>>::take(&stash).is_none() {
+			if <VirtualStakers<T>>::take(&ledger.stash).is_none() {
 				// if not virtual staker, clear locks.
-				asset::kill_stake::<T>(&ledger.stash);
+				asset::kill_stake::<T>(&ledger.stash)?;
 			}
 
 			Ok(())
diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs
index 6361663b2b1c05939ba24f572c171c00af2f77e9..42230cb27b756306446cf23cea536a054b11e119 100644
--- a/substrate/frame/staking/src/lib.rs
+++ b/substrate/frame/staking/src/lib.rs
@@ -312,7 +312,8 @@ use codec::{Decode, Encode, HasCompact, MaxEncodedLen};
 use frame_support::{
 	defensive, defensive_assert,
 	traits::{
-		ConstU32, Currency, Defensive, DefensiveMax, DefensiveSaturating, Get, LockIdentifier,
+		tokens::fungible::{Credit, Debt},
+		ConstU32, Defensive, DefensiveMax, DefensiveSaturating, Get, LockIdentifier,
 	},
 	weights::Weight,
 	BoundedVec, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
@@ -361,12 +362,9 @@ pub type RewardPoint = u32;
 /// The balance type of this pallet.
 pub type BalanceOf<T> = <T as Config>::CurrencyBalance;
 
-type PositiveImbalanceOf<T> = <<T as Config>::Currency as Currency<
-	<T as frame_system::Config>::AccountId,
->>::PositiveImbalance;
-pub type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<
-	<T as frame_system::Config>::AccountId,
->>::NegativeImbalance;
+type PositiveImbalanceOf<T> = Debt<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
+pub type NegativeImbalanceOf<T> =
+	Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
 
 type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
 
diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs
index df8cb38e8b371be821c9843255ce30ffb47ae647..6346949576fa796726b136822737d6bd5c9fb3e4 100644
--- a/substrate/frame/staking/src/mock.rs
+++ b/substrate/frame/staking/src/mock.rs
@@ -25,8 +25,7 @@ use frame_election_provider_support::{
 use frame_support::{
 	assert_ok, derive_impl, ord_parameter_types, parameter_types,
 	traits::{
-		ConstU64, Currency, EitherOfDiverse, FindAuthor, Get, Hooks, Imbalance, LockableCurrency,
-		OnUnbalanced, OneSessionHandler, WithdrawReasons,
+		ConstU64, EitherOfDiverse, FindAuthor, Get, Imbalance, OnUnbalanced, OneSessionHandler,
 	},
 	weights::constants::RocksDbWeight,
 };
@@ -155,7 +154,7 @@ impl pallet_session::historical::Config for Test {
 }
 impl pallet_authorship::Config for Test {
 	type FindAuthor = Author11;
-	type EventHandler = Pallet<Test>;
+	type EventHandler = ();
 }
 
 impl pallet_timestamp::Config for Test {
@@ -264,6 +263,7 @@ pub(crate) const DISABLING_LIMIT_FACTOR: usize = 3;
 
 #[derive_impl(crate::config_preludes::TestDefaultConfig)]
 impl crate::pallet::pallet::Config for Test {
+	type OldCurrency = Balances;
 	type Currency = Balances;
 	type UnixTime = Timestamp;
 	type RewardRemainder = RewardRemainderMock;
@@ -432,6 +432,7 @@ impl ExtBuilder {
 	fn build(self) -> sp_io::TestExternalities {
 		sp_tracing::try_init_simple();
 		let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
+		let ed = ExistentialDeposit::get();
 
 		let _ = pallet_balances::GenesisConfig::<Test> {
 			balances: vec![
@@ -446,19 +447,23 @@ impl ExtBuilder {
 				(40, self.balance_factor),
 				(50, self.balance_factor),
 				// stashes
-				(11, self.balance_factor * 1000),
-				(21, self.balance_factor * 2000),
-				(31, self.balance_factor * 2000),
-				(41, self.balance_factor * 2000),
-				(51, self.balance_factor * 2000),
-				(201, self.balance_factor * 2000),
-				(202, self.balance_factor * 2000),
+				// Note: Previously this pallet used locks and stakers could stake all their
+				// balance including ED. Now with holds, stakers are required to maintain
+				// (non-staked) ED in their accounts. Therefore, we drop an additional existential
+				// deposit to genesis stakers.
+				(11, self.balance_factor * 1000 + ed),
+				(21, self.balance_factor * 2000 + ed),
+				(31, self.balance_factor * 2000 + ed),
+				(41, self.balance_factor * 2000 + ed),
+				(51, self.balance_factor * 2000 + ed),
+				(201, self.balance_factor * 2000 + ed),
+				(202, self.balance_factor * 2000 + ed),
 				// optional nominator
-				(100, self.balance_factor * 2000),
-				(101, self.balance_factor * 2000),
+				(100, self.balance_factor * 2000 + ed),
+				(101, self.balance_factor * 2000 + ed),
 				// aux accounts
 				(60, self.balance_factor),
-				(61, self.balance_factor * 2000),
+				(61, self.balance_factor * 2000 + ed),
 				(70, self.balance_factor),
 				(71, self.balance_factor * 2000),
 				(80, self.balance_factor),
@@ -544,13 +549,10 @@ impl ExtBuilder {
 		let mut ext = sp_io::TestExternalities::from(storage);
 
 		if self.initialize_first_session {
-			// We consider all test to start after timestamp is initialized This must be ensured by
-			// having `timestamp::on_initialize` called before `staking::on_initialize`. Also, if
-			// session length is 1, then it is already triggered.
 			ext.execute_with(|| {
-				System::set_block_number(1);
-				Session::on_initialize(1);
-				<Staking as Hooks<u64>>::on_initialize(1);
+				run_to_block(1);
+
+				// Force reset the timestamp to the initial timestamp for easy testing.
 				Timestamp::set_timestamp(INIT_TIMESTAMP);
 			});
 		}
@@ -578,7 +580,7 @@ pub(crate) fn current_era() -> EraIndex {
 }
 
 pub(crate) fn bond(who: AccountId, val: Balance) {
-	let _ = Balances::make_free_balance_be(&who, val);
+	let _ = asset::set_stakeable_balance::<Test>(&who, val);
 	assert_ok!(Staking::bond(RuntimeOrigin::signed(who), val, RewardDestination::Stash));
 }
 
@@ -603,10 +605,6 @@ pub(crate) fn bond_virtual_nominator(
 	val: Balance,
 	target: Vec<AccountId>,
 ) {
-	// In a real scenario, `who` is a keyless account managed by another pallet which provides for
-	// it.
-	System::inc_providers(&who);
-
 	// Bond who virtually.
 	assert_ok!(<Staking as sp_staking::StakingUnchecked>::virtual_bond(&who, val, &payee));
 	assert_ok!(Staking::nominate(RuntimeOrigin::signed(who), target));
@@ -618,33 +616,31 @@ pub(crate) fn bond_virtual_nominator(
 /// a block import/propose process where we first initialize the block, then execute some stuff (not
 /// in the function), and then finalize the block.
 pub(crate) fn run_to_block(n: BlockNumber) {
-	Staking::on_finalize(System::block_number());
-	for b in (System::block_number() + 1)..=n {
-		System::set_block_number(b);
-		Session::on_initialize(b);
-		<Staking as Hooks<u64>>::on_initialize(b);
-		Timestamp::set_timestamp(System::block_number() * BLOCK_TIME + INIT_TIMESTAMP);
-		if b != n {
-			Staking::on_finalize(System::block_number());
-		}
-	}
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default().after_initialize(|bn| {
+			Timestamp::set_timestamp(bn * BLOCK_TIME + INIT_TIMESTAMP);
+		}),
+	);
 }
 
 /// Progresses from the current block number (whatever that may be) to the `P * session_index + 1`.
-pub(crate) fn start_session(session_index: SessionIndex) {
+pub(crate) fn start_session(end_session_idx: SessionIndex) {
+	let period = Period::get();
 	let end: u64 = if Offset::get().is_zero() {
-		(session_index as u64) * Period::get()
+		(end_session_idx as u64) * period
 	} else {
-		Offset::get() + (session_index.saturating_sub(1) as u64) * Period::get()
+		Offset::get() + (end_session_idx.saturating_sub(1) as u64) * period
 	};
+
 	run_to_block(end);
+
+	let curr_session_idx = Session::current_index();
+
 	// session must have progressed properly.
 	assert_eq!(
-		Session::current_index(),
-		session_index,
-		"current session index = {}, expected = {}",
-		Session::current_index(),
-		session_index,
+		curr_session_idx, end_session_idx,
+		"current session index = {curr_session_idx}, expected = {end_session_idx}",
 	);
 }
 
@@ -814,7 +810,7 @@ pub(crate) fn bond_extra_no_checks(stash: &AccountId, amount: Balance) {
 	let mut ledger = Ledger::<Test>::get(&controller).expect("ledger must exist to bond_extra");
 
 	let new_total = ledger.total + amount;
-	Balances::set_lock(crate::STAKING_ID, stash, new_total, WithdrawReasons::all());
+	let _ = asset::update_stake::<Test>(stash, new_total);
 	ledger.total = new_total;
 	ledger.active = new_total;
 	Ledger::<Test>::insert(controller, ledger);
@@ -823,10 +819,10 @@ pub(crate) fn bond_extra_no_checks(stash: &AccountId, amount: Balance) {
 pub(crate) fn setup_double_bonded_ledgers() {
 	let init_ledgers = Ledger::<Test>::iter().count();
 
-	let _ = Balances::make_free_balance_be(&333, 2000);
-	let _ = Balances::make_free_balance_be(&444, 2000);
-	let _ = Balances::make_free_balance_be(&555, 2000);
-	let _ = Balances::make_free_balance_be(&777, 2000);
+	let _ = asset::set_stakeable_balance::<Test>(&333, 2000);
+	let _ = asset::set_stakeable_balance::<Test>(&444, 2000);
+	let _ = asset::set_stakeable_balance::<Test>(&555, 2000);
+	let _ = asset::set_stakeable_balance::<Test>(&777, 2000);
 
 	assert_ok!(Staking::bond(RuntimeOrigin::signed(333), 10, RewardDestination::Staked));
 	assert_ok!(Staking::bond(RuntimeOrigin::signed(444), 20, RewardDestination::Staked));
@@ -928,5 +924,5 @@ pub(crate) fn staking_events_since_last_call() -> Vec<crate::Event<Test>> {
 }
 
 pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) {
-	(Balances::free_balance(who), Balances::reserved_balance(who))
+	(asset::stakeable_balance::<Test>(who), Balances::reserved_balance(who))
 }
diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs
index 2ae925d036435976c2f58d49e27f81dad1bfc1d1..8c3ff23315a42d94803f2324cebf4c4930951fe0 100644
--- a/substrate/frame/staking/src/pallet/impls.rs
+++ b/substrate/frame/staking/src/pallet/impls.rs
@@ -27,8 +27,8 @@ use frame_support::{
 	dispatch::WithPostDispatchInfo,
 	pallet_prelude::*,
 	traits::{
-		Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len, OnUnbalanced,
-		TryCollect, UnixTime,
+		Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance,
+		InspectLockableCurrency, Len, LockableCurrency, OnUnbalanced, TryCollect, UnixTime,
 	},
 	weights::Weight,
 };
@@ -36,10 +36,9 @@ use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
 use pallet_session::historical;
 use sp_runtime::{
 	traits::{
-		Bounded, CheckedAdd, CheckedSub, Convert, One, SaturatedConversion, Saturating,
-		StaticLookup, Zero,
+		Bounded, CheckedAdd, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero,
 	},
-	ArithmeticError, Perbill, Percent,
+	ArithmeticError, DispatchResult, Perbill, Percent,
 };
 use sp_staking::{
 	currency_to_vote::CurrencyToVote,
@@ -54,6 +53,7 @@ use crate::{
 	BalanceOf, EraInfo, EraPayout, Exposure, ExposureOf, Forcing, IndividualExposure,
 	LedgerIntegrityState, MaxNominationsOf, MaxWinnersOf, Nominations, NominationsQuota,
 	PositiveImbalanceOf, RewardDestination, SessionInterface, StakingLedger, ValidatorPrefs,
+	STAKING_ID,
 };
 use alloc::{boxed::Box, vec, vec::Vec};
 
@@ -96,10 +96,12 @@ impl<T: Config> Pallet<T> {
 	pub(crate) fn inspect_bond_state(
 		stash: &T::AccountId,
 	) -> Result<LedgerIntegrityState, Error<T>> {
-		let lock = asset::staked::<T>(&stash);
+		// look at any old unmigrated lock as well.
+		let hold_or_lock = asset::staked::<T>(&stash)
+			.max(T::OldCurrency::balance_locked(STAKING_ID, &stash).into());
 
 		let controller = <Bonded<T>>::get(stash).ok_or_else(|| {
-			if lock == Zero::zero() {
+			if hold_or_lock == Zero::zero() {
 				Error::<T>::NotStash
 			} else {
 				Error::<T>::BadState
@@ -111,7 +113,7 @@ impl<T: Config> Pallet<T> {
 				if ledger.stash != *stash {
 					Ok(LedgerIntegrityState::Corrupted)
 				} else {
-					if lock != ledger.total {
+					if hold_or_lock != ledger.total {
 						Ok(LedgerIntegrityState::LockCorrupted)
 					} else {
 						Ok(LedgerIntegrityState::Ok)
@@ -163,11 +165,7 @@ impl<T: Config> Pallet<T> {
 			additional
 		} else {
 			// additional amount or actual balance of stash whichever is lower.
-			additional.min(
-				asset::stakeable_balance::<T>(stash)
-					.checked_sub(&ledger.total)
-					.ok_or(ArithmeticError::Overflow)?,
-			)
+			additional.min(asset::free_to_stake::<T>(stash))
 		};
 
 		ledger.total = ledger.total.checked_add(&extra).ok_or(ArithmeticError::Overflow)?;
@@ -416,12 +414,12 @@ impl<T: Config> Pallet<T> {
 		let dest = Self::payee(StakingAccount::Stash(stash.clone()))?;
 
 		let maybe_imbalance = match dest {
-			RewardDestination::Stash => asset::mint_existing::<T>(stash, amount),
+			RewardDestination::Stash => asset::mint_into_existing::<T>(stash, amount),
 			RewardDestination::Staked => Self::ledger(Stash(stash.clone()))
 				.and_then(|mut ledger| {
 					ledger.active += amount;
 					ledger.total += amount;
-					let r = asset::mint_existing::<T>(stash, amount);
+					let r = asset::mint_into_existing::<T>(stash, amount);
 
 					let _ = ledger
 						.update()
@@ -799,8 +797,6 @@ impl<T: Config> Pallet<T> {
 		Self::do_remove_validator(&stash);
 		Self::do_remove_nominator(&stash);
 
-		frame_system::Pallet::<T>::dec_consumers(&stash);
-
 		Ok(())
 	}
 
@@ -1163,6 +1159,81 @@ impl<T: Config> Pallet<T> {
 	) -> Exposure<T::AccountId, BalanceOf<T>> {
 		EraInfo::<T>::get_full_exposure(era, account)
 	}
+
+	pub(super) fn do_migrate_currency(stash: &T::AccountId) -> DispatchResult {
+		if Self::is_virtual_staker(stash) {
+			return Self::do_migrate_virtual_staker(stash);
+		}
+
+		let ledger = Self::ledger(Stash(stash.clone()))?;
+		let staked: BalanceOf<T> = T::OldCurrency::balance_locked(STAKING_ID, stash).into();
+		ensure!(!staked.is_zero(), Error::<T>::AlreadyMigrated);
+		ensure!(ledger.total == staked, Error::<T>::BadState);
+
+		// remove old staking lock
+		T::OldCurrency::remove_lock(STAKING_ID, &stash);
+
+		// check if we can hold all stake.
+		let max_hold = asset::free_to_stake::<T>(&stash);
+		let force_withdraw = if max_hold >= staked {
+			// this means we can hold all stake. yay!
+			asset::update_stake::<T>(&stash, staked)?;
+			Zero::zero()
+		} else {
+			// if we are here, it means we cannot hold all user stake. We will do a force withdraw
+			// from ledger, but that's okay since anyways user do not have funds for it.
+			let force_withdraw = staked.saturating_sub(max_hold);
+
+			// we ignore if active is 0. It implies the locked amount is not actively staked. The
+			// account can still get away from potential slash but we can't do much better here.
+			StakingLedger {
+				total: max_hold,
+				active: ledger.active.saturating_sub(force_withdraw),
+				// we are not changing the stash, so we can keep the stash.
+				..ledger
+			}
+			.update()?;
+			force_withdraw
+		};
+
+		// Get rid of the extra consumer we used to have with OldCurrency.
+		frame_system::Pallet::<T>::dec_consumers(&stash);
+
+		Self::deposit_event(Event::<T>::CurrencyMigrated { stash: stash.clone(), force_withdraw });
+		Ok(())
+	}
+
+	fn do_migrate_virtual_staker(stash: &T::AccountId) -> DispatchResult {
+		// Funds for virtual stakers not managed/held by this pallet. We only need to clear
+		// the extra consumer we used to have with OldCurrency.
+		frame_system::Pallet::<T>::dec_consumers(&stash);
+
+		// The delegation system that manages the virtual staker needed to increment provider
+		// previously because of the consumer needed by this pallet. In reality, this stash
+		// is just a key for managing the ledger and the account does not need to hold any
+		// balance or exist. We decrement this provider.
+		let actual_providers = frame_system::Pallet::<T>::providers(stash);
+
+		let expected_providers =
+			// provider is expected to be 1 but someone can always transfer some free funds to
+			// these accounts, increasing the provider.
+			if asset::free_to_stake::<T>(&stash) >= asset::existential_deposit::<T>() {
+				2
+			} else {
+				1
+			};
+
+		// We should never have more than expected providers.
+		ensure!(actual_providers <= expected_providers, Error::<T>::BadState);
+
+		// if actual provider is less than expected, it is already migrated.
+		ensure!(actual_providers == expected_providers, Error::<T>::AlreadyMigrated);
+
+		// dec provider
+		let _ = frame_system::Pallet::<T>::dec_providers(&stash)?;
+
+		return Ok(())
+	}
 }
 
 impl<T: Config> Pallet<T> {
@@ -1925,9 +1996,10 @@ impl<T: Config> StakingInterface for Pallet<T> {
 }
 
 impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> {
-	fn migrate_to_virtual_staker(who: &Self::AccountId) {
-		asset::kill_stake::<T>(who);
+	fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult {
+		asset::kill_stake::<T>(who)?;
 		VirtualStakers::<T>::insert(who, ());
+		Ok(())
 	}
 
 	/// Virtually bonds `keyless_who` to `payee` with `value`.
@@ -1945,9 +2017,6 @@ impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> {
 		// check if payee not same as who.
 		ensure!(keyless_who != payee, Error::<T>::RewardDestinationRestricted);
 
-		// mark this pallet as consumer of `who`.
-		frame_system::Pallet::<T>::inc_consumers(&keyless_who).map_err(|_| Error::<T>::BadState)?;
-
 		// mark who as a virtual staker.
 		VirtualStakers::<T>::insert(keyless_who, ());
 
@@ -1959,11 +2028,13 @@ impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> {
 		Ok(())
 	}
 
+	/// Only meant to be used in tests.
 	#[cfg(feature = "runtime-benchmarks")]
 	fn migrate_to_direct_staker(who: &Self::AccountId) {
 		assert!(VirtualStakers::<T>::contains_key(who));
 		let ledger = StakingLedger::<T>::get(Stash(who.clone())).unwrap();
-		asset::update_stake::<T>(who, ledger.total);
+		let _ = asset::update_stake::<T>(who, ledger.total)
+			.expect("funds must be transferred to stash");
 		VirtualStakers::<T>::remove(who);
 	}
 }
@@ -2100,7 +2171,7 @@ impl<T: Config> Pallet<T> {
 				if VirtualStakers::<T>::contains_key(stash.clone()) {
 					ensure!(
 						asset::staked::<T>(&stash) == Zero::zero(),
-						"virtual stakers should not have any locked balance"
+						"virtual stakers should not have any staked balance"
 					);
 					ensure!(
 						<Bonded<T>>::get(stash.clone()).unwrap() == stash.clone(),
@@ -2128,7 +2199,7 @@ impl<T: Config> Pallet<T> {
 				} else {
 					ensure!(
 						Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
-						"bond, ledger and/or staking lock inconsistent for a bonded stash."
+						"bond, ledger and/or staking hold inconsistent for a bonded stash."
 					);
 				}
 
diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs
index b3f8c18f704cd7e5158c89f0e432f50d7bcb8c04..7d5da9ea0c49753e0db14092b978d0c88b6dcd63 100644
--- a/substrate/frame/staking/src/pallet/mod.rs
+++ b/substrate/frame/staking/src/pallet/mod.rs
@@ -25,8 +25,12 @@ use frame_election_provider_support::{
 use frame_support::{
 	pallet_prelude::*,
 	traits::{
+		fungible::{
+			hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate},
+			Mutate as FunMutate,
+		},
 		Defensive, DefensiveSaturating, EnsureOrigin, EstimateNextNewSession, Get,
-		InspectLockableCurrency, LockableCurrency, OnUnbalanced, UnixTime,
+		InspectLockableCurrency, OnUnbalanced, UnixTime,
 	},
 	weights::Weight,
 	BoundedVec,
@@ -89,13 +93,27 @@ pub mod pallet {
 
 	#[pallet::config(with_default)]
 	pub trait Config: frame_system::Config {
+		/// The old trait for staking balance. Deprecated and only used for migrating old ledgers.
+		#[pallet::no_default]
+		type OldCurrency: InspectLockableCurrency<
+			Self::AccountId,
+			Moment = BlockNumberFor<Self>,
+			Balance = Self::CurrencyBalance,
+		>;
+
 		/// The staking balance.
 		#[pallet::no_default]
-		type Currency: LockableCurrency<
+		type Currency: FunHoldMutate<
 				Self::AccountId,
-				Moment = BlockNumberFor<Self>,
+				Reason = Self::RuntimeHoldReason,
 				Balance = Self::CurrencyBalance,
-			> + InspectLockableCurrency<Self::AccountId>;
+			> + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
+			+ FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
+
+		/// Overarching hold reason.
+		#[pallet::no_default_bounds]
+		type RuntimeHoldReason: From<HoldReason>;
+
 		/// Just the `Currency::Balance` type; we have this item to allow us to constrain it to
 		/// `From<u64>`.
 		type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
@@ -106,6 +124,8 @@ pub mod pallet {
 			+ Default
 			+ From<u64>
 			+ TypeInfo
+			+ Send
+			+ Sync
 			+ MaxEncodedLen;
 		/// Time used for computing era duration.
 		///
@@ -309,6 +329,14 @@ pub mod pallet {
 		type WeightInfo: WeightInfo;
 	}
 
+	/// A reason for placing a hold on funds.
+	#[pallet::composite_enum]
+	pub enum HoldReason {
+		/// Funds on stake by a nominator or a validator.
+		#[codec(index = 0)]
+		Staking,
+	}
+
 	/// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
 	pub mod config_preludes {
 		use super::*;
@@ -327,6 +355,8 @@ pub mod pallet {
 		impl DefaultConfig for TestDefaultConfig {
 			#[inject_runtime_type]
 			type RuntimeEvent = ();
+			#[inject_runtime_type]
+			type RuntimeHoldReason = ();
 			type CurrencyBalance = u128;
 			type CurrencyToVote = ();
 			type NominationsQuota = crate::FixedNominationsQuota<16>;
@@ -765,7 +795,7 @@ pub mod pallet {
 					status
 				);
 				assert!(
-					asset::stakeable_balance::<T>(stash) >= balance,
+					asset::free_to_stake::<T>(stash) >= balance,
 					"Stash does not have enough balance to bond."
 				);
 				frame_support::assert_ok!(<Pallet<T>>::bond(
@@ -858,6 +888,9 @@ pub mod pallet {
 		ValidatorDisabled { stash: T::AccountId },
 		/// Validator has been re-enabled.
 		ValidatorReenabled { stash: T::AccountId },
+		/// Staking balance migrated from locks to holds, with any balance that could not be held
+		/// is force withdrawn.
+		CurrencyMigrated { stash: T::AccountId, force_withdraw: BalanceOf<T> },
 	}
 
 	#[pallet::error]
@@ -929,6 +962,10 @@ pub mod pallet {
 		NotEnoughFunds,
 		/// Operation not allowed for virtual stakers.
 		VirtualStakerNotAllowed,
+		/// Stash could not be reaped as other pallet might depend on it.
+		CannotReapStash,
+		/// The stake of this account is already migrated to `Fungible` holds.
+		AlreadyMigrated,
 	}
 
 	#[pallet::hooks]
@@ -1172,10 +1209,7 @@ pub mod pallet {
 				return Err(Error::<T>::InsufficientBond.into())
 			}
 
-			// Would fail if account has no provider.
-			frame_system::Pallet::<T>::inc_consumers(&stash)?;
-
-			let stash_balance = asset::stakeable_balance::<T>(&stash);
+			let stash_balance = asset::free_to_stake::<T>(&stash);
 			let value = value.min(stash_balance);
 			Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
 			let ledger = StakingLedger::<T>::new(stash.clone(), value);
@@ -2231,8 +2265,8 @@ pub mod pallet {
 
 					let new_total = if let Some(total) = maybe_total {
 						let new_total = total.min(stash_balance);
-						// enforce lock == ledger.amount.
-						asset::update_stake::<T>(&stash, new_total);
+						// enforce hold == ledger.amount.
+						asset::update_stake::<T>(&stash, new_total)?;
 						new_total
 					} else {
 						current_lock
@@ -2259,13 +2293,13 @@ pub mod pallet {
 					// to enforce a new ledger.total and staking lock for this stash.
 					let new_total =
 						maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
-					asset::update_stake::<T>(&stash, new_total);
+					asset::update_stake::<T>(&stash, new_total)?;
 
 					Ok((stash.clone(), new_total))
 				},
 				Err(Error::<T>::BadState) => {
 					// the stash and ledger do not exist but lock is lingering.
-					asset::kill_stake::<T>(&stash);
+					asset::kill_stake::<T>(&stash)?;
 					ensure!(
 						Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
 						Error::<T>::BadState
@@ -2291,6 +2325,26 @@ pub mod pallet {
 			);
 			Ok(())
 		}
+
+		/// Migrates permissionlessly a stash from locks to holds.
+		///
+		/// This removes the old lock on the stake and creates a hold on it atomically. If all
+		/// stake cannot be held, the best effort is made to hold as much as possible. The remaining
+		/// stake is removed from the ledger.
+		///
+		/// The fee is waived if the migration is successful.
+		#[pallet::call_index(30)]
+		#[pallet::weight(T::WeightInfo::migrate_currency())]
+		pub fn migrate_currency(
+			origin: OriginFor<T>,
+			stash: T::AccountId,
+		) -> DispatchResultWithPostInfo {
+			let _ = ensure_signed(origin)?;
+			Self::do_migrate_currency(&stash)?;
+
+			// Refund the transaction fee if successful.
+			Ok(Pays::No.into())
+		}
 	}
 }
 
diff --git a/substrate/frame/staking/src/testing_utils.rs b/substrate/frame/staking/src/testing_utils.rs
index 81337710aa9043bf975a95c3d5377b0dea0738f8..dfd5422106c081784c95985fa64477b596e93064 100644
--- a/substrate/frame/staking/src/testing_utils.rs
+++ b/substrate/frame/staking/src/testing_utils.rs
@@ -238,3 +238,21 @@ pub fn create_validators_with_nominators_for_era<T: Config>(
 pub fn current_era<T: Config>() -> EraIndex {
 	CurrentEra::<T>::get().unwrap_or(0)
 }
+
+pub fn migrate_to_old_currency<T: Config>(who: T::AccountId) {
+	use frame_support::traits::LockableCurrency;
+	let staked = asset::staked::<T>(&who);
+
+	// apply locks (this also adds a consumer).
+	T::OldCurrency::set_lock(
+		STAKING_ID,
+		&who,
+		staked,
+		frame_support::traits::WithdrawReasons::all(),
+	);
+	// remove holds.
+	asset::kill_stake::<T>(&who).expect("remove hold failed");
+
+	// replicate old behaviour of explicit increment of consumer.
+	frame_system::Pallet::<T>::inc_consumers(&who).expect("increment consumer failed");
+}
diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs
index 6c2335e1aac8a0f32beba70ac6974729e70af441..90841514399403301be4eb3a0d2ba87da543ddc2 100644
--- a/substrate/frame/staking/src/tests.rs
+++ b/substrate/frame/staking/src/tests.rs
@@ -26,8 +26,12 @@ use frame_election_provider_support::{
 use frame_support::{
 	assert_noop, assert_ok, assert_storage_noop,
 	dispatch::{extract_actual_weight, GetDispatchInfo, WithPostDispatchInfo},
+	hypothetically,
 	pallet_prelude::*,
-	traits::{Currency, Get, ReservableCurrency},
+	traits::{
+		fungible::Inspect, Currency, Get, InspectLockableCurrency, LockableCurrency,
+		ReservableCurrency, WithdrawReasons,
+	},
 };
 
 use mock::*;
@@ -108,7 +112,7 @@ fn force_unstake_works() {
 		// Cant transfer
 		assert_noop!(
 			Balances::transfer_allow_death(RuntimeOrigin::signed(11), 1, 10),
-			TokenError::Frozen,
+			TokenError::FundsUnavailable,
 		);
 		// Force unstake requires root.
 		assert_noop!(Staking::force_unstake(RuntimeOrigin::signed(11), 11, 2), BadOrigin);
@@ -229,8 +233,7 @@ fn basic_setup_works() {
 		assert_eq!(active_era(), 0);
 
 		// Account 10 has `balance_factor` free balance
-		assert_eq!(asset::stakeable_balance::<Test>(&10), 1);
-		assert_eq!(asset::stakeable_balance::<Test>(&10), 1);
+		assert_eq!(Balances::balance(&10), 1);
 
 		// New era is not being forced
 		assert_eq!(ForceEra::<Test>::get(), Forcing::NotForcing);
@@ -360,8 +363,16 @@ fn rewards_should_work() {
 				remainder: maximum_payout - total_payout_0
 			}
 		);
+
+		// make note of total issuance before rewards.
+		let total_issuance_0 = asset::total_issuance::<Test>();
+
 		mock::make_all_reward_payment(0);
 
+		// total issuance should have increased
+		let total_issuance_1 = asset::total_issuance::<Test>();
+		assert_eq!(total_issuance_1, total_issuance_0 + total_payout_0);
+
 		assert_eq_error_rate!(
 			asset::total_balance::<Test>(&11),
 			init_balance_11 + part_for_11 * total_payout_0 * 2 / 3,
@@ -401,6 +412,7 @@ fn rewards_should_work() {
 		);
 		mock::make_all_reward_payment(1);
 
+		assert_eq!(asset::total_issuance::<Test>(), total_issuance_1 + total_payout_1);
 		assert_eq_error_rate!(
 			asset::total_balance::<Test>(&11),
 			init_balance_11 + part_for_11 * (total_payout_0 * 2 / 3 + total_payout_1),
@@ -490,7 +502,7 @@ fn staking_should_work() {
 			}
 		);
 		// e.g. it cannot reserve more than 500 that it has free from the total 2000
-		assert_noop!(Balances::reserve(&3, 501), BalancesError::<Test, _>::LiquidityRestrictions);
+		assert_noop!(Balances::reserve(&3, 501), DispatchError::ConsumerRemaining);
 		assert_ok!(Balances::reserve(&3, 409));
 	});
 }
@@ -689,7 +701,7 @@ fn nominating_and_rewards_should_work() {
 			);
 			// Nominator 3: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==>
 			// 2/9 + 3/11
-			assert_eq!(asset::total_balance::<Test>(&3), initial_balance);
+			assert_eq!(asset::stakeable_balance::<Test>(&3), initial_balance);
 			// 333 is the reward destination for 3.
 			assert_eq_error_rate!(
 				asset::total_balance::<Test>(&333),
@@ -992,9 +1004,9 @@ fn cannot_transfer_staked_balance() {
 	ExtBuilder::default().nominate(false).build_and_execute(|| {
 		// Confirm account 11 is stashed
 		assert_eq!(Staking::bonded(&11), Some(11));
-		// Confirm account 11 has some free balance
+		// Confirm account 11 has some stakeable balance
 		assert_eq!(asset::stakeable_balance::<Test>(&11), 1000);
-		// Confirm account 11 (via controller) is totally staked
+		// Confirm account 11 is totally staked
 		assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000);
 		// Confirm account 11 cannot transfer as a result
 		assert_noop!(
@@ -1021,11 +1033,12 @@ fn cannot_transfer_staked_balance_2() {
 		assert_eq!(asset::stakeable_balance::<Test>(&21), 2000);
 		// Confirm account 21 (via controller) is totally staked
 		assert_eq!(Staking::eras_stakers(active_era(), &21).total, 1000);
-		// Confirm account 21 can transfer at most 1000
+		// Confirm account 21 cannot transfer more than 1000
 		assert_noop!(
 			Balances::transfer_allow_death(RuntimeOrigin::signed(21), 21, 1001),
 			TokenError::Frozen,
 		);
+		// Confirm account 21 needs to leave at least ED in free balance to be able to transfer
 		assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(21), 21, 1000));
 	});
 }
@@ -1036,17 +1049,61 @@ fn cannot_reserve_staked_balance() {
 	ExtBuilder::default().build_and_execute(|| {
 		// Confirm account 11 is stashed
 		assert_eq!(Staking::bonded(&11), Some(11));
-		// Confirm account 11 has some free balance
-		assert_eq!(asset::stakeable_balance::<Test>(&11), 1000);
-		// Confirm account 11 (via controller 10) is totally staked
-		assert_eq!(Staking::eras_stakers(active_era(), &11).own, 1000);
+		// Confirm account 11 is totally staked
+		assert_eq!(asset::staked::<Test>(&11), 1000);
+
 		// Confirm account 11 cannot reserve as a result
-		assert_noop!(Balances::reserve(&11, 1), BalancesError::<Test, _>::LiquidityRestrictions);
+		assert_noop!(Balances::reserve(&11, 2), BalancesError::<Test, _>::InsufficientBalance);
+		assert_noop!(Balances::reserve(&11, 1), DispatchError::ConsumerRemaining);
 
 		// Give account 11 extra free balance
-		let _ = asset::set_stakeable_balance::<Test>(&11, 10000);
+		let _ = asset::set_stakeable_balance::<Test>(&11, 1000 + 1000);
+		assert_eq!(asset::free_to_stake::<Test>(&11), 1000);
+
 		// Confirm account 11 can now reserve balance
-		assert_ok!(Balances::reserve(&11, 1));
+		assert_ok!(Balances::reserve(&11, 500));
+
+		// free to stake balance has reduced
+		assert_eq!(asset::free_to_stake::<Test>(&11), 500);
+	});
+}
+
+#[test]
+fn locked_balance_can_be_staked() {
+	// Checks that a bonded account cannot reserve balance from free balance
+	ExtBuilder::default().build_and_execute(|| {
+		// Confirm account 11 is stashed
+		assert_eq!(Staking::bonded(&11), Some(11));
+		assert_eq!(asset::staked::<Test>(&11), 1000);
+		assert_eq!(asset::free_to_stake::<Test>(&11), 0);
+
+		// add some staking balance to 11
+		let _ = asset::set_stakeable_balance::<Test>(&11, 1000 + 1000);
+		// free to stake is 1000
+		assert_eq!(asset::free_to_stake::<Test>(&11), 1000);
+
+		// lock some balance
+		Balances::set_lock(*b"somelock", &11, 500, WithdrawReasons::all());
+
+		// locked balance still available for staking
+		assert_eq!(asset::free_to_stake::<Test>(&11), 1000);
+
+		// can stake free balance
+		assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 500));
+		assert_eq!(asset::staked::<Test>(&11), 1500);
+
+		// Can stake the locked balance
+		assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 500));
+		assert_eq!(asset::staked::<Test>(&11), 2000);
+		// no balance left to stake
+		assert_eq!(asset::free_to_stake::<Test>(&11), 0);
+
+		// this does not fail if someone tries to stake more than free balance but just stakes
+		// whatever is available. (not sure if that is the best way, but we keep it backward
+		// compatible)
+		assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 10));
+		// no extra balance staked.
+		assert_eq!(asset::staked::<Test>(&11), 2000);
 	});
 }
 
@@ -1057,9 +1114,9 @@ fn reward_destination_works() {
 		// Check that account 11 is a validator
 		assert!(Session::validators().contains(&11));
 		// Check the balance of the validator account
-		assert_eq!(asset::stakeable_balance::<Test>(&10), 1);
+		assert_eq!(asset::total_balance::<Test>(&10), 1);
 		// Check the balance of the stash account
-		assert_eq!(asset::stakeable_balance::<Test>(&11), 1000);
+		assert_eq!(asset::total_balance::<Test>(&11), 1001);
 		// Check how much is at stake
 		assert_eq!(
 			Staking::ledger(11.into()).unwrap(),
@@ -1294,12 +1351,12 @@ fn bond_extra_and_withdraw_unbonded_works() {
 		// Give account 11 some large free balance greater than total
 		let _ = asset::set_stakeable_balance::<Test>(&11, 1000000);
 
+		// ensure it has the correct balance.
+		assert_eq!(asset::stakeable_balance::<Test>(&11), 1000000);
+
 		// Initial config should be correct
 		assert_eq!(active_era(), 0);
 
-		// check the balance of a validator accounts.
-		assert_eq!(asset::total_balance::<Test>(&11), 1000000);
-
 		// confirm that 10 is a normal validator and gets paid at the end of the era.
 		mock::start_active_era(1);
 
@@ -2077,7 +2134,7 @@ fn bond_with_no_staked_value() {
 			);
 			// bonded with absolute minimum value possible.
 			assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 5, RewardDestination::Account(1)));
-			assert_eq!(pallet_balances::Locks::<Test>::get(&1)[0].amount, 5);
+			assert_eq!(pallet_balances::Holds::<Test>::get(&1)[0].amount, 5);
 
 			// unbonding even 1 will cause all to be unbonded.
 			assert_ok!(Staking::unbond(RuntimeOrigin::signed(1), 1));
@@ -2098,14 +2155,14 @@ fn bond_with_no_staked_value() {
 			// not yet removed.
 			assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0));
 			assert!(Staking::ledger(1.into()).is_ok());
-			assert_eq!(pallet_balances::Locks::<Test>::get(&1)[0].amount, 5);
+			assert_eq!(pallet_balances::Holds::<Test>::get(&1)[0].amount, 5);
 
 			mock::start_active_era(3);
 
 			// poof. Account 1 is removed from the staking system.
 			assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(1), 0));
 			assert!(Staking::ledger(1.into()).is_err());
-			assert_eq!(pallet_balances::Locks::<Test>::get(&1).len(), 0);
+			assert_eq!(pallet_balances::Holds::<Test>::get(&1).len(), 0);
 		});
 }
 
@@ -2338,9 +2395,20 @@ fn reward_validator_slashing_validator_does_not_overflow() {
 		EraInfo::<Test>::set_exposure(0, &11, exposure);
 		ErasValidatorReward::<Test>::insert(0, stake);
 		assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 0));
-		assert_eq!(asset::total_balance::<Test>(&11), stake * 2);
+		assert_eq!(asset::stakeable_balance::<Test>(&11), stake * 2);
 
-		// Set staker
+		// ensure ledger has `stake` and no more.
+		Ledger::<Test>::insert(
+			11,
+			StakingLedgerInspect {
+				stash: 11,
+				total: stake,
+				active: stake,
+				unlocking: Default::default(),
+				legacy_claimed_rewards: bounded_vec![1],
+			},
+		);
+		// Set staker (unsafe, can reduce balance below actual stake)
 		let _ = asset::set_stakeable_balance::<Test>(&11, stake);
 		let _ = asset::set_stakeable_balance::<Test>(&2, stake);
 
@@ -2366,8 +2434,8 @@ fn reward_validator_slashing_validator_does_not_overflow() {
 			&[Perbill::from_percent(100)],
 		);
 
-		assert_eq!(asset::total_balance::<Test>(&11), stake - 1);
-		assert_eq!(asset::total_balance::<Test>(&2), 1);
+		assert_eq!(asset::stakeable_balance::<Test>(&11), stake - 1);
+		assert_eq!(asset::stakeable_balance::<Test>(&2), 1);
 	})
 }
 
@@ -2627,8 +2695,8 @@ fn reporters_receive_their_slice() {
 		// 50% * (10% * initial_balance / 2)
 		let reward = (initial_balance / 20) / 2;
 		let reward_each = reward / 2; // split into two pieces.
-		assert_eq!(asset::stakeable_balance::<Test>(&1), 10 + reward_each);
-		assert_eq!(asset::stakeable_balance::<Test>(&2), 20 + reward_each);
+		assert_eq!(asset::total_balance::<Test>(&1), 10 + reward_each);
+		assert_eq!(asset::total_balance::<Test>(&2), 20 + reward_each);
 	});
 }
 
@@ -2653,7 +2721,7 @@ fn subsequent_reports_in_same_span_pay_out_less() {
 		// F1 * (reward_proportion * slash - 0)
 		// 50% * (10% * initial_balance * 20%)
 		let reward = (initial_balance / 5) / 20;
-		assert_eq!(asset::stakeable_balance::<Test>(&1), 10 + reward);
+		assert_eq!(asset::total_balance::<Test>(&1), 10 + reward);
 
 		on_offence_now(
 			&[OffenceDetails {
@@ -2668,7 +2736,7 @@ fn subsequent_reports_in_same_span_pay_out_less() {
 		// F1 * (reward_proportion * slash - prior_payout)
 		// 50% * (10% * (initial_balance / 2) - prior_payout)
 		let reward = ((initial_balance / 20) - prior_payout) / 2;
-		assert_eq!(asset::stakeable_balance::<Test>(&1), 10 + prior_payout + reward);
+		assert_eq!(asset::total_balance::<Test>(&1), 10 + prior_payout + reward);
 	});
 }
 
@@ -2812,8 +2880,9 @@ fn garbage_collection_after_slashing() {
 			// validator and nominator slash in era are garbage-collected by era change,
 			// so we don't test those here.
 
-			assert_eq!(asset::stakeable_balance::<Test>(&11), 2);
-			assert_eq!(asset::total_balance::<Test>(&11), 2);
+			assert_eq!(asset::stakeable_balance::<Test>(&11), 0);
+			// Non staked balance is not touched.
+			assert_eq!(asset::total_balance::<Test>(&11), ExistentialDeposit::get());
 
 			let slashing_spans = SlashingSpans::<Test>::get(&11).unwrap();
 			assert_eq!(slashing_spans.iter().count(), 2);
@@ -6092,7 +6161,7 @@ fn nomination_quota_max_changes_decoding() {
 		.add_staker(70, 71, 10, StakerStatus::Nominator(vec![1, 2, 3]))
 		.add_staker(30, 330, 10, StakerStatus::Nominator(vec![1, 2, 3, 4]))
 		.add_staker(50, 550, 10, StakerStatus::Nominator(vec![1, 2, 3, 4]))
-		.balance_factor(10)
+		.balance_factor(11)
 		.build_and_execute(|| {
 			// pre-condition.
 			assert_eq!(MaxNominationsOf::<Test>::get(), 16);
@@ -6208,240 +6277,248 @@ fn force_apply_min_commission_works() {
 
 #[test]
 fn proportional_slash_stop_slashing_if_remaining_zero() {
-	let c = |era, value| UnlockChunk::<Balance> { era, value };
+	ExtBuilder::default().nominate(true).build_and_execute(|| {
+		let c = |era, value| UnlockChunk::<Balance> { era, value };
 
-	// we have some chunks, but they are not affected.
-	let unlocking = bounded_vec![c(1, 10), c(2, 10)];
+		// we have some chunks, but they are not affected.
+		let unlocking = bounded_vec![c(1, 10), c(2, 10)];
 
-	// Given
-	let mut ledger = StakingLedger::<Test>::new(123, 20);
-	ledger.total = 40;
-	ledger.unlocking = unlocking;
+		// Given
+		let mut ledger = StakingLedger::<Test>::new(123, 20);
+		ledger.total = 40;
+		ledger.unlocking = unlocking;
 
-	assert_eq!(BondingDuration::get(), 3);
+		assert_eq!(BondingDuration::get(), 3);
 
-	// should not slash more than the amount requested, by accidentally slashing the first chunk.
-	assert_eq!(ledger.slash(18, 1, 0), 18);
+		// should not slash more than the amount requested, by accidentally slashing the first
+		// chunk.
+		assert_eq!(ledger.slash(18, 1, 0), 18);
+	});
 }
 
 #[test]
 fn proportional_ledger_slash_works() {
-	let c = |era, value| UnlockChunk::<Balance> { era, value };
-	// Given
-	let mut ledger = StakingLedger::<Test>::new(123, 10);
-	assert_eq!(BondingDuration::get(), 3);
-
-	// When we slash a ledger with no unlocking chunks
-	assert_eq!(ledger.slash(5, 1, 0), 5);
-	// Then
-	assert_eq!(ledger.total, 5);
-	assert_eq!(ledger.active, 5);
-	assert_eq!(LedgerSlashPerEra::get().0, 5);
-	assert_eq!(LedgerSlashPerEra::get().1, Default::default());
-
-	// When we slash a ledger with no unlocking chunks and the slash amount is greater then the
-	// total
-	assert_eq!(ledger.slash(11, 1, 0), 5);
-	// Then
-	assert_eq!(ledger.total, 0);
-	assert_eq!(ledger.active, 0);
-	assert_eq!(LedgerSlashPerEra::get().0, 0);
-	assert_eq!(LedgerSlashPerEra::get().1, Default::default());
-
-	// Given
-	ledger.unlocking = bounded_vec![c(4, 10), c(5, 10)];
-	ledger.total = 2 * 10;
-	ledger.active = 0;
-	// When all the chunks overlap with the slash eras
-	assert_eq!(ledger.slash(20, 0, 0), 20);
-	// Then
-	assert_eq!(ledger.unlocking, vec![]);
-	assert_eq!(ledger.total, 0);
-	assert_eq!(LedgerSlashPerEra::get().0, 0);
-	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0)]));
-
-	// Given
-	ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)];
-	ledger.total = 4 * 100;
-	ledger.active = 0;
-	// When the first 2 chunks don't overlap with the affected range of unlock eras.
-	assert_eq!(ledger.slash(140, 0, 3), 140);
-	// Then
-	assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 30), c(7, 30)]);
-	assert_eq!(ledger.total, 4 * 100 - 140);
-	assert_eq!(LedgerSlashPerEra::get().0, 0);
-	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 30), (7, 30)]));
-
-	// Given
-	ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)];
-	ledger.total = 4 * 100;
-	ledger.active = 0;
-	// When the first 2 chunks don't overlap with the affected range of unlock eras.
-	assert_eq!(ledger.slash(15, 0, 3), 15);
-	// Then
-	assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 100 - 8), c(7, 100 - 7)]);
-	assert_eq!(ledger.total, 4 * 100 - 15);
-	assert_eq!(LedgerSlashPerEra::get().0, 0);
-	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 92), (7, 93)]));
-
-	// Given
-	ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
-	ledger.active = 500;
-	// 900
-	ledger.total = 40 + 10 + 100 + 250 + 500;
-	// When we have a partial slash that touches all chunks
-	assert_eq!(ledger.slash(900 / 2, 0, 0), 450);
-	// Then
-	assert_eq!(ledger.active, 500 / 2);
-	assert_eq!(ledger.unlocking, vec![c(4, 40 / 2), c(5, 100 / 2), c(6, 10 / 2), c(7, 250 / 2)]);
-	assert_eq!(ledger.total, 900 / 2);
-	assert_eq!(LedgerSlashPerEra::get().0, 500 / 2);
-	assert_eq!(
-		LedgerSlashPerEra::get().1,
-		BTreeMap::from([(4, 40 / 2), (5, 100 / 2), (6, 10 / 2), (7, 250 / 2)])
-	);
+	ExtBuilder::default().nominate(true).build_and_execute(|| {
+		let c = |era, value| UnlockChunk::<Balance> { era, value };
+		// Given
+		let mut ledger = StakingLedger::<Test>::new(123, 10);
+		assert_eq!(BondingDuration::get(), 3);
 
-	// slash 1/4th with not chunk.
-	ledger.unlocking = bounded_vec![];
-	ledger.active = 500;
-	ledger.total = 500;
-	// When we have a partial slash that touches all chunks
-	assert_eq!(ledger.slash(500 / 4, 0, 0), 500 / 4);
-	// Then
-	assert_eq!(ledger.active, 3 * 500 / 4);
-	assert_eq!(ledger.unlocking, vec![]);
-	assert_eq!(ledger.total, ledger.active);
-	assert_eq!(LedgerSlashPerEra::get().0, 3 * 500 / 4);
-	assert_eq!(LedgerSlashPerEra::get().1, Default::default());
-
-	// Given we have the same as above,
-	ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
-	ledger.active = 500;
-	ledger.total = 40 + 10 + 100 + 250 + 500; // 900
-	assert_eq!(ledger.total, 900);
-	// When we have a higher min balance
-	assert_eq!(
-		ledger.slash(
-			900 / 2,
-			25, /* min balance - chunks with era 0 & 2 will be slashed to <=25, causing it to
-			     * get swept */
-			0
-		),
-		450
-	);
-	assert_eq!(ledger.active, 500 / 2);
-	// the last chunk was not slashed 50% like all the rest, because some other earlier chunks got
-	// dusted.
-	assert_eq!(ledger.unlocking, vec![c(5, 100 / 2), c(7, 150)]);
-	assert_eq!(ledger.total, 900 / 2);
-	assert_eq!(LedgerSlashPerEra::get().0, 500 / 2);
-	assert_eq!(
-		LedgerSlashPerEra::get().1,
-		BTreeMap::from([(4, 0), (5, 100 / 2), (6, 0), (7, 150)])
-	);
+		// When we slash a ledger with no unlocking chunks
+		assert_eq!(ledger.slash(5, 1, 0), 5);
+		// Then
+		assert_eq!(ledger.total, 5);
+		assert_eq!(ledger.active, 5);
+		assert_eq!(LedgerSlashPerEra::get().0, 5);
+		assert_eq!(LedgerSlashPerEra::get().1, Default::default());
+
+		// When we slash a ledger with no unlocking chunks and the slash amount is greater then the
+		// total
+		assert_eq!(ledger.slash(11, 1, 0), 5);
+		// Then
+		assert_eq!(ledger.total, 0);
+		assert_eq!(ledger.active, 0);
+		assert_eq!(LedgerSlashPerEra::get().0, 0);
+		assert_eq!(LedgerSlashPerEra::get().1, Default::default());
 
-	// Given
-	// slash order --------------------NA--------2----------0----------1----
-	ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
-	ledger.active = 500;
-	ledger.total = 40 + 10 + 100 + 250 + 500; // 900
-	assert_eq!(
-		ledger.slash(
-			500 + 10 + 250 + 100 / 2, // active + era 6 + era 7 + era 5 / 2
-			0,
-			3 /* slash era 6 first, so the affected parts are era 6, era 7 and
-			   * ledge.active. This will cause the affected to go to zero, and then we will
-			   * start slashing older chunks */
-		),
-		500 + 250 + 10 + 100 / 2
-	);
-	// Then
-	assert_eq!(ledger.active, 0);
-	assert_eq!(ledger.unlocking, vec![c(4, 40), c(5, 100 / 2)]);
-	assert_eq!(ledger.total, 90);
-	assert_eq!(LedgerSlashPerEra::get().0, 0);
-	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 100 / 2), (6, 0), (7, 0)]));
-
-	// Given
-	// iteration order------------------NA---------2----------0----------1----
-	ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)];
-	ledger.active = 100;
-	ledger.total = 5 * 100;
-	// When
-	assert_eq!(
-		ledger.slash(
-			351, // active + era 6 + era 7 + era 5 / 2 + 1
-			50,  // min balance - everything slashed below 50 will get dusted
-			3    /* slash era 3+3 first, so the affected parts are era 6, era 7 and
-			      * ledge.active. This will cause the affected to go to zero, and then we will
-			      * start slashing older chunks */
-		),
-		400
-	);
-	// Then
-	assert_eq!(ledger.active, 0);
-	assert_eq!(ledger.unlocking, vec![c(4, 100)]);
-	assert_eq!(ledger.total, 100);
-	assert_eq!(LedgerSlashPerEra::get().0, 0);
-	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 0), (6, 0), (7, 0)]));
-
-	// Tests for saturating arithmetic
-
-	// Given
-	let slash = u64::MAX as Balance * 2;
-	// The value of the other parts of ledger that will get slashed
-	let value = slash - (10 * 4);
-
-	ledger.active = 10;
-	ledger.unlocking = bounded_vec![c(4, 10), c(5, 10), c(6, 10), c(7, value)];
-	ledger.total = value + 40;
-	// When
-	let slash_amount = ledger.slash(slash, 0, 0);
-	assert_eq_error_rate!(slash_amount, slash, 5);
-	// Then
-	assert_eq!(ledger.active, 0); // slash of 9
-	assert_eq!(ledger.unlocking, vec![]);
-	assert_eq!(ledger.total, 0);
-	assert_eq!(LedgerSlashPerEra::get().0, 0);
-	assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0), (6, 0), (7, 0)]));
-
-	// Given
-	use sp_runtime::PerThing as _;
-	let slash = u64::MAX as Balance * 2;
-	let value = u64::MAX as Balance * 2;
-	let unit = 100;
-	// slash * value that will saturate
-	assert!(slash.checked_mul(value).is_none());
-	// but slash * unit won't.
-	assert!(slash.checked_mul(unit).is_some());
-	ledger.unlocking = bounded_vec![c(4, unit), c(5, value), c(6, unit), c(7, unit)];
-	//--------------------------------------note value^^^
-	ledger.active = unit;
-	ledger.total = unit * 4 + value;
-	// When
-	assert_eq!(ledger.slash(slash, 0, 0), slash);
-	// Then
-	// The amount slashed out of `unit`
-	let affected_balance = value + unit * 4;
-	let ratio =
-		Perquintill::from_rational_with_rounding(slash, affected_balance, Rounding::Up).unwrap();
-	// `unit` after the slash is applied
-	let unit_slashed = {
-		let unit_slash = ratio.mul_ceil(unit);
-		unit - unit_slash
-	};
-	let value_slashed = {
-		let value_slash = ratio.mul_ceil(value);
-		value - value_slash
-	};
-	assert_eq!(ledger.active, unit_slashed);
-	assert_eq!(ledger.unlocking, vec![c(5, value_slashed), c(7, 32)]);
-	assert_eq!(ledger.total, value_slashed + 32);
-	assert_eq!(LedgerSlashPerEra::get().0, 0);
-	assert_eq!(
-		LedgerSlashPerEra::get().1,
-		BTreeMap::from([(4, 0), (5, value_slashed), (6, 0), (7, 32)])
-	);
+		// Given
+		ledger.unlocking = bounded_vec![c(4, 10), c(5, 10)];
+		ledger.total = 2 * 10;
+		ledger.active = 0;
+		// When all the chunks overlap with the slash eras
+		assert_eq!(ledger.slash(20, 0, 0), 20);
+		// Then
+		assert_eq!(ledger.unlocking, vec![]);
+		assert_eq!(ledger.total, 0);
+		assert_eq!(LedgerSlashPerEra::get().0, 0);
+		assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0)]));
+
+		// Given
+		ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)];
+		ledger.total = 4 * 100;
+		ledger.active = 0;
+		// When the first 2 chunks don't overlap with the affected range of unlock eras.
+		assert_eq!(ledger.slash(140, 0, 3), 140);
+		// Then
+		assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 30), c(7, 30)]);
+		assert_eq!(ledger.total, 4 * 100 - 140);
+		assert_eq!(LedgerSlashPerEra::get().0, 0);
+		assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 30), (7, 30)]));
+
+		// Given
+		ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)];
+		ledger.total = 4 * 100;
+		ledger.active = 0;
+		// When the first 2 chunks don't overlap with the affected range of unlock eras.
+		assert_eq!(ledger.slash(15, 0, 3), 15);
+		// Then
+		assert_eq!(ledger.unlocking, vec![c(4, 100), c(5, 100), c(6, 100 - 8), c(7, 100 - 7)]);
+		assert_eq!(ledger.total, 4 * 100 - 15);
+		assert_eq!(LedgerSlashPerEra::get().0, 0);
+		assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(6, 92), (7, 93)]));
+
+		// Given
+		ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
+		ledger.active = 500;
+		// 900
+		ledger.total = 40 + 10 + 100 + 250 + 500;
+		// When we have a partial slash that touches all chunks
+		assert_eq!(ledger.slash(900 / 2, 0, 0), 450);
+		// Then
+		assert_eq!(ledger.active, 500 / 2);
+		assert_eq!(
+			ledger.unlocking,
+			vec![c(4, 40 / 2), c(5, 100 / 2), c(6, 10 / 2), c(7, 250 / 2)]
+		);
+		assert_eq!(ledger.total, 900 / 2);
+		assert_eq!(LedgerSlashPerEra::get().0, 500 / 2);
+		assert_eq!(
+			LedgerSlashPerEra::get().1,
+			BTreeMap::from([(4, 40 / 2), (5, 100 / 2), (6, 10 / 2), (7, 250 / 2)])
+		);
+
+		// slash 1/4th with not chunk.
+		ledger.unlocking = bounded_vec![];
+		ledger.active = 500;
+		ledger.total = 500;
+		// When we have a partial slash that touches all chunks
+		assert_eq!(ledger.slash(500 / 4, 0, 0), 500 / 4);
+		// Then
+		assert_eq!(ledger.active, 3 * 500 / 4);
+		assert_eq!(ledger.unlocking, vec![]);
+		assert_eq!(ledger.total, ledger.active);
+		assert_eq!(LedgerSlashPerEra::get().0, 3 * 500 / 4);
+		assert_eq!(LedgerSlashPerEra::get().1, Default::default());
+
+		// Given we have the same as above,
+		ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
+		ledger.active = 500;
+		ledger.total = 40 + 10 + 100 + 250 + 500; // 900
+		assert_eq!(ledger.total, 900);
+		// When we have a higher min balance
+		assert_eq!(
+			ledger.slash(
+				900 / 2,
+				25, /* min balance - chunks with era 0 & 2 will be slashed to <=25, causing it
+				     * to get swept */
+				0
+			),
+			450
+		);
+		assert_eq!(ledger.active, 500 / 2);
+		// the last chunk was not slashed 50% like all the rest, because some other earlier chunks
+		// got dusted.
+		assert_eq!(ledger.unlocking, vec![c(5, 100 / 2), c(7, 150)]);
+		assert_eq!(ledger.total, 900 / 2);
+		assert_eq!(LedgerSlashPerEra::get().0, 500 / 2);
+		assert_eq!(
+			LedgerSlashPerEra::get().1,
+			BTreeMap::from([(4, 0), (5, 100 / 2), (6, 0), (7, 150)])
+		);
+
+		// Given
+		// slash order --------------------NA--------2----------0----------1----
+		ledger.unlocking = bounded_vec![c(4, 40), c(5, 100), c(6, 10), c(7, 250)];
+		ledger.active = 500;
+		ledger.total = 40 + 10 + 100 + 250 + 500; // 900
+		assert_eq!(
+			ledger.slash(
+				500 + 10 + 250 + 100 / 2, // active + era 6 + era 7 + era 5 / 2
+				0,
+				3 /* slash era 6 first, so the affected parts are era 6, era 7 and
+				   * ledge.active. This will cause the affected to go to zero, and then we will
+				   * start slashing older chunks */
+			),
+			500 + 250 + 10 + 100 / 2
+		);
+		// Then
+		assert_eq!(ledger.active, 0);
+		assert_eq!(ledger.unlocking, vec![c(4, 40), c(5, 100 / 2)]);
+		assert_eq!(ledger.total, 90);
+		assert_eq!(LedgerSlashPerEra::get().0, 0);
+		assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 100 / 2), (6, 0), (7, 0)]));
+
+		// Given
+		// iteration order------------------NA---------2----------0----------1----
+		ledger.unlocking = bounded_vec![c(4, 100), c(5, 100), c(6, 100), c(7, 100)];
+		ledger.active = 100;
+		ledger.total = 5 * 100;
+		// When
+		assert_eq!(
+			ledger.slash(
+				351, // active + era 6 + era 7 + era 5 / 2 + 1
+				50,  // min balance - everything slashed below 50 will get dusted
+				3    /* slash era 3+3 first, so the affected parts are era 6, era 7 and
+				      * ledge.active. This will cause the affected to go to zero, and then we
+				      * will start slashing older chunks */
+			),
+			400
+		);
+		// Then
+		assert_eq!(ledger.active, 0);
+		assert_eq!(ledger.unlocking, vec![c(4, 100)]);
+		assert_eq!(ledger.total, 100);
+		assert_eq!(LedgerSlashPerEra::get().0, 0);
+		assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(5, 0), (6, 0), (7, 0)]));
+
+		// Tests for saturating arithmetic
+
+		// Given
+		let slash = u64::MAX as Balance * 2;
+		// The value of the other parts of ledger that will get slashed
+		let value = slash - (10 * 4);
+
+		ledger.active = 10;
+		ledger.unlocking = bounded_vec![c(4, 10), c(5, 10), c(6, 10), c(7, value)];
+		ledger.total = value + 40;
+		// When
+		let slash_amount = ledger.slash(slash, 0, 0);
+		assert_eq_error_rate!(slash_amount, slash, 5);
+		// Then
+		assert_eq!(ledger.active, 0); // slash of 9
+		assert_eq!(ledger.unlocking, vec![]);
+		assert_eq!(ledger.total, 0);
+		assert_eq!(LedgerSlashPerEra::get().0, 0);
+		assert_eq!(LedgerSlashPerEra::get().1, BTreeMap::from([(4, 0), (5, 0), (6, 0), (7, 0)]));
+
+		// Given
+		use sp_runtime::PerThing as _;
+		let slash = u64::MAX as Balance * 2;
+		let value = u64::MAX as Balance * 2;
+		let unit = 100;
+		// slash * value that will saturate
+		assert!(slash.checked_mul(value).is_none());
+		// but slash * unit won't.
+		assert!(slash.checked_mul(unit).is_some());
+		ledger.unlocking = bounded_vec![c(4, unit), c(5, value), c(6, unit), c(7, unit)];
+		//--------------------------------------note value^^^
+		ledger.active = unit;
+		ledger.total = unit * 4 + value;
+		// When
+		assert_eq!(ledger.slash(slash, 0, 0), slash);
+		// Then
+		// The amount slashed out of `unit`
+		let affected_balance = value + unit * 4;
+		let ratio = Perquintill::from_rational_with_rounding(slash, affected_balance, Rounding::Up)
+			.unwrap();
+		// `unit` after the slash is applied
+		let unit_slashed = {
+			let unit_slash = ratio.mul_ceil(unit);
+			unit - unit_slash
+		};
+		let value_slashed = {
+			let value_slash = ratio.mul_ceil(value);
+			value - value_slash
+		};
+		assert_eq!(ledger.active, unit_slashed);
+		assert_eq!(ledger.unlocking, vec![c(5, value_slashed), c(7, 32)]);
+		assert_eq!(ledger.total, value_slashed + 32);
+		assert_eq!(LedgerSlashPerEra::get().0, 0);
+		assert_eq!(
+			LedgerSlashPerEra::get().1,
+			BTreeMap::from([(4, 0), (5, value_slashed), (6, 0), (7, 32)])
+		);
+	});
 }
 
 #[test]
@@ -7126,7 +7203,7 @@ mod staking_unchecked {
 	fn virtual_bond_does_not_lock() {
 		ExtBuilder::default().build_and_execute(|| {
 			mock::start_active_era(1);
-			assert_eq!(asset::stakeable_balance::<Test>(&10), 1);
+			assert_eq!(asset::total_balance::<Test>(&10), 1);
 			// 10 can bond more than its balance amount since we do not require lock for virtual
 			// bonding.
 			assert_ok!(<Staking as StakingUnchecked>::virtual_bond(&10, 100, &15));
@@ -7265,7 +7342,7 @@ mod staking_unchecked {
 			assert_eq!(asset::staked::<Test>(&200), 1000);
 
 			// migrate them to virtual staker
-			<Staking as StakingUnchecked>::migrate_to_virtual_staker(&200);
+			assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&200));
 			// payee needs to be updated to a non-stash account.
 			assert_ok!(<Staking as StakingInterface>::set_payee(&200, &201));
 
@@ -7292,7 +7369,7 @@ mod staking_unchecked {
 				// 101 is a nominator for 11
 				assert_eq!(initial_exposure.others.first().unwrap().who, 101);
 				// make 101 a virtual nominator
-				<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101);
+				assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101));
 				// set payee different to self.
 				assert_ok!(<Staking as StakingInterface>::set_payee(&101, &102));
 
@@ -7367,7 +7444,7 @@ mod staking_unchecked {
 				// 101 is a nominator for 11
 				assert_eq!(initial_exposure.others.first().unwrap().who, 101);
 				// make 101 a virtual nominator
-				<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101);
+				assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101));
 				// set payee different to self.
 				assert_ok!(<Staking as StakingInterface>::set_payee(&101, &102));
 
@@ -7423,7 +7500,7 @@ mod staking_unchecked {
 			// 333 is corrupted
 			assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted);
 			// migrate to virtual staker.
-			<Staking as StakingUnchecked>::migrate_to_virtual_staker(&333);
+			assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&333));
 
 			// recover the ledger won't work for virtual staker
 			assert_noop!(
@@ -8034,8 +8111,7 @@ mod ledger_recovery {
 			// side effects on 333 - ledger, bonded, payee, lock should be intact.
 			assert_eq!(asset::staked::<Test>(&333), lock_333_before); // OK
 			assert_eq!(Bonded::<Test>::get(&333), Some(444)); // OK
-			assert!(Payee::<Test>::get(&333).is_some()); // OK
-
+			assert!(Payee::<Test>::get(&333).is_some());
 			// however, ledger associated with its controller was killed.
 			assert!(Ledger::<Test>::get(&444).is_none()); // NOK
 
@@ -9081,3 +9157,249 @@ mod getters {
 		});
 	}
 }
+
+mod hold_migration {
+	use super::*;
+	use sp_staking::{Stake, StakingInterface};
+
+	#[test]
+	fn ledger_update_creates_hold() {
+		ExtBuilder::default().has_stakers(true).build_and_execute(|| {
+			// GIVEN alice who is a nominator with old currency
+			let alice = 300;
+			bond_nominator(alice, 1000, vec![11]);
+			assert_eq!(asset::staked::<Test>(&alice), 1000);
+			assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0);
+			// migrate alice currency to legacy locks
+			testing_utils::migrate_to_old_currency::<Test>(alice);
+			// no more holds
+			assert_eq!(asset::staked::<Test>(&alice), 0);
+			assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000);
+			assert_eq!(
+				<Staking as StakingInterface>::stake(&alice),
+				Ok(Stake { total: 1000, active: 1000 })
+			);
+
+			// any ledger mutation should create a hold
+			hypothetically!({
+				// give some extra balance to alice.
+				let _ = asset::mint_into_existing::<Test>(&alice, 100);
+
+				// WHEN new fund is bonded to ledger.
+				assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(alice), 100));
+
+				// THEN new hold is created
+				assert_eq!(asset::staked::<Test>(&alice), 1000 + 100);
+				assert_eq!(
+					<Staking as StakingInterface>::stake(&alice),
+					Ok(Stake { total: 1100, active: 1100 })
+				);
+
+				// old locked balance is untouched
+				assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000);
+			});
+
+			hypothetically!({
+				// WHEN new fund is unbonded from ledger.
+				assert_ok!(Staking::unbond(RuntimeOrigin::signed(alice), 100));
+
+				// THEN hold is updated.
+				assert_eq!(asset::staked::<Test>(&alice), 1000);
+				assert_eq!(
+					<Staking as StakingInterface>::stake(&alice),
+					Ok(Stake { total: 1000, active: 900 })
+				);
+
+				// old locked balance is untouched
+				assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000);
+			});
+
+			// WHEN alice currency is migrated.
+			assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice));
+
+			// THEN hold is updated.
+			assert_eq!(asset::staked::<Test>(&alice), 1000);
+			assert_eq!(
+				<Staking as StakingInterface>::stake(&alice),
+				Ok(Stake { total: 1000, active: 1000 })
+			);
+
+			// ensure cannot migrate again.
+			assert_noop!(
+				Staking::migrate_currency(RuntimeOrigin::signed(1), alice),
+				Error::<Test>::AlreadyMigrated
+			);
+
+			// locked balance is removed
+			assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0);
+		});
+	}
+
+	#[test]
+	fn migrate_removes_old_lock() {
+		ExtBuilder::default().has_stakers(true).build_and_execute(|| {
+			// GIVEN alice who is a nominator with old currency
+			let alice = 300;
+			bond_nominator(alice, 1000, vec![11]);
+			testing_utils::migrate_to_old_currency::<Test>(alice);
+			assert_eq!(asset::staked::<Test>(&alice), 0);
+			assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000);
+			let pre_migrate_consumer = System::consumers(&alice);
+			System::reset_events();
+
+			// WHEN alice currency is migrated.
+			assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice));
+
+			// THEN
+			// the extra consumer from old code is removed.
+			assert_eq!(System::consumers(&alice), pre_migrate_consumer - 1);
+			// ensure no lock
+			assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0);
+			// ensure stake and hold are same.
+			assert_eq!(
+				<Staking as StakingInterface>::stake(&alice),
+				Ok(Stake { total: 1000, active: 1000 })
+			);
+			assert_eq!(asset::staked::<Test>(&alice), 1000);
+			// ensure events are emitted.
+			assert_eq!(
+				staking_events_since_last_call(),
+				vec![Event::CurrencyMigrated { stash: alice, force_withdraw: 0 }]
+			);
+
+			// ensure cannot migrate again.
+			assert_noop!(
+				Staking::migrate_currency(RuntimeOrigin::signed(1), alice),
+				Error::<Test>::AlreadyMigrated
+			);
+		});
+	}
+	#[test]
+	fn cannot_hold_all_stake() {
+		// When there is not enough funds to hold all stake, part of the stake if force withdrawn.
+		// At end of the migration, the stake and hold should be same.
+		ExtBuilder::default().has_stakers(true).build_and_execute(|| {
+			// GIVEN alice who is a nominator with old currency.
+			let alice = 300;
+			let stake = 1000;
+			bond_nominator(alice, stake, vec![11]);
+			testing_utils::migrate_to_old_currency::<Test>(alice);
+			assert_eq!(asset::staked::<Test>(&alice), 0);
+			assert_eq!(Balances::balance_locked(STAKING_ID, &alice), stake);
+			// ledger has 1000 staked.
+			assert_eq!(
+				<Staking as StakingInterface>::stake(&alice),
+				Ok(Stake { total: stake, active: stake })
+			);
+
+			// Get rid of the extra ED to emulate all their balance including ED is staked.
+			assert_ok!(Balances::transfer_allow_death(
+				RuntimeOrigin::signed(alice),
+				10,
+				ExistentialDeposit::get()
+			));
+
+			let expected_force_withdraw = ExistentialDeposit::get();
+
+			// ledger mutation would fail in this case before migration because of failing hold.
+			assert_noop!(
+				Staking::unbond(RuntimeOrigin::signed(alice), 100),
+				Error::<Test>::NotEnoughFunds
+			);
+
+			// clear events
+			System::reset_events();
+
+			// WHEN alice currency is migrated.
+			assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice));
+
+			// THEN
+			let expected_hold = stake - expected_force_withdraw;
+			// ensure no lock
+			assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0);
+			// ensure stake and hold are same.
+			assert_eq!(
+				<Staking as StakingInterface>::stake(&alice),
+				Ok(Stake { total: expected_hold, active: expected_hold })
+			);
+			assert_eq!(asset::staked::<Test>(&alice), expected_hold);
+			// ensure events are emitted.
+			assert_eq!(
+				staking_events_since_last_call(),
+				vec![Event::CurrencyMigrated {
+					stash: alice,
+					force_withdraw: expected_force_withdraw
+				}]
+			);
+
+			// ensure cannot migrate again.
+			assert_noop!(
+				Staking::migrate_currency(RuntimeOrigin::signed(1), alice),
+				Error::<Test>::AlreadyMigrated
+			);
+
+			// unbond works after migration.
+			assert_ok!(Staking::unbond(RuntimeOrigin::signed(alice), 100));
+		});
+	}
+
+	#[test]
+	fn virtual_staker_consumer_provider_dec() {
+		// Ensure virtual stakers consumer and provider count is decremented.
+		ExtBuilder::default().has_stakers(true).build_and_execute(|| {
+			// 200 virtual bonds
+			bond_virtual_nominator(200, 201, 500, vec![11, 21]);
+
+			// previously the virtual nominator had a provider inc by the delegation system as
+			// well as a consumer by this pallet.
+			System::inc_providers(&200);
+			System::inc_consumers(&200).expect("has provider, can consume");
+
+			hypothetically!({
+				// migrate 200
+				assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), 200));
+
+				// ensure account does not exist in system anymore.
+				assert_eq!(System::consumers(&200), 0);
+				assert_eq!(System::providers(&200), 0);
+				assert!(!System::account_exists(&200));
+
+				// ensure cannot migrate again.
+				assert_noop!(
+					Staking::migrate_currency(RuntimeOrigin::signed(1), 200),
+					Error::<Test>::AlreadyMigrated
+				);
+			});
+
+			hypothetically!({
+				// 200 has an erroneously extra provider
+				System::inc_providers(&200);
+
+				// causes migration to fail.
+				assert_noop!(
+					Staking::migrate_currency(RuntimeOrigin::signed(1), 200),
+					Error::<Test>::BadState
+				);
+			});
+
+			// 200 is funded for more than ED by a random account.
+			assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(999), 200, 10));
+
+			// it has an extra provider now.
+			assert_eq!(System::providers(&200), 2);
+
+			// migrate 200
+			assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), 200));
+
+			// 1 provider is left, consumers is 0.
+			assert_eq!(System::providers(&200), 1);
+			assert_eq!(System::consumers(&200), 0);
+
+			// ensure cannot migrate again.
+			assert_noop!(
+				Staking::migrate_currency(RuntimeOrigin::signed(1), 200),
+				Error::<Test>::AlreadyMigrated
+			);
+		});
+	}
+}
diff --git a/substrate/frame/staking/src/weights.rs b/substrate/frame/staking/src/weights.rs
index 56f561679cfc78aee1cca7f1a5b759e17b2f93d9..02ccdacb01c4295f29e4735cda32704e2438f828 100644
--- a/substrate/frame/staking/src/weights.rs
+++ b/substrate/frame/staking/src/weights.rs
@@ -18,27 +18,25 @@
 //! Autogenerated weights for `pallet_staking`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
-//! DATE: 2024-04-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2024-09-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
+//! HOSTNAME: `runner-obbyq9g6-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
 //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
 
 // Executed Command:
-// ./target/production/substrate-node
+// target/production/substrate-node
 // benchmark
 // pallet
-// --chain=dev
 // --steps=50
 // --repeat=20
-// --pallet=pallet_staking
-// --no-storage-info
-// --no-median-slopes
-// --no-min-squares
 // --extrinsic=*
 // --wasm-execution=compiled
 // --heap-pages=4096
-// --output=./substrate/frame/staking/src/weights.rs
+// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json
+// --pallet=pallet_staking
+// --chain=dev
 // --header=./substrate/HEADER-APACHE2
+// --output=./substrate/frame/staking/src/weights.rs
 // --template=./substrate/.maintain/frame-weight-template.hbs
 
 #![cfg_attr(rustfmt, rustfmt_skip)]
@@ -83,6 +81,7 @@ pub trait WeightInfo {
 	fn force_apply_min_commission() -> Weight;
 	fn set_min_commission() -> Weight;
 	fn restore_ledger() -> Weight;
+	fn migrate_currency() -> Weight;
 }
 
 /// Weights for `pallet_staking` using the Substrate node and recommended hardware.
@@ -92,18 +91,18 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Payee` (r:0 w:1)
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn bond() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1042`
-		//  Estimated: `4764`
-		// Minimum execution time: 46_504_000 picoseconds.
-		Weight::from_parts(48_459_000, 4764)
+		//  Measured:  `1068`
+		//  Estimated: `4556`
+		// Minimum execution time: 71_854_000 picoseconds.
+		Weight::from_parts(73_408_000, 4556)
 			.saturating_add(T::DbWeight::get().reads(4_u64))
 			.saturating_add(T::DbWeight::get().writes(4_u64))
 	}
@@ -111,20 +110,20 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
 	/// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`)
 	fn bond_extra() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1990`
+		//  Measured:  `2049`
 		//  Estimated: `8877`
-		// Minimum execution time: 90_475_000 picoseconds.
-		Weight::from_parts(93_619_000, 8877)
+		// Minimum execution time: 127_442_000 picoseconds.
+		Weight::from_parts(130_845_000, 8877)
 			.saturating_add(T::DbWeight::get().reads(9_u64))
 			.saturating_add(T::DbWeight::get().writes(7_u64))
 	}
@@ -138,22 +137,22 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::CurrentEra` (r:1 w:0)
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
 	/// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`)
 	fn unbond() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2195`
+		//  Measured:  `2151`
 		//  Estimated: `8877`
-		// Minimum execution time: 99_335_000 picoseconds.
-		Weight::from_parts(101_440_000, 8877)
+		// Minimum execution time: 105_259_000 picoseconds.
+		Weight::from_parts(107_112_000, 8877)
 			.saturating_add(T::DbWeight::get().reads(12_u64))
-			.saturating_add(T::DbWeight::get().writes(7_u64))
+			.saturating_add(T::DbWeight::get().writes(6_u64))
 	}
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
@@ -161,21 +160,21 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::CurrentEra` (r:1 w:0)
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0)
 	/// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
 	/// The range of component `s` is `[0, 100]`.
 	fn withdraw_unbonded_update(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1297`
-		//  Estimated: `4764`
-		// Minimum execution time: 50_067_000 picoseconds.
-		Weight::from_parts(52_396_327, 4764)
-			// Standard Error: 1_419
-			.saturating_add(Weight::from_parts(51_406, 0).saturating_mul(s.into()))
+		//  Measured:  `1393`
+		//  Estimated: `4556`
+		// Minimum execution time: 77_158_000 picoseconds.
+		Weight::from_parts(79_140_122, 4556)
+			// Standard Error: 1_688
+			.saturating_add(Weight::from_parts(62_663, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(6_u64))
 			.saturating_add(T::DbWeight::get().writes(2_u64))
 	}
@@ -187,10 +186,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::SlashingSpans` (r:1 w:1)
 	/// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Nominators` (r:1 w:1)
@@ -210,14 +209,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `s` is `[0, 100]`.
 	fn withdraw_unbonded_kill(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2196 + s * (4 ±0)`
+		//  Measured:  `2255 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 92_931_000 picoseconds.
-		Weight::from_parts(101_398_156, 6248)
-			// Standard Error: 4_180
-			.saturating_add(Weight::from_parts(1_377_850, 0).saturating_mul(s.into()))
+		// Minimum execution time: 125_396_000 picoseconds.
+		Weight::from_parts(134_915_543, 6248)
+			// Standard Error: 3_660
+			.saturating_add(Weight::from_parts(1_324_736, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(13_u64))
-			.saturating_add(T::DbWeight::get().writes(11_u64))
+			.saturating_add(T::DbWeight::get().writes(12_u64))
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -245,10 +244,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::CounterForValidators` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn validate() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1372`
+		//  Measured:  `1438`
 		//  Estimated: `4556`
-		// Minimum execution time: 56_291_000 picoseconds.
-		Weight::from_parts(58_372_000, 4556)
+		// Minimum execution time: 68_826_000 picoseconds.
+		Weight::from_parts(71_261_000, 4556)
 			.saturating_add(T::DbWeight::get().reads(11_u64))
 			.saturating_add(T::DbWeight::get().writes(5_u64))
 	}
@@ -261,12 +260,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `k` is `[1, 128]`.
 	fn kick(k: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1815 + k * (572 ±0)`
+		//  Measured:  `1848 + k * (572 ±0)`
 		//  Estimated: `4556 + k * (3033 ±0)`
-		// Minimum execution time: 36_218_000 picoseconds.
-		Weight::from_parts(38_811_308, 4556)
-			// Standard Error: 8_352
-			.saturating_add(Weight::from_parts(6_527_398, 0).saturating_mul(k.into()))
+		// Minimum execution time: 46_082_000 picoseconds.
+		Weight::from_parts(49_541_374, 4556)
+			// Standard Error: 7_218
+			.saturating_add(Weight::from_parts(7_281_079, 0).saturating_mul(k.into()))
 			.saturating_add(T::DbWeight::get().reads(2_u64))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into())))
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into())))
@@ -297,12 +296,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `n` is `[1, 16]`.
 	fn nominate(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1866 + n * (102 ±0)`
+		//  Measured:  `1932 + n * (102 ±0)`
 		//  Estimated: `6248 + n * (2520 ±0)`
-		// Minimum execution time: 68_607_000 picoseconds.
-		Weight::from_parts(66_831_185, 6248)
-			// Standard Error: 14_014
-			.saturating_add(Weight::from_parts(4_031_635, 0).saturating_mul(n.into()))
+		// Minimum execution time: 83_854_000 picoseconds.
+		Weight::from_parts(81_387_241, 6248)
+			// Standard Error: 16_811
+			.saturating_add(Weight::from_parts(4_900_554, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(12_u64))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into())))
 			.saturating_add(T::DbWeight::get().writes(6_u64))
@@ -326,10 +325,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn chill() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1816`
+		//  Measured:  `1882`
 		//  Estimated: `6248`
-		// Minimum execution time: 60_088_000 picoseconds.
-		Weight::from_parts(62_471_000, 6248)
+		// Minimum execution time: 73_939_000 picoseconds.
+		Weight::from_parts(75_639_000, 6248)
 			.saturating_add(T::DbWeight::get().reads(9_u64))
 			.saturating_add(T::DbWeight::get().writes(6_u64))
 	}
@@ -341,10 +340,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn set_payee() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `902`
+		//  Measured:  `935`
 		//  Estimated: `4556`
-		// Minimum execution time: 19_777_000 picoseconds.
-		Weight::from_parts(20_690_000, 4556)
+		// Minimum execution time: 24_592_000 picoseconds.
+		Weight::from_parts(25_092_000, 4556)
 			.saturating_add(T::DbWeight::get().reads(2_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
@@ -356,10 +355,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn update_payee() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `969`
+		//  Measured:  `1002`
 		//  Estimated: `4556`
-		// Minimum execution time: 23_705_000 picoseconds.
-		Weight::from_parts(24_409_000, 4556)
+		// Minimum execution time: 29_735_000 picoseconds.
+		Weight::from_parts(30_546_000, 4556)
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
@@ -369,10 +368,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	fn set_controller() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `902`
+		//  Measured:  `935`
 		//  Estimated: `8122`
-		// Minimum execution time: 23_479_000 picoseconds.
-		Weight::from_parts(24_502_000, 8122)
+		// Minimum execution time: 28_728_000 picoseconds.
+		Weight::from_parts(29_709_000, 8122)
 			.saturating_add(T::DbWeight::get().reads(3_u64))
 			.saturating_add(T::DbWeight::get().writes(3_u64))
 	}
@@ -382,8 +381,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_675_000 picoseconds.
-		Weight::from_parts(2_802_000, 0)
+		// Minimum execution time: 2_519_000 picoseconds.
+		Weight::from_parts(2_673_000, 0)
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::ForceEra` (r:0 w:1)
@@ -392,8 +391,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 7_067_000 picoseconds.
-		Weight::from_parts(7_413_000, 0)
+		// Minimum execution time: 8_050_000 picoseconds.
+		Weight::from_parts(8_268_000, 0)
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::ForceEra` (r:0 w:1)
@@ -402,8 +401,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 6_977_000 picoseconds.
-		Weight::from_parts(7_353_000, 0)
+		// Minimum execution time: 8_131_000 picoseconds.
+		Weight::from_parts(8_349_000, 0)
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::ForceEra` (r:0 w:1)
@@ -412,8 +411,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 7_071_000 picoseconds.
-		Weight::from_parts(7_463_000, 0)
+		// Minimum execution time: 8_104_000 picoseconds.
+		Weight::from_parts(8_317_000, 0)
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::Invulnerables` (r:0 w:1)
@@ -423,10 +422,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_833_000 picoseconds.
-		Weight::from_parts(3_328_130, 0)
-			// Standard Error: 30
-			.saturating_add(Weight::from_parts(10_058, 0).saturating_mul(v.into()))
+		// Minimum execution time: 2_669_000 picoseconds.
+		Weight::from_parts(3_013_436, 0)
+			// Standard Error: 31
+			.saturating_add(Weight::from_parts(10_704, 0).saturating_mul(v.into()))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::Ledger` (r:11800 w:11800)
@@ -438,12 +437,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `i` is `[0, 5900]`.
 	fn deprecate_controller_batch(i: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1746 + i * (229 ±0)`
+		//  Measured:  `1779 + i * (229 ±0)`
 		//  Estimated: `990 + i * (7132 ±0)`
-		// Minimum execution time: 5_300_000 picoseconds.
-		Weight::from_parts(5_437_000, 990)
-			// Standard Error: 66_261
-			.saturating_add(Weight::from_parts(30_172_457, 0).saturating_mul(i.into()))
+		// Minimum execution time: 5_101_000 picoseconds.
+		Weight::from_parts(5_368_000, 990)
+			// Standard Error: 75_180
+			.saturating_add(Weight::from_parts(33_781_643, 0).saturating_mul(i.into()))
 			.saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(i.into())))
 			.saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(i.into())))
 			.saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into()))
@@ -454,10 +453,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `System::Account` (r:1 w:1)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
@@ -479,14 +478,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `s` is `[0, 100]`.
 	fn force_unstake(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2196 + s * (4 ±0)`
+		//  Measured:  `2255 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 87_677_000 picoseconds.
-		Weight::from_parts(96_386_462, 6248)
-			// Standard Error: 3_717
-			.saturating_add(Weight::from_parts(1_370_585, 0).saturating_mul(s.into()))
+		// Minimum execution time: 119_955_000 picoseconds.
+		Weight::from_parts(128_392_032, 6248)
+			// Standard Error: 3_773
+			.saturating_add(Weight::from_parts(1_302_488, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(13_u64))
-			.saturating_add(T::DbWeight::get().writes(12_u64))
+			.saturating_add(T::DbWeight::get().writes(13_u64))
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -495,12 +494,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `s` is `[1, 1000]`.
 	fn cancel_deferred_slash(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `66672`
-		//  Estimated: `70137`
-		// Minimum execution time: 105_086_000 picoseconds.
-		Weight::from_parts(1_167_895_222, 70137)
-			// Standard Error: 77_022
-			.saturating_add(Weight::from_parts(6_487_305, 0).saturating_mul(s.into()))
+		//  Measured:  `66705`
+		//  Estimated: `70170`
+		// Minimum execution time: 139_290_000 picoseconds.
+		Weight::from_parts(959_667_494, 70170)
+			// Standard Error: 56_271
+			.saturating_add(Weight::from_parts(4_798_293, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(1_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
@@ -518,12 +517,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::ErasValidatorReward` (r:1 w:0)
 	/// Proof: `Staking::ErasValidatorReward` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:257 w:257)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:257 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
-	/// Storage: `System::Account` (r:257 w:257)
-	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:257 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:257 w:257)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::ErasStakersPaged` (r:1 w:0)
 	/// Proof: `Staking::ErasStakersPaged` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Staking::ErasRewardPoints` (r:1 w:0)
@@ -532,29 +529,31 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::ErasValidatorPrefs` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Payee` (r:257 w:0)
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:257 w:257)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// The range of component `n` is `[0, 256]`.
 	fn payout_stakers_alive_staked(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `33297 + n * (377 ±0)`
-		//  Estimated: `30944 + n * (3774 ±3)`
-		// Minimum execution time: 154_210_000 picoseconds.
-		Weight::from_parts(192_836_012, 30944)
-			// Standard Error: 40_441
-			.saturating_add(Weight::from_parts(47_646_642, 0).saturating_mul(n.into()))
+		//  Measured:  `33283 + n * (370 ±0)`
+		//  Estimated: `30958 + n * (3566 ±0)`
+		// Minimum execution time: 193_068_000 picoseconds.
+		Weight::from_parts(252_762_568, 30958)
+			// Standard Error: 22_743
+			.saturating_add(Weight::from_parts(81_185_306, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(14_u64))
 			.saturating_add(T::DbWeight::get().reads((6_u64).saturating_mul(n.into())))
 			.saturating_add(T::DbWeight::get().writes(4_u64))
 			.saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into())))
-			.saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into()))
+			.saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into()))
 	}
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:0)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
@@ -562,25 +561,25 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `l` is `[1, 32]`.
 	fn rebond(l: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1991 + l * (7 ±0)`
+		//  Measured:  `1947 + l * (7 ±0)`
 		//  Estimated: `8877`
-		// Minimum execution time: 88_337_000 picoseconds.
-		Weight::from_parts(91_391_254, 8877)
-			// Standard Error: 4_485
-			.saturating_add(Weight::from_parts(103_443, 0).saturating_mul(l.into()))
+		// Minimum execution time: 91_151_000 picoseconds.
+		Weight::from_parts(93_596_096, 8877)
+			// Standard Error: 5_313
+			.saturating_add(Weight::from_parts(124_684, 0).saturating_mul(l.into()))
 			.saturating_add(T::DbWeight::get().reads(9_u64))
-			.saturating_add(T::DbWeight::get().writes(7_u64))
+			.saturating_add(T::DbWeight::get().writes(6_u64))
 	}
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:1)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::SlashingSpans` (r:1 w:1)
 	/// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Nominators` (r:1 w:1)
@@ -600,14 +599,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `s` is `[1, 100]`.
 	fn reap_stash(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2196 + s * (4 ±0)`
+		//  Measured:  `2255 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 98_014_000 picoseconds.
-		Weight::from_parts(102_537_670, 6248)
-			// Standard Error: 3_324
-			.saturating_add(Weight::from_parts(1_353_142, 0).saturating_mul(s.into()))
+		// Minimum execution time: 133_214_000 picoseconds.
+		Weight::from_parts(137_290_527, 6248)
+			// Standard Error: 4_153
+			.saturating_add(Weight::from_parts(1_291_007, 0).saturating_mul(s.into()))
 			.saturating_add(T::DbWeight::get().reads(12_u64))
-			.saturating_add(T::DbWeight::get().writes(11_u64))
+			.saturating_add(T::DbWeight::get().writes(12_u64))
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -651,12 +650,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0 + n * (720 ±0) + v * (3598 ±0)`
 		//  Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)`
-		// Minimum execution time: 608_575_000 picoseconds.
-		Weight::from_parts(613_663_000, 512390)
-			// Standard Error: 2_286_521
-			.saturating_add(Weight::from_parts(72_108_001, 0).saturating_mul(v.into()))
-			// Standard Error: 227_839
-			.saturating_add(Weight::from_parts(20_314_085, 0).saturating_mul(n.into()))
+		// Minimum execution time: 692_301_000 picoseconds.
+		Weight::from_parts(708_732_000, 512390)
+			// Standard Error: 2_117_299
+			.saturating_add(Weight::from_parts(70_087_600, 0).saturating_mul(v.into()))
+			// Standard Error: 210_977
+			.saturating_add(Weight::from_parts(22_953_405, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(206_u64))
 			.saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into())))
 			.saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into())))
@@ -685,14 +684,14 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `n` is `[500, 1000]`.
 	fn get_npos_voters(v: u32, n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3175 + n * (911 ±0) + v * (395 ±0)`
+		//  Measured:  `3241 + n * (911 ±0) + v * (395 ±0)`
 		//  Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)`
-		// Minimum execution time: 37_173_756_000 picoseconds.
-		Weight::from_parts(37_488_937_000, 512390)
-			// Standard Error: 467_413
-			.saturating_add(Weight::from_parts(8_086_367, 0).saturating_mul(v.into()))
-			// Standard Error: 467_413
-			.saturating_add(Weight::from_parts(3_108_193, 0).saturating_mul(n.into()))
+		// Minimum execution time: 43_708_472_000 picoseconds.
+		Weight::from_parts(44_048_436_000, 512390)
+			// Standard Error: 493_244
+			.saturating_add(Weight::from_parts(6_697_278, 0).saturating_mul(v.into()))
+			// Standard Error: 493_244
+			.saturating_add(Weight::from_parts(4_559_779, 0).saturating_mul(n.into()))
 			.saturating_add(T::DbWeight::get().reads(201_u64))
 			.saturating_add(T::DbWeight::get().reads((5_u64).saturating_mul(v.into())))
 			.saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into())))
@@ -707,12 +706,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// The range of component `v` is `[500, 1000]`.
 	fn get_npos_targets(v: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `979 + v * (50 ±0)`
+		//  Measured:  `1012 + v * (50 ±0)`
 		//  Estimated: `3510 + v * (2520 ±0)`
-		// Minimum execution time: 2_641_258_000 picoseconds.
-		Weight::from_parts(382_882_595, 3510)
-			// Standard Error: 11_991
-			.saturating_add(Weight::from_parts(4_695_820, 0).saturating_mul(v.into()))
+		// Minimum execution time: 2_917_165_000 picoseconds.
+		Weight::from_parts(2_948_999_000, 3510)
+			// Standard Error: 33_372
+			.saturating_add(Weight::from_parts(2_126_909, 0).saturating_mul(v.into()))
 			.saturating_add(T::DbWeight::get().reads(2_u64))
 			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(v.into())))
 			.saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into()))
@@ -735,8 +734,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 5_753_000 picoseconds.
-		Weight::from_parts(6_529_000, 0)
+		// Minimum execution time: 4_748_000 picoseconds.
+		Weight::from_parts(5_052_000, 0)
 			.saturating_add(T::DbWeight::get().writes(7_u64))
 	}
 	/// Storage: `Staking::MinCommission` (r:0 w:1)
@@ -757,8 +756,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 5_212_000 picoseconds.
-		Weight::from_parts(5_451_000, 0)
+		// Minimum execution time: 4_316_000 picoseconds.
+		Weight::from_parts(4_526_000, 0)
 			.saturating_add(T::DbWeight::get().writes(7_u64))
 	}
 	/// Storage: `Staking::Bonded` (r:1 w:0)
@@ -785,10 +784,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn chill_other() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1939`
+		//  Measured:  `2005`
 		//  Estimated: `6248`
-		// Minimum execution time: 73_000_000 picoseconds.
-		Weight::from_parts(75_184_000, 6248)
+		// Minimum execution time: 87_374_000 picoseconds.
+		Weight::from_parts(89_848_000, 6248)
 			.saturating_add(T::DbWeight::get().reads(12_u64))
 			.saturating_add(T::DbWeight::get().writes(6_u64))
 	}
@@ -798,10 +797,10 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	fn force_apply_min_commission() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `691`
+		//  Measured:  `724`
 		//  Estimated: `3510`
-		// Minimum execution time: 13_056_000 picoseconds.
-		Weight::from_parts(13_517_000, 3510)
+		// Minimum execution time: 15_529_000 picoseconds.
+		Weight::from_parts(16_094_000, 3510)
 			.saturating_add(T::DbWeight::get().reads(2_u64))
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
@@ -811,28 +810,51 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 3_201_000 picoseconds.
-		Weight::from_parts(3_442_000, 0)
+		// Minimum execution time: 2_533_000 picoseconds.
+		Weight::from_parts(2_817_000, 0)
 			.saturating_add(T::DbWeight::get().writes(1_u64))
 	}
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `System::Account` (r:1 w:1)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:0)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Locks` (r:1 w:0)
+	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:1)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
+	fn restore_ledger() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1110`
+		//  Estimated: `4764`
+		// Minimum execution time: 50_105_000 picoseconds.
+		Weight::from_parts(50_966_000, 4764)
+			.saturating_add(T::DbWeight::get().reads(6_u64))
+			.saturating_add(T::DbWeight::get().writes(2_u64))
+	}
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::Bonded` (r:1 w:0)
+	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::Ledger` (r:1 w:0)
+	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Locks` (r:1 w:1)
+	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `Balances::Freezes` (r:1 w:0)
 	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
-	fn restore_ledger() -> Weight {
+	fn migrate_currency() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1047`
+		//  Measured:  `1246`
 		//  Estimated: `4764`
-		// Minimum execution time: 44_671_000 picoseconds.
-		Weight::from_parts(45_611_000, 4764)
-			.saturating_add(T::DbWeight::get().reads(5_u64))
-			.saturating_add(T::DbWeight::get().writes(4_u64))
+		// Minimum execution time: 94_054_000 picoseconds.
+		Weight::from_parts(96_272_000, 4764)
+			.saturating_add(T::DbWeight::get().reads(6_u64))
+			.saturating_add(T::DbWeight::get().writes(2_u64))
 	}
 }
 
@@ -842,18 +864,18 @@ impl WeightInfo for () {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Payee` (r:0 w:1)
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn bond() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1042`
-		//  Estimated: `4764`
-		// Minimum execution time: 46_504_000 picoseconds.
-		Weight::from_parts(48_459_000, 4764)
+		//  Measured:  `1068`
+		//  Estimated: `4556`
+		// Minimum execution time: 71_854_000 picoseconds.
+		Weight::from_parts(73_408_000, 4556)
 			.saturating_add(RocksDbWeight::get().reads(4_u64))
 			.saturating_add(RocksDbWeight::get().writes(4_u64))
 	}
@@ -861,20 +883,20 @@ impl WeightInfo for () {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
 	/// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`)
 	fn bond_extra() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1990`
+		//  Measured:  `2049`
 		//  Estimated: `8877`
-		// Minimum execution time: 90_475_000 picoseconds.
-		Weight::from_parts(93_619_000, 8877)
+		// Minimum execution time: 127_442_000 picoseconds.
+		Weight::from_parts(130_845_000, 8877)
 			.saturating_add(RocksDbWeight::get().reads(9_u64))
 			.saturating_add(RocksDbWeight::get().writes(7_u64))
 	}
@@ -888,22 +910,22 @@ impl WeightInfo for () {
 	/// Proof: `Staking::MinNominatorBond` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::CurrentEra` (r:1 w:0)
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
 	/// Proof: `VoterList::ListBags` (`max_values`: None, `max_size`: Some(82), added: 2557, mode: `MaxEncodedLen`)
 	fn unbond() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2195`
+		//  Measured:  `2151`
 		//  Estimated: `8877`
-		// Minimum execution time: 99_335_000 picoseconds.
-		Weight::from_parts(101_440_000, 8877)
+		// Minimum execution time: 105_259_000 picoseconds.
+		Weight::from_parts(107_112_000, 8877)
 			.saturating_add(RocksDbWeight::get().reads(12_u64))
-			.saturating_add(RocksDbWeight::get().writes(7_u64))
+			.saturating_add(RocksDbWeight::get().writes(6_u64))
 	}
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
@@ -911,21 +933,21 @@ impl WeightInfo for () {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::CurrentEra` (r:1 w:0)
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `NominationPools::ReversePoolIdLookup` (r:1 w:0)
 	/// Proof: `NominationPools::ReversePoolIdLookup` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
 	/// The range of component `s` is `[0, 100]`.
 	fn withdraw_unbonded_update(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1297`
-		//  Estimated: `4764`
-		// Minimum execution time: 50_067_000 picoseconds.
-		Weight::from_parts(52_396_327, 4764)
-			// Standard Error: 1_419
-			.saturating_add(Weight::from_parts(51_406, 0).saturating_mul(s.into()))
+		//  Measured:  `1393`
+		//  Estimated: `4556`
+		// Minimum execution time: 77_158_000 picoseconds.
+		Weight::from_parts(79_140_122, 4556)
+			// Standard Error: 1_688
+			.saturating_add(Weight::from_parts(62_663, 0).saturating_mul(s.into()))
 			.saturating_add(RocksDbWeight::get().reads(6_u64))
 			.saturating_add(RocksDbWeight::get().writes(2_u64))
 	}
@@ -937,10 +959,10 @@ impl WeightInfo for () {
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::SlashingSpans` (r:1 w:1)
 	/// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Nominators` (r:1 w:1)
@@ -960,14 +982,14 @@ impl WeightInfo for () {
 	/// The range of component `s` is `[0, 100]`.
 	fn withdraw_unbonded_kill(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2196 + s * (4 ±0)`
+		//  Measured:  `2255 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 92_931_000 picoseconds.
-		Weight::from_parts(101_398_156, 6248)
-			// Standard Error: 4_180
-			.saturating_add(Weight::from_parts(1_377_850, 0).saturating_mul(s.into()))
+		// Minimum execution time: 125_396_000 picoseconds.
+		Weight::from_parts(134_915_543, 6248)
+			// Standard Error: 3_660
+			.saturating_add(Weight::from_parts(1_324_736, 0).saturating_mul(s.into()))
 			.saturating_add(RocksDbWeight::get().reads(13_u64))
-			.saturating_add(RocksDbWeight::get().writes(11_u64))
+			.saturating_add(RocksDbWeight::get().writes(12_u64))
 			.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -995,10 +1017,10 @@ impl WeightInfo for () {
 	/// Proof: `Staking::CounterForValidators` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn validate() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1372`
+		//  Measured:  `1438`
 		//  Estimated: `4556`
-		// Minimum execution time: 56_291_000 picoseconds.
-		Weight::from_parts(58_372_000, 4556)
+		// Minimum execution time: 68_826_000 picoseconds.
+		Weight::from_parts(71_261_000, 4556)
 			.saturating_add(RocksDbWeight::get().reads(11_u64))
 			.saturating_add(RocksDbWeight::get().writes(5_u64))
 	}
@@ -1011,12 +1033,12 @@ impl WeightInfo for () {
 	/// The range of component `k` is `[1, 128]`.
 	fn kick(k: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1815 + k * (572 ±0)`
+		//  Measured:  `1848 + k * (572 ±0)`
 		//  Estimated: `4556 + k * (3033 ±0)`
-		// Minimum execution time: 36_218_000 picoseconds.
-		Weight::from_parts(38_811_308, 4556)
-			// Standard Error: 8_352
-			.saturating_add(Weight::from_parts(6_527_398, 0).saturating_mul(k.into()))
+		// Minimum execution time: 46_082_000 picoseconds.
+		Weight::from_parts(49_541_374, 4556)
+			// Standard Error: 7_218
+			.saturating_add(Weight::from_parts(7_281_079, 0).saturating_mul(k.into()))
 			.saturating_add(RocksDbWeight::get().reads(2_u64))
 			.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into())))
 			.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into())))
@@ -1047,12 +1069,12 @@ impl WeightInfo for () {
 	/// The range of component `n` is `[1, 16]`.
 	fn nominate(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1866 + n * (102 ±0)`
+		//  Measured:  `1932 + n * (102 ±0)`
 		//  Estimated: `6248 + n * (2520 ±0)`
-		// Minimum execution time: 68_607_000 picoseconds.
-		Weight::from_parts(66_831_185, 6248)
-			// Standard Error: 14_014
-			.saturating_add(Weight::from_parts(4_031_635, 0).saturating_mul(n.into()))
+		// Minimum execution time: 83_854_000 picoseconds.
+		Weight::from_parts(81_387_241, 6248)
+			// Standard Error: 16_811
+			.saturating_add(Weight::from_parts(4_900_554, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(12_u64))
 			.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into())))
 			.saturating_add(RocksDbWeight::get().writes(6_u64))
@@ -1076,10 +1098,10 @@ impl WeightInfo for () {
 	/// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn chill() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1816`
+		//  Measured:  `1882`
 		//  Estimated: `6248`
-		// Minimum execution time: 60_088_000 picoseconds.
-		Weight::from_parts(62_471_000, 6248)
+		// Minimum execution time: 73_939_000 picoseconds.
+		Weight::from_parts(75_639_000, 6248)
 			.saturating_add(RocksDbWeight::get().reads(9_u64))
 			.saturating_add(RocksDbWeight::get().writes(6_u64))
 	}
@@ -1091,10 +1113,10 @@ impl WeightInfo for () {
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn set_payee() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `902`
+		//  Measured:  `935`
 		//  Estimated: `4556`
-		// Minimum execution time: 19_777_000 picoseconds.
-		Weight::from_parts(20_690_000, 4556)
+		// Minimum execution time: 24_592_000 picoseconds.
+		Weight::from_parts(25_092_000, 4556)
 			.saturating_add(RocksDbWeight::get().reads(2_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
@@ -1106,10 +1128,10 @@ impl WeightInfo for () {
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
 	fn update_payee() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `969`
+		//  Measured:  `1002`
 		//  Estimated: `4556`
-		// Minimum execution time: 23_705_000 picoseconds.
-		Weight::from_parts(24_409_000, 4556)
+		// Minimum execution time: 29_735_000 picoseconds.
+		Weight::from_parts(30_546_000, 4556)
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
@@ -1119,10 +1141,10 @@ impl WeightInfo for () {
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	fn set_controller() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `902`
+		//  Measured:  `935`
 		//  Estimated: `8122`
-		// Minimum execution time: 23_479_000 picoseconds.
-		Weight::from_parts(24_502_000, 8122)
+		// Minimum execution time: 28_728_000 picoseconds.
+		Weight::from_parts(29_709_000, 8122)
 			.saturating_add(RocksDbWeight::get().reads(3_u64))
 			.saturating_add(RocksDbWeight::get().writes(3_u64))
 	}
@@ -1132,8 +1154,8 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_675_000 picoseconds.
-		Weight::from_parts(2_802_000, 0)
+		// Minimum execution time: 2_519_000 picoseconds.
+		Weight::from_parts(2_673_000, 0)
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::ForceEra` (r:0 w:1)
@@ -1142,8 +1164,8 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 7_067_000 picoseconds.
-		Weight::from_parts(7_413_000, 0)
+		// Minimum execution time: 8_050_000 picoseconds.
+		Weight::from_parts(8_268_000, 0)
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::ForceEra` (r:0 w:1)
@@ -1152,8 +1174,8 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 6_977_000 picoseconds.
-		Weight::from_parts(7_353_000, 0)
+		// Minimum execution time: 8_131_000 picoseconds.
+		Weight::from_parts(8_349_000, 0)
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::ForceEra` (r:0 w:1)
@@ -1162,8 +1184,8 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 7_071_000 picoseconds.
-		Weight::from_parts(7_463_000, 0)
+		// Minimum execution time: 8_104_000 picoseconds.
+		Weight::from_parts(8_317_000, 0)
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::Invulnerables` (r:0 w:1)
@@ -1173,10 +1195,10 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 2_833_000 picoseconds.
-		Weight::from_parts(3_328_130, 0)
-			// Standard Error: 30
-			.saturating_add(Weight::from_parts(10_058, 0).saturating_mul(v.into()))
+		// Minimum execution time: 2_669_000 picoseconds.
+		Weight::from_parts(3_013_436, 0)
+			// Standard Error: 31
+			.saturating_add(Weight::from_parts(10_704, 0).saturating_mul(v.into()))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
 	/// Storage: `Staking::Ledger` (r:11800 w:11800)
@@ -1188,12 +1210,12 @@ impl WeightInfo for () {
 	/// The range of component `i` is `[0, 5900]`.
 	fn deprecate_controller_batch(i: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1746 + i * (229 ±0)`
+		//  Measured:  `1779 + i * (229 ±0)`
 		//  Estimated: `990 + i * (7132 ±0)`
-		// Minimum execution time: 5_300_000 picoseconds.
-		Weight::from_parts(5_437_000, 990)
-			// Standard Error: 66_261
-			.saturating_add(Weight::from_parts(30_172_457, 0).saturating_mul(i.into()))
+		// Minimum execution time: 5_101_000 picoseconds.
+		Weight::from_parts(5_368_000, 990)
+			// Standard Error: 75_180
+			.saturating_add(Weight::from_parts(33_781_643, 0).saturating_mul(i.into()))
 			.saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(i.into())))
 			.saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(i.into())))
 			.saturating_add(Weight::from_parts(0, 7132).saturating_mul(i.into()))
@@ -1204,10 +1226,10 @@ impl WeightInfo for () {
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `System::Account` (r:1 w:1)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
@@ -1229,14 +1251,14 @@ impl WeightInfo for () {
 	/// The range of component `s` is `[0, 100]`.
 	fn force_unstake(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2196 + s * (4 ±0)`
+		//  Measured:  `2255 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 87_677_000 picoseconds.
-		Weight::from_parts(96_386_462, 6248)
-			// Standard Error: 3_717
-			.saturating_add(Weight::from_parts(1_370_585, 0).saturating_mul(s.into()))
+		// Minimum execution time: 119_955_000 picoseconds.
+		Weight::from_parts(128_392_032, 6248)
+			// Standard Error: 3_773
+			.saturating_add(Weight::from_parts(1_302_488, 0).saturating_mul(s.into()))
 			.saturating_add(RocksDbWeight::get().reads(13_u64))
-			.saturating_add(RocksDbWeight::get().writes(12_u64))
+			.saturating_add(RocksDbWeight::get().writes(13_u64))
 			.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -1245,12 +1267,12 @@ impl WeightInfo for () {
 	/// The range of component `s` is `[1, 1000]`.
 	fn cancel_deferred_slash(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `66672`
-		//  Estimated: `70137`
-		// Minimum execution time: 105_086_000 picoseconds.
-		Weight::from_parts(1_167_895_222, 70137)
-			// Standard Error: 77_022
-			.saturating_add(Weight::from_parts(6_487_305, 0).saturating_mul(s.into()))
+		//  Measured:  `66705`
+		//  Estimated: `70170`
+		// Minimum execution time: 139_290_000 picoseconds.
+		Weight::from_parts(959_667_494, 70170)
+			// Standard Error: 56_271
+			.saturating_add(Weight::from_parts(4_798_293, 0).saturating_mul(s.into()))
 			.saturating_add(RocksDbWeight::get().reads(1_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
@@ -1268,12 +1290,10 @@ impl WeightInfo for () {
 	/// Proof: `Staking::CurrentEra` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::ErasValidatorReward` (r:1 w:0)
 	/// Proof: `Staking::ErasValidatorReward` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:257 w:257)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:257 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
-	/// Storage: `System::Account` (r:257 w:257)
-	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:257 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:257 w:257)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::ErasStakersPaged` (r:1 w:0)
 	/// Proof: `Staking::ErasStakersPaged` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// Storage: `Staking::ErasRewardPoints` (r:1 w:0)
@@ -1282,29 +1302,31 @@ impl WeightInfo for () {
 	/// Proof: `Staking::ErasValidatorPrefs` (`max_values`: None, `max_size`: Some(57), added: 2532, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Payee` (r:257 w:0)
 	/// Proof: `Staking::Payee` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:257 w:257)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
 	/// The range of component `n` is `[0, 256]`.
 	fn payout_stakers_alive_staked(n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `33297 + n * (377 ±0)`
-		//  Estimated: `30944 + n * (3774 ±3)`
-		// Minimum execution time: 154_210_000 picoseconds.
-		Weight::from_parts(192_836_012, 30944)
-			// Standard Error: 40_441
-			.saturating_add(Weight::from_parts(47_646_642, 0).saturating_mul(n.into()))
+		//  Measured:  `33283 + n * (370 ±0)`
+		//  Estimated: `30958 + n * (3566 ±0)`
+		// Minimum execution time: 193_068_000 picoseconds.
+		Weight::from_parts(252_762_568, 30958)
+			// Standard Error: 22_743
+			.saturating_add(Weight::from_parts(81_185_306, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(14_u64))
 			.saturating_add(RocksDbWeight::get().reads((6_u64).saturating_mul(n.into())))
 			.saturating_add(RocksDbWeight::get().writes(4_u64))
 			.saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into())))
-			.saturating_add(Weight::from_parts(0, 3774).saturating_mul(n.into()))
+			.saturating_add(Weight::from_parts(0, 3566).saturating_mul(n.into()))
 	}
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:0)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListNodes` (r:3 w:3)
 	/// Proof: `VoterList::ListNodes` (`max_values`: None, `max_size`: Some(154), added: 2629, mode: `MaxEncodedLen`)
 	/// Storage: `VoterList::ListBags` (r:2 w:2)
@@ -1312,25 +1334,25 @@ impl WeightInfo for () {
 	/// The range of component `l` is `[1, 32]`.
 	fn rebond(l: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1991 + l * (7 ±0)`
+		//  Measured:  `1947 + l * (7 ±0)`
 		//  Estimated: `8877`
-		// Minimum execution time: 88_337_000 picoseconds.
-		Weight::from_parts(91_391_254, 8877)
-			// Standard Error: 4_485
-			.saturating_add(Weight::from_parts(103_443, 0).saturating_mul(l.into()))
+		// Minimum execution time: 91_151_000 picoseconds.
+		Weight::from_parts(93_596_096, 8877)
+			// Standard Error: 5_313
+			.saturating_add(Weight::from_parts(124_684, 0).saturating_mul(l.into()))
 			.saturating_add(RocksDbWeight::get().reads(9_u64))
-			.saturating_add(RocksDbWeight::get().writes(7_u64))
+			.saturating_add(RocksDbWeight::get().writes(6_u64))
 	}
+	/// Storage: `Staking::VirtualStakers` (r:1 w:1)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:1)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::SlashingSpans` (r:1 w:1)
 	/// Proof: `Staking::SlashingSpans` (`max_values`: None, `max_size`: None, mode: `Measured`)
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `Balances::Freezes` (r:1 w:0)
-	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Validators` (r:1 w:0)
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Nominators` (r:1 w:1)
@@ -1350,14 +1372,14 @@ impl WeightInfo for () {
 	/// The range of component `s` is `[1, 100]`.
 	fn reap_stash(s: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `2196 + s * (4 ±0)`
+		//  Measured:  `2255 + s * (4 ±0)`
 		//  Estimated: `6248 + s * (4 ±0)`
-		// Minimum execution time: 98_014_000 picoseconds.
-		Weight::from_parts(102_537_670, 6248)
-			// Standard Error: 3_324
-			.saturating_add(Weight::from_parts(1_353_142, 0).saturating_mul(s.into()))
+		// Minimum execution time: 133_214_000 picoseconds.
+		Weight::from_parts(137_290_527, 6248)
+			// Standard Error: 4_153
+			.saturating_add(Weight::from_parts(1_291_007, 0).saturating_mul(s.into()))
 			.saturating_add(RocksDbWeight::get().reads(12_u64))
-			.saturating_add(RocksDbWeight::get().writes(11_u64))
+			.saturating_add(RocksDbWeight::get().writes(12_u64))
 			.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(s.into())))
 			.saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into()))
 	}
@@ -1401,12 +1423,12 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0 + n * (720 ±0) + v * (3598 ±0)`
 		//  Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)`
-		// Minimum execution time: 608_575_000 picoseconds.
-		Weight::from_parts(613_663_000, 512390)
-			// Standard Error: 2_286_521
-			.saturating_add(Weight::from_parts(72_108_001, 0).saturating_mul(v.into()))
-			// Standard Error: 227_839
-			.saturating_add(Weight::from_parts(20_314_085, 0).saturating_mul(n.into()))
+		// Minimum execution time: 692_301_000 picoseconds.
+		Weight::from_parts(708_732_000, 512390)
+			// Standard Error: 2_117_299
+			.saturating_add(Weight::from_parts(70_087_600, 0).saturating_mul(v.into()))
+			// Standard Error: 210_977
+			.saturating_add(Weight::from_parts(22_953_405, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(206_u64))
 			.saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into())))
 			.saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into())))
@@ -1435,14 +1457,14 @@ impl WeightInfo for () {
 	/// The range of component `n` is `[500, 1000]`.
 	fn get_npos_voters(v: u32, n: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `3175 + n * (911 ±0) + v * (395 ±0)`
+		//  Measured:  `3241 + n * (911 ±0) + v * (395 ±0)`
 		//  Estimated: `512390 + n * (3566 ±0) + v * (3566 ±0)`
-		// Minimum execution time: 37_173_756_000 picoseconds.
-		Weight::from_parts(37_488_937_000, 512390)
-			// Standard Error: 467_413
-			.saturating_add(Weight::from_parts(8_086_367, 0).saturating_mul(v.into()))
-			// Standard Error: 467_413
-			.saturating_add(Weight::from_parts(3_108_193, 0).saturating_mul(n.into()))
+		// Minimum execution time: 43_708_472_000 picoseconds.
+		Weight::from_parts(44_048_436_000, 512390)
+			// Standard Error: 493_244
+			.saturating_add(Weight::from_parts(6_697_278, 0).saturating_mul(v.into()))
+			// Standard Error: 493_244
+			.saturating_add(Weight::from_parts(4_559_779, 0).saturating_mul(n.into()))
 			.saturating_add(RocksDbWeight::get().reads(201_u64))
 			.saturating_add(RocksDbWeight::get().reads((5_u64).saturating_mul(v.into())))
 			.saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into())))
@@ -1457,12 +1479,12 @@ impl WeightInfo for () {
 	/// The range of component `v` is `[500, 1000]`.
 	fn get_npos_targets(v: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `979 + v * (50 ±0)`
+		//  Measured:  `1012 + v * (50 ±0)`
 		//  Estimated: `3510 + v * (2520 ±0)`
-		// Minimum execution time: 2_641_258_000 picoseconds.
-		Weight::from_parts(382_882_595, 3510)
-			// Standard Error: 11_991
-			.saturating_add(Weight::from_parts(4_695_820, 0).saturating_mul(v.into()))
+		// Minimum execution time: 2_917_165_000 picoseconds.
+		Weight::from_parts(2_948_999_000, 3510)
+			// Standard Error: 33_372
+			.saturating_add(Weight::from_parts(2_126_909, 0).saturating_mul(v.into()))
 			.saturating_add(RocksDbWeight::get().reads(2_u64))
 			.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(v.into())))
 			.saturating_add(Weight::from_parts(0, 2520).saturating_mul(v.into()))
@@ -1485,8 +1507,8 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 5_753_000 picoseconds.
-		Weight::from_parts(6_529_000, 0)
+		// Minimum execution time: 4_748_000 picoseconds.
+		Weight::from_parts(5_052_000, 0)
 			.saturating_add(RocksDbWeight::get().writes(7_u64))
 	}
 	/// Storage: `Staking::MinCommission` (r:0 w:1)
@@ -1507,8 +1529,8 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 5_212_000 picoseconds.
-		Weight::from_parts(5_451_000, 0)
+		// Minimum execution time: 4_316_000 picoseconds.
+		Weight::from_parts(4_526_000, 0)
 			.saturating_add(RocksDbWeight::get().writes(7_u64))
 	}
 	/// Storage: `Staking::Bonded` (r:1 w:0)
@@ -1535,10 +1557,10 @@ impl WeightInfo for () {
 	/// Proof: `VoterList::CounterForListNodes` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
 	fn chill_other() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1939`
+		//  Measured:  `2005`
 		//  Estimated: `6248`
-		// Minimum execution time: 73_000_000 picoseconds.
-		Weight::from_parts(75_184_000, 6248)
+		// Minimum execution time: 87_374_000 picoseconds.
+		Weight::from_parts(89_848_000, 6248)
 			.saturating_add(RocksDbWeight::get().reads(12_u64))
 			.saturating_add(RocksDbWeight::get().writes(6_u64))
 	}
@@ -1548,10 +1570,10 @@ impl WeightInfo for () {
 	/// Proof: `Staking::Validators` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`)
 	fn force_apply_min_commission() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `691`
+		//  Measured:  `724`
 		//  Estimated: `3510`
-		// Minimum execution time: 13_056_000 picoseconds.
-		Weight::from_parts(13_517_000, 3510)
+		// Minimum execution time: 15_529_000 picoseconds.
+		Weight::from_parts(16_094_000, 3510)
 			.saturating_add(RocksDbWeight::get().reads(2_u64))
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
@@ -1561,27 +1583,50 @@ impl WeightInfo for () {
 		// Proof Size summary in bytes:
 		//  Measured:  `0`
 		//  Estimated: `0`
-		// Minimum execution time: 3_201_000 picoseconds.
-		Weight::from_parts(3_442_000, 0)
+		// Minimum execution time: 2_533_000 picoseconds.
+		Weight::from_parts(2_817_000, 0)
 			.saturating_add(RocksDbWeight::get().writes(1_u64))
 	}
-	/// Storage: `Balances::Locks` (r:1 w:1)
-	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
-	/// Storage: `System::Account` (r:1 w:1)
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:0)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:0)
 	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Locks` (r:1 w:0)
+	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Bonded` (r:1 w:1)
 	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
 	/// Storage: `Staking::Ledger` (r:1 w:1)
 	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
+	fn restore_ledger() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1110`
+		//  Estimated: `4764`
+		// Minimum execution time: 50_105_000 picoseconds.
+		Weight::from_parts(50_966_000, 4764)
+			.saturating_add(RocksDbWeight::get().reads(6_u64))
+			.saturating_add(RocksDbWeight::get().writes(2_u64))
+	}
+	/// Storage: `Staking::VirtualStakers` (r:1 w:0)
+	/// Proof: `Staking::VirtualStakers` (`max_values`: None, `max_size`: Some(40), added: 2515, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::Bonded` (r:1 w:0)
+	/// Proof: `Staking::Bonded` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
+	/// Storage: `Staking::Ledger` (r:1 w:0)
+	/// Proof: `Staking::Ledger` (`max_values`: None, `max_size`: Some(1091), added: 3566, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Locks` (r:1 w:1)
+	/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::Holds` (r:1 w:1)
+	/// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(355), added: 2830, mode: `MaxEncodedLen`)
 	/// Storage: `Balances::Freezes` (r:1 w:0)
 	/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
-	fn restore_ledger() -> Weight {
+	fn migrate_currency() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `1047`
+		//  Measured:  `1246`
 		//  Estimated: `4764`
-		// Minimum execution time: 44_671_000 picoseconds.
-		Weight::from_parts(45_611_000, 4764)
-			.saturating_add(RocksDbWeight::get().reads(5_u64))
-			.saturating_add(RocksDbWeight::get().writes(4_u64))
+		// Minimum execution time: 94_054_000 picoseconds.
+		Weight::from_parts(96_272_000, 4764)
+			.saturating_add(RocksDbWeight::get().reads(6_u64))
+			.saturating_add(RocksDbWeight::get().writes(2_u64))
 	}
 }
\ No newline at end of file
diff --git a/substrate/frame/state-trie-migration/src/lib.rs b/substrate/frame/state-trie-migration/src/lib.rs
index 61323b70b33d22be543c1da9c9ad9797b7f41885..1dc1a3928f2b89e3b3aaa31dd8789a10423fa7c5 100644
--- a/substrate/frame/state-trie-migration/src/lib.rs
+++ b/substrate/frame/state-trie-migration/src/lib.rs
@@ -1309,16 +1309,17 @@ mod mock {
 	pub(crate) fn run_to_block(n: u32) -> (H256, Weight) {
 		let mut root = Default::default();
 		let mut weight_sum = Weight::zero();
+
 		log::trace!(target: LOG_TARGET, "running from {:?} to {:?}", System::block_number(), n);
-		while System::block_number() < n {
-			System::set_block_number(System::block_number() + 1);
-			System::on_initialize(System::block_number());
 
-			weight_sum += StateTrieMigration::on_initialize(System::block_number());
+		System::run_to_block_with::<AllPalletsWithSystem>(
+			n,
+			frame_system::RunToBlockHooks::default().after_initialize(|bn| {
+				weight_sum += StateTrieMigration::on_initialize(bn);
+				root = *System::finalize().state_root();
+			}),
+		);
 
-			root = *System::finalize().state_root();
-			System::on_finalize(System::block_number());
-		}
 		(root, weight_sum)
 	}
 }
diff --git a/substrate/frame/support/src/dispatch.rs b/substrate/frame/support/src/dispatch.rs
index 990996830030ce1a12ea165ffbe77052661e6edc..14bc2667def179cc0d403287acca811939824bbe 100644
--- a/substrate/frame/support/src/dispatch.rs
+++ b/substrate/frame/support/src/dispatch.rs
@@ -315,10 +315,8 @@ impl PostDispatchInfo {
 					"Post dispatch weight is greater than pre dispatch weight. \
 					Pre dispatch weight may underestimating the actual weight. \
 					Greater post dispatch weight components are ignored.
-					Pre dispatch weight: {:?},
-					Post dispatch weight: {:?}",
-					actual_weight,
-					info_total_weight,
+					Pre dispatch weight: {info_total_weight:?},
+					Post dispatch weight: {actual_weight:?}",
 				);
 			}
 			actual_weight.min(info.total_weight())
diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs
index 728426cc84c71087015cea70dd5a23a50d49c982..4a83c809a6a5ece39a9818f7111c92e6e4b7b100 100644
--- a/substrate/frame/support/src/traits.rs
+++ b/substrate/frame/support/src/traits.rs
@@ -96,8 +96,9 @@ mod storage;
 #[cfg(feature = "experimental")]
 pub use storage::MaybeConsideration;
 pub use storage::{
-	Consideration, Footprint, Incrementable, Instance, LinearStoragePrice, PartialStorageInfoTrait,
-	StorageInfo, StorageInfoTrait, StorageInstance, TrackedStorageKey, WhitelistedStorageKeys,
+	Consideration, ConstantStoragePrice, Footprint, Incrementable, Instance, LinearStoragePrice,
+	PartialStorageInfoTrait, StorageInfo, StorageInfoTrait, StorageInstance, TrackedStorageKey,
+	WhitelistedStorageKeys,
 };
 
 mod dispatch;
diff --git a/substrate/frame/support/src/traits/storage.rs b/substrate/frame/support/src/traits/storage.rs
index 2b8e437073894cc03634bbd950313f541b59fe76..676b73e03d3c4472ebdb1ab024b6c9accb430ac1 100644
--- a/substrate/frame/support/src/traits/storage.rs
+++ b/substrate/frame/support/src/traits/storage.rs
@@ -200,6 +200,18 @@ where
 	}
 }
 
+/// Constant `Price` regardless of the given [`Footprint`].
+pub struct ConstantStoragePrice<Price, Balance>(PhantomData<(Price, Balance)>);
+impl<Price, Balance> Convert<Footprint, Balance> for ConstantStoragePrice<Price, Balance>
+where
+	Price: Get<Balance>,
+	Balance: From<u64> + sp_runtime::Saturating,
+{
+	fn convert(_: Footprint) -> Balance {
+		Price::get()
+	}
+}
+
 /// Some sort of cost taken from account temporarily in order to offset the cost to the chain of
 /// holding some data [`Footprint`] in state.
 ///
diff --git a/substrate/frame/support/test/stg_frame_crate/Cargo.toml b/substrate/frame/support/test/stg_frame_crate/Cargo.toml
index f627d29cd563067cdad9b48e4dc5a7ac9bdefbd8..157361dbd5d6dc1df062ebd2e4632da830b7362b 100644
--- a/substrate/frame/support/test/stg_frame_crate/Cargo.toml
+++ b/substrate/frame/support/test/stg_frame_crate/Cargo.toml
@@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
 codec = { features = ["derive"], workspace = true }
-frame = { features = ["experimental", "runtime"], workspace = true }
+frame = { features = ["runtime"], workspace = true }
 scale-info = { features = ["derive"], workspace = true }
 
 [features]
diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs
index 894e1898ed15523576eef010c211a3f0f79dece5..f2bb5e290c94378a9afa9381e51720b2e724aa4f 100644
--- a/substrate/frame/system/src/lib.rs
+++ b/substrate/frame/system/src/lib.rs
@@ -1974,6 +1974,51 @@ impl<T: Config> Pallet<T> {
 			.collect::<_>()
 	}
 
+	/// Simulate the execution of a block sequence up to a specified height, injecting the
+	/// provided hooks at each block.
+	///
+	/// `on_finalize` is always called before `on_initialize` with the current block number.
+	/// `on_initalize` is always called with the next block number.
+	///
+	/// These hooks allows custom logic to be executed at each block at specific location.
+	/// For example, you might use one of them to set a timestamp for each block.
+	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
+	pub fn run_to_block_with<AllPalletsWithSystem>(
+		n: BlockNumberFor<T>,
+		mut hooks: RunToBlockHooks<T>,
+	) where
+		AllPalletsWithSystem: frame_support::traits::OnInitialize<BlockNumberFor<T>>
+			+ frame_support::traits::OnFinalize<BlockNumberFor<T>>,
+	{
+		let mut bn = Self::block_number();
+
+		while bn < n {
+			// Skip block 0.
+			if !bn.is_zero() {
+				(hooks.before_finalize)(bn);
+				AllPalletsWithSystem::on_finalize(bn);
+				(hooks.after_finalize)(bn);
+			}
+
+			bn += One::one();
+
+			Self::set_block_number(bn);
+			(hooks.before_initialize)(bn);
+			AllPalletsWithSystem::on_initialize(bn);
+			(hooks.after_initialize)(bn);
+		}
+	}
+
+	/// Simulate the execution of a block sequence up to a specified height.
+	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
+	pub fn run_to_block<AllPalletsWithSystem>(n: BlockNumberFor<T>)
+	where
+		AllPalletsWithSystem: frame_support::traits::OnInitialize<BlockNumberFor<T>>
+			+ frame_support::traits::OnFinalize<BlockNumberFor<T>>,
+	{
+		Self::run_to_block_with::<AllPalletsWithSystem>(n, Default::default());
+	}
+
 	/// Set the block number to something in particular. Can be used as an alternative to
 	/// `initialize` for tests that don't need to bother with the other environment entries.
 	#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
@@ -2347,6 +2392,72 @@ impl<T: Config> Lookup for ChainContext<T> {
 	}
 }
 
+/// Hooks for the [`Pallet::run_to_block_with`] function.
+#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
+pub struct RunToBlockHooks<'a, T>
+where
+	T: 'a + Config,
+{
+	before_initialize: Box<dyn 'a + FnMut(BlockNumberFor<T>)>,
+	after_initialize: Box<dyn 'a + FnMut(BlockNumberFor<T>)>,
+	before_finalize: Box<dyn 'a + FnMut(BlockNumberFor<T>)>,
+	after_finalize: Box<dyn 'a + FnMut(BlockNumberFor<T>)>,
+}
+
+#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
+impl<'a, T> RunToBlockHooks<'a, T>
+where
+	T: 'a + Config,
+{
+	/// Set the hook function logic before the initialization of the block.
+	pub fn before_initialize<F>(mut self, f: F) -> Self
+	where
+		F: 'a + FnMut(BlockNumberFor<T>),
+	{
+		self.before_initialize = Box::new(f);
+		self
+	}
+	/// Set the hook function logic after the initialization of the block.
+	pub fn after_initialize<F>(mut self, f: F) -> Self
+	where
+		F: 'a + FnMut(BlockNumberFor<T>),
+	{
+		self.after_initialize = Box::new(f);
+		self
+	}
+	/// Set the hook function logic before the finalization of the block.
+	pub fn before_finalize<F>(mut self, f: F) -> Self
+	where
+		F: 'a + FnMut(BlockNumberFor<T>),
+	{
+		self.before_finalize = Box::new(f);
+		self
+	}
+	/// Set the hook function logic after the finalization of the block.
+	pub fn after_finalize<F>(mut self, f: F) -> Self
+	where
+		F: 'a + FnMut(BlockNumberFor<T>),
+	{
+		self.after_finalize = Box::new(f);
+		self
+	}
+}
+
+#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
+impl<'a, T> Default for RunToBlockHooks<'a, T>
+where
+	T: Config,
+{
+	fn default() -> Self {
+		Self {
+			before_initialize: Box::new(|_| {}),
+			after_initialize: Box::new(|_| {}),
+			before_finalize: Box::new(|_| {}),
+			after_finalize: Box::new(|_| {}),
+		}
+	}
+}
+
 /// Prelude to be used alongside pallet macro, for ease of use.
 pub mod pallet_prelude {
 	pub use crate::{ensure_none, ensure_root, ensure_signed, ensure_signed_or_root};
diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs
index dd752989c3662d2123c9d7783786bcb3d9de80d2..4a96cbcacb5804541aa6130702869b05cbd32637 100644
--- a/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs
+++ b/substrate/frame/transaction-payment/asset-tx-payment/src/lib.rs
@@ -202,7 +202,7 @@ where
 		debug_assert!(self.tip <= fee, "tip should be included in the computed fee");
 		if fee.is_zero() {
 			Ok((fee, InitialPayment::Nothing))
-		} else if let Some(asset_id) = self.asset_id {
+		} else if let Some(asset_id) = self.asset_id.clone() {
 			T::OnChargeAssetTransaction::withdraw_fee(
 				who,
 				call,
@@ -233,7 +233,7 @@ where
 		debug_assert!(self.tip <= fee, "tip should be included in the computed fee");
 		if fee.is_zero() {
 			Ok(())
-		} else if let Some(asset_id) = self.asset_id {
+		} else if let Some(asset_id) = self.asset_id.clone() {
 			T::OnChargeAssetTransaction::can_withdraw_fee(
 				who,
 				call,
@@ -358,7 +358,7 @@ where
 					tip,
 					who,
 					initial_payment,
-					asset_id: self.asset_id,
+					asset_id: self.asset_id.clone(),
 					weight: self.weight(call),
 				})
 			},
diff --git a/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs b/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs
index 2074b1476f45ae9187778a3c846e0b21a1097176..7b7ae855bf8f34bea492b98a912c2094fdd8343d 100644
--- a/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs
+++ b/substrate/frame/transaction-payment/asset-tx-payment/src/payment.rs
@@ -40,7 +40,7 @@ pub trait OnChargeAssetTransaction<T: Config> {
 	/// The underlying integer type in which fees are calculated.
 	type Balance: Balance;
 	/// The type used to identify the assets used for transaction payment.
-	type AssetId: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo;
+	type AssetId: FullCodec + Clone + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo;
 	/// The type used to store the intermediate values between pre- and post-dispatch.
 	type LiquidityInfo;
 
@@ -112,7 +112,7 @@ where
 	T: Config,
 	CON: ConversionToAssetBalance<BalanceOf<T>, AssetIdOf<T>, AssetBalanceOf<T>>,
 	HC: HandleCredit<T::AccountId, T::Fungibles>,
-	AssetIdOf<T>: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo,
+	AssetIdOf<T>: FullCodec + Clone + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo,
 {
 	type Balance = BalanceOf<T>;
 	type AssetId = AssetIdOf<T>;
@@ -133,11 +133,14 @@ where
 		// less than one (e.g. 0.5) but gets rounded down by integer division we introduce a minimum
 		// fee.
 		let min_converted_fee = if fee.is_zero() { Zero::zero() } else { One::one() };
-		let converted_fee = CON::to_asset_balance(fee, asset_id)
+		let converted_fee = CON::to_asset_balance(fee, asset_id.clone())
 			.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?
 			.max(min_converted_fee);
-		let can_withdraw =
-			<T::Fungibles as Inspect<T::AccountId>>::can_withdraw(asset_id, who, converted_fee);
+		let can_withdraw = <T::Fungibles as Inspect<T::AccountId>>::can_withdraw(
+			asset_id.clone(),
+			who,
+			converted_fee,
+		);
 		if can_withdraw != WithdrawConsequence::Success {
 			return Err(InvalidTransaction::Payment.into())
 		}
@@ -167,7 +170,7 @@ where
 		// less than one (e.g. 0.5) but gets rounded down by integer division we introduce a minimum
 		// fee.
 		let min_converted_fee = if fee.is_zero() { Zero::zero() } else { One::one() };
-		let converted_fee = CON::to_asset_balance(fee, asset_id)
+		let converted_fee = CON::to_asset_balance(fee, asset_id.clone())
 			.map_err(|_| TransactionValidityError::from(InvalidTransaction::Payment))?
 			.max(min_converted_fee);
 		let can_withdraw =
diff --git a/substrate/frame/transaction-storage/src/mock.rs b/substrate/frame/transaction-storage/src/mock.rs
index 73174b73dbacc7c803ad947c3108165688832767..84a77043d577c010bdd71a60e5673d4da241f11b 100644
--- a/substrate/frame/transaction-storage/src/mock.rs
+++ b/substrate/frame/transaction-storage/src/mock.rs
@@ -21,10 +21,7 @@ use crate::{
 	self as pallet_transaction_storage, TransactionStorageProof, DEFAULT_MAX_BLOCK_TRANSACTIONS,
 	DEFAULT_MAX_TRANSACTION_SIZE,
 };
-use frame_support::{
-	derive_impl,
-	traits::{ConstU32, OnFinalize, OnInitialize},
-};
+use frame_support::{derive_impl, traits::ConstU32};
 use sp_runtime::{traits::IdentityLookup, BuildStorage};
 
 pub type Block = frame_system::mocking::MockBlock<Test>;
@@ -80,15 +77,13 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 	t.into()
 }
 
-pub fn run_to_block(n: u64, f: impl Fn() -> Option<TransactionStorageProof>) {
-	while System::block_number() < n {
-		if let Some(proof) = f() {
-			TransactionStorage::check_proof(RuntimeOrigin::none(), proof).unwrap();
-		}
-		TransactionStorage::on_finalize(System::block_number());
-		System::on_finalize(System::block_number());
-		System::set_block_number(System::block_number() + 1);
-		System::on_initialize(System::block_number());
-		TransactionStorage::on_initialize(System::block_number());
-	}
+pub fn run_to_block(n: u64, f: impl Fn() -> Option<TransactionStorageProof> + 'static) {
+	System::run_to_block_with::<AllPalletsWithSystem>(
+		n,
+		frame_system::RunToBlockHooks::default().before_finalize(|_| {
+			if let Some(proof) = f() {
+				TransactionStorage::check_proof(RuntimeOrigin::none(), proof).unwrap();
+			}
+		}),
+	);
 }
diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs
index 17010a8907fc23325d2726e59285313f5fea53a6..8e23c6800a9d53a240261b51a7f87dd20017b9e0 100644
--- a/substrate/primitives/staking/src/lib.rs
+++ b/substrate/primitives/staking/src/lib.rs
@@ -325,7 +325,7 @@ pub trait StakingUnchecked: StakingInterface {
 	/// Migrate an existing staker to a virtual staker.
 	///
 	/// It would release all funds held by the implementation pallet.
-	fn migrate_to_virtual_staker(who: &Self::AccountId);
+	fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult;
 
 	/// Book-keep a new bond for `keyless_who` without applying any locks (hence virtual).
 	///
diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml
index 17a7c02e82592c5765f7290183e8ee6711e44aff..fc0b2d5a140ed18419a3cece320f03f6fab8b711 100644
--- a/umbrella/Cargo.toml
+++ b/umbrella/Cargo.toml
@@ -57,6 +57,7 @@ std = [
 	"pallet-asset-conversion-tx-payment?/std",
 	"pallet-asset-conversion?/std",
 	"pallet-asset-rate?/std",
+	"pallet-asset-rewards?/std",
 	"pallet-asset-tx-payment?/std",
 	"pallet-assets-freezer?/std",
 	"pallet-assets?/std",
@@ -256,6 +257,7 @@ runtime-benchmarks = [
 	"pallet-asset-conversion-tx-payment?/runtime-benchmarks",
 	"pallet-asset-conversion?/runtime-benchmarks",
 	"pallet-asset-rate?/runtime-benchmarks",
+	"pallet-asset-rewards?/runtime-benchmarks",
 	"pallet-asset-tx-payment?/runtime-benchmarks",
 	"pallet-assets-freezer?/runtime-benchmarks",
 	"pallet-assets?/runtime-benchmarks",
@@ -386,6 +388,7 @@ try-runtime = [
 	"pallet-asset-conversion-tx-payment?/try-runtime",
 	"pallet-asset-conversion?/try-runtime",
 	"pallet-asset-rate?/try-runtime",
+	"pallet-asset-rewards?/try-runtime",
 	"pallet-asset-tx-payment?/try-runtime",
 	"pallet-assets-freezer?/try-runtime",
 	"pallet-assets?/try-runtime",
@@ -543,7 +546,7 @@ with-tracing = [
 	"sp-tracing?/with-tracing",
 	"sp-tracing?/with-tracing",
 ]
-runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-weight-reclaim", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"]
+runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-weight-reclaim", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-rewards", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"]
 runtime = [
 	"frame-benchmarking",
 	"frame-benchmarking-pallet-pov",
@@ -870,6 +873,11 @@ default-features = false
 optional = true
 path = "../substrate/frame/asset-rate"
 
+[dependencies.pallet-asset-rewards]
+default-features = false
+optional = true
+path = "../substrate/frame/asset-rewards"
+
 [dependencies.pallet-asset-tx-payment]
 default-features = false
 optional = true
diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs
index 3504f081f2957b5a9a8eccaea15343281ee98292..a132f16a2c33f617ee1a916deb585534d3e6f99c 100644
--- a/umbrella/src/lib.rs
+++ b/umbrella/src/lib.rs
@@ -312,6 +312,10 @@ pub use pallet_asset_conversion_tx_payment;
 #[cfg(feature = "pallet-asset-rate")]
 pub use pallet_asset_rate;
 
+/// FRAME asset rewards pallet.
+#[cfg(feature = "pallet-asset-rewards")]
+pub use pallet_asset_rewards;
+
 /// pallet to manage transaction payments in assets.
 #[cfg(feature = "pallet-asset-tx-payment")]
 pub use pallet_asset_tx_payment;