diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 76dc64305a1dde4ce2a4cd8cd605a2a50f6b4763..11e3e7c068fbb8c0f880b0192aee8acb0b8a862c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -26,6 +26,9 @@ workflow:
 .is_network_branch: &is_network_branch
   if: $CI_PIPELINE_SOURCE != "merge_request_event" && $CI_COMMIT_BRANCH =~ /^(network\/).+/
 
+.is_runtime_branch: &is_runtime_branch
+  if: $CI_PIPELINE_SOURCE != "merge_request_event" && $CI_COMMIT_BRANCH =~ /^(runtime\/).+/
+
 sanity_tests:
   extends: .env
   rules:
@@ -185,15 +188,19 @@ tests:
   - echo "NETWORK = $NETWORK"
   - export RUNTIME=$(echo $NETWORK | grep -Po "gdev|gtest|g1")
   - echo "RUNTIME = $RUNTIME"
+  # srtool specific
+  - export RUNTIME_DIR=runtime/$RUNTIME
+  - echo "RUNTIME_DIR = $RUNTIME_DIR"
+  # srtool specific
   - export PACKAGE=$RUNTIME-runtime
   - echo "PACKAGE = $PACKAGE"
   # GitLab milestone : used for both GitLab and Docker releases. Milestone must match source code's runtime version to fetch the git changes for release notes.
   - export CLIENT_VERSION=$(cat node/Cargo.toml | grep version | sed "s/version = \"//g" | sed "s/\"//")
   - echo $CLIENT_VERSION
-  - export RUNTIME_VERSION=$(cat runtime/gdev/src/lib.rs | grep "spec_version:" | sed "s/ *spec_version. //g" | sed "s/,//g")
+  - export RUNTIME_VERSION=$(cat runtime/$RUNTIME/src/lib.rs | grep "spec_version:" | sed "s/ *spec_version. //g" | sed "s/,//g")
   - echo $RUNTIME_VERSION
-  - export RUNTIME_MILESTONE="runtime-$RUNTIME_VERSION"
-  - echo $RUNTIME_MILESTONE
+  - export CLIENT_MILESTONE="client-$CLIENT_VERSION"
+  - echo $CLIENT_MILESTONE
   - export NETWORK_RELEASE="$NETWORK"
   - echo $NETWORK_RELEASE
   - export DOCKER_TAG="$RUNTIME_VERSION-$CLIENT_VERSION"
@@ -218,6 +225,36 @@ tests:
   - export CLIENT_RELEASE_NAME=$RUNTIME-$RUNTIME_VERSION-$CLIENT_VERSION
   - echo $CLIENT_RELEASE_NAME
 
+.release_runtime_vars: &define_release_runtime_vars
+  - echo "RUNTIME = $RUNTIME"
+  - export RUNTIME_VERSION=$(cat runtime/$RUNTIME/src/lib.rs | grep "spec_version:" | sed "s/ *spec_version. //g" | sed "s/,//g")
+  - echo $RUNTIME_VERSION
+  - export RUNTIME_MILESTONE="runtime-$RUNTIME_VERSION"
+  - echo $RUNTIME_MILESTONE
+  - export SRTOOL_OUTPUT=$CI_PROJECT_DIR/release/srtool_output_$RUNTIME.json
+  - echo "SRTOOL_OUTPUT = $SRTOOL_OUTPUT"
+  - export RELEASE_FILE_WASM=release/${RUNTIME}_runtime.compact.compressed.wasm
+  - echo $RELEASE_FILE_WASM
+  # srtool specific
+  - export RUNTIME_DIR=runtime/$RUNTIME
+  - echo "RUNTIME_DIR = $RUNTIME_DIR"
+  # srtool specific
+  - export PACKAGE=$RUNTIME-runtime
+  - echo "PACKAGE = $PACKAGE"
+
+trigger_runtime_release:
+  stage: build
+  rules:
+    - <<: *is_runtime_branch
+      when: manual
+  variables:
+    RUNTIME: gdev
+  script:
+    - *define_release_runtime_vars
+    - echo "Vérification de l'absence de la release $RUNTIME_MILESTONE"
+    - echo "Contrôle de l'URL https://git.duniter.org/api/v4/projects/$CI_PROJECT_ID/releases/$RUNTIME_MILESTONE"
+    - curl -s https://git.duniter.org/api/v4/projects/$CI_PROJECT_ID/releases/$RUNTIME_MILESTONE --fail 1>/dev/null && (echo "Release déjà présente" && exit 1) || echo "Release absente"
+
 trigger_network_release:
   stage: build
   rules:
@@ -225,7 +262,7 @@ trigger_network_release:
       when: manual
   script:
     - *define_network_branch_vars
-    - echo "Vérification de la présence de la release $NETWORK"
+    - echo "Vérification de l'absence de la release $NETWORK"
     - echo "Contrôle de l'URL https://git.duniter.org/api/v4/projects/$CI_PROJECT_ID/releases/$NETWORK"
     - curl -s https://git.duniter.org/api/v4/projects/$CI_PROJECT_ID/releases/$NETWORK --fail 1>/dev/null && (echo "Release déjà présente" && exit 1) || echo "Release absente"
 
@@ -252,7 +289,7 @@ docker_deploy:
   script:
     - *define_network_branch_vars
     - podman manifest rm "$MANIFEST" 2>/dev/null || true
-    - podman build --layers --platform linux/amd64 --manifest "$MANIFEST" -f docker/Dockerfile --build-arg chain=$RUNTIME .
+    - podman build --layers --platform linux/amd64,linux/arm64 --manifest "$MANIFEST" -f docker/Dockerfile --build-arg chain=$RUNTIME .
     - podman manifest push --all "$MANIFEST" "docker://docker.io/$IMAGE_NAME:$DOCKER_TAG"
     - podman manifest push --all "$MANIFEST" "docker://docker.io/$IMAGE_NAME:latest"
   after_script:
@@ -264,23 +301,43 @@ docker_deploy:
 
 ############## SRTOOL ##############
 
-# We always build the runtime on a network/ branch, either it is for:
-# - creating a network release (i.e.: genesis)
-# - creating a client release (i.e.: which also includes the runtime)
-build_runtime:
+# The Network Runtime is only built when creating a network release (i.e.: genesis)
+build_network_runtime:
   stage: build
+  needs: ["trigger_network_release"]
   rules:
     - <<: *is_network_branch
   image: paritytech/srtool:1.81.0-0.17.0
   variables:
-    SRTOOL_OUTPUT: $CI_PROJECT_DIR/release/srtool_output.json
+    SRTOOL_OUTPUT: $CI_PROJECT_DIR/release/network_srtool_output.json
   script:
     - *define_network_branch_vars
-    - export RUNTIME_DIR=runtime/$RUNTIME
-    - echo "RUNTIME_DIR = $RUNTIME_DIR"
     - echo "SRTOOL_OUTPUT = $SRTOOL_OUTPUT"
     - mkdir -p $CI_PROJECT_DIR/release
-    - echo -e "[toolchain]\nchannel = \"`curl -s https://raw.githubusercontent.com/paritytech/srtool/master/RUSTC_VERSION`\"\ncomponents = [ \"rust-std\", \"rust-src\" ]" > $RUNTIME_DIR/rust-toolchain.toml # Workaround see !239
+    # Copy sources to the expected directory of srtool
+    - cp -R * /build/
+    # Build the runtime
+    - /srtool/build --app --json -cM | tee -a $SRTOOL_OUTPUT
+    - mv /build/runtime/$RUNTIME/target/srtool/release/wbuild/$RUNTIME-runtime/${RUNTIME}_runtime.compact.compressed.wasm $CI_PROJECT_DIR/release/
+  artifacts:
+    expire_in: never
+    name: "runtime"
+    paths:
+      - $CI_PROJECT_DIR/release
+  tags:
+    - kepler
+
+build_gdev_runtime:
+  stage: build
+  needs: ["trigger_runtime_release"]
+  rules:
+    - <<: *is_runtime_branch
+  image: paritytech/srtool:1.77.0-0.15.0
+  variables:
+    RUNTIME: gdev
+  script:
+    - *define_release_runtime_vars
+    - mkdir -p $CI_PROJECT_DIR/release
     # Copy sources to the expected directory of srtool
     - cp -R * /build/
     # Build the runtime
@@ -344,7 +401,7 @@ g1_data:
 
 build_specs:
   stage: build
-  needs: ["build_runtime", "g1_data"]
+  needs: ["build_network_runtime", "g1_data"]
   rules:
     - <<: *is_network_branch
   image: rust:1-bullseye
@@ -384,9 +441,17 @@ build_raw_specs:
     - export FEATURES="--features $RUNTIME --no-default-features"
     - echo "FEATURES = $FEATURES"
     - apt-get update
-    - apt-get install -y clang cmake protobuf-compiler
+    - apt-get install -y clang cmake protobuf-compiler jq
     # Print chainspec to file
-    - cargo xtask print-spec $NETWORK_RELEASE > ${RUNTIME}.json
+    - cargo xtask print-spec $NETWORK_RELEASE > ${RUNTIME}-printed.json
+    # Merge client specs into chainspec file (to update bootnodes for example)
+    # 1. Download yq to create a json client spec file
+    - wget https://github.com/mikefarah/yq/releases/download/v4.44.6/yq_linux_arm64 -O yq
+    - chmod +x ./yq
+    # 2. YML -> JSON for the client specs
+    - cat node/specs/${RUNTIME}_client-specs.yaml | ./yq --output-format json > node/specs/${RUNTIME}_client-specs.json
+    # 3. Merge the client spec file into chainspec file and create the final spec file (e.g. gdev.json)
+    - jq -s '.[0] * .[1]' node/specs/${RUNTIME}_client-specs.json ${RUNTIME}-printed.json > ${RUNTIME}.json
     # Produce raw spec file
     - mkdir -p $CI_PROJECT_DIR/release
     - cargo run -Zgit=shallow-deps ${FEATURES} -- build-spec --chain=${RUNTIME}.json --disable-default-bootnode --raw > $RELEASE_FILE_RAW_SPEC
@@ -408,7 +473,7 @@ create_network_release:
   image: rust:1-bullseye
   variables:
     # Used by `release-network` command
-    SRTOOL_OUTPUT: $CI_PROJECT_DIR/release/srtool_output.json
+    SRTOOL_OUTPUT: $CI_PROJECT_DIR/release/network_srtool_output.json
   script:
     - *define_network_branch_vars
     # Release creation
@@ -430,17 +495,15 @@ create_network_release:
 
 create_client_release:
   stage: release
-  needs: ["build_runtime", "build_raw_specs"]
+  needs: ["build_raw_specs"]
   rules:
     - <<: *is_network_branch
   image: rust:1-bullseye
-  variables:
-    # Used by `release-runtime` command
-    SRTOOL_OUTPUT: $CI_PROJECT_DIR/release/srtool_output.json
   script:
     - *define_network_branch_vars
-    - cargo xtask release-runtime $CLIENT_RELEASE_NAME $NETWORK_RELEASE $CI_COMMIT_BRANCH $RUNTIME_MILESTONE
-    - cargo xtask create-asset-link $CLIENT_RELEASE_NAME ${RUNTIME}_runtime.compact.compressed.wasm https://nodes.pages.duniter.org/-/rust/duniter-v2s/-/jobs/$CI_JOB_ID/artifacts/$RELEASE_FILE_WASM
+    # Create the GitLab release page + tag and associate the milestone
+    - cargo xtask release-client $CLIENT_RELEASE_NAME $CI_COMMIT_BRANCH $CLIENT_MILESTONE
+    # Add the client assets
     - cargo xtask create-asset-link $CLIENT_RELEASE_NAME ${RUNTIME}_client-specs.yaml https://nodes.pages.duniter.org/-/rust/duniter-v2s/-/jobs/$CI_JOB_ID/artifacts/$RELEASE_FILE_CLIENT_SPEC
     - cargo xtask create-asset-link $CLIENT_RELEASE_NAME ${RUNTIME}-raw.json https://nodes.pages.duniter.org/-/rust/duniter-v2s/-/jobs/$CI_JOB_ID/artifacts/$RELEASE_FILE_RAW_SPEC
   artifacts:
@@ -449,3 +512,25 @@ create_client_release:
       - $CI_PROJECT_DIR/release/
   tags:
     - kepler
+
+create_runtime_release:
+  stage: release
+  needs: ["build_gdev_runtime"]
+  rules:
+    - <<: *is_runtime_branch
+  image: rust:1-bullseye
+  variables:
+    RUNTIME: gdev
+  script:
+    - *define_release_runtime_vars
+    # Create the GitLab release page + tag and associate the milestone.
+    # Note: the release name = the release tag = runtime milesone ($RUNTIME_MILESTONE)
+    - cargo xtask release-runtime $RUNTIME_MILESTONE $RUNTIME $CI_COMMIT_BRANCH $RUNTIME_MILESTONE
+    - cargo xtask create-asset-link $RUNTIME_MILESTONE ${RUNTIME}_runtime.compact.compressed.wasm https://nodes.pages.duniter.org/-/rust/duniter-v2s/-/jobs/$CI_JOB_ID/artifacts/$RELEASE_FILE_WASM
+    # In the future: also build gtest and g1 runtimes and atach them
+  artifacts:
+    expire_in: never
+    paths:
+      - $CI_PROJECT_DIR/release/
+  tags:
+    - kepler
diff --git a/Cargo.lock b/Cargo.lock
index 30051cc42849449b7b8ca5a398fb3860b9d1af3e..993aecf73f4f5f17e0559d06e2c664754ece2eae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8333,10 +8333,14 @@ dependencies = [
 name = "pallet-identity"
 version = "1.0.0"
 dependencies = [
+ "base64 0.22.1",
+ "bs58 0.5.1",
  "duniter-primitives",
+ "ed25519-dalek",
  "frame-benchmarking",
  "frame-support",
  "frame-system",
+ "log",
  "parity-scale-codec",
  "scale-info",
  "serde",
diff --git a/Cargo.toml b/Cargo.toml
index 63fb142bf441fe2df0bb9d5d0ba9eb00554ec627..5c450f3c364740c06bb71198b556adc8e4158901 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -48,9 +48,11 @@ version = '1.0.0'
 [workspace.dependencies]
 # crates.io dependencies
 anyhow = { version = "1.0.81", default-features = false }
+base64 = { version = "0.22.1", default-features = false }
 countmap = { version = "0.2.0", default-features = false }
 ctrlc = { version = "3.4.4", default-features = false }
 cucumber = { version = "0.20.2", default-features = false }
+ed25519-dalek = { version = "2.1.1", default-features = false }
 env_logger = { version = "0.11.3", default-features = false }
 notify = { version = "6.1.1", default-features = false }
 portpicker = { version = "0.1.1", default-features = false }
@@ -59,13 +61,11 @@ async-io = { version = "2.3.1", default-features = false }
 async-trait = { version = "0.1.78", default-features = false }
 thiserror = { version = "1.0.58", default-features = false }
 frame-metadata = { version = "16.0.0", default-features = false }
-graphql_client = { version = "0.10.0" }
-bs58 = { version = "0.5.0", default-features = false }
-placeholder = { version = "1.1.3", default-features = false }
-getrandom = { version = "0.2.12", default-features = false }
-impl-trait-for-tuples = { version = "0.2.2", default-features = false }
-clap = { version = "4.4.18" }
-clap_complete = { version = "4.4.10" }
+graphql_client = { version = "0.13.0" }
+bs58 = { version = "0.5.1", default-features = false }
+placeholder = { version = "1.1.4", default-features = false }
+clap = { version = "4.5.3" }
+clap_complete = { version = "4.5.1" }
 reqwest = { version = "0.12.0", default-features = false, features = [
     "rustls-tls",
 ] }
diff --git a/README.md b/README.md
index 513381125b9ed7e92c3a3a96f443a8c093f2f21a..f2fe8eb1a705d80b52f0d210fc2434d1fbea2c82 100644
--- a/README.md
+++ b/README.md
@@ -10,39 +10,46 @@
     <img alt="logov2" src="https://duniter.fr/img/duniterv2.svg" width="128" height="128"/>
 </div>
 
-## Documentation TOC
+## Documentation
 
-- [README](./README.md)
+Multiple documentation sources are available depending on the level of detail you need.
+
+- Full technical Rust doc (auto-generated with `cargo xtask gen-doc`) : https://doc-duniter-org.ipns.pagu.re/duniter/
+- User and client developer doc (official website) : https://duniter.org/wiki/duniter-v2/
+- Internal documentation (within git repository), see table of contents below : [./doc](./doc)
+
+### Internal documentation TOC
+
+- [README](./README.md) (this file)
   - [Use](#use)
-  - [Test](#test)
   - [Contribute](#contribute)
-  - [Structure](#project-structure)
-- [docker](./docker/) docker-related documentation
-- [docs](./docs/)
-  - [api](./docs/api/)
-    - [manual](./docs/api/manual.md)
+  - [License](#license)
+- [docs](./docs/) internal documentation
+  - [api](./docs/api/) API
+    - [manual](./docs/api/manual.md) manage account and identities
     - [runtime-calls](./docs/api/runtime-calls.md) the calls you can submit through the RPC API
     - [runtime-errors](./docs/api/runtime-errors.md) the errors you can get submitting a call
     - [runtime-events](./docs/api/runtime-events.md) the events you can get submitting a call
-  - [dev](./docs/dev/)
+  - [dev](./docs/dev/) developer documentation
     - [beginner-walkthrough](./docs/dev/beginner-walkthrough.md)
     - [git-conventions](./docs/dev/git-conventions.md)
+    - [pallet_conventions](./docs/dev/pallet_conventions.md)
     - [launch-a-live-network](./docs/dev/launch-a-live-network.md)
     - [setup](./docs/dev/setup.md)
     - [compilation features](./docs/dev/compilation.md)
     - [verify-runtime-code](./docs/dev/verify-runtime-code.md)
     - [weights-benchmarking](./docs/dev/weights-benchmarking.md)
     - [upgrade-substrate](./docs/dev/upgrade-substrate.md)
-  - [test](./docs/test/)
     - [replay-block](./docs/test/replay-block.md)
-  - [user](./docs/user/)
+  - [user](./docs/user/) user documentation
     - [autocompletion](./docs/user/autocompletion.md)
-    - [mirror](./docs/user/mirror.md) deploy a permanent ǦDev mirror node
-    - [smith](./docs/user/smith.md) deploy a permanent ǦDev validator node
     - [debian installation](./docs/user/installation_debian.md)
-  - [packaging](./docs/packaging/)
-    - [build-for-arm](./docs/packaging/build-for-arm.md)
+    - [distance](./docs/user/distance.md)
+    - [fees](./docs/user/fees.md)
+  - [packaging](./docs/packaging/) packaging
+    - [build-for-arm](./docs/packaging/build-for-arm.md) build for ARM architecture
     - [build-debian](./docs/packaging/build-deb.md) build a native Debian package
+- [docker](./docker/) docker-related documentation
 - [end2end-tests](./end2end-tests/) automated end to end tests written with cucumber
 - [live-tests](./live-tests/) sanity checks to test the storage of a live chain
 
@@ -52,23 +59,23 @@
 
 The easiest way is to use the docker image.
 
-Minimal command to deploy a **temporary** mirror peer:
+Minimal command to deploy a temporary mirror peer:
 
 ```docker
-docker run -it -p9944:9944 -e DUNITER_CHAIN_NAME=gdev duniter/duniter-v2s:v0.4.0 --tmp --execution=Wasm
+docker run -it -p9944:9944 -e DUNITER_CHAIN_NAME=gdev duniter/duniter-v2s-gdev-800:latest
 ```
 
-To go further, read [How to deploy a permanent mirror node on ĞDev network](./docs/user/rpc.md).
+To go further, read [How to deploy a permanent mirror node on ĞDev network 🔗](https://duniter.org/wiki/duniter-v2/#run-a-mirror-node).
 
 ### Create your local blockchain
 
 It can be useful to deploy your local blockchain, for instance to have a controlled environment to develop/test an application that interacts with the blockchain.
 
 ```docker
-docker run -it -p9944:9944 duniter/duniter-v2s:v0.4.0 --tmp
+docker run -it -p9944:9944 duniter/duniter-v2s-gdev-800:latest
 ```
 
-Or use the `docker-compose.yml` at the root of this repository.
+Or use the [`docker-compose.yml`](./docker-compose.yml) at the root of this repository.
 
 #### Control when your local blockchain should produce blocks
 
@@ -79,34 +86,9 @@ You can decide when to produce blocks with the cli option `--sealing` which has
 - `--sealing=instant`: produce a block immediately upon receiving a transaction into the transaction pool
 - `--sealing=manual`: produce a block upon receiving an RPC request (method `engine_createBlock`).
 
-### Autocompletion
-
-See [autocompletion](./docs/user/autocompletion.md).
+### Shell autocompletion
 
-## Test
-
-### Test a specific commit
-
-At each commit on master, an image with the tag `debug-sha-********` is published, where `********`
-corresponds to the first 8 hash characters of the commit.
-
-Usage:
-
-```docker
-docker run -it -p9944:9944 --name duniter-v2s duniter/duniter-v2s:debug-sha-b836f1a6
-```
-
-Then open `https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944` in a browser.
-
-Enable detailed logging:
-
-```docker
-docker run -it -p9944:9944 --name duniter-v2s \
-  -e RUST_LOG=debug \
-  -e RUST_BACKTRACE=1 \
-  -lruntime=debug \
-  duniter/duniter-v2s:debug-sha-b836f1a6
-```
+See [autocompletion](./docs/user/autocompletion.md) to generate shell autocompletion for duniter commands.
 
 ## Contribute
 
@@ -133,20 +115,11 @@ cargo build
 Use Rust's native `cargo` command to build and launch the node:
 
 ```sh
-cargo run -- --dev --tmp
+cargo run -- --dev
 ```
 
 This will deploy a local blockchain with test accounts (Alice, Bob, etc) in the genesis.
-
-## Single-Node Development Chain
-
-This command will start the single-node development chain with persistent state:
-
-```bash
-./target/debug/duniter --dev --tmp
-```
-
-Then open `https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944` in a browser.
+Open `https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944` to watch and interact with your node.
 
 Start the development chain with detailed logging:
 
@@ -154,138 +127,11 @@ Start the development chain with detailed logging:
 RUST_LOG=debug RUST_BACKTRACE=1 ./target/debug/duniter -lruntime=debug --dev
 ```
 
-## Multi-Node Local Testnet
-
-If you want to see the multi-node consensus algorithm in action, refer to
-[our Start a Private Network tutorial](https://substrate.dev/docs/en/tutorials/start-a-private-network/).
-
-### Purge previous local testnet
-
-```
-./target/debug/duniter purge-chain --base-path /tmp/alice --chain local
-./target/debug/duniter purge-chain --base-path /tmp/bob --chain local
-
-```
-
-### Start Alice's node
-
-```bash
-./target/debug/duniter \
-  --base-path /tmp/alice \
-  --chain local \
-  --alice \
-  --port 30333 \
-  --rpc-port 9944 \
-  --node-key 0000000000000000000000000000000000000000000000000000000000000001 \
-  --validator
-```
-
-### Start Bob's node
+## License
 
-```bash
-./target/debug/duniter \
-  --base-path /tmp/bob \
-  --chain local \
-  --bob \
-  --port 30334 \
-  --rpc-port 9945 \
-  --validator \
-  --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp
-```
+See [LICENSE](./LICENSE)
 
-## Project Structure
-
-A Substrate project such as this consists of a number of components that are spread across a few
-directories.
-
-### Node
-
-A blockchain node is an application that allows users to participate in a blockchain network.
-Substrate-based blockchain nodes expose a number of capabilities:
-
-- Networking: Substrate nodes use the [`libp2p`](https://libp2p.io/) networking stack to allow the
-  nodes in the network to communicate with one another.
-- Consensus: Blockchains must have a way to come to
-  [consensus](https://substrate.dev/docs/en/knowledgebase/advanced/consensus) on the state of the
-  network. Substrate makes it possible to supply custom consensus engines and also ships with
-  several consensus mechanisms that have been built on top of
-  [Web3 Foundation research](https://research.web3.foundation/en/latest/polkadot/NPoS/index.html).
-- RPC Server: A remote procedure call (RPC) server is used to interact with Substrate nodes.
-
-There are several files in the `node` directory - take special note of the following:
-
-- [`chain_spec.rs`](./node/src/chain_spec.rs): A
-  [chain specification](https://substrate.dev/docs/en/knowledgebase/integrate/chain-spec) is a
-  source code file that defines a Substrate chain's initial (genesis) state. Chain specifications
-  are useful for development and testing, and critical when architecting the launch of a
-  production chain. Take note of the `development_chain_spec` and `testnet_genesis` functions, which
-  are used to define the genesis state for the local development chain configuration. These
-  functions identify some
-  [well-known accounts](https://substrate.dev/docs/en/knowledgebase/integrate/subkey#well-known-keys)
-  and use them to configure the blockchain's initial state.
-- [`service.rs`](./node/src/service.rs): This file defines the node implementation. Take note of
-  the libraries that this file imports and the names of the functions it invokes. In particular,
-  there are references to consensus-related topics, such as the
-  [longest chain rule](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#longest-chain-rule),
-  the [Babe](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#babe) block authoring
-  mechanism and the
-  [GRANDPA](https://substrate.dev/docs/en/knowledgebase/advanced/consensus#grandpa) finality
-  gadget.
-
-After the node has been [built](#build), refer to the embedded documentation to learn more about the
-capabilities and configuration parameters that it exposes:
-
-```shell
-./target/debug/duniter --help
 ```
-
-### Runtime
-
-In Substrate, the terms
-"[runtime](https://substrate.dev/docs/en/knowledgebase/getting-started/glossary#runtime)" and
-"[state transition function](https://substrate.dev/docs/en/knowledgebase/getting-started/glossary#stf-state-transition-function)"
-are analogous - they refer to the core logic of the blockchain that is responsible for validating
-blocks and executing the state changes they define. The Substrate project in this repository uses
-the [FRAME](https://substrate.dev/docs/en/knowledgebase/runtime/frame) framework to construct a
-blockchain runtime. FRAME allows runtime developers to declare domain-specific logic in modules
-called "pallets". At the heart of FRAME is a helpful
-[macro language](https://substrate.dev/docs/en/knowledgebase/runtime/macros) that makes it easy to
-create pallets and flexibly compose them to create blockchains that can address
-[a variety of needs](https://www.substrate.io/substrate-users/).
-
-Review the [FRAME runtime implementation](./runtime/src/lib.rs) included in this template and note
-the following:
-
-- This file configures several pallets to include in the runtime. Each pallet configuration is
-  defined by a code block that begins with `impl $PALLET_NAME::Config for Runtime`.
-- The pallets are composed into a single runtime by way of the
-  [`construct_runtime!`](https://crates.parity.io/frame_support/macro.construct_runtime.html)
-  macro, which is part of the core
-  [FRAME Support](https://substrate.dev/docs/en/knowledgebase/runtime/frame#support-library)
-  library.
-
-### Pallets
-
-The runtime in this project is constructed using many FRAME pallets that ship with the
-[core Substrate repository](https://github.com/paritytech/substrate/tree/master/frame) and a
-template pallet that is [defined in the `pallets`](./pallets/template/src/lib.rs) directory.
-
-A FRAME pallet is compromised of a number of blockchain primitives:
-
-- Storage: FRAME defines a rich set of powerful
-  [storage abstractions](https://substrate.dev/docs/en/knowledgebase/runtime/storage) that makes
-  it easy to use Substrate's efficient key-value database to manage the evolving state of a
-  blockchain.
-- Dispatchables: FRAME pallets define special types of functions that can be invoked (dispatched)
-  from outside of the runtime in order to update its state.
-- Events: Substrate uses [events](https://substrate.dev/docs/en/knowledgebase/runtime/events) to
-  notify users of important changes in the runtime.
-- Errors: When a dispatchable fails, it returns an error.
-- Config: The `Config` configuration interface is used to define the types and parameters upon
-  which a FRAME pallet depends.
-
-## License
-
 CopyLeft 2021-2023 Axiom-Team
 
 Some parts borrowed from Polkadot (Parity Technologies (UK) Ltd.)
@@ -301,3 +147,4 @@ GNU Affero General Public License for more details.
 
 You should have received a copy of the GNU Affero General Public License
 along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
+```
diff --git a/client/distance/src/lib.rs b/client/distance/src/lib.rs
index c36091612c42bb77aca3b7a61ef62e796f4ad713..88d15b88ffa20a8d61291b39c0571e96351b512c 100644
--- a/client/distance/src/lib.rs
+++ b/client/distance/src/lib.rs
@@ -59,37 +59,35 @@ pub fn create_distance_inherent_data_provider<B, C, Backend>(
     parent: B::Hash,
     distance_dir: PathBuf,
     owner_keys: &[sp_core::sr25519::Public],
-) -> Result<sp_distance::InherentDataProvider<IdtyIndex>, sc_client_api::blockchain::Error>
+) -> sp_distance::InherentDataProvider<IdtyIndex>
 where
     B: BlockT,
     C: ProvideUncles<B> + StorageProvider<B, Backend>,
     Backend: sc_client_api::Backend<B>,
     IdtyIndex: Decode + Encode + PartialEq + TypeInfo,
 {
-    // Retrieve the period_index from storage. If storage is inaccessible or the data is corrupted,
-    // return the appropriate error.
+    // Retrieve the period_index from storage.
     let period_index = client
         .storage(
             parent,
             &StorageKey(
                 frame_support::storage::storage_prefix(b"Distance", b"CurrentPeriodIndex").to_vec(),
             ),
-        )?
-        .map_or_else(
-            || {
-                Err(sc_client_api::blockchain::Error::Storage(
-                    "CurrentPeriodIndex value not found".to_string(),
-                ))
-            },
-            |raw| {
-                u32::decode(&mut &raw.0[..])
-                    .map_err(|e| sc_client_api::blockchain::Error::from_state(Box::new(e)))
-            },
-        )?;
+        )
+        .ok()
+        .flatten()
+        .and_then(|raw| u32::decode(&mut &raw.0[..]).ok());
+
+    // Return early if the storage is inaccessible or the data is corrupted.
+    let period_index = match period_index {
+        Some(index) => index,
+        None => {
+            log::error!("🧙 [distance inherent] PeriodIndex decoding failed.");
+            return sp_distance::InherentDataProvider::<IdtyIndex>::new(None);
+        }
+    };
 
     // Retrieve the published_results from storage.
-    // Return an error if the storage is inaccessible.
-    // If accessible, continue execution. If None, it means there are no published_results at this block.
     let published_results = client
         .storage(
             parent,
@@ -105,47 +103,61 @@ where
                 )
                 .to_vec(),
             ),
-        )?
+        )
+        .ok()
+        .flatten()
         .and_then(|raw| {
             pallet_distance::EvaluationPool::<AccountId32, IdtyIndex>::decode(&mut &raw.0[..]).ok()
         });
 
-    // Have we already published a result for this period?
-    if let Some(results) = published_results {
-        // Find the account associated with the BABE key that is in our owner keys.
-        let mut local_account = None;
-        for key in owner_keys {
-            // Session::KeyOwner is StorageMap<_, Twox64Concat, (KeyTypeId, Vec<u8>), AccountId32, OptionQuery>
-            // Slices (variable length) and array (fixed length) are encoded differently, so the `.as_slice()` is needed
-            let item_key = (sp_runtime::KeyTypeId(*b"babe"), key.0.as_slice()).encode();
-            let mut storage_key =
-                frame_support::storage::storage_prefix(b"Session", b"KeyOwner").to_vec();
-            storage_key.extend_from_slice(&sp_core::twox_64(&item_key));
-            storage_key.extend_from_slice(&item_key);
+    // Return early if the storage is inaccessible or the data is corrupted.
+    let published_results = match published_results {
+        Some(published_results) => published_results,
+        None => {
+            log::info!("🧙 [distance inherent] No published result at this block.");
+            return sp_distance::InherentDataProvider::<IdtyIndex>::new(None);
+        }
+    };
 
-            if let Some(raw_data) = client.storage(parent, &StorageKey(storage_key))? {
-                if let Ok(key_owner) = AccountId32::decode(&mut &raw_data.0[..]) {
-                    local_account = Some(key_owner);
-                    break;
-                } else {
-                    log::warn!("🧙 [distance oracle] Cannot decode key owner value");
-                }
+    // Find the account associated with the BABE key that is in our owner keys.
+    let mut local_account = None;
+    for key in owner_keys {
+        // Session::KeyOwner is StorageMap<_, Twox64Concat, (KeyTypeId, Vec<u8>), AccountId32, OptionQuery>
+        // Slices (variable length) and array (fixed length) are encoded differently, so the `.as_slice()` is needed
+        let item_key = (sp_runtime::KeyTypeId(*b"babe"), key.0.as_slice()).encode();
+        let mut storage_key =
+            frame_support::storage::storage_prefix(b"Session", b"KeyOwner").to_vec();
+        storage_key.extend_from_slice(&sp_core::twox_64(&item_key));
+        storage_key.extend_from_slice(&item_key);
+
+        if let Some(raw_data) = client
+            .storage(parent, &StorageKey(storage_key))
+            .ok()
+            .flatten()
+        {
+            if let Ok(key_owner) = AccountId32::decode(&mut &raw_data.0[..]) {
+                local_account = Some(key_owner);
+                break;
+            } else {
+                log::warn!("🧙 [distance inherent] Cannot decode key owner value");
             }
         }
-        if let Some(local_account) = local_account {
-            if results.evaluators.contains(&local_account) {
-                log::debug!("🧙 [distance oracle] Already published a result for this period");
-                return Ok(sp_distance::InherentDataProvider::<IdtyIndex>::new(None));
-            }
-        } else {
-            log::error!("🧙 [distance oracle] Cannot find our BABE owner key");
-            return Ok(sp_distance::InherentDataProvider::<IdtyIndex>::new(None));
+    }
+
+    // Have we already published a result for this period?
+    if let Some(local_account) = local_account {
+        if published_results.evaluators.contains(&local_account) {
+            log::debug!("🧙 [distance inherent] Already published a result for this period");
+            return sp_distance::InherentDataProvider::<IdtyIndex>::new(None);
         }
+    } else {
+        log::error!("🧙 [distance inherent] Cannot find our BABE owner key");
+        return sp_distance::InherentDataProvider::<IdtyIndex>::new(None);
     }
 
     // Read evaluation result from file, if it exists
     log::debug!(
-        "🧙 [distance oracle] Reading evaluation result from file {:?}",
+        "🧙 [distance inherent] Reading evaluation result from file {:?}",
         distance_dir.clone().join(period_index.to_string())
     );
     let evaluation_result = match std::fs::read(
@@ -155,20 +167,20 @@ where
         Err(e) => {
             match e.kind() {
                 std::io::ErrorKind::NotFound => {
-                    log::debug!("🧙 [distance oracle] Evaluation result file not found. Please ensure that the oracle version matches {}", VERSION_PREFIX);
+                    log::debug!("🧙 [distance inherent] Evaluation result file not found. Please ensure that the oracle version matches {}", VERSION_PREFIX);
                 }
                 _ => {
                     log::error!(
-                        "🧙 [distance oracle] Cannot read distance evaluation result file: {e:?}"
+                        "🧙 [distance inherent] Cannot read distance evaluation result file: {e:?}"
                     );
                 }
             }
-            return Ok(sp_distance::InherentDataProvider::<IdtyIndex>::new(None));
+            return sp_distance::InherentDataProvider::<IdtyIndex>::new(None);
         }
     };
 
-    log::info!("🧙 [distance oracle] Providing evaluation result");
-    Ok(sp_distance::InherentDataProvider::<IdtyIndex>::new(Some(
+    log::info!("🧙 [distance inherent] Providing evaluation result");
+    sp_distance::InherentDataProvider::<IdtyIndex>::new(Some(
         sp_distance::ComputationResult::decode(&mut evaluation_result.as_slice()).unwrap(),
-    )))
+    ))
 }
diff --git a/docker-compose.yml b/docker-compose.yml
index 85dac1986254ae50a35fd08e9e303dcd59edf593..699e3920a09102e4fb0f52e7145a60a4b5569c4a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,7 +5,7 @@ services:
     container_name: duniter-v2s-mirror
     # the image tells which network you are connecting to
     # here it is gdev network
-    image: duniter/duniter-v2s-gdev:latest
+    image: duniter/duniter-v2s-gdev-800:latest
     ports:
       # prometheus telemetry to monitor resource use
       - 9615:9615
diff --git a/docker/Dockerfile b/docker/Dockerfile
index de4215ee03149c605324709f97160353fd3fb626..f0336f5f14932b8ee65067ab58e9c2f0f2676bf8 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,5 +1,5 @@
 # Workaround for https://github.com/containers/buildah/issues/4742
-FROM debian:bullseye-slim as target
+FROM debian:bullseye-slim AS target
 
 # ------------------------------------------------------------------------------
 # Build Stage
@@ -7,7 +7,7 @@ FROM debian:bullseye-slim as target
 
 # When building for a foreign arch, use cross-compilation
 # https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
-FROM --platform=$BUILDPLATFORM rust:1-bullseye as build
+FROM --platform=$BUILDPLATFORM rust:1-bullseye AS build
 ARG BUILDPLATFORM
 ARG TARGETPLATFORM
 
diff --git a/docker/compose/docker-compose.smith.yml b/docker/compose/docker-compose.smith.yml
index cf36c98ed0cd71ba368011871b9750592a31e0e8..fc1a48fe3e1901f8ba69e61be9d2e4b0c1a59a36 100644
--- a/docker/compose/docker-compose.smith.yml
+++ b/docker/compose/docker-compose.smith.yml
@@ -4,7 +4,7 @@ services:
   # duniter smith node
   duniter-v2s-smith:
     container_name: duniter-v2s-smith
-    image: duniter/duniter-v2s-gdev:latest
+    image: duniter/duniter-v2s-gdev-800:latest
     ports:
       # RPC API of a smith node should not be exposed publicly!
       - 127.0.0.1:9944:9944
diff --git a/docker/docker-entrypoint b/docker/docker-entrypoint
index 16b0952aae28cddbf257bb88509449c3ce924431..3666aad0e0dc0310a7be807203167e5cbad149c1 100755
--- a/docker/docker-entrypoint
+++ b/docker/docker-entrypoint
@@ -21,27 +21,45 @@ function ternary () {
   fi
 }
 
+# Define chain name at the beginning
+# with #274 we could have default given in network branch
+DUNITER_CHAIN_NAME="${DUNITER_CHAIN_NAME:-dev}"
+case "$DUNITER_CHAIN_NAME" in
+  dev)
+    chain=(--dev)
+    ;;
+  *)
+    chain=(--chain "$DUNITER_CHAIN_NAME")
+    ;;
+esac
+
+# Node name will appear on network
 DUNITER_NODE_NAME="${DUNITER_NODE_NAME:-$DUNITER_INSTANCE_NAME}"
 if [ -n "$DUNITER_NODE_NAME" ]; then
   set -- "$@" --name "$DUNITER_NODE_NAME"
 fi
 
+# Path of key file. Should be generated below if not present before starting Duniter
 _DUNITER_KEY_FILE=/var/lib/duniter/node.key
 set -- "$@" --node-key-file "$_DUNITER_KEY_FILE"
 
+# Generate node.key if not existing (chain name is required)
 if [ ! -f "$_DUNITER_KEY_FILE" ]; then
   echo "Generating node key file '$_DUNITER_KEY_FILE'..."
-  duniter key generate-node-key --file "$_DUNITER_KEY_FILE"
+  duniter key generate-node-key --file "$_DUNITER_KEY_FILE" "${chain[@]}"
 else
   echo "Node key file '$_DUNITER_KEY_FILE' exists."
 fi
+# Log peer ID
 _DUNITER_PEER_ID="$(duniter key inspect-node-key --file "$_DUNITER_KEY_FILE")"
 echo "Node peer ID is '$_DUNITER_PEER_ID'."
 
+# Define public address (with dns, correct port and protocol for instance)
 if [ -n "$DUNITER_PUBLIC_ADDR" ]; then
   set -- "$@" --public-addr "$DUNITER_PUBLIC_ADDR"
 fi
 
+# Define listen address (inside docker)
 if [ -n "$DUNITER_LISTEN_ADDR" ]; then
   set -- "$@" --listen-addr "$DUNITER_LISTEN_ADDR"
 fi
@@ -49,6 +67,7 @@ fi
 DUNITER_RPC_CORS="${DUNITER_RPC_CORS:-all}"
 set -- "$@" --rpc-cors "$DUNITER_RPC_CORS"
 
+# In case of validator, unsafe rpc methods are needed (like rotate_key) and should not be exposed publicly
 DUNITER_VALIDATOR=$(boolean "${DUNITER_VALIDATOR:-false}")
 if [ "$DUNITER_VALIDATOR" = true ]; then
   set -- "$@" --rpc-methods Unsafe --validator
@@ -64,6 +83,7 @@ if [ "$DUNITER_DISABLE_TELEMETRY" = true ]; then
   set -- "$@" --no-telemetry
 fi
 
+# Set pruning profile
 DUNITER_PRUNING_PROFILE="${DUNITER_PRUNING_PROFILE:-default}"
 case "$DUNITER_PRUNING_PROFILE" in
   default)
@@ -79,16 +99,9 @@ case "$DUNITER_PRUNING_PROFILE" in
     ;;
 esac
 
-DUNITER_CHAIN_NAME="${DUNITER_CHAIN_NAME:-dev}"
-case "$DUNITER_CHAIN_NAME" in
-  dev)
-    chain=(--dev)
-    ;;
-  *)
-    chain=(--chain "$DUNITER_CHAIN_NAME")
-    ;;
-esac
-
+# Set main command
+# Since we are inside docker, we can bind to all interfaces.
+# User will bind port to host interface or set reverse proxy when needed.
 set -- "$@" \
   "${chain[@]}" \
   -d /var/lib/duniter --unsafe-rpc-external
diff --git a/docs/api/manual.md b/docs/api/manual.md
index 0b9a91eb7f77a347a0379ee6e4515435460db990..a43b76b1c5679adf47e1e53a057cef9e0bdc0101 100644
--- a/docs/api/manual.md
+++ b/docs/api/manual.md
@@ -3,7 +3,7 @@
 This functional documentation presents how wallets can interact with the blockchain.
 It is intended to complete the [runtime calls documentation](./runtime-calls.md) in a runtime-specific way to fit the real needs of wallet developers.
 
-Only ĞDev is covered for now.
+NOTE : a more detailed doc is available at <https://duniter.org/wiki/duniter-v2/doc/>
 
 ## Notations
 
@@ -11,26 +11,27 @@ Only ĞDev is covered for now.
 
 ## Account existence
 
-An account exists if and only if it contains at least the existential deposit (2 ĞD).
+An account exists if and only if it contains at least the existential deposit (`balances.existentialDeposit` = 1 ĞD).
 
 ## Become member
 
-Only use `identity` pallet. The `membership` calls are disabled.
+Only use `identity` pallet.
 
 1. The account that wants to gain membership needs to exists.
 1. Any account that already has membership and respects the identity creation period can create an identity for another account, using `identity.createIdentity`.
-1. The account has to confirm its identity with a name, using `identity.confirmIdentity`. The name must be ASCII alphanumeric, punctuation or space characters: ``/^[-!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~a-zA-Z0-9 ]{3,64}$/`` (additionally, trailing spaces and double spaces are forbidden, as a phishing countermeasure). If the name is already used, the call will fail.
+1. The account has to confirm its identity with a name, using `identity.confirmIdentity`. The name must be ASCII alphanumeric, punctuation or space characters: `` /^[-!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~a-zA-Z0-9 ]{3,64}$/ `` (additionally, trailing spaces and double spaces are forbidden, as a phishing countermeasure). If the name is already used, the call will fail.
 1. 4 different member accounts must certify the account using `cert.addCert`.
-1. The distance evaluation must be requested for the pending identity using `distance.evaluateDistance`.
-1. 3 sessions later, if the distance rule is respected, `identity.validateIdentity` can be called.
+1. The distance evaluation must be requested for the pending identity using `distance.requestDistanceEvaluation`.
+1. 3 distance sessions later, if the distance rule is respected, identity is validated automatically.
 
 ## Change key
 
 A member can request a key change via the `identity.change_onwner_key` call. It needs the following SCALE encoded (see SCALE encoding section below) payload:
-* The new owner key payload prefix (rust definition: `b"icok"`)
-* the genesis block hash. (rust type `[u8; 32]` (`H256`))
-* The identity index (rust type `u64`)
-* The old key (rust type `u64`)
+
+- The new owner key payload prefix (rust definition: `b"icok"`)
+- the genesis block hash. (rust type `[u8; 32]` (`H256`))
+- The identity index (rust type `u64`)
+- The old key (rust type `u64`)
 
 This payload must be signed with the new key.
 
@@ -43,9 +44,10 @@ This feature is useful in case the user has lost their private key since the rev
 ### Generate the revocation payload
 
 The revocation needs this SCALE encoded (see SCALE encoding section below) payload:
-* The revocation payload prefix (rust definition: `b"revo"`)
-* The identity index (rust type `u64`)
-* the genesis block hash. (rust type `[u8; 32]` (`H256`))
+
+- The revocation payload prefix (rust definition: `b"revo"`)
+- The identity index (rust type `u64`)
+- the genesis block hash. (rust type `[u8; 32]` (`H256`))
 
 This payload must be signed with the corresponding revocation key.
 
diff --git a/docs/api/runtime-calls.md b/docs/api/runtime-calls.md
index 3ab03a9326b6db1555a061be6a0dc5cd63c3f172..5adde95a843e80509d6e673d0b10f95218751734 100644
--- a/docs/api/runtime-calls.md
+++ b/docs/api/runtime-calls.md
@@ -3,243 +3,40 @@
 Calls are categorized according to the dispatch origin they require:
 
 1. **User calls**: the dispatch origin for this kind of call must be signed by
-the transactor. This is the only call category that can be submitted with an extrinsic.
+   the transactor. This is the only call category that can be submitted with an extrinsic.
 1. **Root calls**: This kind of call requires a special origin that can only be invoked
-through on-chain governance mechanisms.
+   through on-chain governance mechanisms.
 1. **Inherent calls**: This kind of call is invoked by the author of the block itself
-(usually automatically by the node).
+   (usually automatically by the node).
 1. **Disabled calls**: These calls can not be called directly, they are reserved for internal use by other runtime calls.
 
+We only document user calls below.
 
-## User calls
+There are **63** user calls from **17** pallets.
 
-There are **86** user calls from **21** pallets.
+## Account - 1
 
-### Account - 1
-
-#### unlink_identity - 0
+### unlink_identity - 0
 
 <details><summary><code>unlink_identity()</code></summary>
 
-Taking 0.0111 % of a block.
-
-```rust
-```
-</details>
-
-
-Unlink the identity associated with the account.
-
-### Scheduler - 2
-
-#### schedule - 0
-
-<details><summary><code>schedule(when, maybe_periodic, priority, call)</code></summary>
-
-Taking 0.0122 % of a block.
-
-```rust
-when: BlockNumberFor<T>
-maybe_periodic: Option<schedule::Period<BlockNumberFor<T>>>
-priority: schedule::Priority
-call: Box<<T as Config>::RuntimeCall>
-```
-</details>
-
-
-Anonymously schedule a task.
-
-#### cancel - 1
-
-<details><summary><code>cancel(when, index)</code></summary>
-
-Taking 0.0236 % of a block.
-
-```rust
-when: BlockNumberFor<T>
-index: u32
-```
-</details>
-
-
-Cancel an anonymously scheduled task.
-
-#### schedule_named - 2
-
-<details><summary><code>schedule_named(id, when, maybe_periodic, priority, call)</code></summary>
-
-Taking 0.0189 % of a block.
-
-```rust
-id: TaskName
-when: BlockNumberFor<T>
-maybe_periodic: Option<schedule::Period<BlockNumberFor<T>>>
-priority: schedule::Priority
-call: Box<<T as Config>::RuntimeCall>
-```
-</details>
-
-
-Schedule a named task.
-
-#### cancel_named - 3
-
-<details><summary><code>cancel_named(id)</code></summary>
-
-Taking 0.0248 % of a block.
-
-```rust
-id: TaskName
-```
-</details>
-
-
-Cancel a named scheduled task.
-
-#### schedule_after - 4
-
-<details><summary><code>schedule_after(after, maybe_periodic, priority, call)</code></summary>
-
-No weight available.
-
-```rust
-after: BlockNumberFor<T>
-maybe_periodic: Option<schedule::Period<BlockNumberFor<T>>>
-priority: schedule::Priority
-call: Box<<T as Config>::RuntimeCall>
-```
-</details>
-
-
-Anonymously schedule a task after a delay.
-
-#### schedule_named_after - 5
-
-<details><summary><code>schedule_named_after(id, after, maybe_periodic, priority, call)</code></summary>
-
-No weight available.
-
-```rust
-id: TaskName
-after: BlockNumberFor<T>
-maybe_periodic: Option<schedule::Period<BlockNumberFor<T>>>
-priority: schedule::Priority
-call: Box<<T as Config>::RuntimeCall>
-```
-</details>
-
-
-Schedule a named task after a delay.
-
-#### set_retry - 6
-
-<details><summary><code>set_retry(task, retries, period)</code></summary>
-
-Taking 0.012 % of a block.
-
-```rust
-task: TaskAddress<BlockNumberFor<T>>
-retries: u8
-period: BlockNumberFor<T>
-```
-</details>
-
-
-Set a retry configuration for a task so that, in case its scheduled run fails, it will
-be retried after `period` blocks, for a total amount of `retries` retries or until it
-succeeds.
-
-Tasks which need to be scheduled for a retry are still subject to weight metering and
-agenda space, same as a regular task. If a periodic task fails, it will be scheduled
-normally while the task is retrying.
-
-Tasks scheduled as a result of a retry for a periodic task are unnamed, non-periodic
-clones of the original task. Their retry configuration will be derived from the
-original task's configuration, but will have a lower value for `remaining` than the
-original `total_retries`.
-
-#### set_retry_named - 7
-
-<details><summary><code>set_retry_named(id, retries, period)</code></summary>
-
-Taking 0.0132 % of a block.
-
-```rust
-id: TaskName
-retries: u8
-period: BlockNumberFor<T>
-```
-</details>
-
-
-Set a retry configuration for a named task so that, in case its scheduled run fails, it
-will be retried after `period` blocks, for a total amount of `retries` retries or until
-it succeeds.
-
-Tasks which need to be scheduled for a retry are still subject to weight metering and
-agenda space, same as a regular task. If a periodic task fails, it will be scheduled
-normally while the task is retrying.
-
-Tasks scheduled as a result of a retry for a periodic task are unnamed, non-periodic
-clones of the original task. Their retry configuration will be derived from the
-original task's configuration, but will have a lower value for `remaining` than the
-original `total_retries`.
-
-#### cancel_retry - 8
-
-<details><summary><code>cancel_retry(task)</code></summary>
-
-Taking 0.012 % of a block.
-
-```rust
-task: TaskAddress<BlockNumberFor<T>>
-```
-</details>
-
-
-Removes the retry configuration of a task.
-
-#### cancel_retry_named - 9
-
-<details><summary><code>cancel_retry_named(id)</code></summary>
-
-Taking 0.0132 % of a block.
+Taking 0.0109 % of a block.
 
 ```rust
-id: TaskName
 ```
 </details>
 
 
-Cancel the retry configuration of a named task.
-
-### Babe - 3
-
-#### report_equivocation - 0
-
-<details><summary><code>report_equivocation(equivocation_proof, key_owner_proof)</code></summary>
-
-No weight available.
-
-```rust
-equivocation_proof: Box<EquivocationProof<HeaderFor<T>>>
-key_owner_proof: T::KeyOwnerProof
-```
-</details>
-
 
-Report authority equivocation/misbehavior. This method will verify
-the equivocation proof and validate the given key ownership proof
-against the extracted offender. If both are valid, the offence will
-be reported.
+Unlink the identity associated with the account.
 
-### Balances - 6
+## Balances - 6
 
-#### transfer_allow_death - 0
+### transfer_allow_death - 0
 
 <details><summary><code>transfer_allow_death(dest, value)</code></summary>
 
-Taking 0.0199 % of a block.
+Taking 0.0194 % of a block.
 
 ```rust
 dest: AccountIdLookupOf<T>
@@ -248,6 +45,7 @@ value: T::Balance
 </details>
 
 
+
 Transfer some liquid free balance to another account.
 
 `transfer_allow_death` will set the `FreeBalance` of the sender and receiver.
@@ -256,11 +54,11 @@ of the transfer, the account will be reaped.
 
 The dispatch origin for this call must be `Signed` by the transactor.
 
-#### transfer_keep_alive - 3
+### transfer_keep_alive - 3
 
 <details><summary><code>transfer_keep_alive(dest, value)</code></summary>
 
-Taking 0.0125 % of a block.
+Taking 0.0122 % of a block.
 
 ```rust
 dest: AccountIdLookupOf<T>
@@ -269,6 +67,7 @@ value: T::Balance
 </details>
 
 
+
 Same as the [`transfer_allow_death`] call, but with a check that the transfer will not
 kill the origin account.
 
@@ -276,11 +75,11 @@ kill the origin account.
 
 [`transfer_allow_death`]: struct.Pallet.html#method.transfer
 
-#### transfer_all - 4
+### transfer_all - 4
 
 <details><summary><code>transfer_all(dest, keep_alive)</code></summary>
 
-Taking 0.0129 % of a block.
+Taking 0.0126 % of a block.
 
 ```rust
 dest: AccountIdLookupOf<T>
@@ -289,6 +88,7 @@ keep_alive: bool
 </details>
 
 
+
 Transfer the entire transferable balance from the caller account.
 
 NOTE: This function only attempts to transfer _transferable_ balances. This means that
@@ -305,78 +105,13 @@ The dispatch origin of this call must be Signed.
   transfer everything except at least the existential deposit, which will guarantee to
   keep the sender account alive (true).
 
-#### force_set_balance - 8
-
-<details><summary><code>force_set_balance(who, new_free)</code></summary>
-
-No weight available.
-
-```rust
-who: AccountIdLookupOf<T>
-new_free: T::Balance
-```
-</details>
-
+## OneshotAccount - 7
 
-Upgrade a specified account.
-
-- `origin`: Must be `Signed`.
-- `who`: The account to be upgraded.
-
-This will waive the transaction fee if at least all but 10% of the accounts needed to
-be upgraded. (We let some not have to be upgraded just in order to allow for the
-possibility of churn).
-Set the regular balance of a given account.
-
-The dispatch origin for this call is `root`.
-
-#### force_adjust_total_issuance - 9
-
-<details><summary><code>force_adjust_total_issuance(direction, delta)</code></summary>
-
-Taking 0.0048 % of a block.
-
-```rust
-direction: AdjustmentDirection
-delta: T::Balance
-```
-</details>
-
-
-Adjust the total issuance in a saturating way.
-
-Can only be called by root and always needs a positive `delta`.
-
-# Example
-
-#### burn - 10
-
-<details><summary><code>burn(value, keep_alive)</code></summary>
-
-No weight available.
-
-```rust
-value: T::Balance
-keep_alive: bool
-```
-</details>
-
-
-Burn the specified liquid free balance from the origin account.
-
-If the origin's account ends up below the existential deposit as a result
-of the burn and `keep_alive` is false, the account will be reaped.
-
-Unlike sending funds to a _burn_ address, which merely makes the funds inaccessible,
-this `burn` operation will reduce total issuance by the amount _burned_.
-
-### OneshotAccount - 7
-
-#### create_oneshot_account - 0
+### create_oneshot_account - 0
 
 <details><summary><code>create_oneshot_account(dest, value)</code></summary>
 
-Taking 0.012 % of a block.
+Taking 0.0117 % of a block.
 
 ```rust
 dest: <T::Lookup as StaticLookup>::Source
@@ -385,6 +120,7 @@ value: BalanceOf<T>
 </details>
 
 
+
 Create an account that can only be consumed once
 
 - `dest`: The oneshot account to be created.
@@ -392,11 +128,11 @@ Create an account that can only be consumed once
 
 Origin account is kept alive.
 
-#### consume_oneshot_account - 1
+### consume_oneshot_account - 1
 
 <details><summary><code>consume_oneshot_account(block_height, dest)</code></summary>
 
-Taking 0.0197 % of a block.
+Taking 0.0195 % of a block.
 
 ```rust
 block_height: BlockNumberFor<T>
@@ -405,17 +141,18 @@ dest: Account<<T::Lookup as StaticLookup>::Source>
 </details>
 
 
+
 Consume a oneshot account and transfer its balance to an account
 
 - `block_height`: Must be a recent block number. The limit is `BlockHashCount` in the past. (this is to prevent replay attacks)
 - `dest`: The destination account.
 - `dest_is_oneshot`: If set to `true`, then a oneshot account is created at `dest`. Else, `dest` has to be an existing account.
 
-#### consume_oneshot_account_with_remaining - 2
+### consume_oneshot_account_with_remaining - 2
 
 <details><summary><code>consume_oneshot_account_with_remaining(block_height, dest, remaining_to, balance)</code></summary>
 
-Taking 0.0268 % of a block.
+Taking 0.0265 % of a block.
 
 ```rust
 block_height: BlockNumberFor<T>
@@ -426,6 +163,7 @@ balance: BalanceOf<T>
 </details>
 
 
+
 Consume a oneshot account then transfer some amount to an account,
 and the remaining amount to another account.
 
@@ -437,13 +175,13 @@ and the remaining amount to another account.
 - `dest2_is_oneshot`: If set to `true`, then a oneshot account is created at `dest2`. Else, `dest2` has to be an existing account.
 - `balance1`: The amount transfered to `dest`, the leftover being transfered to `dest2`.
 
-### SmithMembers - 10
+## SmithMembers - 10
 
-#### invite_smith - 0
+### invite_smith - 0
 
 <details><summary><code>invite_smith(receiver)</code></summary>
 
-Taking 0.0235 % of a block.
+Taking 0.024 % of a block.
 
 ```rust
 receiver: T::IdtyIndex
@@ -451,22 +189,24 @@ receiver: T::IdtyIndex
 </details>
 
 
+
 Invite a member of the Web of Trust to attempt becoming a Smith.
 
-#### accept_invitation - 1
+### accept_invitation - 1
 
 <details><summary><code>accept_invitation()</code></summary>
 
-Taking 0.0127 % of a block.
+Taking 0.0126 % of a block.
 
 ```rust
 ```
 </details>
 
 
+
 Accept an invitation to become a Smith (must have been invited first).
 
-#### certify_smith - 2
+### certify_smith - 2
 
 <details><summary><code>certify_smith(receiver)</code></summary>
 
@@ -478,41 +218,44 @@ receiver: T::IdtyIndex
 </details>
 
 
+
 Certify an invited Smith, which can lead the certified to become a Smith.
 
-### AuthorityMembers - 11
+## AuthorityMembers - 11
 
-#### go_offline - 0
+### go_offline - 0
 
 <details><summary><code>go_offline()</code></summary>
 
-Taking 0.0167 % of a block.
+Taking 0.0172 % of a block.
 
 ```rust
 ```
 </details>
 
 
+
 Request to leave the set of validators two sessions later.
 
-#### go_online - 1
+### go_online - 1
 
 <details><summary><code>go_online()</code></summary>
 
-Taking 0.0189 % of a block.
+Taking 0.0199 % of a block.
 
 ```rust
 ```
 </details>
 
 
+
 Request to join the set of validators two sessions later.
 
-#### set_session_keys - 2
+### set_session_keys - 2
 
 <details><summary><code>set_session_keys(keys)</code></summary>
 
-Taking 0.0249 % of a block.
+Taking 0.0256 % of a block.
 
 ```rust
 keys: T::Keys
@@ -520,71 +263,16 @@ keys: T::Keys
 </details>
 
 
-Declare new session keys to replace current ones.
-
-#### remove_member_from_blacklist - 4
-
-<details><summary><code>remove_member_from_blacklist(member_id)</code></summary>
-
-Taking 0.0114 % of a block.
-
-```rust
-member_id: T::MemberId
-```
-</details>
-
-
-Remove a member from the blacklist.
-remove an identity from the blacklist
-
-### Grandpa - 16
-
-#### report_equivocation - 0
-
-<details><summary><code>report_equivocation(equivocation_proof, key_owner_proof)</code></summary>
-
-No weight available.
-
-```rust
-equivocation_proof: Box<EquivocationProof<T::Hash, BlockNumberFor<T>>>
-key_owner_proof: T::KeyOwnerProof
-```
-</details>
-
-
-Report voter equivocation/misbehavior. This method will verify the
-equivocation proof and validate the given key ownership proof
-against the extracted offender. If both are valid, the offence
-will be reported.
-
-### UpgradeOrigin - 21
-
-#### dispatch_as_root_unchecked_weight - 1
-
-<details><summary><code>dispatch_as_root_unchecked_weight(call, weight)</code></summary>
-
-No weight available.
-
-```rust
-call: Box<<T as Config>::Call>
-weight: Weight
-```
-</details>
-
-
-Dispatches a function call from root origin.
-This function does not check the weight of the call, and instead allows the
-caller to specify the weight of the call.
 
-The weight of this call is defined by the caller.
+Declare new session keys to replace current ones.
 
-### Preimage - 22
+## Preimage - 22
 
-#### note_preimage - 0
+### note_preimage - 0
 
 <details><summary><code>note_preimage(bytes)</code></summary>
 
-Taking 0.2947 % of a block.
+Taking 0.2845 % of a block.
 
 ```rust
 bytes: Vec<u8>
@@ -592,16 +280,17 @@ bytes: Vec<u8>
 </details>
 
 
+
 Register a preimage on-chain.
 
 If the preimage was previously requested, no fees or deposits are taken for providing
 the preimage. Otherwise, a deposit is taken proportional to the size of the preimage.
 
-#### unnote_preimage - 1
+### unnote_preimage - 1
 
 <details><summary><code>unnote_preimage(hash)</code></summary>
 
-Taking 0.0184 % of a block.
+Taking 0.0181 % of a block.
 
 ```rust
 hash: T::Hash
@@ -609,6 +298,7 @@ hash: T::Hash
 </details>
 
 
+
 Clear an unrequested preimage from the runtime storage.
 
 If `len` is provided, then it will be a much cheaper operation.
@@ -616,11 +306,11 @@ If `len` is provided, then it will be a much cheaper operation.
 - `hash`: The hash of the preimage to be removed from the store.
 - `len`: The length of the preimage of `hash`.
 
-#### request_preimage - 2
+### request_preimage - 2
 
 <details><summary><code>request_preimage(hash)</code></summary>
 
-Taking 0.0129 % of a block.
+Taking 0.0128 % of a block.
 
 ```rust
 hash: T::Hash
@@ -628,16 +318,17 @@ hash: T::Hash
 </details>
 
 
+
 Request a preimage be uploaded to the chain without paying any fees or deposits.
 
 If the preimage requests has already been provided on-chain, we unreserve any deposit
 a user may have paid, and take the control of the preimage out of their hands.
 
-#### unrequest_preimage - 3
+### unrequest_preimage - 3
 
 <details><summary><code>unrequest_preimage(hash)</code></summary>
 
-Taking 0.0184 % of a block.
+Taking 0.018 % of a block.
 
 ```rust
 hash: T::Hash
@@ -645,15 +336,16 @@ hash: T::Hash
 </details>
 
 
+
 Clear a previously made request for a preimage.
 
 NOTE: THIS MUST NOT BE CALLED ON `hash` MORE TIMES THAN `request_preimage`.
 
-#### ensure_updated - 4
+### ensure_updated - 4
 
 <details><summary><code>ensure_updated(hashes)</code></summary>
 
-Taking 19.3634 % of a block.
+Taking 18.8992 % of a block.
 
 ```rust
 hashes: Vec<T::Hash>
@@ -661,13 +353,14 @@ hashes: Vec<T::Hash>
 </details>
 
 
+
 Ensure that the a bulk of pre-images is upgraded.
 
 The caller pays no fee if at least 90% of pre-images were successfully updated.
 
-### TechnicalCommittee - 23
+## TechnicalCommittee - 23
 
-#### execute - 1
+### execute - 1
 
 <details><summary><code>execute(proposal, length_bound)</code></summary>
 
@@ -680,17 +373,18 @@ length_bound: u32
 </details>
 
 
+
 Dispatch a proposal from a member using the `Member` origin.
 
 Origin must be a member of the collective.
 
-**Complexity**:
+###### Complexity:
 - `O(B + M + P)` where:
 - `B` is `proposal` size in bytes (length-fee-bounded)
 - `M` members-count (code-bounded)
 - `P` complexity of dispatching `proposal`
 
-#### propose - 2
+### propose - 2
 
 <details><summary><code>propose(threshold, proposal, length_bound)</code></summary>
 
@@ -704,6 +398,7 @@ length_bound: u32
 </details>
 
 
+
 Add a new proposal to either be voted on or executed directly.
 
 Requires the sender to be member.
@@ -711,7 +406,7 @@ Requires the sender to be member.
 `threshold` determines whether `proposal` is executed directly (`threshold < 2`)
 or put up for voting.
 
-**Complexity**
+###### Complexity
 - `O(B + M + P1)` or `O(B + M + P2)` where:
   - `B` is `proposal` size in bytes (length-fee-bounded)
   - `M` is members-count (code- and governance-bounded)
@@ -719,11 +414,11 @@ or put up for voting.
     - `P1` is proposal execution complexity (`threshold < 2`)
     - `P2` is proposals-count (code-bounded) (`threshold >= 2`)
 
-#### vote - 3
+### vote - 3
 
 <details><summary><code>vote(proposal, index, approve)</code></summary>
 
-Taking 0.0129 % of a block.
+Taking 0.0128 % of a block.
 
 ```rust
 proposal: T::Hash
@@ -733,6 +428,7 @@ approve: bool
 </details>
 
 
+
 Add an aye or nay vote for the sender to the given proposal.
 
 Requires the sender to be a member.
@@ -740,10 +436,10 @@ Requires the sender to be a member.
 Transaction fees will be waived if the member is voting on any particular proposal
 for the first time and the call is successful. Subsequent vote changes will charge a
 fee.
-**Complexity**
+###### Complexity
 - `O(M)` where `M` is members-count (code- and governance-bounded)
 
-#### close - 6
+### close - 6
 
 <details><summary><code>close(proposal_hash, index, proposal_weight_bound, length_bound)</code></summary>
 
@@ -758,6 +454,7 @@ length_bound: u32
 </details>
 
 
+
 Close a vote that is either approved, disapproved or whose voting period has ended.
 
 May be called by any signed account in order to finish voting and close the proposal.
@@ -776,33 +473,34 @@ proposal.
 + `length_bound`: The upper bound for the length of the proposal in storage. Checked via
 `storage::read` so it is `size_of::<u32>() == 4` larger than the pure length.
 
-**Complexity**
+###### Complexity
 - `O(B + M + P1 + P2)` where:
   - `B` is `proposal` size in bytes (length-fee-bounded)
   - `M` is members-count (code- and governance-bounded)
   - `P1` is the complexity of `proposal` preimage.
   - `P2` is proposal-count (code-bounded)
 
-### UniversalDividend - 30
+## UniversalDividend - 30
 
-#### claim_uds - 0
+### claim_uds - 0
 
 <details><summary><code>claim_uds()</code></summary>
 
-Taking 0.0218 % of a block.
+Taking 0.0219 % of a block.
 
 ```rust
 ```
 </details>
 
 
+
 Claim Universal Dividends.
 
-#### transfer_ud - 1
+### transfer_ud - 1
 
 <details><summary><code>transfer_ud(dest, value)</code></summary>
 
-Taking 0.021 % of a block.
+Taking 0.0207 % of a block.
 
 ```rust
 dest: <T::Lookup as StaticLookup>::Source
@@ -811,13 +509,14 @@ value: BalanceOf<T>
 </details>
 
 
+
 Transfer some liquid free balance to another account, in milliUD.
 
-#### transfer_ud_keep_alive - 2
+### transfer_ud_keep_alive - 2
 
 <details><summary><code>transfer_ud_keep_alive(dest, value)</code></summary>
 
-Taking 0.0135 % of a block.
+Taking 0.0134 % of a block.
 
 ```rust
 dest: <T::Lookup as StaticLookup>::Source
@@ -826,15 +525,16 @@ value: BalanceOf<T>
 </details>
 
 
+
 Transfer some liquid free balance to another account in milliUD and keep the account alive.
 
-### Identity - 41
+## Identity - 41
 
-#### create_identity - 0
+### create_identity - 0
 
 <details><summary><code>create_identity(owner_key)</code></summary>
 
-Taking 0.0856 % of a block.
+Taking 0.0843 % of a block.
 
 ```rust
 owner_key: T::AccountId
@@ -842,17 +542,18 @@ owner_key: T::AccountId
 </details>
 
 
+
 Create an identity for an existing account
 
 - `owner_key`: the public key corresponding to the identity to be created
 
 The origin must be allowed to create an identity.
 
-#### confirm_identity - 1
+### confirm_identity - 1
 
 <details><summary><code>confirm_identity(idty_name)</code></summary>
 
-Taking 0.0327 % of a block.
+Taking 0.0322 % of a block.
 
 ```rust
 idty_name: IdtyName
@@ -860,17 +561,18 @@ idty_name: IdtyName
 </details>
 
 
+
 Confirm the creation of an identity and give it a name
 
 - `idty_name`: the name uniquely associated to this identity. Must match the validation rules defined by the runtime.
 
 The identity must have been created using `create_identity` before it can be confirmed.
 
-#### change_owner_key - 3
+### change_owner_key - 3
 
 <details><summary><code>change_owner_key(new_key, new_key_sig)</code></summary>
 
-Taking 0.0424 % of a block.
+Taking 0.0421 % of a block.
 
 ```rust
 new_key: T::AccountId
@@ -879,6 +581,7 @@ new_key_sig: T::Signature
 </details>
 
 
+
 Change identity owner key.
 
 - `new_key`: the new owner key.
@@ -887,11 +590,11 @@ Change identity owner key.
 
 The origin should be the old identity owner key.
 
-#### revoke_identity - 4
+### revoke_identity - 4
 
 <details><summary><code>revoke_identity(idty_index, revocation_key, revocation_sig)</code></summary>
 
-Taking 0.0399 % of a block.
+Taking 0.0392 % of a block.
 
 ```rust
 idty_index: T::IdtyIndex
@@ -901,6 +604,7 @@ revocation_sig: T::Signature
 </details>
 
 
+
 Revoke an identity using a revocation signature
 
 - `idty_index`: the index of the identity to be revoked.
@@ -910,30 +614,26 @@ Revoke an identity using a revocation signature
 
 Any signed origin can execute this call.
 
-#### fix_sufficients - 7
+### revoke_identity_legacy - 9
 
-<details><summary><code>fix_sufficients(owner_key, inc)</code></summary>
+<details><summary><code>revoke_identity_legacy(revocation_document)</code></summary>
 
-Taking 0.0113 % of a block.
+Taking 0.0392 % of a block.
 
 ```rust
-owner_key: T::AccountId
-inc: bool
+revocation_document: Vec<u8>
 ```
 </details>
 
 
-Change sufficient reference count for a given key.
 
-This function allows a privileged root origin to increment or decrement the sufficient
-reference count associated with a specified owner key.
+Revoke an identity using a legacy (DUBP) revocation document
 
-- `origin` - The origin of the call. It must be root.
-- `owner_key` - The account whose sufficient reference count will be modified.
-- `inc` - A boolean indicating whether to increment (`true`) or decrement (`false`) the count.
+- `revocation document`: the full-length revocation document, signature included
 
+Any signed origin can execute this call.
 
-#### link_account - 8
+### link_account - 8
 
 <details><summary><code>link_account(account_id, payload_sig)</code></summary>
 
@@ -946,6 +646,7 @@ payload_sig: T::Signature
 </details>
 
 
+
 Link an account to an identity.
 
 This function links a specified account to an identity, requiring both the account and the
@@ -955,13 +656,13 @@ identity to sign the operation.
 - `account_id` - The account ID to link, which must sign the payload.
 - `payload_sig` - The signature with the linked identity.
 
-### Certification - 43
+## Certification - 43
 
-#### add_cert - 0
+### add_cert - 0
 
 <details><summary><code>add_cert(receiver)</code></summary>
 
-Taking 0.0356 % of a block.
+Taking 0.0357 % of a block.
 
 ```rust
 receiver: T::IdtyIndex
@@ -969,79 +670,49 @@ receiver: T::IdtyIndex
 </details>
 
 
+
 Add a new certification.
 
-#### renew_cert - 3
+### renew_cert - 3
 
 <details><summary><code>renew_cert(receiver)</code></summary>
 
-Taking 0.0292 % of a block.
-
-```rust
-receiver: T::IdtyIndex
-```
-</details>
-
-
-Renew an existing certification.
-
-#### del_cert - 1
-
-<details><summary><code>del_cert(issuer, receiver)</code></summary>
-
-Taking 0.0257 % of a block.
+Taking 0.0295 % of a block.
 
 ```rust
-issuer: T::IdtyIndex
 receiver: T::IdtyIndex
 ```
 </details>
 
 
-Remove one certification given the issuer and the receiver.
-
-- `origin`: Must be `Root`.
-
-#### remove_all_certs_received_by - 2
-
-<details><summary><code>remove_all_certs_received_by(idty_index)</code></summary>
-
-Taking 6.846 % of a block.
-
-```rust
-idty_index: T::IdtyIndex
-```
-</details>
-
-
-Remove all certifications received by an identity.
 
-- `origin`: Must be `Root`.
+Renew an existing certification.
 
-### Distance - 44
+## Distance - 44
 
-#### request_distance_evaluation - 0
+### request_distance_evaluation - 0
 
 <details><summary><code>request_distance_evaluation()</code></summary>
 
-Taking 0.0389 % of a block.
+Taking 0.0393 % of a block.
 
 ```rust
 ```
 </details>
 
 
+
 Request evaluation of the caller's identity distance.
 
 This function allows the caller to request an evaluation of their distance.
 A positive evaluation will lead to claiming or renewing membership, while a negative
 evaluation will result in slashing for the caller.
 
-#### request_distance_evaluation_for - 4
+### request_distance_evaluation_for - 4
 
 <details><summary><code>request_distance_evaluation_for(target)</code></summary>
 
-Taking 0.0399 % of a block.
+Taking 0.0404 % of a block.
 
 ```rust
 target: T::IdtyIndex
@@ -1049,81 +720,29 @@ target: T::IdtyIndex
 </details>
 
 
+
 Request evaluation of a target identity's distance.
 
 This function allows the caller to request an evaluation of a specific target identity's distance.
 This action is only permitted for unvalidated identities.
 
-#### update_evaluation - 1
+## AtomicSwap - 50
+
+### create_swap - 0
 
-<details><summary><code>update_evaluation(computation_result)</code></summary>
+<details><summary><code>create_swap(target, hashed_proof, action, duration)</code></summary>
 
-Taking 0.0341 % of a block.
+No weight available.
 
 ```rust
-computation_result: ComputationResult
+target: T::AccountId
+hashed_proof: HashedProof
+action: T::SwapAction
+duration: BlockNumberFor<T>
 ```
 </details>
 
 
-Push an evaluation result to the pool.
-
-This inherent function is called internally by validators to push an evaluation result
-to the evaluation pool.
-
-#### force_update_evaluation - 2
-
-<details><summary><code>force_update_evaluation(evaluator, computation_result)</code></summary>
-
-Taking 0.0192 % of a block.
-
-```rust
-evaluator: <T as frame_system::Config>::AccountId
-computation_result: ComputationResult
-```
-</details>
-
-
-Force push an evaluation result to the pool.
-
-It is primarily used for testing purposes.
-
-- `origin`: Must be `Root`.
-
-#### force_valid_distance_status - 3
-
-<details><summary><code>force_valid_distance_status(identity)</code></summary>
-
-Taking 0.027 % of a block.
-
-```rust
-identity: <T as pallet_identity::Config>::IdtyIndex
-```
-</details>
-
-
-Force set the distance evaluation status of an identity.
-
-It is primarily used for testing purposes.
-
-- `origin`: Must be `Root`.
-
-### AtomicSwap - 50
-
-#### create_swap - 0
-
-<details><summary><code>create_swap(target, hashed_proof, action, duration)</code></summary>
-
-No weight available.
-
-```rust
-target: T::AccountId
-hashed_proof: HashedProof
-action: T::SwapAction
-duration: BlockNumberFor<T>
-```
-</details>
-
 
 Register a new atomic swap, declaring an intention to send funds from origin to target
 on the current blockchain. The target can claim the fund using the revealed proof. If
@@ -1138,7 +757,7 @@ The dispatch origin for this call must be _Signed_.
   that the revealer uses a shorter duration than the counterparty, to prevent the
   situation where the revealer reveals the proof too late around the end block.
 
-#### claim_swap - 1
+### claim_swap - 1
 
 <details><summary><code>claim_swap(proof, action)</code></summary>
 
@@ -1151,6 +770,7 @@ action: T::SwapAction
 </details>
 
 
+
 Claim an atomic swap.
 
 The dispatch origin for this call must be _Signed_.
@@ -1159,7 +779,7 @@ The dispatch origin for this call must be _Signed_.
 - `action`: Action defined in the swap, it must match the entry in blockchain. Otherwise
   the operation fails. This is used for weight calculation.
 
-#### cancel_swap - 2
+### cancel_swap - 2
 
 <details><summary><code>cancel_swap(target, hashed_proof)</code></summary>
 
@@ -1172,6 +792,7 @@ hashed_proof: HashedProof
 </details>
 
 
+
 Cancel an atomic swap. Only possible after the originally set duration has passed.
 
 The dispatch origin for this call must be _Signed_.
@@ -1179,13 +800,13 @@ The dispatch origin for this call must be _Signed_.
 - `target`: Target of the original atomic swap.
 - `hashed_proof`: Hashed proof of the original atomic swap.
 
-### Multisig - 51
+## Multisig - 51
 
-#### as_multi_threshold_1 - 0
+### as_multi_threshold_1 - 0
 
 <details><summary><code>as_multi_threshold_1(other_signatories, call)</code></summary>
 
-Taking 0.005 % of a block.
+Taking 0.0048 % of a block.
 
 ```rust
 other_signatories: Vec<T::AccountId>
@@ -1194,6 +815,7 @@ call: Box<<T as Config>::RuntimeCall>
 </details>
 
 
+
 Immediately dispatch a multi-signature call using a single approval from the caller.
 
 The dispatch origin for this call must be _Signed_.
@@ -1204,10 +826,10 @@ multi-signature, but do not participate in the approval process.
 
 Result is equivalent to the dispatched result.
 
-**Complexity**
+###### Complexity
 O(Z + C) where Z is the length of the call and C its execution weight.
 
-#### as_multi - 1
+### as_multi - 1
 
 <details><summary><code>as_multi(threshold, other_signatories, maybe_timepoint, call, max_weight)</code></summary>
 
@@ -1223,6 +845,7 @@ max_weight: Weight
 </details>
 
 
+
 Register approval for a dispatch to be made from a deterministic composite account if
 approved by a total of `threshold - 1` of `other_signatories`.
 
@@ -1249,7 +872,7 @@ Result is equivalent to the dispatched result if `threshold` is exactly `1`. Oth
 on success, result is `Ok` and the result from the interior call, if it was executed,
 may be found in the deposited `MultisigExecuted` event.
 
-**Complexity**
+###### Complexity
 - `O(S + Z + Call)`.
 - Up to one balance-reserve or unreserve operation.
 - One passthrough operation, one insert, both `O(S)` where `S` is the number of
@@ -1263,7 +886,7 @@ may be found in the deposited `MultisigExecuted` event.
 - Storage: inserts one item, value size bounded by `MaxSignatories`, with a deposit
   taken for its lifetime of `DepositBase + threshold * DepositFactor`.
 
-#### approve_as_multi - 2
+### approve_as_multi - 2
 
 <details><summary><code>approve_as_multi(threshold, other_signatories, maybe_timepoint, call_hash, max_weight)</code></summary>
 
@@ -1279,6 +902,7 @@ max_weight: Weight
 </details>
 
 
+
 Register approval for a dispatch to be made from a deterministic composite account if
 approved by a total of `threshold - 1` of `other_signatories`.
 
@@ -1298,7 +922,7 @@ transaction index) of the first approval transaction.
 
 NOTE: If this is the final approval, you will want to use `as_multi` instead.
 
-**Complexity**
+###### Complexity
 - `O(S)`.
 - Up to one balance-reserve or unreserve operation.
 - One passthrough operation, one insert, both `O(S)` where `S` is the number of
@@ -1310,11 +934,11 @@ NOTE: If this is the final approval, you will want to use `as_multi` instead.
 - Storage: inserts one item, value size bounded by `MaxSignatories`, with a deposit
   taken for its lifetime of `DepositBase + threshold * DepositFactor`.
 
-#### cancel_as_multi - 3
+### cancel_as_multi - 3
 
 <details><summary><code>cancel_as_multi(threshold, other_signatories, timepoint, call_hash)</code></summary>
 
-Taking 0.0123 % of a block.
+Taking 0.0121 % of a block.
 
 ```rust
 threshold: u16
@@ -1325,6 +949,7 @@ call_hash: [u8; 32]
 </details>
 
 
+
 Cancel a pre-existing, on-going multisig transaction. Any deposit reserved previously
 for this operation will be unreserved on success.
 
@@ -1337,7 +962,7 @@ dispatch. May not be empty.
 transaction for this dispatch.
 - `call_hash`: The hash of the call to be executed.
 
-**Complexity**
+###### Complexity
 - `O(S)`.
 - Up to one balance-reserve or unreserve operation.
 - One passthrough operation, one insert, both `O(S)` where `S` is the number of
@@ -1347,13 +972,13 @@ transaction for this dispatch.
 - I/O: 1 read `O(S)`, one remove.
 - Storage: removes one item.
 
-### ProvideRandomness - 52
+## ProvideRandomness - 52
 
-#### request - 0
+### request - 0
 
 <details><summary><code>request(randomness_type, salt)</code></summary>
 
-Taking 0.0404 % of a block.
+Taking 0.0401 % of a block.
 
 ```rust
 randomness_type: RandomnessType
@@ -1362,11 +987,12 @@ salt: H256
 </details>
 
 
+
 Request randomness.
 
-### Proxy - 53
+## Proxy - 53
 
-#### proxy - 0
+### proxy - 0
 
 <details><summary><code>proxy(real, force_proxy_type, call)</code></summary>
 
@@ -1380,6 +1006,7 @@ call: Box<<T as Config>::RuntimeCall>
 </details>
 
 
+
 Dispatch the given `call` from an account that the sender is authorised for through
 `add_proxy`.
 
@@ -1390,11 +1017,11 @@ Parameters:
 - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.
 - `call`: The call to be made by the `real` account.
 
-#### add_proxy - 1
+### add_proxy - 1
 
 <details><summary><code>add_proxy(delegate, proxy_type, delay)</code></summary>
 
-Taking 0.0121 % of a block.
+Taking 0.0118 % of a block.
 
 ```rust
 delegate: AccountIdLookupOf<T>
@@ -1404,6 +1031,7 @@ delay: BlockNumberFor<T>
 </details>
 
 
+
 Register a proxy account for the sender that is able to make calls on its behalf.
 
 The dispatch origin for this call must be _Signed_.
@@ -1414,11 +1042,11 @@ Parameters:
 - `delay`: The announcement period required of the initial proxy. Will generally be
 zero.
 
-#### remove_proxy - 2
+### remove_proxy - 2
 
 <details><summary><code>remove_proxy(delegate, proxy_type, delay)</code></summary>
 
-Taking 0.0121 % of a block.
+Taking 0.0118 % of a block.
 
 ```rust
 delegate: AccountIdLookupOf<T>
@@ -1428,6 +1056,7 @@ delay: BlockNumberFor<T>
 </details>
 
 
+
 Unregister a proxy account for the sender.
 
 The dispatch origin for this call must be _Signed_.
@@ -1436,17 +1065,18 @@ Parameters:
 - `proxy`: The account that the `caller` would like to remove as a proxy.
 - `proxy_type`: The permissions currently enabled for the removed proxy account.
 
-#### remove_proxies - 3
+### remove_proxies - 3
 
 <details><summary><code>remove_proxies()</code></summary>
 
-Taking 0.012 % of a block.
+Taking 0.0117 % of a block.
 
 ```rust
 ```
 </details>
 
 
+
 Unregister all proxy accounts for the sender.
 
 The dispatch origin for this call must be _Signed_.
@@ -1454,11 +1084,11 @@ The dispatch origin for this call must be _Signed_.
 WARNING: This may be called on accounts created by `pure`, however if done, then
 the unreserved fees will be inaccessible. **All access to this account will be lost.**
 
-#### create_pure - 4
+### create_pure - 4
 
 <details><summary><code>create_pure(proxy_type, delay, index)</code></summary>
 
-Taking 0.0121 % of a block.
+Taking 0.0118 % of a block.
 
 ```rust
 proxy_type: T::ProxyType
@@ -1468,6 +1098,7 @@ index: u16
 </details>
 
 
+
 Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and
 initialize it with a proxy of `proxy_type` for `origin` sender.
 
@@ -1487,11 +1118,11 @@ same sender, with the same parameters.
 
 Fails if there are insufficient funds to pay for deposit.
 
-#### kill_pure - 5
+### kill_pure - 5
 
 <details><summary><code>kill_pure(spawner, proxy_type, index, height, ext_index)</code></summary>
 
-Taking 0.012 % of a block.
+Taking 0.0117 % of a block.
 
 ```rust
 spawner: AccountIdLookupOf<T>
@@ -1503,6 +1134,7 @@ ext_index: u32
 </details>
 
 
+
 Removes a previously spawned pure proxy.
 
 WARNING: **All access to this account will be lost.** Any funds held in it will be
@@ -1520,11 +1152,11 @@ Requires a `Signed` origin, and the sender account must have been created by a c
 Fails with `NoPermission` in case the caller is not a previously created pure
 account whose `pure` call has corresponding parameters.
 
-#### announce - 6
+### announce - 6
 
 <details><summary><code>announce(real, call_hash)</code></summary>
 
-Taking 0.0199 % of a block.
+Taking 0.0197 % of a block.
 
 ```rust
 real: AccountIdLookupOf<T>
@@ -1533,6 +1165,7 @@ call_hash: CallHashOf<T>
 </details>
 
 
+
 Publish the hash of a proxy-call that will be made in the future.
 
 This must be called some number of blocks before the corresponding `proxy` is attempted
@@ -1549,11 +1182,11 @@ Parameters:
 - `real`: The account that the proxy will make a call on behalf of.
 - `call_hash`: The hash of the call to be made by the `real` account.
 
-#### remove_announcement - 7
+### remove_announcement - 7
 
 <details><summary><code>remove_announcement(real, call_hash)</code></summary>
 
-Taking 0.0186 % of a block.
+Taking 0.0182 % of a block.
 
 ```rust
 real: AccountIdLookupOf<T>
@@ -1562,6 +1195,7 @@ call_hash: CallHashOf<T>
 </details>
 
 
+
 Remove a given announcement.
 
 May be called by a proxy account to remove a call they previously announced and return
@@ -1573,11 +1207,11 @@ Parameters:
 - `real`: The account that the proxy will make a call on behalf of.
 - `call_hash`: The hash of the call to be made by the `real` account.
 
-#### reject_announcement - 8
+### reject_announcement - 8
 
 <details><summary><code>reject_announcement(delegate, call_hash)</code></summary>
 
-Taking 0.0186 % of a block.
+Taking 0.0182 % of a block.
 
 ```rust
 delegate: AccountIdLookupOf<T>
@@ -1586,6 +1220,7 @@ call_hash: CallHashOf<T>
 </details>
 
 
+
 Remove the given announcement of a delegate.
 
 May be called by a target (proxied) account to remove a call that one of their delegates
@@ -1597,11 +1232,11 @@ Parameters:
 - `delegate`: The account that previously announced the call.
 - `call_hash`: The hash of the call to be made.
 
-#### proxy_announced - 9
+### proxy_announced - 9
 
 <details><summary><code>proxy_announced(delegate, real, force_proxy_type, call)</code></summary>
 
-Taking 0.02 % of a block.
+Taking 0.0198 % of a block.
 
 ```rust
 delegate: AccountIdLookupOf<T>
@@ -1612,6 +1247,7 @@ call: Box<<T as Config>::RuntimeCall>
 </details>
 
 
+
 Dispatch the given `call` from an account that the sender is authorized for through
 `add_proxy`.
 
@@ -1624,13 +1260,13 @@ Parameters:
 - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.
 - `call`: The call to be made by the `real` account.
 
-### Utility - 54
+## Utility - 54
 
-#### batch - 0
+### batch - 0
 
 <details><summary><code>batch(calls)</code></summary>
 
-Taking 0.1149 % of a block.
+Taking 0.1076 % of a block.
 
 ```rust
 calls: Vec<<T as Config>::RuntimeCall>
@@ -1638,6 +1274,7 @@ calls: Vec<<T as Config>::RuntimeCall>
 </details>
 
 
+
 Send a batch of dispatch calls.
 
 May be called from any origin except `None`.
@@ -1648,7 +1285,7 @@ May be called from any origin except `None`.
 If origin is root then the calls are dispatched without checking origin filter. (This
 includes bypassing `frame_system::Config::BaseCallFilter`).
 
-**Complexity**
+###### Complexity
 - O(C) where C is the number of calls to be batched.
 
 This will return `Ok` in all circumstances. To determine the success of the batch, an
@@ -1657,11 +1294,11 @@ event is deposited. If a call failed and the batch was interrupted, then the
 and the error of the failed call. If all were successful, then the `BatchCompleted`
 event is deposited.
 
-#### as_derivative - 1
+### as_derivative - 1
 
 <details><summary><code>as_derivative(index, call)</code></summary>
 
-Taking 0.0047 % of a block.
+Taking 0.0046 % of a block.
 
 ```rust
 index: u16
@@ -1670,6 +1307,7 @@ call: Box<<T as Config>::RuntimeCall>
 </details>
 
 
+
 Send a call through an indexed pseudonym of the sender.
 
 Filter from origin are passed along. The call will be dispatched with an origin which
@@ -1684,11 +1322,11 @@ NOTE: Prior to version *12, this was called `as_limited_sub`.
 
 The dispatch origin for this call must be _Signed_.
 
-#### batch_all - 2
+### batch_all - 2
 
 <details><summary><code>batch_all(calls)</code></summary>
 
-Taking 0.1241 % of a block.
+Taking 0.1148 % of a block.
 
 ```rust
 calls: Vec<<T as Config>::RuntimeCall>
@@ -1696,6 +1334,7 @@ calls: Vec<<T as Config>::RuntimeCall>
 </details>
 
 
+
 Send a batch of dispatch calls and atomically execute them.
 The whole transaction will rollback and fail if any of the calls failed.
 
@@ -1707,14 +1346,14 @@ May be called from any origin except `None`.
 If origin is root then the calls are dispatched without checking origin filter. (This
 includes bypassing `frame_system::Config::BaseCallFilter`).
 
-**Complexity**
+###### Complexity
 - O(C) where C is the number of calls to be batched.
 
-#### force_batch - 4
+### force_batch - 4
 
 <details><summary><code>force_batch(calls)</code></summary>
 
-Taking 0.1155 % of a block.
+Taking 0.1074 % of a block.
 
 ```rust
 calls: Vec<<T as Config>::RuntimeCall>
@@ -1722,6 +1361,7 @@ calls: Vec<<T as Config>::RuntimeCall>
 </details>
 
 
+
 Send a batch of dispatch calls.
 Unlike `batch`, it allows errors and won't interrupt.
 
@@ -1733,36 +1373,16 @@ May be called from any origin except `None`.
 If origin is root then the calls are dispatch without checking origin filter. (This
 includes bypassing `frame_system::Config::BaseCallFilter`).
 
-**Complexity**
+###### Complexity
 - O(C) where C is the number of calls to be batched.
 
-#### with_weight - 5
-
-<details><summary><code>with_weight(call, weight)</code></summary>
-
-No weight available.
-
-```rust
-call: Box<<T as Config>::RuntimeCall>
-weight: Weight
-```
-</details>
-
-
-Dispatch a function call with a specified weight.
-
-This function does not check the weight of the call, and instead allows the
-Root origin to specify the weight of the call.
+## Treasury - 55
 
-The dispatch origin for this call must be _Root_.
-
-### Treasury - 55
-
-#### spend_local - 3
+### spend_local - 3
 
 <details><summary><code>spend_local(amount, beneficiary)</code></summary>
 
-Taking 0.0045 % of a block.
+Taking 0.0044 % of a block.
 
 ```rust
 amount: BalanceOf<T, I>
@@ -1771,25 +1391,26 @@ beneficiary: AccountIdLookupOf<T>
 </details>
 
 
+
 Propose and approve a spend of treasury funds.
 
-## Dispatch Origin
+###### Dispatch Origin
 
 Must be [`Config::SpendOrigin`] with the `Success` value being at least `amount`.
 
-### Details
+###### Details
 NOTE: For record-keeping purposes, the proposer is deemed to be equivalent to the
 beneficiary.
 
-### Parameters
+###### Parameters
 - `amount`: The amount to be transferred from the treasury to the `beneficiary`.
 - `beneficiary`: The destination account for the transfer.
 
-## Events
+###### Events
 
 Emits [`Event::SpendApproved`] if successful.
 
-#### remove_approval - 4
+### remove_approval - 4
 
 <details><summary><code>remove_approval(proposal_id)</code></summary>
 
@@ -1801,33 +1422,34 @@ proposal_id: ProposalIndex
 </details>
 
 
+
 Force a previously approved proposal to be removed from the approval queue.
 
-## Dispatch Origin
+###### Dispatch Origin
 
 Must be [`Config::RejectOrigin`].
 
-## Details
+###### Details
 
 The original deposit will no longer be returned.
 
-### Parameters
+###### Parameters
 - `proposal_id`: The index of a proposal
 
-#**Complexity**
+###### Complexity
 - O(A) where `A` is the number of approvals
 
-### Errors
+###### Errors
 - [`Error::ProposalNotApproved`]: The `proposal_id` supplied was not found in the
   approval queue, i.e., the proposal has not been approved. This could also mean the
   proposal does not exist altogether, thus there is no way it would have been approved
   in the first place.
 
-#### spend - 5
+### spend - 5
 
 <details><summary><code>spend(asset_kind, amount, beneficiary, valid_from)</code></summary>
 
-Taking 0.0045 % of a block.
+Taking 0.0044 % of a block.
 
 ```rust
 asset_kind: Box<T::AssetKind>
@@ -1838,21 +1460,22 @@ valid_from: Option<BlockNumberFor<T>>
 </details>
 
 
+
 Propose and approve a spend of treasury funds.
 
-## Dispatch Origin
+###### Dispatch Origin
 
 Must be [`Config::SpendOrigin`] with the `Success` value being at least
 `amount` of `asset_kind` in the native asset. The amount of `asset_kind` is converted
 for assertion using the [`Config::BalanceConverter`].
 
-## Details
+###### Details
 
 Create an approved spend for transferring a specific `amount` of `asset_kind` to a
 designated beneficiary. The spend must be claimed using the `payout` dispatchable within
 the [`Config::PayoutPeriod`].
 
-### Parameters
+###### Parameters
 - `asset_kind`: An indicator of the specific asset class to be spent.
 - `amount`: The amount to be transferred from the treasury to the `beneficiary`.
 - `beneficiary`: The beneficiary of the spend.
@@ -1861,11 +1484,11 @@ the [`Config::PayoutPeriod`].
   [`Config::PayoutPeriod`]. If `None`, the spend can be claimed immediately after
   approval.
 
-## Events
+###### Events
 
 Emits [`Event::AssetSpendApproved`] if successful.
 
-#### payout - 6
+### payout - 6
 
 <details><summary><code>payout(index)</code></summary>
 
@@ -1877,27 +1500,28 @@ index: SpendIndex
 </details>
 
 
+
 Claim a spend.
 
-## Dispatch Origin
+###### Dispatch Origin
 
 Must be signed
 
-## Details
+###### Details
 
 Spends must be claimed within some temporal bounds. A spend may be claimed within one
 [`Config::PayoutPeriod`] from the `valid_from` block.
 In case of a payout failure, the spend status must be updated with the `check_status`
 dispatchable before retrying with the current function.
 
-### Parameters
+###### Parameters
 - `index`: The spend index.
 
-## Events
+###### Events
 
 Emits [`Event::Paid`] if successful.
 
-#### check_status - 7
+### check_status - 7
 
 <details><summary><code>check_status(index)</code></summary>
 
@@ -1909,31 +1533,32 @@ index: SpendIndex
 </details>
 
 
+
 Check the status of the spend and remove it from the storage if processed.
 
-## Dispatch Origin
+###### Dispatch Origin
 
 Must be signed.
 
-## Details
+###### Details
 
 The status check is a prerequisite for retrying a failed payout.
 If a spend has either succeeded or expired, it is removed from the storage by this
 function. In such instances, transaction fees are refunded.
 
-### Parameters
+###### Parameters
 - `index`: The spend index.
 
-## Events
+###### Events
 
 Emits [`Event::PaymentFailed`] if the spend payout has failed.
 Emits [`Event::SpendProcessed`] if the spend payout has succeed.
 
-#### void_spend - 8
+### void_spend - 8
 
 <details><summary><code>void_spend(index)</code></summary>
 
-Taking 0.0057 % of a block.
+Taking 0.0056 % of a block.
 
 ```rust
 index: SpendIndex
@@ -1941,476 +1566,22 @@ index: SpendIndex
 </details>
 
 
+
 Void previously approved spend.
 
-## Dispatch Origin
+###### Dispatch Origin
 
 Must be [`Config::RejectOrigin`].
 
-## Details
+###### Details
 
 A spend void is only possible if the payout has not been attempted yet.
 
-### Parameters
+###### Parameters
 - `index`: The spend index.
 
-## Events
+###### Events
 
 Emits [`Event::AssetSpendVoided`] if successful.
 
 
-
-## Root calls
-
-There are **18** root calls from **8** pallets.
-
-### System - 0
-
-#### set_heap_pages - 1
-
-<details><summary><code>set_heap_pages(pages)</code></summary>
-
-Taking 0.0165 % of a block.
-
-```rust
-pages: u64
-```
-</details>
-
-
-Set the number of pages in the WebAssembly environment's heap.
-
-#### set_code - 2
-
-<details><summary><code>set_code(code)</code></summary>
-
-Taking 3.9234 % of a block.
-
-```rust
-code: Vec<u8>
-```
-</details>
-
-
-Set the new runtime code.
-
-#### set_code_without_checks - 3
-
-<details><summary><code>set_code_without_checks(code)</code></summary>
-
-No weight available.
-
-```rust
-code: Vec<u8>
-```
-</details>
-
-
-Set the new runtime code without doing any checks of the given `code`.
-
-Note that runtime upgrades will not run if this is called with a not-increasing spec
-version!
-
-#### set_storage - 4
-
-<details><summary><code>set_storage(items)</code></summary>
-
-Taking 5.4867 % of a block.
-
-```rust
-items: Vec<KeyValue>
-```
-</details>
-
-
-Set some items of storage.
-
-#### kill_storage - 5
-
-<details><summary><code>kill_storage(keys)</code></summary>
-
-Taking 5.4797 % of a block.
-
-```rust
-keys: Vec<Key>
-```
-</details>
-
-
-Kill some items from storage.
-
-#### kill_prefix - 6
-
-<details><summary><code>kill_prefix(prefix, subkeys)</code></summary>
-
-Taking 6.4024 % of a block.
-
-```rust
-prefix: Key
-subkeys: u32
-```
-</details>
-
-
-Kill all storage items with a key that starts with the given prefix.
-
-**NOTE:** We rely on the Root origin to provide us the number of subkeys under
-the prefix we are removing to accurately calculate the weight of this function.
-
-#### authorize_upgrade - 9
-
-<details><summary><code>authorize_upgrade(code_hash)</code></summary>
-
-Taking 0.0105 % of a block.
-
-```rust
-code_hash: T::Hash
-```
-</details>
-
-
-Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied
-later.
-
-This call requires Root origin.
-
-#### authorize_upgrade_without_checks - 10
-
-<details><summary><code>authorize_upgrade_without_checks(code_hash)</code></summary>
-
-No weight available.
-
-```rust
-code_hash: T::Hash
-```
-</details>
-
-
-Authorize an upgrade to a given `code_hash` for the runtime. The runtime can be supplied
-later.
-
-WARNING: This authorizes an upgrade that will take place without any safety checks, for
-example that the spec name remains the same and that the version number increases. Not
-recommended for normal use. Use `authorize_upgrade` instead.
-
-This call requires Root origin.
-
-#### apply_authorized_upgrade - 11
-
-<details><summary><code>apply_authorized_upgrade(code)</code></summary>
-
-Taking 4.1629 % of a block.
-
-```rust
-code: Vec<u8>
-```
-</details>
-
-
-Provide the preimage (runtime binary) `code` for an upgrade that has been authorized.
-
-If the authorization required a version check, this call will ensure the spec name
-remains unchanged and that the spec version has increased.
-
-Depending on the runtime's `OnSetCode` configuration, this function may directly apply
-the new `code` in the same block or attempt to schedule the upgrade.
-
-All origins are allowed.
-
-### Babe - 3
-
-#### plan_config_change - 2
-
-<details><summary><code>plan_config_change(config)</code></summary>
-
-No weight available.
-
-```rust
-config: NextConfigDescriptor
-```
-</details>
-
-
-Plan an epoch config change. The epoch config change is recorded and will be enacted on
-the next call to `enact_epoch_change`. The config will be activated one epoch after.
-Multiple calls to this method will replace any existing planned config change that had
-not been enacted yet.
-
-### Balances - 6
-
-#### force_transfer - 2
-
-<details><summary><code>force_transfer(source, dest, value)</code></summary>
-
-Taking 0.0262 % of a block.
-
-```rust
-source: AccountIdLookupOf<T>
-dest: AccountIdLookupOf<T>
-value: T::Balance
-```
-</details>
-
-
-Exactly as `transfer_allow_death`, except the origin must be root and the source account
-may be specified.
-
-#### force_unreserve - 5
-
-<details><summary><code>force_unreserve(who, amount)</code></summary>
-
-Taking 0.0117 % of a block.
-
-```rust
-who: AccountIdLookupOf<T>
-amount: T::Balance
-```
-</details>
-
-
-Unreserve some balance from a user by force.
-
-Can only be called by ROOT.
-
-### AuthorityMembers - 11
-
-#### remove_member - 3
-
-<details><summary><code>remove_member(member_id)</code></summary>
-
-Taking 0.0665 % of a block.
-
-```rust
-member_id: T::MemberId
-```
-</details>
-
-
-Remove a member from the set of validators.
-
-### Grandpa - 16
-
-#### note_stalled - 2
-
-<details><summary><code>note_stalled(delay, best_finalized_block_number)</code></summary>
-
-No weight available.
-
-```rust
-delay: BlockNumberFor<T>
-best_finalized_block_number: BlockNumberFor<T>
-```
-</details>
-
-
-Note that the current authority set of the GRANDPA finality gadget has stalled.
-
-This will trigger a forced authority set change at the beginning of the next session, to
-be enacted `delay` blocks after that. The `delay` should be high enough to safely assume
-that the block signalling the forced change will not be re-orged e.g. 1000 blocks.
-The block production rate (which may be slowed down because of finality lagging) should
-be taken into account when choosing the `delay`. The GRANDPA voters based on the new
-authority will start voting on top of `best_finalized_block_number` for new finalized
-blocks. `best_finalized_block_number` should be the highest of the latest finalized
-block of all validators of the new authority set.
-
-Only callable by root.
-
-### TechnicalCommittee - 23
-
-#### set_members - 0
-
-<details><summary><code>set_members(new_members, prime, old_count)</code></summary>
-
-Taking 0.1558 % of a block.
-
-```rust
-new_members: Vec<T::AccountId>
-prime: Option<T::AccountId>
-old_count: MemberCount
-```
-</details>
-
-
-Set the collective's membership.
-
-- `new_members`: The new member list. Be nice to the chain and provide it sorted.
-- `prime`: The prime member whose vote sets the default.
-- `old_count`: The upper bound for the previous number of members in storage. Used for
-  weight estimation.
-
-The dispatch of this call must be `SetMembersOrigin`.
-
-NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but
-      the weight estimations rely on it to estimate dispatchable weight.
-
-WARNING:
-
-The `pallet-collective` can also be managed by logic outside of the pallet through the
-implementation of the trait [`ChangeMembers`].
-Any call to `set_members` must be careful that the member set doesn't get out of sync
-with other logic managing the member set.
-
-**Complexity**:
-- `O(MP + N)` where:
-  - `M` old-members-count (code- and governance-bounded)
-  - `N` new-members-count (code- and governance-bounded)
-  - `P` proposals-count (code-bounded)
-
-#### disapprove_proposal - 5
-
-<details><summary><code>disapprove_proposal(proposal_hash)</code></summary>
-
-Taking 0.0226 % of a block.
-
-```rust
-proposal_hash: T::Hash
-```
-</details>
-
-
-Disapprove a proposal, close, and remove it from the system, regardless of its current
-state.
-
-Must be called by the Root origin.
-
-Parameters:
-* `proposal_hash`: The hash of the proposal that should be disapproved.
-
-**Complexity**
-O(P) where P is the number of max proposals
-
-### Identity - 41
-
-#### prune_item_identities_names - 6
-
-<details><summary><code>prune_item_identities_names(names)</code></summary>
-
-Taking 5.524 % of a block.
-
-```rust
-names: Vec<IdtyName>
-```
-</details>
-
-
-Remove identity names from storage.
-
-This function allows a privileged root origin to remove multiple identity names from storage
-in bulk.
-
-- `origin` - The origin of the call. It must be root.
-- `names` - A vector containing the identity names to be removed from storage.
-
-### Utility - 54
-
-#### dispatch_as - 3
-
-<details><summary><code>dispatch_as(as_origin, call)</code></summary>
-
-Taking 0.0048 % of a block.
-
-```rust
-as_origin: Box<T::PalletsOrigin>
-call: Box<<T as Config>::RuntimeCall>
-```
-</details>
-
-
-Dispatches a function call with a provided origin.
-
-The dispatch origin for this call must be _Root_.
-
-**Complexity**
-- O(1).
-
-
-
-
-
-
-## Disabled calls
-
-There are **4** disabled calls from **2** pallets.
-
-### System - 0
-
-#### remark - 0
-
-<details><summary><code>remark(remark)</code></summary>
-
-Taking 0.0546 % of a block.
-
-```rust
-remark: Vec<u8>
-```
-</details>
-
-
-Make some on-chain remark.
-
-Can be executed by every `origin`.
-
-#### remark_with_event - 7
-
-<details><summary><code>remark_with_event(remark)</code></summary>
-
-Taking 0.2093 % of a block.
-
-```rust
-remark: Vec<u8>
-```
-</details>
-
-
-Make some on-chain remark and emit event.
-
-### Session - 15
-
-#### set_keys - 0
-
-<details><summary><code>set_keys(keys, proof)</code></summary>
-
-Taking 0.0378 % of a block.
-
-```rust
-keys: T::Keys
-proof: Vec<u8>
-```
-</details>
-
-
-Sets the session key(s) of the function caller to `keys`.
-Allows an account to set its session key prior to becoming a validator.
-This doesn't take effect until the next session.
-
-The dispatch origin of this function must be signed.
-
-**Complexity**
-- `O(1)`. Actual cost depends on the number of length of `T::Keys::key_ids()` which is
-  fixed.
-
-#### purge_keys - 1
-
-<details><summary><code>purge_keys()</code></summary>
-
-Taking 0.0336 % of a block.
-
-```rust
-```
-</details>
-
-
-Removes any session key(s) of the function caller.
-
-This doesn't take effect until the next session.
-
-The dispatch origin of this function must be Signed and the account must be either be
-convertible to a validator ID using the chain's typical addressing system (this usually
-means being a controller account) or directly convertible into a validator ID (which
-usually means being a stash account).
-
-**Complexity**
-- `O(1)` in number of key types. Actual cost depends on the number of length of
-  `T::Keys::key_ids()` which is fixed.
-
diff --git a/docs/api/runtime-errors.md b/docs/api/runtime-errors.md
index ac6528e906befc2b6d5b7f9068867f929903bfd3..ead935a5d35710a25df4d3e3b5acf6a9118d329e 100644
--- a/docs/api/runtime-errors.md
+++ b/docs/api/runtime-errors.md
@@ -1,6 +1,6 @@
 # Runtime errors
 
-There are **190** errors from **35** pallets.
+There are **191** errors from **35** pallets.
 
 <ul>
 <li>System - 0
@@ -986,6 +986,13 @@ Insufficient balance to create an identity.
 Owner key currently used as validator.
 </details>
 </li>
+<li>
+<details>
+<summary>
+<code>InvalidLegacyRevocationFormat</code> - 19</summary>
+Legacy revocation document format is invalid
+</details>
+</li>
 </ul>
 </li>
 <li>Membership - 42
diff --git a/docs/api/runtime-errors.po b/docs/api/runtime-errors.po
index 6be74ca09b7c1987f058632f6fcac7f59162b97c..f16ca07c1442240f65e612a00423617d59309782 100644
--- a/docs/api/runtime-errors.po
+++ b/docs/api/runtime-errors.po
@@ -254,6 +254,8 @@ msgid "Identity.InsufficientBalance"
 msgstr "Insufficient balance to create an identity."
 msgid "Identity.OwnerKeyUsedAsValidator"
 msgstr "Owner key currently used as validator."
+msgid "Identity.InvalidLegacyRevocationFormat"
+msgstr "Legacy revocation document format is invalid"
 msgid "Membership.MembershipNotFound"
 msgstr "Membership not found, can not renew."
 msgid "Membership.AlreadyMember"
diff --git a/docs/test/replay-block.md b/docs/dev/replay-block.md
similarity index 95%
rename from docs/test/replay-block.md
rename to docs/dev/replay-block.md
index 1db20cd3cd0d22b3c74647aa7dab5e85fae5f437..429e0becb39440d8ee9721bae49552e086e57219 100644
--- a/docs/test/replay-block.md
+++ b/docs/dev/replay-block.md
@@ -1,5 +1,7 @@
 # How to replay a block
 
+WARN: try-runtime is not properly implemented
+
 You can use `try-runtime` subcommand to replay a block against a real state from a live network.
 
 1. Checkout the git tag of the runtime version at the block you want to replay
diff --git a/docs/user/installation_debian.md b/docs/user/installation_debian.md
index 63bc8b37a46f3891c665666eb41fa8ee18a0dd6a..00e34d140a227177c190b469e56c25c2b5db8fd3 100644
--- a/docs/user/installation_debian.md
+++ b/docs/user/installation_debian.md
@@ -22,5 +22,5 @@
 A Smith node needs to be installed.
 
 1. Change the default configuration by modifying `/etc/duniter/env_file`.
-2. Start the service: `sudo systemctl start distance-oracle.timer`.
-3. Enable the service at startup: `sudo systemctl enable distance-oracle.timer`.
+2. Start the service: `sudo systemctl start distance-oracle.service`.
+3. Enable the service at startup: `sudo systemctl enable distance-oracle.service`.
diff --git a/docs/user/mirror.md b/docs/user/mirror.md
deleted file mode 100644
index 4777bfd8bdde4e31b5ba99950bbd9fa4cd301efd..0000000000000000000000000000000000000000
--- a/docs/user/mirror.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# How to deploy a permanent rpc node on ĞDev network
-
-## Publish a node
-
-### Duniter part
-
-See [docker documentation](../../docker/README.md) to install, configure, and start a node.
-
-### Reverse proxy part
-
-See [nginx reverse proxy](./nginx_reverse_proxy.md).
-
-To go further, read [How to become a (black)smith](./smith.md)
-
-## Upgrade your node with minimal interruption
-
-1. Modify docker image tag on your compose file
-2. Run `docker compose pull`, this will pull the new image.
-3. Run `docker compose up -d --remove-orphans`, this will recreate the container
-4. Verify that your node restarted well `docker compose logs duniter-rpc`
-5. Remove the old image `docker rmi duniter/duniter-v2s:OLD_TAG`
diff --git a/docs/user/nginx_reverse_proxy.md b/docs/user/nginx_reverse_proxy.md
deleted file mode 100644
index cd17455eb47b8e0588b29a8d774435aef1acd284..0000000000000000000000000000000000000000
--- a/docs/user/nginx_reverse_proxy.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Nginx reverse proxy example
-
-In `/etc/nginx/sites-enabled/gdev.YOUR_DOMAIN` put (you can probably do simpler):
-
-```nginx
-# see http://nginx.org/en/docs/http/websocket.html
-map $http_upgrade $connection_upgrade {
-    default upgrade;
-    ''      close;
-}
-
-server {
-  server_name gdev.YOUR_DOMAIN.fr;
-
-  listen 443 ssl http2;
-  listen [::]:443 ssl http2;
-  ssl_certificate /etc/nginx/ssl/YOUR_DOMAIN.cert;
-  ssl_certificate_key /etc/nginx/ssl/YOUR_DOMAIN.key;
-
-  root /nowhere;
-
-  add_header X-Frame-Options SAMEORIGIN;
-  add_header X-XSS-Protection "1; mode=block";
-  proxy_redirect off;
-  proxy_buffering off;
-  proxy_set_header Host $host;
-  proxy_set_header X-Real-IP $remote_addr;
-  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-  proxy_set_header X-Forwarded-Proto $scheme;
-  proxy_set_header X-Forwarded-Port $server_port;
-  proxy_read_timeout 90;
-
-
-  location / {
-    proxy_pass http://localhost:9944;
-
-    proxy_set_header Upgrade $http_upgrade;
-    proxy_set_header Connection $connection_upgrade;
-    proxy_http_version 1.1;
-
-    proxy_read_timeout 1200s;
-    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-    proxy_set_header Host $host;
-  }
-}
-```
-
-and replace `YOUR_DOMAIN` by your domain each time.
-
-- [generate your ssl certificates](https://github.com/acmesh-official/acme.sh) with let's encrypt
-  if you don't already have a wildcard certificate.
-- `service nginx reload`
-
-Your node is now online as a rpc node. It's fully capable for wallet use.
diff --git a/docs/user/smith.md b/docs/user/smith.md
deleted file mode 100644
index 02411c2f0d59fa43409160019e8d9dc7a6f75911..0000000000000000000000000000000000000000
--- a/docs/user/smith.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# How to become a (black)smith
-
-## Publish a node
-
-### Duniter part
-
-See [docker documentation](../../docker/README.md) to install, configure, and start a node. For a smith node, you want to set `DUNITER_VALIDATOR` to `true`.
-
-### Reverse proxy part
-
-See [nginx reverse proxy](./nginx_reverse_proxy.md).
-
-## Join the Smith WoT
-
-Only members of the smith WoT can author blocks. This WoT is a subset of the main WoT, hence before joining it you need to join the main WoT.
-
-1. Create an SSH bridge from your computer to your server: `ssh -L 9945:localhost:9945 SSH_USER@YOUR_SERVER`
-2. Install PolkadotJS browser extension. It will manage your private keys and known pubkeys safely.
-3. Go to [a PolkadotJS web UI](https://polkadot.js.org/apps/?rpc=ws://127.0.0.1/ws:9945). (it's not the same thing as the browser extension)
-  - If using another port or address, change it accordingly in the left panel.
-4. In the UI: developer > RPC call > author > rotateKeys() and copy the result in clipboard
-5. In the UI: developer > extrinsics > YOUR_SMITH_ACCOUNT > smithMembership > requestMembership(metadata)
-  - add your p2p endpoint (optional)
-  - add your session keys
-  - send the query
-6. Wait 48h to ensure your node keeps sync (**both** best **and** finalized block numbers must increase every 6s)
-7. Await at least 3 smith certifications. Members of the smith WoT can certify you with this extrinsic:
-  - In the UI: developer > extrinsics > CERTIFIER_SMITH_ACCOUNT > smithCert > addCert(receiver)
-  - This is not automatic, you can ask for certs on the forum or the Matrix chatroom.
-8. In the UI: developer > extrinsics > YOUR_SMITH_ACCOUNT > smithMembership > claimMembership(maybe_idty_id)
-  - maybe_idty_id can be left empty since your identity id will be infered from your account address.
-
-All extrinsics can be sent while connected to any Duniter node, but the RPC calls need a direct connection to your server. As some RPC calls should not be publicly callable for security reasons, the only ways to call them is from the server localhost or using an SSH bridge or other kind of secure tunnel.
-
-rotateKeys can be called anytime you want. Then you have to call setSessionKeys with the new keys.
-
-## Validate blocks (blacksmith work)
-
-Once all the previous steps are completed, you can start to actually author blocks.
-
-In the UI: developer > extrinsics > YOUR_SMITH_ACCOUNT > authorityMembers > goOnline()
-
-If you're not able to monitor, reboot, act on your node, goOffline() to avoid penalty to the blockchain and to you. It will take effect after 2h, so please do it in advance if you plan to disconnect your server soon. goOnline can always be called after this, but after 100 days without authoring blocks you will loose your smith membership.
-
-## Upgrade your node with minimal interruption
-
-1. Modify docker image tag on your compose file
-2. Run `docker compose pull`, this will pull the new image.
-3. Run `docker compose up -d --remove-orphans`, this will recreate the container
-4. Verify that your node restarted well `docker compose logs duniter-validator`
-5. Remove the old image `docker rmi duniter/duniter-v2s:OLD_TAG`
diff --git a/end2end-tests/cucumber-features/universal_dividend.feature b/end2end-tests/cucumber-features/universal_dividend.feature
new file mode 100644
index 0000000000000000000000000000000000000000..c858c5e1936a4e5a9dff666ceb84d3475ecbfa68
--- /dev/null
+++ b/end2end-tests/cucumber-features/universal_dividend.feature
@@ -0,0 +1,15 @@
+@genesis.wot
+
+Feature: Universal Dividend
+
+    Scenario: Eligibility at genesis
+        When 2 blocks later
+
+        # Members
+        Then alice should be eligible to UD
+        Then bob should be eligible to UD
+        Then charlie should be eligible to UD
+
+        # Not members
+        Then eve should not be eligible to UD
+        Then ferdie should not be eligible to UD
diff --git a/end2end-tests/cucumber-genesis/wot.json b/end2end-tests/cucumber-genesis/wot.json
index 26042cf39ac533154c31b76d3608bc7e0253652a..0bc2d54571b77d96583fcfc0f231576ea5ce33bd 100644
--- a/end2end-tests/cucumber-genesis/wot.json
+++ b/end2end-tests/cucumber-genesis/wot.json
@@ -59,6 +59,31 @@
       "membership_revokes_on": 2700000001,
       "revoked": false,
       "next_cert_issuable_on": 0
+    },
+    "Eve": {
+      "index": 5,
+      "balance": 1000,
+      "certs_received": {
+        "Alice": 2700000000,
+        "Bob": 2700000000
+      },
+      "owner_address": "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw",
+      "membership_expire_on": 2700000000,
+      "membership_revokes_on": 2700000001,
+      "revoked": true,
+      "next_cert_issuable_on": 0
+    },
+    "Ferdie": {
+      "index": 6,
+      "balance": 1000,
+      "certs_received": {
+        "Alice": 2700000000
+      },
+      "owner_address": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
+      "membership_expire_on": 2700000000,
+      "membership_revokes_on": 2700000001,
+      "revoked": false,
+      "next_cert_issuable_on": 0
     }
   },
   "parameters": {
@@ -99,7 +124,7 @@
   ],
   "treasury_funder_pubkey": "FHNpKmJrUtusuvKPGomAygQqeiks98bdV6yD61Stb6vg",
   "ud": 1000,
-  "initial_monetary_mass": 4000,
+  "initial_monetary_mass": 6000,
   "current_block": {
     "number": 0,
     "medianTime": 1700000000
diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs
index 642779ec85b58b5f02da11126586dda095156499..58a532b2d6b1f8bbfe2e59a06123e90528061785 100644
--- a/end2end-tests/tests/cucumber_tests.rs
+++ b/end2end-tests/tests/cucumber_tests.rs
@@ -526,6 +526,26 @@ async fn should_be_certified_by(
     }
 }
 
+#[then(regex = r"([a-zA-Z]+) should (not )?be eligible to UD")]
+async fn should_be_eligible_to_ud(
+    world: &mut DuniterWorld,
+    identity: String,
+    not: String,
+) -> Result<()> {
+    let eligible = not.is_empty();
+
+    assert_eq!(
+        identity::get_identity_value(world, identity)
+            .await
+            .expect("Identity not found")
+            .data
+            .first_eligible_ud
+            != 0,
+        eligible
+    );
+    Ok(())
+}
+
 use gdev::runtime_types::pallet_identity::types::IdtyStatus;
 
 // status from string
diff --git a/node/src/chain_spec/g1.rs b/node/src/chain_spec/g1.rs
index 8ad20d230ab6f271536fae861681bcc21ab576b4..78f879a8c8e75c9c6775634e93b75560c2477c9e 100644
--- a/node/src/chain_spec/g1.rs
+++ b/node/src/chain_spec/g1.rs
@@ -198,7 +198,14 @@ fn genesis_data_to_g1_genesis_conf(
                         index: idty_index,
                         name: common_runtime::IdtyName::from(name.as_str()),
                         value: common_runtime::IdtyValue {
-                            data: IdtyData::new(),
+                            data: IdtyData {
+                                first_eligible_ud: match status {
+                                    // Only members are eligible to UD.
+                                    // The first claimable UD has the minimum index (1).
+                                    common_runtime::IdtyStatus::Member => g1_runtime::pallet_universal_dividend::FirstEligibleUd::min(),
+                                    _ => g1_runtime::pallet_universal_dividend::FirstEligibleUd(None),
+                                }
+                            },
                             next_creatable_identity_on: 0,
                             old_owner_key: None,
                             owner_key,
diff --git a/node/src/chain_spec/gdev.rs b/node/src/chain_spec/gdev.rs
index c78154891055aa953851add1c16ab0b668137624..d6ff6806f28d596fb4ef9ce6755dbbc0749668af 100644
--- a/node/src/chain_spec/gdev.rs
+++ b/node/src/chain_spec/gdev.rs
@@ -286,7 +286,14 @@ fn genesis_data_to_gdev_genesis_conf(
                         index: idty_index,
                         name: common_runtime::IdtyName::from(name.as_str()),
                         value: common_runtime::IdtyValue {
-                            data: IdtyData::new(),
+                            data: IdtyData {
+                                first_eligible_ud: match status {
+                                    // Only members are eligible to UD.
+                                    // The first claimable UD has the minimum index (1).
+                                    common_runtime::IdtyStatus::Member => gdev_runtime::pallet_universal_dividend::FirstEligibleUd::min(),
+                                    _ => gdev_runtime::pallet_universal_dividend::FirstEligibleUd(None),
+                                }
+                            },
                             next_creatable_identity_on: 0,
                             old_owner_key: None,
                             owner_key,
diff --git a/node/src/chain_spec/gen_genesis_data.rs b/node/src/chain_spec/gen_genesis_data.rs
index a7c338ac60e080bc48569576b511bdd2f8b9a5a2..59717f6cd970ee6541070b08796b0dcb5705d672 100644
--- a/node/src/chain_spec/gen_genesis_data.rs
+++ b/node/src/chain_spec/gen_genesis_data.rs
@@ -187,8 +187,6 @@ struct IdentityV1 {
     membership_revokes_on: TimestampV1,
     /// whether the identity is revoked (manually or automatically)
     revoked: bool,
-    /// timestamp at which the next cert can be emitted
-    next_cert_issuable_on: TimestampV1, // TODO: unused?
     /// balance of the account of this identity
     balance: u64,
     /// certs received with their expiration timestamp
@@ -212,8 +210,6 @@ struct IdentityV2 {
     identity_revoke_on: u32,
     /// whether the identity is revoked (manually or automatically)
     revoked: bool,
-    /// block at which the next cert can be emitted
-    next_cert_issuable_on: u32,
     /// balance of the account of this identity
     balance: u64,
     /// certs received with their expiration block
@@ -1232,10 +1228,6 @@ fn genesis_data_to_identities_v2(
                         genesis_timestamp,
                     ),
                     revoked: i.revoked,
-                    next_cert_issuable_on: timestamp_to_relative_blocs(
-                        i.next_cert_issuable_on,
-                        genesis_timestamp,
-                    ),
                     balance: i.balance,
                     certs_received: i
                         .certs_received
@@ -1276,7 +1268,6 @@ fn make_authority_exist<SessionKeys: Encode, SKP: SessionKeysProvider<SessionKey
                 membership_expire_on: common_parameters.membership_membership_period,
                 identity_revoke_on: common_parameters.membership_membership_period,
                 revoked: false,
-                next_cert_issuable_on: 0,
             },
         );
     };
diff --git a/node/src/chain_spec/gtest.rs b/node/src/chain_spec/gtest.rs
index 2b4020664fa5d8cfcc4af95891538a9ac420866d..b80ad714fd552c675bc9bebe9ef9cd0316b07a41 100644
--- a/node/src/chain_spec/gtest.rs
+++ b/node/src/chain_spec/gtest.rs
@@ -291,7 +291,14 @@ fn genesis_data_to_gtest_genesis_conf(
                         index: idty_index,
                         name: common_runtime::IdtyName::from(name.as_str()),
                         value: common_runtime::IdtyValue {
-                            data: IdtyData::new(),
+                            data: IdtyData {
+                                first_eligible_ud: match status {
+                                    // Only members are eligible to UD.
+                                    // The first claimable UD has the minimum index (1).
+                                    common_runtime::IdtyStatus::Member => gtest_runtime::pallet_universal_dividend::FirstEligibleUd::min(),
+                                    _ => gtest_runtime::pallet_universal_dividend::FirstEligibleUd(None),
+                                }
+                            },
                             next_creatable_identity_on: 0,
                             old_owner_key: None,
                             owner_key,
diff --git a/node/src/service.rs b/node/src/service.rs
index fb14fb678a0179ed9744e9f03cda45ec8942059e..3cc5ec6337691057741515fc0a3c1faeb59ea823 100644
--- a/node/src/service.rs
+++ b/node/src/service.rs
@@ -193,7 +193,7 @@ where
         .transpose()?;
 
     #[cfg(feature = "native")]
-    let executor = sc_service::new_native_or_wasm_executor(&config.executor);
+    let executor = sc_service::new_native_or_wasm_executor(&config);
     #[cfg(not(feature = "native"))]
     let executor = sc_service::new_wasm_executor(&config.executor);
 
@@ -503,8 +503,7 @@ where
                                     FullBackend,
                                 >(
                                     &*client, parent, distance_dir, &babe_owner_keys.clone()
-                                )?;
-                            // in case of manual sealing, the distance is forced to succeed
+                                );
                             Ok((timestamp, babe, distance))
                         }
                     },
@@ -552,16 +551,6 @@ where
                             &*client, parent, distance_dir, &babe_owner_keys.clone()
                         );
 
-                        // provides fallback when distance inherent data provider crashes
-                        // (only when sealing is not manual)
-                        let distance = match distance {
-                            Ok(distance) => distance,
-                            Err(e) => {
-                                log::warn!("{:?}", e);
-                                sp_distance::InherentDataProvider::new(None)
-                            }
-                        };
-
                         Ok((slot, timestamp, storage_proof, distance))
                     }
                 },
diff --git a/pallets/authority-members/src/lib.rs b/pallets/authority-members/src/lib.rs
index 587e82aa343c51fbc62bf36d61d3755a68d536f4..f4118ee3aed9eb692ef20fecf8cd7dbd22cc3cff 100644
--- a/pallets/authority-members/src/lib.rs
+++ b/pallets/authority-members/src/lib.rs
@@ -575,14 +575,25 @@ impl<T: Config> pallet_session::SessionManager<T::ValidatorId> for Pallet<T> {
             return None;
         }
 
+        // -- handle incoming members
+        // callback when smith is incoming
         for member_id in members_ids_to_add.iter() {
             T::OnIncomingMember::on_incoming_member(*member_id);
+        }
+        // a single event with all authorities if some
+        if !members_ids_to_add.is_empty() {
             Self::deposit_event(Event::IncomingAuthorities {
                 members: members_ids_to_add.clone(),
             });
         }
+
+        // -- handle outgoing members
+        // callback when smith is outgoing
         for member_id in members_ids_to_del.iter() {
             T::OnOutgoingMember::on_outgoing_member(*member_id);
+        }
+        // a single event with all authorities if some
+        if !members_ids_to_del.is_empty() {
             Self::deposit_event(Event::OutgoingAuthorities {
                 members: members_ids_to_del.clone(),
             });
diff --git a/pallets/certification/src/benchmarking.rs b/pallets/certification/src/benchmarking.rs
index 1fa4b0bfb708c1573e8dc46f2fdd60c2488b723c..668d5cdfae938ace52c59b76c7e78e4386d81ca2 100644
--- a/pallets/certification/src/benchmarking.rs
+++ b/pallets/certification/src/benchmarking.rs
@@ -37,11 +37,11 @@ mod benchmarks {
 
     fn add_certs<T: Config>(i: u32, receiver: T::IdtyIndex) -> Result<(), &'static str> {
         Pallet::<T>::remove_all_certs_received_by(RawOrigin::Root.into(), receiver)?;
-        for j in 1..i {
+        for j in 1..i + 1 {
             Pallet::<T>::do_add_cert_checked(j.into(), receiver, false)?;
         }
         assert!(
-            CertsByReceiver::<T>::get(receiver).len() as u32 == i - 1,
+            CertsByReceiver::<T>::get(receiver).len() as u32 == i,
             "Certs not added",
         );
         Ok(())
@@ -157,6 +157,29 @@ mod benchmarks {
         Ok(())
     }
 
+    #[benchmark]
+    fn do_remove_all_certs_received_by(i: Linear<2, 100>) -> Result<(), BenchmarkError> {
+        let receiver: T::IdtyIndex = 0.into();
+        add_certs::<T>(i, receiver)?;
+
+        #[block]
+        {
+            Pallet::<T>::do_remove_all_certs_received_by(receiver);
+        }
+
+        for issuer in 1..i + 1 {
+            assert_has_event::<T>(
+                Event::<T>::CertRemoved {
+                    issuer: issuer.into(),
+                    receiver,
+                    expiration: false,
+                }
+                .into(),
+            );
+        }
+        Ok(())
+    }
+
     impl_benchmark_test_suite!(
         Pallet,
         crate::mock::new_test_ext(crate::mock::DefaultCertificationConfig {
diff --git a/pallets/certification/src/lib.rs b/pallets/certification/src/lib.rs
index 08aa992f71627b50bc57fb8bf059eeb8b3dabc72..204c8cb08d70bb44323b8eaa53df92db2ff69154 100644
--- a/pallets/certification/src/lib.rs
+++ b/pallets/certification/src/lib.rs
@@ -362,11 +362,28 @@ pub mod pallet {
     impl<T: Config> Pallet<T> {
         /// Perform removal of all certifications received by an identity.
         pub fn do_remove_all_certs_received_by(idty_index: T::IdtyIndex) -> Weight {
-            let mut weight = T::DbWeight::get().reads_writes(1, 0);
-            for (issuer, _) in CertsByReceiver::<T>::get(idty_index) {
-                weight = weight.saturating_add(Self::do_remove_cert(issuer, idty_index, None));
+            let received_certs = CertsByReceiver::<T>::take(idty_index);
+            for (receiver_received_count, (issuer, _)) in received_certs.iter().enumerate().rev() {
+                let issuer_issued_count =
+                    <StorageIdtyCertMeta<T>>::mutate_exists(issuer, |cert_meta_opt| {
+                        let cert_meta = cert_meta_opt.get_or_insert(IdtyCertMeta::default());
+                        cert_meta.issued_count = cert_meta.issued_count.saturating_sub(1);
+                        cert_meta.issued_count
+                    });
+                T::OnRemovedCert::on_removed_cert(
+                    *issuer,
+                    issuer_issued_count,
+                    idty_index,
+                    receiver_received_count as u32,
+                    false,
+                );
+                Self::deposit_event(Event::CertRemoved {
+                    issuer: *issuer,
+                    receiver: idty_index,
+                    expiration: false,
+                });
             }
-            weight
+            T::WeightInfo::do_remove_all_certs_received_by(received_certs.len() as u32)
         }
 
         /// Get the issuer index from the origin.
diff --git a/pallets/certification/src/weights.rs b/pallets/certification/src/weights.rs
index e58c33831e312ab841d57ef2d91c56159d941e23..49280e9f297d4eec2b90db86fe011f30b891b630 100644
--- a/pallets/certification/src/weights.rs
+++ b/pallets/certification/src/weights.rs
@@ -27,6 +27,7 @@ pub trait WeightInfo {
     fn on_initialize() -> Weight;
     fn do_remove_cert_noop() -> Weight;
     fn do_remove_cert() -> Weight;
+    fn do_remove_all_certs_received_by(i: u32) -> Weight;
 }
 
 // Insecure weights implementation, use it for tests only!
@@ -102,4 +103,20 @@ impl WeightInfo for () {
             .saturating_add(RocksDbWeight::get().reads(7 as u64))
             .saturating_add(RocksDbWeight::get().writes(4 as u64))
     }
+
+    // Storage: Cert CertsByReceiver (r:1 w:1)
+    // Storage: Cert StorageIdtyCertMeta (r:2 w:2)
+    // Storage: Parameters ParametersStorage (r:1 w:0)
+    // Storage: Membership Membership (r:1 w:0)
+    /// The range of component `i` is `[2, 1000]`.
+    fn do_remove_all_certs_received_by(i: u32) -> Weight {
+        // Minimum execution time: 223_292 nanoseconds.
+        Weight::from_parts(233_586_000 as u64, 0)
+            // Standard Error: 598_929
+            .saturating_add(Weight::from_parts(53_659_501 as u64, 0).saturating_mul(i as u64))
+            .saturating_add(RocksDbWeight::get().reads(3 as u64))
+            .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(i as u64)))
+            .saturating_add(RocksDbWeight::get().writes(1 as u64))
+            .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(i as u64)))
+    }
 }
diff --git a/pallets/identity/Cargo.toml b/pallets/identity/Cargo.toml
index 7e163e2ab298f42ee421f0429d1eee12e79feab6..f86d0f991a07c616da8cd97aaa0975a96297bb8b 100644
--- a/pallets/identity/Cargo.toml
+++ b/pallets/identity/Cargo.toml
@@ -42,11 +42,15 @@ default-features = false
 targets = ["x86_64-unknown-linux-gnu"]
 
 [dependencies]
+base64 = { workspace = true }
+bs58 = { workspace = true }
 codec = { workspace = true, features = ["derive"] }
 duniter-primitives = { workspace = true }
+ed25519-dalek = { workspace = true }
 frame-benchmarking = { workspace = true, optional = true }
 frame-support = { workspace = true }
 frame-system = { workspace = true }
+log = { workspace = true }
 scale-info = { workspace = true, features = ["derive"] }
 serde = { workspace = true, features = ["derive"] }
 sp-core = { workspace = true }
diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs
index 981a029bab39cad33777bbc92f8210712189a37e..266d4aa99ef5c5139f35c25e1c3ba6a461272b40 100644
--- a/pallets/identity/src/lib.rs
+++ b/pallets/identity/src/lib.rs
@@ -86,6 +86,7 @@ pub const LINK_IDTY_PAYLOAD_PREFIX: [u8; 4] = [b'l', b'i', b'n', b'k'];
 #[frame_support::pallet]
 pub mod pallet {
     use super::*;
+    use base64::Engine;
     use frame_support::{pallet_prelude::*, traits::StorageVersion};
     use frame_system::pallet_prelude::*;
 
@@ -570,6 +571,114 @@ pub mod pallet {
             Ok(().into())
         }
 
+        /// Revoke an identity using a legacy (DUBP) revocation document
+        ///
+        /// - `revocation document`: the full-length revocation document, signature included
+        ///
+        /// Any signed origin can execute this call.
+        #[pallet::call_index(9)]
+        #[pallet::weight(T::WeightInfo::revoke_identity_legacy())]
+        pub fn revoke_identity_legacy(
+            origin: OriginFor<T>,
+            revocation_document: Vec<u8>,
+        ) -> DispatchResultWithPostInfo {
+            let _ = ensure_signed(origin)?;
+
+            // Strip possible Unicode magic number that is not part of the protocol
+            let revocation_document = revocation_document
+                .strip_prefix(b"\xef\xbb\xbf")
+                .unwrap_or(&revocation_document);
+            let mut lines = revocation_document.split(|b| *b == b'\n');
+            ensure!(
+                lines.next() == Some(b"Version: 10"),
+                Error::<T>::InvalidLegacyRevocationFormat
+            );
+            ensure!(
+                lines.next() == Some(b"Type: Revocation"),
+                Error::<T>::InvalidLegacyRevocationFormat
+            );
+            ensure!(
+                lines.next() == Some(b"Currency: g1"),
+                Error::<T>::InvalidLegacyRevocationFormat
+            );
+            let line_issuer = lines
+                .next()
+                .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?;
+            let line_username = lines
+                .next()
+                .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?;
+            let _line_blockstamp = lines
+                .next()
+                .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?;
+            let _line_idty_signature = lines
+                .next()
+                .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?;
+            let line_signature = lines
+                .next()
+                .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?;
+            ensure!(
+                lines.next() == Some(b""),
+                Error::<T>::InvalidLegacyRevocationFormat
+            );
+            ensure!(
+                lines.next().is_none(),
+                Error::<T>::InvalidLegacyRevocationFormat
+            );
+            let document = revocation_document
+                .get(0..revocation_document.len().saturating_sub(89))
+                .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?;
+            let mut signature = [0; 64];
+            base64::prelude::BASE64_STANDARD
+                .decode_slice(line_signature, &mut signature)
+                .map_err(|_| Error::<T>::InvalidLegacyRevocationFormat)?;
+            let issuer = bs58::decode(
+                line_issuer
+                    .get(8..)
+                    .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?,
+            )
+            .into_array_const::<32>()
+            .map_err(|_| Error::<T>::InvalidLegacyRevocationFormat)?;
+            ed25519_dalek::VerifyingKey::from_bytes(&issuer)
+                .map_err(|_| Error::<T>::InvalidLegacyRevocationFormat)?
+                .verify_strict(document, &ed25519_dalek::Signature::from_bytes(&signature))
+                .map_err(|_| Error::<T>::InvalidSignature)?;
+            let username = line_username
+                .get(14..)
+                .ok_or(Error::<T>::InvalidLegacyRevocationFormat)?;
+            let idty_index = <IdentitiesNames<T>>::get(IdtyName(username.into()))
+                .ok_or(Error::<T>::IdtyNotFound)?;
+
+            let idty_value = Identities::<T>::get(idty_index).ok_or(Error::<T>::IdtyNotFound)?;
+
+            match idty_value.status {
+                IdtyStatus::Unconfirmed => Err(Error::<T>::CanNotRevokeUnconfirmed),
+                IdtyStatus::Unvalidated => Err(Error::<T>::CanNotRevokeUnvalidated),
+                IdtyStatus::Member => Ok(()),
+                IdtyStatus::NotMember => Ok(()),
+                IdtyStatus::Revoked => Err(Error::<T>::AlreadyRevoked),
+            }?;
+
+            let revocation_key = T::AccountId::decode(&mut &issuer[..]).unwrap();
+
+            ensure!(
+                if let Some((ref old_owner_key, last_change)) = idty_value.old_owner_key {
+                    // old owner key can also revoke the identity until the period expired
+                    revocation_key == idty_value.owner_key
+                        || (&revocation_key == old_owner_key
+                            && frame_system::Pallet::<T>::block_number()
+                                < last_change + T::ChangeOwnerKeyPeriod::get())
+                } else {
+                    revocation_key == idty_value.owner_key
+                },
+                Error::<T>::InvalidRevocationKey
+            );
+
+            // finally if all checks pass, remove identity
+            Self::do_revoke_identity(idty_index, RevocationReason::User);
+
+            Ok(().into())
+        }
+
         /// Remove identity names from storage.
         ///
         /// This function allows a privileged root origin to remove multiple identity names from storage
@@ -701,6 +810,8 @@ pub mod pallet {
         InsufficientBalance,
         /// Owner key currently used as validator.
         OwnerKeyUsedAsValidator,
+        /// Legacy revocation document format is invalid
+        InvalidLegacyRevocationFormat,
     }
 
     // INTERNAL FUNCTIONS //
diff --git a/pallets/identity/src/tests.rs b/pallets/identity/src/tests.rs
index 9bc2205ebad0eba7b654884651743deca7a1d199..ac853fc925c2b027cb22f3a87f9d204df886609d 100644
--- a/pallets/identity/src/tests.rs
+++ b/pallets/identity/src/tests.rs
@@ -14,11 +14,13 @@
 // You should have received a copy of the GNU Affero General Public License
 // along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
 
+use core::str::FromStr;
+
 use crate::{mock::*, *};
 use codec::Encode;
 use frame_support::{assert_noop, assert_ok, dispatch::DispatchResultWithPostInfo};
 use sp_core::{sr25519::Pair as KeyPair, Pair};
-use sp_runtime::{MultiSignature, MultiSigner};
+use sp_runtime::{AccountId32, MultiSignature, MultiSigner};
 
 type IdtyVal = IdtyValue<u64, AccountId, ()>;
 
@@ -87,6 +89,23 @@ fn inactive_bob() -> GenesisIdty<Test> {
     }
 }
 
+// From legacy credentials Charlie:Charlie
+fn legacy_charlie() -> GenesisIdty<Test> {
+    GenesisIdty {
+        index: 102,
+        name: IdtyName::from("Charlie"),
+        value: IdtyVal {
+            data: (),
+            next_creatable_identity_on: 0,
+            old_owner_key: None,
+            owner_key: AccountId32::from_str("5H2nLXGku46iztpqdRwsCAiP6vHZbShhKmSV4yyufQgEUFvV")
+                .unwrap(),
+            next_scheduled: 0,
+            status: crate::IdtyStatus::Member,
+        },
+    }
+}
+
 #[test]
 fn test_no_identity() {
     new_test_ext(IdentityConfig {
@@ -610,6 +629,86 @@ fn test_idty_revocation() {
         );
     });
 }
+
+// # Generate dummy revocation documents in Python.
+// # The seed derivation is not the same as sr25519.from_seed so this doesn't work yet.
+// ```python
+// import duniterpy, substrateinterface
+// s = duniterpy.key.SigningKey.from_credentials("Charlie", "Charlie")
+// block = duniterpy.documents.BlockID(42, "A"*64)
+// idty = duniterpy.documents.Identity(s.pubkey, "Charlie", block, s)
+// r = duniterpy.documents.Revocation(idty, s)
+// print("SS58 address:", substrateinterface.base.ss58_encode(s.vk))
+// print(r.signed_raw())
+// ```
+#[test]
+fn test_idty_revocation_legacy() {
+    new_test_ext(IdentityConfig {
+        identities: vec![alice(), legacy_charlie()],
+    })
+    .execute_with(|| {
+        // We need to initialize at least one block before any call
+        run_to_block(1);
+
+        let valid_revocation_document = r"Version: 10
+Type: Revocation
+Currency: g1
+Issuer: Fnf2xaxYdQpB4kU45DMLQ9Ey4bd6DtoebKJajRkLBUXm
+IdtyUniqueID: Charlie
+IdtyTimestamp: 42-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+IdtySignature: 7KUagcMiQw05rwbkBsRrnNqPRHu/Y5ukCLoAEpb/1tXAQsSNf2gRi1h5PWIGs9y/vHnFXvF5epKsOjA6X75vDg==
+CfiG4xhcWS+/DgxY0xFIyOA9TVr4Im3XEXcCApNgXC+Ns9jy2yrNoC3NF8MCD63cZ8QTRfrr4Iv6n3leYCCcDQ==
+";
+
+let revocation_document_bad_username = r"Version: 10
+Type: Revocation
+Currency: g1
+Issuer: Fnf2xaxYdQpB4kU45DMLQ9Ey4bd6DtoebKJajRkLBUXm
+IdtyUniqueID: Alice
+IdtyTimestamp: 42-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+IdtySignature: dqO8nnYWZDDDzadMXpOVwehJXQ9wocE9QTKsVBa88rPONLhz12QA6Ytib2+VtPU+gnewO2mRVOvzdYKXemQPDg==
+0q/Dy4jwLTjZGSOu4GWdkfW+SqXRAPHUwwvWQenqiuNuL2eEc0x2hM0MWhIOuSLy2ifNq6PfSH/dBrV5CgYIAw==
+";
+
+let revocation_document_bad_signer = r"Version: 10
+Type: Revocation
+Currency: g1
+Issuer: 9cFLFh12MZSL8HHW9KvEDGTEEyKALGjEdHpNP8rqmmw3
+IdtyUniqueID: Charlie
+IdtyTimestamp: 42-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+IdtySignature: 8P2vjDHZf4tHaGpZYTuTXJ9Xe+3qQ0FAM6fypvwl2mYqLs1ZfE07gp4mqRpNY90rC9+CIIi7eHvv2uAlFVpfCQ==
+iWOssQ1y2svWeUD4byjJx6n+/Xgf0pgMe1FDhnR9oN76Ri9B8SQfP+hFD3GCth7sZRD162sR83g3UvYpFHJLBQ==
+";
+
+        assert_eq!(
+            Identity::revoke_identity_legacy(
+                RuntimeOrigin::signed(account(1).id),
+                revocation_document_bad_username.into()
+            ),
+            Err(Error::<Test>::InvalidRevocationKey.into())
+        );
+
+        assert_eq!(
+            Identity::revoke_identity_legacy(
+                RuntimeOrigin::signed(account(1).id),
+                revocation_document_bad_signer.into()
+            ),
+            Err(Error::<Test>::InvalidRevocationKey.into())
+        );
+
+        // Anyone can submit a revocation payload
+        assert_ok!(Identity::revoke_identity_legacy(
+            RuntimeOrigin::signed(account(42).id),
+            valid_revocation_document.into()
+        ));
+
+        System::assert_has_event(RuntimeEvent::Identity(crate::Event::IdtyRevoked {
+            idty_index: 102,
+            reason: RevocationReason::User,
+        }));
+    });
+}
+
 #[test]
 fn test_inactive_genesis_members() {
     new_test_ext(IdentityConfig {
diff --git a/pallets/identity/src/weights.rs b/pallets/identity/src/weights.rs
index 61d15aba77a28c887301ec22b8c7863c24dedc58..f72819c3717da2372c724ff703a9bcc4eb23b4f6 100644
--- a/pallets/identity/src/weights.rs
+++ b/pallets/identity/src/weights.rs
@@ -23,6 +23,7 @@ pub trait WeightInfo {
     fn confirm_identity() -> Weight;
     fn change_owner_key() -> Weight;
     fn revoke_identity() -> Weight;
+    fn revoke_identity_legacy() -> Weight;
     fn prune_item_identities_names(i: u32) -> Weight;
     fn fix_sufficients() -> Weight;
     fn link_account() -> Weight;
@@ -84,6 +85,17 @@ impl WeightInfo for () {
             .saturating_add(RocksDbWeight::get().writes(6))
     }
 
+    fn revoke_identity_legacy() -> Weight {
+        // Proof Size summary in bytes:
+        //  Measured:  `778`
+        //  Estimated: `6718`
+        // Minimum execution time: 829_174_000 picoseconds.
+        Weight::from_parts(869_308_000, 0)
+            .saturating_add(Weight::from_parts(0, 6718))
+            .saturating_add(RocksDbWeight::get().reads(6))
+            .saturating_add(RocksDbWeight::get().writes(6))
+    }
+
     fn prune_item_identities_names(i: u32) -> Weight {
         // Proof Size summary in bytes:
         //  Measured:  `0`
diff --git a/pallets/smith-members/src/lib.rs b/pallets/smith-members/src/lib.rs
index 35918f55a00283378fd80f27686c8ecf7c6e0845..e7c48962a8d187dd46348e21b72b1c86623375c6 100644
--- a/pallets/smith-members/src/lib.rs
+++ b/pallets/smith-members/src/lib.rs
@@ -240,10 +240,13 @@ pub mod pallet {
                         received_certs: issuers_,
                     },
                 );
-                ExpiresOn::<T>::append(
-                    CurrentSession::<T>::get() + T::SmithInactivityMaxDuration::get(),
-                    receiver,
-                );
+                // if smith is offline, schedule expire
+                if !*is_online {
+                    ExpiresOn::<T>::append(
+                        CurrentSession::<T>::get() + T::SmithInactivityMaxDuration::get(),
+                        receiver,
+                    );
+                }
             }
 
             for (issuer, issued_certs) in cert_meta_by_issuer {
@@ -516,7 +519,7 @@ impl<T: Config> Pallet<T> {
 
     /// Handle the removal of Smiths whose expiration time has been reached at a given session index.
     fn on_exclude_expired_smiths(at: SessionIndex) {
-        if let Some(smiths_to_remove) = ExpiresOn::<T>::get(at) {
+        if let Some(smiths_to_remove) = ExpiresOn::<T>::take(at) {
             for smith in smiths_to_remove {
                 if let Some(smith_meta) = Smiths::<T>::get(smith) {
                     if let Some(expires_on) = smith_meta.expires_on {
@@ -582,7 +585,7 @@ impl<T: Config> Pallet<T> {
                     if let Some(smith_meta) = maybe_smith_meta {
                         // As long as the smith is online, it cannot expire
                         smith_meta.expires_on = None;
-                        // FIXME: unschedule old expiry
+                        // FIXME: unschedule old expiry (#182)
                     }
                 });
             }
@@ -592,10 +595,12 @@ impl<T: Config> Pallet<T> {
     /// Handle the event when a Smith goes offline.
     pub fn on_smith_goes_offline(idty_index: T::IdtyIndex) {
         if let Some(smith_meta) = Smiths::<T>::get(idty_index) {
-            if smith_meta.expires_on.is_none() {
+            // Smith can go offline after main membership expiry
+            // in this case, there is no scheduled expiry since it is already excluded
+            if smith_meta.status != SmithStatus::Excluded {
                 Smiths::<T>::mutate(idty_index, |maybe_smith_meta| {
                     if let Some(smith_meta) = maybe_smith_meta {
-                        // As long as the smith is online, it cannot expire
+                        // schedule expiry
                         let new_expires_on =
                             CurrentSession::<T>::get() + T::SmithInactivityMaxDuration::get();
                         smith_meta.expires_on = Some(new_expires_on);
diff --git a/pallets/smith-members/src/tests.rs b/pallets/smith-members/src/tests.rs
index 0ac9ba7ed63137d4519d484f20523a607cc6c329..c92bffb680caedd2e006ad2b7c8378ce04e529d6 100644
--- a/pallets/smith-members/src/tests.rs
+++ b/pallets/smith-members/src/tests.rs
@@ -628,6 +628,48 @@ fn certifying_an_online_smith() {
     });
 }
 
+/// Test that scheduled expiration is removed after session
+#[test]
+fn expires_on_cleans_up() {
+    new_test_ext(GenesisConfig {
+        initial_smiths: btreemap![
+            1 => (true, vec![2, 3]),
+            2 => (true, vec![1,3]),
+            3 => (true, vec![1,2]),
+        ],
+    })
+    .execute_with(|| {
+        // Alice goes offline, and is set to expire
+        Pallet::<Runtime>::on_smith_goes_offline(1);
+        // The expiration block is present in smith data
+        assert_eq!(
+            Smiths::<Runtime>::get(1),
+            Some(SmithMeta {
+                status: Smith,
+                expires_on: Some(5),
+                issued_certs: vec![2, 3],
+                received_certs: vec![2, 3]
+            })
+        );
+        // It is also present in ExpiresOn schedule
+        assert_eq!(ExpiresOn::<Runtime>::get(5), Some(vec![1]));
+        // Go to expiration session
+        Pallet::<Runtime>::on_new_session(5);
+        // Alice is expired
+        assert_eq!(
+            Smiths::<Runtime>::get(1),
+            Some(SmithMeta {
+                status: Excluded,
+                expires_on: None,
+                issued_certs: vec![2, 3],
+                received_certs: vec![]
+            })
+        );
+        // ExpiresOn is clean
+        assert_eq!(ExpiresOn::<Runtime>::get(5), None);
+    });
+}
+
 #[test]
 fn invitation_on_non_wot_member() {
     new_test_ext(GenesisConfig {
diff --git a/pallets/universal-dividend/src/compute_claim_uds.rs b/pallets/universal-dividend/src/compute_claim_uds.rs
index 7d58edcea4bbdb5061eca67b188380c54045d492..1c2cbe8b7065cf01a5efc22555dd7900ec71f7d6 100644
--- a/pallets/universal-dividend/src/compute_claim_uds.rs
+++ b/pallets/universal-dividend/src/compute_claim_uds.rs
@@ -24,18 +24,22 @@ pub(super) fn compute_claim_uds<Balance: AtLeast32BitUnsigned>(
 ) -> (UdIndex, Balance) {
     let mut total_amount = Zero::zero();
     let mut total_count = 0;
-    for (ud_index, ud_amount) in past_reevals.rev() {
-        if ud_index <= first_ud_index {
+    // We start in reverse order, i.e. the most recent reeval first
+    for (reeval_index, ud_amount) in past_reevals.rev() {
+        // Therefore, if our first UD is above the current reeval index, we have reached our final useful reeval and must break
+        if reeval_index <= first_ud_index {
             let count = current_ud_index - first_ud_index;
             total_amount += Balance::from(count) * ud_amount;
             total_count += count;
             // First unclaimed UD is reached; stop counting now.
             break;
-        } else {
-            let count = current_ud_index - ud_index;
+        }
+        // Otherwise, we consume the full reeval contained UDs
+        else {
+            let count = current_ud_index - reeval_index;
             total_amount += Balance::from(count) * ud_amount;
             total_count += count;
-            current_ud_index = ud_index;
+            current_ud_index = reeval_index;
         }
     }
 
@@ -109,4 +113,19 @@ mod tests {
             (2, 110_000)
         );
     }
+
+    #[test]
+    fn very_old_unclaimed_ud_out_of_reevals() {
+        let past_reevals = vec![
+            // (3, 100 as Balance), "old" reeval which has gone out of reevals window.
+            (4, 1_000 as Balance),
+            (5, 10_000 as Balance),
+            (6, 100_000 as Balance),
+        ];
+        // All the UDs out of the reeval window must produce 0 money units
+        assert_eq!(
+            compute_claim_uds(7, 1, past_reevals.into_iter()),
+            (3, 111_000)
+        );
+    }
 }
diff --git a/pallets/universal-dividend/src/lib.rs b/pallets/universal-dividend/src/lib.rs
index 70518f7fd0f83752431c2e5f5c69a5385401cde9..6fda9a68c9621cb34358ede9a3cc138b7eb58905 100644
--- a/pallets/universal-dividend/src/lib.rs
+++ b/pallets/universal-dividend/src/lib.rs
@@ -331,10 +331,11 @@ pub mod pallet {
                             core::num::NonZeroU16::new(current_ud_index)
                                 .expect("unreachable because current_ud_index is never zero."),
                         );
-                        let _ = T::Currency::deposit(who, uds_total, Precision::Exact);
+                        // Currency is issued here
+                        let actual_total = T::Currency::mint_into(who, uds_total)?;
                         Self::deposit_event(Event::UdsClaimed {
                             count: uds_count,
-                            total: uds_total,
+                            total: actual_total,
                             who: who.clone(),
                         });
                         Ok(().into())
diff --git a/pallets/universal-dividend/src/tests.rs b/pallets/universal-dividend/src/tests.rs
index 3240e9b5fdc713dedd381870e57e6ad22c2e0e39..c8a726fb2fe8f36fd32c32b92f29c803c9cd27d8 100644
--- a/pallets/universal-dividend/src/tests.rs
+++ b/pallets/universal-dividend/src/tests.rs
@@ -62,6 +62,11 @@ fn test_claim_uds() {
             total: 1_000,
             who: 1,
         }));
+        // the expected event from pallet balances is Minted
+        System::assert_has_event(RuntimeEvent::Balances(pallet_balances::Event::Minted {
+            who: 1,
+            amount: 1000,
+        }));
         assert_eq!(Balances::free_balance(1), 1_000);
         // Others members should not receive any UDs with Alice claim
         assert_eq!(Balances::free_balance(2), 0);
diff --git a/pallets/universal-dividend/src/types.rs b/pallets/universal-dividend/src/types.rs
index bc5bcd7214fbd2a8514064b6a0d66d83670e168b..b5b5907e6d2663407d07adb933d5f350bf306b44 100644
--- a/pallets/universal-dividend/src/types.rs
+++ b/pallets/universal-dividend/src/types.rs
@@ -22,12 +22,18 @@ use sp_runtime::RuntimeDebug;
 pub type UdIndex = u16;
 
 /// Represents the first eligible Universal Dividend.
-#[derive(
-    Clone, Copy, Default, Eq, PartialEq, RuntimeDebug, serde::Deserialize, serde::Serialize,
-)]
+#[derive(Clone, Eq, PartialEq, RuntimeDebug, serde::Deserialize, serde::Serialize)]
 pub struct FirstEligibleUd(pub Option<NonZeroU16>);
 
+/// Default is not eligible
+impl Default for FirstEligibleUd {
+    fn default() -> Self {
+        FirstEligibleUd(None)
+    }
+}
+
 impl FirstEligibleUd {
+    /// Eligible at the first UD index
     pub fn min() -> Self {
         Self(Some(NonZeroU16::new(1).expect("unreachable")))
     }
diff --git a/primitives/distance/src/lib.rs b/primitives/distance/src/lib.rs
index acaf53c381bfe586b6dc48980875e1ab2f597faa..c447da1020bb48d8ba944c5a8af22ba9e98cdf11 100644
--- a/primitives/distance/src/lib.rs
+++ b/primitives/distance/src/lib.rs
@@ -35,29 +35,14 @@ pub struct ComputationResult {
     pub distances: scale_info::prelude::vec::Vec<Perbill>,
 }
 
+/// Errors that can occur while checking the inherent data in `ProvideInherent::check_inherent` from pallet-distance.
 #[derive(Encode, sp_runtime::RuntimeDebug)]
 #[cfg_attr(feature = "std", derive(Decode, thiserror::Error))]
-pub enum InherentError {
-    #[cfg_attr(feature = "std", error("InvalidComputationResultFile"))]
-    InvalidComputationResultFile,
-}
+pub enum InherentError {}
 
 impl IsFatalError for InherentError {
     fn is_fatal_error(&self) -> bool {
-        match self {
-            InherentError::InvalidComputationResultFile => false,
-        }
-    }
-}
-
-impl InherentError {
-    #[cfg(feature = "std")]
-    pub fn try_from(id: &InherentIdentifier, mut data: &[u8]) -> Option<Self> {
-        if id == &INHERENT_IDENTIFIER {
-            <InherentError as codec::Decode>::decode(&mut data).ok()
-        } else {
-            None
-        }
+        false
     }
 }
 
@@ -94,15 +79,12 @@ impl<IdtyIndex: Decode + Encode + PartialEq + TypeInfo + Send + Sync>
 
     async fn try_handle_error(
         &self,
-        identifier: &InherentIdentifier,
-        error: &[u8],
+        _identifier: &InherentIdentifier,
+        _error: &[u8],
     ) -> Option<Result<(), sp_inherents::Error>> {
-        if *identifier != INHERENT_IDENTIFIER {
-            return None;
-        }
-
-        Some(Err(sp_inherents::Error::Application(Box::from(
-            InherentError::try_from(&INHERENT_IDENTIFIER, error)?,
-        ))))
+        // No errors occur here.
+        // Errors handled here are emitted in the `ProvideInherent::check_inherent`
+        // (from pallet-distance) which is not implemented.
+        None
     }
 }
diff --git a/resources/debian/distance-oracle.service b/resources/debian/distance-oracle.service
index 66fe13e536b2ee0055d081f11f196e03fb273638..8bd2fb7630677de81ca00a57f82b97ccb7e9f0dc 100644
--- a/resources/debian/distance-oracle.service
+++ b/resources/debian/distance-oracle.service
@@ -5,6 +5,6 @@ After=duniter-smith.service
 
 [Service]
 EnvironmentFile=/etc/duniter/env_file
-ExecStart=/usr/bin/duniter2 distance-oracle --evaluation-result-dir ${BASE_PATH}/chains/${DUNITER_CHAIN_NAME}/distance --rpc-url ${ORACLE_RPC_URL} --log ${ORACLE_LOG_LEVEL}
+ExecStart=/usr/bin/duniter2 distance-oracle --evaluation-result-dir ${BASE_PATH}/chains/${DUNITER_CHAIN_NAME}/distance --rpc-url ${ORACLE_RPC_URL} --interval ${ORACLE_INTERVAL} --log ${ORACLE_LOG_LEVEL}
 User=duniter
 Group=duniter
diff --git a/resources/debian/distance-oracle.timer b/resources/debian/distance-oracle.timer
deleted file mode 100644
index de70093292fb6c3c65379a854e8e180feb61e43e..0000000000000000000000000000000000000000
--- a/resources/debian/distance-oracle.timer
+++ /dev/null
@@ -1,10 +0,0 @@
-[Unit]
-Description=Duniter distance oracle timer.
-
-[Timer]
-EnvironmentFile=/etc/duniter/env_file
-OnBootSec=1min
-OnUnitActiveSec=2min
-
-[Install]
-WantedBy=multi-user.target
diff --git a/resources/debian/env_file b/resources/debian/env_file
index e896759f70e3df66de01624fb8e61f9fa540504e..61ebfbd281d0a384e94ddfa8fed25507ac60654e 100644
--- a/resources/debian/env_file
+++ b/resources/debian/env_file
@@ -36,6 +36,11 @@ BASE_PATH=/home/duniter/.local/share/duniter
 # Default: ws://127.0.0.1:9944 for a local WebSocket RPC server.
 ORACLE_RPC_URL=ws://127.0.0.1:9944
 
+# Interval in seconds at which the oracle is run.
+# This should not exceed the evaluation period of the blockchain.
+# Default: 600 seconds
+ORACLE_INTERVAL=600
+
 # Determines the log level for the Oracle.
 # Options include 'error', 'warn', 'info', 'debug', 'trace'.
 # 'info' is a good default that provides useful runtime information without too much detail.
diff --git a/runtime/common/src/entities.rs b/runtime/common/src/entities.rs
index 4f21fa71b02d2d026b5752f3b3c2fc5d070d412e..8884f399ff9c9867f5619286a8288fad155901e7 100644
--- a/runtime/common/src/entities.rs
+++ b/runtime/common/src/entities.rs
@@ -53,15 +53,6 @@ pub struct IdtyData {
     pub first_eligible_ud: pallet_universal_dividend::FirstEligibleUd,
 }
 
-#[cfg(feature = "std")]
-impl IdtyData {
-    pub fn new() -> Self {
-        Self {
-            first_eligible_ud: pallet_universal_dividend::FirstEligibleUd::min(),
-        }
-    }
-}
-
 impl From<IdtyData> for pallet_universal_dividend::FirstEligibleUd {
     fn from(idty_data: IdtyData) -> Self {
         idty_data.first_eligible_ud
diff --git a/runtime/common/src/handlers.rs b/runtime/common/src/handlers.rs
index ff9aba8518717b9418d79b400f8baf7436c15219..46c61afa63fcd8048d97f2398656b5f754d85262 100644
--- a/runtime/common/src/handlers.rs
+++ b/runtime/common/src/handlers.rs
@@ -111,21 +111,20 @@ impl<
         // duniter-wot related actions
         let mut weight = pallet_duniter_wot::Pallet::<Runtime>::on_removed(idty_index);
 
-        let mut add_db_reads_writes = |reads, writes| {
-            weight += Runtime::DbWeight::get().reads_writes(reads, writes);
-        };
-
-        // When membership is removed, call on_removed_member handler which auto claims UD.
-        if let Some(idty_value) = pallet_identity::Identities::<Runtime>::get(idty_index) {
-            add_db_reads_writes(1, 0);
-            if let Some(first_ud_index) = idty_value.data.first_eligible_ud.into() {
-                add_db_reads_writes(1, 0);
-                weight += pallet_universal_dividend::Pallet::<Runtime>::on_removed_member(
-                    first_ud_index,
-                    &idty_value.owner_key,
-                );
+        // When membership is removed:
+        // - call on_removed_member handler which auto claims UD;
+        // - set the first_eligible_ud to None so the identity cannot claim UD anymore.
+        pallet_identity::Identities::<Runtime>::mutate(idty_index, |maybe_idty_value| {
+            if let Some(idty_value) = maybe_idty_value {
+                if let Some(first_ud_index) = idty_value.data.first_eligible_ud.0.take() {
+                    weight += pallet_universal_dividend::Pallet::<Runtime>::on_removed_member(
+                        first_ud_index.into(),
+                        &idty_value.owner_key,
+                    );
+                }
             }
-        }
+        });
+        weight += Runtime::DbWeight::get().reads_writes(1, 1);
 
         // When membership is removed, also remove from smith member.
         weight.saturating_add(
diff --git a/runtime/common/src/providers.rs b/runtime/common/src/providers.rs
index 015043f01480ff7b71ba9434ab4b3a4ab0b6b181..1b20fbd0a9fb5ece67b1f96ecefb7d5bc1c4ec6d 100644
--- a/runtime/common/src/providers.rs
+++ b/runtime/common/src/providers.rs
@@ -59,7 +59,7 @@ where
     ) -> Result<R, E> {
         pallet_identity::Pallet::<T>::try_mutate_exists(key, |maybe_idty_data| {
             if let Some(ref mut idty_data) = maybe_idty_data {
-                let mut maybe_first_eligible_ud = Some(idty_data.first_eligible_ud);
+                let mut maybe_first_eligible_ud = Some(idty_data.first_eligible_ud.clone());
                 let result = f(&mut maybe_first_eligible_ud)?;
                 if let Some(first_eligible_ud) = maybe_first_eligible_ud {
                     idty_data.first_eligible_ud = first_eligible_ud;
diff --git a/runtime/g1/src/weights/pallet_certification.rs b/runtime/g1/src/weights/pallet_certification.rs
index 9b40f1ab37e273f17ab497b9d7e33a201f161f63..3b52d4e48a4d49b559614a03f87b12da53d58d20 100644
--- a/runtime/g1/src/weights/pallet_certification.rs
+++ b/runtime/g1/src/weights/pallet_certification.rs
@@ -188,4 +188,26 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T>
 			.saturating_add(T::DbWeight::get().reads(4))
 			.saturating_add(T::DbWeight::get().writes(3))
 	}
+	/// Storage: `Certification::CertsByReceiver` (r:1 w:1)
+	/// Proof: `Certification::CertsByReceiver` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `Certification::StorageIdtyCertMeta` (r:1000 w:1000)
+	/// Proof: `Certification::StorageIdtyCertMeta` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `Membership::Membership` (r:1 w:0)
+	/// Proof: `Membership::Membership` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// The range of component `i` is `[2, 1000]`.
+	fn do_remove_all_certs_received_by(i: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `554 + i * (35 ±0)`
+		//  Estimated: `4018 + i * (2511 ±0)`
+		// Minimum execution time: 30_026_000 picoseconds.
+		Weight::from_parts(30_298_000, 0)
+			.saturating_add(Weight::from_parts(0, 4018))
+			// Standard Error: 24_742
+			.saturating_add(Weight::from_parts(8_970_118, 0).saturating_mul(i.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into())))
+			.saturating_add(T::DbWeight::get().writes(1))
+			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into())))
+			.saturating_add(Weight::from_parts(0, 2511).saturating_mul(i.into()))
+	}
 }
diff --git a/runtime/g1/src/weights/pallet_identity.rs b/runtime/g1/src/weights/pallet_identity.rs
index 69fdb129d8d0f9a3414b06fc03b6fe80e663bdd3..2082d2e1fb36f0071c70d7b18a135416f88a782a 100644
--- a/runtime/g1/src/weights/pallet_identity.rs
+++ b/runtime/g1/src/weights/pallet_identity.rs
@@ -135,6 +135,16 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> {
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(5))
 	}
+	fn revoke_identity_legacy() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `697`
+		//  Estimated: `6637`
+		// Minimum execution time: 70_321_000 picoseconds.
+		Weight::from_parts(72_274_000, 0)
+			.saturating_add(Weight::from_parts(0, 6637))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(5))
+	}
 	/// Storage: `Identity::IdentitiesNames` (r:0 w:999)
 	/// Proof: `Identity::IdentitiesNames` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// The range of component `i` is `[2, 1000]`.
diff --git a/runtime/gdev/Cargo.toml b/runtime/gdev/Cargo.toml
index 3299b812d1622b27fc3fea94ee79aa01471c580d..08fc4435aad8cf4a23a28a6c102e5826216355c4 100644
--- a/runtime/gdev/Cargo.toml
+++ b/runtime/gdev/Cargo.toml
@@ -112,6 +112,7 @@ std = [
 	"sp-genesis-builder/std",
 	"sp-inherents/std",
 	"sp-io/std",
+	"sp-keyring/std",
 	"sp-membership/std",
 	"sp-offchain/std",
 	"sp-runtime/std",
diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs
index 4fce49908f3fab30e3472d2604190c474f50657b..9152ae3fb9f2ab71a6c105451c4d33420d4bc7d0 100644
--- a/runtime/gdev/src/lib.rs
+++ b/runtime/gdev/src/lib.rs
@@ -173,7 +173,12 @@ mod benches {
 pub struct BaseCallFilter;
 impl Contains<RuntimeCall> for BaseCallFilter {
     fn contains(call: &RuntimeCall) -> bool {
-        !matches!(call, RuntimeCall::Session(_))
+        // not allowed to run session calls directly
+        // not allowed to burn currency
+        !matches!(
+            call,
+            RuntimeCall::Session(_) | RuntimeCall::Balances(pallet_balances::Call::burn { .. })
+        )
     }
 }
 
diff --git a/runtime/gdev/src/weights/pallet_certification.rs b/runtime/gdev/src/weights/pallet_certification.rs
index e3accd5ecafa1602dc6bfb4affcd1d4e0e4e5f93..73df17f4b80ad7deb0ff5c2a0ab985425abdb755 100644
--- a/runtime/gdev/src/weights/pallet_certification.rs
+++ b/runtime/gdev/src/weights/pallet_certification.rs
@@ -172,4 +172,28 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T>
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(3))
 	}
+	/// Storage: `Certification::CertsByReceiver` (r:1 w:1)
+	/// Proof: `Certification::CertsByReceiver` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `Certification::StorageIdtyCertMeta` (r:1000 w:1000)
+	/// Proof: `Certification::StorageIdtyCertMeta` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `Parameters::ParametersStorage` (r:1 w:0)
+	/// Proof: `Parameters::ParametersStorage` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `Membership::Membership` (r:1 w:0)
+	/// Proof: `Membership::Membership` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// The range of component `i` is `[2, 1000]`.
+	fn do_remove_all_certs_received_by(i: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `665 + i * (35 ±0)`
+		//  Estimated: `4129 + i * (2511 ±0)`
+		// Minimum execution time: 33_392_000 picoseconds.
+		Weight::from_parts(34_295_000, 0)
+			.saturating_add(Weight::from_parts(0, 4129))
+			// Standard Error: 24_307
+			.saturating_add(Weight::from_parts(9_192_872, 0).saturating_mul(i.into()))
+			.saturating_add(T::DbWeight::get().reads(3))
+			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into())))
+			.saturating_add(T::DbWeight::get().writes(1))
+			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into())))
+			.saturating_add(Weight::from_parts(0, 2511).saturating_mul(i.into()))
+	}
 }
diff --git a/runtime/gdev/src/weights/pallet_identity.rs b/runtime/gdev/src/weights/pallet_identity.rs
index 9a72d7a2eccf70d26c2550b5b873ea5e14ebd91d..b42e2bfe2d2fbb7472e31fac3a841459fe64b073 100644
--- a/runtime/gdev/src/weights/pallet_identity.rs
+++ b/runtime/gdev/src/weights/pallet_identity.rs
@@ -137,6 +137,16 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> {
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(5))
 	}
+	fn revoke_identity_legacy() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `697`
+		//  Estimated: `6637`
+		// Minimum execution time: 70_095_000 picoseconds.
+		Weight::from_parts(71_457_000, 0)
+			.saturating_add(Weight::from_parts(0, 6637))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(5))
+	}
 	/// Storage: `Identity::IdentitiesNames` (r:0 w:999)
 	/// Proof: `Identity::IdentitiesNames` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// The range of component `i` is `[2, 1000]`.
diff --git a/runtime/gdev/tests/common/mod.rs b/runtime/gdev/tests/common/mod.rs
index ae5c557d7b761427cb6455464ba79c151eb6bab4..82b5ade8d586dfb587c83b5ebd850b5d6b303500 100644
--- a/runtime/gdev/tests/common/mod.rs
+++ b/runtime/gdev/tests/common/mod.rs
@@ -223,7 +223,9 @@ impl ExtBuilder {
                     index: i as u32 + 1,
                     name: name.clone(),
                     value: IdtyValue {
-                        data: IdtyData::new(),
+                        data: IdtyData {
+                            first_eligible_ud: pallet_universal_dividend::FirstEligibleUd::min(),
+                        },
                         next_creatable_identity_on: Default::default(),
                         owner_key: owner_key.clone(),
                         old_owner_key: None,
diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs
index 2d6b5d1577cc7b043c775cc5ca179e4e9e8f856b..911d2be7e54d1a0d68a24119dd30e52cdb04f5c7 100644
--- a/runtime/gdev/tests/integration_tests.rs
+++ b/runtime/gdev/tests/integration_tests.rs
@@ -25,6 +25,7 @@ use frame_support::{
 use gdev_runtime::*;
 use pallet_identity::{RevocationPayload, REVOCATION_PAYLOAD_PREFIX};
 use pallet_membership::MembershipRemovalReason;
+use pallet_session::historical::SessionManager;
 use pallet_smith_members::{SmithMeta, SmithStatus};
 use scale_info::prelude::num::NonZeroU16;
 use sp_core::{Encode, Pair};
@@ -165,9 +166,9 @@ fn test_total_issuance_vs_monetary_mass() {
                 6000
             );
             // Alice claims her UD
-            assert_ok!(UniversalDividend::claim_uds(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into()
-            ));
+            assert_ok!(UniversalDividend::claim_uds(RuntimeOrigin::signed(
+                AccountKeyring::Alice.to_account_id()
+            )));
             assert_eq!(Balances::total_issuance(), 4000);
             assert_eq!(
                 pallet_universal_dividend::MonetaryMass::<Runtime>::get(),
@@ -176,9 +177,9 @@ fn test_total_issuance_vs_monetary_mass() {
             // second UD creation
             run_to_block(21);
             // Bob claims his 2 UDs
-            assert_ok!(UniversalDividend::claim_uds(
-                frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into()
-            ));
+            assert_ok!(UniversalDividend::claim_uds(RuntimeOrigin::signed(
+                AccountKeyring::Bob.to_account_id()
+            )));
             assert_eq!(Balances::total_issuance(), 6000);
             assert_eq!(
                 pallet_universal_dividend::MonetaryMass::<Runtime>::get(),
@@ -199,7 +200,7 @@ fn test_identity_below_ed() {
             // a transfer below ED will lead to frozen token error
             assert_noop!(
                 Balances::transfer_allow_death(
-                    frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                    RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                     MultiAddress::Id(AccountKeyring::Bob.to_account_id()),
                     850
                 ),
@@ -208,7 +209,7 @@ fn test_identity_below_ed() {
             // // Old behavior below
             // // Should be able to go below existential deposit, loose dust, and still not die
             // assert_ok!(Balances::transfer(
-            //     frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+            //     RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
             //     MultiAddress::Id(AccountKeyring::Bob.to_account_id()),
             //     800
             // ));
@@ -336,29 +337,29 @@ fn test_validate_identity_when_claim() {
             run_to_block(1);
             // alice create identity for Eve
             assert_ok!(Identity::create_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 AccountKeyring::Eve.to_account_id(),
             ));
             run_to_block(2);
             // eve confirms her identity
             assert_ok!(Identity::confirm_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
                 "Eeeeeveeeee".into(),
             ));
             run_to_block(3);
             // eve gets certified by bob and charlie
             assert_ok!(Certification::add_cert(
-                frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Bob.to_account_id()),
                 5
             ));
             assert_ok!(Certification::add_cert(
-                frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Charlie.to_account_id()),
                 5
             ));
 
             // eve request distance evaluation for herself
             assert_ok!(Distance::request_distance_evaluation(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
             ));
 
             // Pass 2nd evaluation period
@@ -366,7 +367,7 @@ fn test_validate_identity_when_claim() {
             run_to_block(2 * eval_period);
             // simulate an evaluation published by smith Alice
             assert_ok!(Distance::force_update_evaluation(
-                frame_system::RawOrigin::Root.into(),
+                RuntimeOrigin::root(),
                 AccountKeyring::Alice.to_account_id(),
                 pallet_distance::ComputationResult {
                     distances: vec![Perbill::one()],
@@ -385,7 +386,7 @@ fn test_validate_identity_when_claim() {
             // the following call does not exist anymore
             // assert_noop!(
             //     Membership::claim_membership(
-            //         frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+            //         RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
             //     ),
             //     pallet_membership::Error::<Runtime>::AlreadyMember
             // );
@@ -416,7 +417,7 @@ fn test_identity_creation_workflow() {
             run_to_block(1);
             // alice create identity for Eve
             assert_ok!(Identity::create_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 AccountKeyring::Eve.to_account_id(),
             ));
             assert_eq!(
@@ -433,7 +434,7 @@ fn test_identity_creation_workflow() {
             run_to_block(2);
             // eve confirms her identity
             assert_ok!(Identity::confirm_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
                 "Eeeeeveeeee".into(),
             ));
             assert_eq!(
@@ -450,17 +451,17 @@ fn test_identity_creation_workflow() {
             run_to_block(3);
             // eve gets certified by bob and charlie
             assert_ok!(Certification::add_cert(
-                frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Bob.to_account_id()),
                 5
             ));
             assert_ok!(Certification::add_cert(
-                frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Charlie.to_account_id()),
                 5
             ));
             // charlie also request distance evaluation for eve
             // (could be done in batch)
             assert_ok!(Distance::request_distance_evaluation_for(
-                frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Charlie.to_account_id()),
                 5
             ));
             // then the evaluation is pending
@@ -474,7 +475,7 @@ fn test_identity_creation_workflow() {
             run_to_block(2 * eval_period);
             // simulate evaluation published by smith Alice
             assert_ok!(Distance::force_update_evaluation(
-                frame_system::RawOrigin::Root.into(),
+                RuntimeOrigin::root(),
                 AccountKeyring::Alice.to_account_id(),
                 pallet_distance::ComputationResult {
                     distances: vec![Perbill::one()],
@@ -523,9 +524,9 @@ fn test_identity_creation_workflow() {
                     members_count: 5,
                 },
             ));
-            assert_ok!(UniversalDividend::claim_uds(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
-            ));
+            assert_ok!(UniversalDividend::claim_uds(RuntimeOrigin::signed(
+                AccountKeyring::Eve.to_account_id()
+            ),));
             System::assert_has_event(RuntimeEvent::UniversalDividend(
                 pallet_universal_dividend::Event::UdsClaimed {
                     count: (10 - first_eligible),
@@ -554,7 +555,7 @@ fn test_can_not_issue_cert_when_membership_lost() {
         // Bob can not issue a certification
         assert_noop!(
             Certification::add_cert(
-                frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Bob.to_account_id()),
                 3,
             ),
             pallet_duniter_wot::Error::<gdev_runtime::Runtime>::IssuerNotMember
@@ -613,23 +614,23 @@ fn test_membership_renewal() {
         .execute_with(|| {
             // can not renew membership immediately
             assert_noop!(
-                Distance::request_distance_evaluation(
-                    frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
-                ),
+                Distance::request_distance_evaluation(RuntimeOrigin::signed(
+                    AccountKeyring::Alice.to_account_id()
+                ),),
                 pallet_duniter_wot::Error::<Runtime>::MembershipRenewalPeriodNotRespected,
             );
 
             // but ok after waiting 10 blocks delay
             run_to_block(11);
             assert_ok!(Distance::request_distance_evaluation(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
             ));
 
             // Pass 3rd evaluation period
             let eval_period: u32 = <Runtime as pallet_distance::Config>::EvaluationPeriod::get();
             run_to_block(3 * eval_period);
             assert_ok!(Distance::force_update_evaluation(
-                frame_system::RawOrigin::Root.into(),
+                RuntimeOrigin::root(),
                 AccountKeyring::Alice.to_account_id(),
                 pallet_distance::ComputationResult {
                     distances: vec![Perbill::one()],
@@ -649,9 +650,9 @@ fn test_membership_renewal() {
             // not possible to renew manually
             // can not ask renewal when period is not respected
             assert_noop!(
-                Distance::request_distance_evaluation(
-                    frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
-                ),
+                Distance::request_distance_evaluation(RuntimeOrigin::signed(
+                    AccountKeyring::Alice.to_account_id()
+                ),),
                 pallet_duniter_wot::Error::<Runtime>::MembershipRenewalPeriodNotRespected,
             );
 
@@ -731,6 +732,57 @@ fn test_revoke_identity_after_one_ud() {
     });
 }
 
+// test that UD cannot be claimed after revocation
+#[test]
+fn test_claim_ud_after_revoke() {
+    ExtBuilder::new(1, 3, 4).build().execute_with(|| {
+        run_to_block(
+            (<Runtime as pallet_universal_dividend::Config>::UdCreationPeriod::get()
+                / <Runtime as pallet_babe::Config>::ExpectedBlockTime::get()
+                + 1) as u32,
+        );
+
+        // before UD, bob has 0 (initial amount)
+        run_to_block(1);
+        assert_eq!(
+            Balances::free_balance(AccountKeyring::Bob.to_account_id()),
+            0
+        );
+
+        // revoke identity
+        Identity::do_revoke_identity(2, pallet_identity::RevocationReason::Root);
+
+        assert_eq!(
+            Balances::free_balance(AccountKeyring::Bob.to_account_id()),
+            1_000
+        );
+
+        // go after UD creation block
+        run_to_block(
+            (<Runtime as pallet_universal_dividend::Config>::UdCreationPeriod::get()
+                / <Runtime as pallet_babe::Config>::ExpectedBlockTime::get()
+                + 1) as u32,
+        );
+
+        assert_eq!(
+            Balances::free_balance(AccountKeyring::Bob.to_account_id()),
+            1_000
+        );
+
+        assert_err!(
+            UniversalDividend::claim_uds(RuntimeOrigin::signed(
+                AccountKeyring::Bob.to_account_id()
+            )),
+            pallet_universal_dividend::Error::<Runtime>::AccountNotAllowedToClaimUds,
+        );
+
+        assert_eq!(
+            Balances::free_balance(AccountKeyring::Bob.to_account_id()),
+            1_000
+        );
+    });
+}
+
 /// test that UD are auto claimed when membership expires
 /// and that claimed UD matches expectations
 #[test]
@@ -781,14 +833,14 @@ fn test_ud_claimed_membership_on_and_off() {
 
         // alice claims back her membership through distance evaluation
         assert_ok!(Distance::force_valid_distance_status(
-            frame_system::RawOrigin::Root.into(),
+            RuntimeOrigin::root(),
             1,
         ));
         // it can not be done manually
         // because the call does not exist anymore
         // assert_noop!(
         //     Membership::claim_membership(
-        //         frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+        //         RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
         //     ),
         //     pallet_membership::Error::<Runtime>::AlreadyMember
         // );
@@ -812,9 +864,9 @@ fn test_ud_claimed_membership_on_and_off() {
 
         // one block later, alice claims her new UD
         run_to_block(25);
-        assert_ok!(UniversalDividend::claim_uds(
-            frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into()
-        ));
+        assert_ok!(UniversalDividend::claim_uds(RuntimeOrigin::signed(
+            AccountKeyring::Alice.to_account_id()
+        )));
         System::assert_has_event(RuntimeEvent::UniversalDividend(
             pallet_universal_dividend::Event::UdsClaimed {
                 count: 1,
@@ -867,7 +919,7 @@ fn test_smith_certification() {
 
         assert_noop!(
             SmithMembers::certify_smith(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 2
             ),
             pallet_smith_members::Error::<Runtime>::CertificationAlreadyExists
@@ -875,7 +927,7 @@ fn test_smith_certification() {
 
         assert_noop!(
             SmithMembers::certify_smith(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 4
             ),
             pallet_smith_members::Error::<Runtime>::CertificationReceiverMustHaveBeenInvited
@@ -914,17 +966,15 @@ fn test_smith_process() {
             // no more membership request
 
             assert_ok!(SmithMembers::invite_smith(
-                frame_system::RawOrigin::Signed(alice.clone()).into(),
+                RuntimeOrigin::signed(alice.clone()),
                 4
             ));
-            assert_ok!(SmithMembers::accept_invitation(
-                frame_system::RawOrigin::Signed(dave).into(),
-            ));
+            assert_ok!(SmithMembers::accept_invitation(RuntimeOrigin::signed(dave),));
 
             // Dave cannot (yet) set his session keys
             assert_err!(
                 AuthorityMembers::set_session_keys(
-                    frame_system::RawOrigin::Signed(AccountKeyring::Dave.to_account_id()).into(),
+                    RuntimeOrigin::signed(AccountKeyring::Dave.to_account_id()),
                     dummy_session_keys.clone()
                 ),
                 pallet_authority_members::Error::<Runtime>::NotMember
@@ -932,15 +982,15 @@ fn test_smith_process() {
 
             // Alice Bob and Charlie can certify Dave
             assert_ok!(SmithMembers::certify_smith(
-                frame_system::RawOrigin::Signed(alice.clone()).into(),
+                RuntimeOrigin::signed(alice.clone()),
                 4
             ));
             assert_ok!(SmithMembers::certify_smith(
-                frame_system::RawOrigin::Signed(bob.clone()).into(),
+                RuntimeOrigin::signed(bob.clone()),
                 4
             ));
             assert_ok!(SmithMembers::certify_smith(
-                frame_system::RawOrigin::Signed(charlie.clone()).into(),
+                RuntimeOrigin::signed(charlie.clone()),
                 4
             ));
 
@@ -958,17 +1008,104 @@ fn test_smith_process() {
 
             // Dave can set his (dummy) session keys
             assert_ok!(AuthorityMembers::set_session_keys(
-                frame_system::RawOrigin::Signed(AccountKeyring::Dave.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Dave.to_account_id()),
                 dummy_session_keys
             ));
 
             // Dave can go online
-            assert_ok!(AuthorityMembers::go_online(
-                frame_system::RawOrigin::Signed(AccountKeyring::Dave.to_account_id()).into(),
-            ));
+            assert_ok!(AuthorityMembers::go_online(RuntimeOrigin::signed(
+                AccountKeyring::Dave.to_account_id()
+            ),));
         })
 }
 
+// reveal bug from #243
+#[test]
+fn test_expired_smith_has_null_expires_on() {
+    // initial_authorities_len = 2 → Alice and Bob are online
+    // initial_smiths_len = 3 → Charlie is offline Smith
+    // initial_identities_len = 4 → Dave is member but not smith
+    ExtBuilder::new(2, 3, 4).build().execute_with(|| {
+        run_to_block(1);
+
+        // Bob is smith
+        assert_eq!(
+            SmithMembers::smiths(2),
+            Some(pallet_smith_members::SmithMeta {
+                status: SmithStatus::Smith,
+                expires_on: None, // because online
+                issued_certs: vec![1, 3],
+                received_certs: vec![1, 3],
+            })
+        );
+
+        // force Bob to leave by expiring his main WoT membership
+        Membership::do_remove_membership(2, MembershipRemovalReason::System);
+
+        // check events
+        // membership removal
+        System::assert_has_event(RuntimeEvent::Membership(
+            pallet_membership::Event::MembershipRemoved {
+                member: 2,
+                reason: MembershipRemovalReason::System,
+            },
+        ));
+        // smith membership removal
+        System::assert_has_event(RuntimeEvent::SmithMembers(
+            pallet_smith_members::Event::SmithMembershipRemoved { idty_index: 2 },
+        ));
+        System::assert_has_event(RuntimeEvent::AuthorityMembers(
+            pallet_authority_members::Event::MemberRemoved { member: 2 },
+        ));
+        // also events for certifications
+
+        // check state
+        // Bob is not Smith anymore
+        assert_eq!(
+            SmithMembers::smiths(2),
+            Some(pallet_smith_members::SmithMeta {
+                status: SmithStatus::Excluded, // automatically excluded
+                expires_on: None,              // because excluded, no expiry is scheduled
+                issued_certs: vec![1, 3],
+                received_certs: vec![], // received certs are deleted
+            })
+        );
+        // Alice smith cert to Bob has been deleted
+        assert_eq!(
+            SmithMembers::smiths(1),
+            Some(pallet_smith_members::SmithMeta {
+                status: SmithStatus::Smith,
+                expires_on: None,      // because online
+                issued_certs: vec![3], // cert to Bob has been deleted
+                received_certs: vec![2, 3],
+            })
+        );
+
+        // run to next block
+        run_to_block(2);
+
+        // simulate new session
+        AuthorityMembers::new_session(2);
+        // check event
+        System::assert_has_event(RuntimeEvent::AuthorityMembers(
+            pallet_authority_members::Event::OutgoingAuthorities { members: vec![2] },
+        ));
+
+        // control state is still ok
+        assert_eq!(
+            SmithMembers::smiths(2),
+            Some(pallet_smith_members::SmithMeta {
+                status: SmithStatus::Excluded, // still excluded
+                expires_on: None,              // should be still None
+                issued_certs: vec![1, 3],
+                received_certs: vec![],
+            })
+        );
+
+        // println!("{:#?}", System::events()); // with -- --nocapture
+    })
+}
+
 /// test new account creation
 #[test]
 fn test_create_new_account() {
@@ -981,7 +1118,7 @@ fn test_create_new_account() {
 
             // Should be able to transfer 5 units to a new account
             assert_ok!(Balances::transfer_allow_death(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 MultiAddress::Id(AccountKeyring::Eve.to_account_id()),
                 500
             ));
@@ -1009,7 +1146,7 @@ fn test_create_new_account() {
 
             // can remove an account using transfer
             assert_ok!(Balances::transfer_allow_death(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
                 MultiAddress::Id(AccountKeyring::Alice.to_account_id()),
                 500
             ));
@@ -1038,26 +1175,26 @@ fn test_create_new_idty() {
 
             // Should be able to create an identity
             assert_ok!(Balances::transfer_allow_death(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 MultiAddress::Id(AccountKeyring::Eve.to_account_id()),
                 200
             ));
             assert_noop!(
                 Identity::create_identity(
-                    frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                    RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                     AccountKeyring::Eve.to_account_id(),
                 ),
                 pallet_identity::Error::<Runtime>::InsufficientBalance
             );
 
             assert_ok!(Balances::transfer_allow_death(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 MultiAddress::Id(AccountKeyring::Eve.to_account_id()),
                 200
             ));
 
             assert_ok!(Identity::create_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 AccountKeyring::Eve.to_account_id(),
             ));
 
@@ -1083,7 +1220,7 @@ fn test_create_new_idty_without_founds() {
             // Should not be able to create an identity without founds
             assert_noop!(
                 Identity::create_identity(
-                    frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                    RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                     AccountKeyring::Eve.to_account_id(),
                 ),
                 pallet_identity::Error::<Runtime>::AccountNotExist
@@ -1091,13 +1228,13 @@ fn test_create_new_idty_without_founds() {
 
             // Deposit some founds on the account
             assert_ok!(Balances::transfer_allow_death(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 MultiAddress::Id(AccountKeyring::Eve.to_account_id()),
                 500
             ));
 
             assert_ok!(Identity::create_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 AccountKeyring::Eve.to_account_id(),
             ));
             System::assert_has_event(RuntimeEvent::Identity(
@@ -1137,38 +1274,38 @@ fn test_validate_new_idty_after_few_uds() {
 
             // Should be able to create an identity
             assert_ok!(Balances::transfer_allow_death(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 MultiAddress::Id(AccountKeyring::Eve.to_account_id()),
                 200
             ));
             assert_ok!(Identity::create_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 AccountKeyring::Eve.to_account_id(),
             ));
 
             // At next block, the created identity should be confirmed by its owner
             run_to_block(22);
             assert_ok!(Identity::confirm_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
                 pallet_identity::IdtyName::from("Eve"),
             ));
 
             // At next block, Bob should be able to certify the new identity
             run_to_block(23);
             assert_ok!(Certification::add_cert(
-                frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Bob.to_account_id()),
                 5,
             ));
             // valid distance status should trigger identity validation
             assert_ok!(Distance::force_valid_distance_status(
-                frame_system::RawOrigin::Root.into(),
+                RuntimeOrigin::root(),
                 5,
             ));
             // and it is not possible to call it manually
             // because the call does not exist anymore
             // assert_noop!(
             //     Membership::claim_membership(
-            //         frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+            //         RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
             //     ),
             //     pallet_membership::Error::<Runtime>::AlreadyMember
             // );
@@ -1202,34 +1339,34 @@ fn test_claim_memberhsip_after_few_uds() {
 
             // Should be able to create an identity
             assert_ok!(Identity::create_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 AccountKeyring::Eve.to_account_id(),
             ));
 
             // At next block, the created identity should be confirmed by its owner
             run_to_block(22);
             assert_ok!(Identity::confirm_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
                 pallet_identity::IdtyName::from("Eve"),
             ));
 
             // At next block, Bob should be able to certify the new identity
             run_to_block(23);
             assert_ok!(Certification::add_cert(
-                frame_system::RawOrigin::Signed(AccountKeyring::Bob.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Bob.to_account_id()),
                 5,
             ));
 
             // eve membership should be able to be claimed through distance evaluation
             assert_ok!(Distance::force_valid_distance_status(
-                frame_system::RawOrigin::Root.into(),
+                RuntimeOrigin::root(),
                 5,
             ));
             // but not manually
             // because the call does not exist
             // assert_noop!(
             //     Membership::claim_membership(
-            //         frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+            //         RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
             //     ),
             //     pallet_membership::Error::<Runtime>::AlreadyMember
             // );
@@ -1259,7 +1396,7 @@ fn test_oneshot_accounts() {
             run_to_block(6);
 
             assert_ok!(OneshotAccount::create_oneshot_account(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 MultiAddress::Id(AccountKeyring::Eve.to_account_id()),
                 400
             ));
@@ -1270,7 +1407,7 @@ fn test_oneshot_accounts() {
             run_to_block(7);
 
             assert_ok!(OneshotAccount::consume_oneshot_account_with_remaining(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
                 0,
                 pallet_oneshot_account::Account::Oneshot(MultiAddress::Id(
                     AccountKeyring::Ferdie.to_account_id()
@@ -1286,7 +1423,7 @@ fn test_oneshot_accounts() {
             );
             assert_noop!(
                 OneshotAccount::consume_oneshot_account(
-                    frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                    RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
                     0,
                     pallet_oneshot_account::Account::Oneshot(MultiAddress::Id(
                         AccountKeyring::Ferdie.to_account_id()
@@ -1302,7 +1439,7 @@ fn test_oneshot_accounts() {
             );
 
             assert_ok!(OneshotAccount::consume_oneshot_account(
-                frame_system::RawOrigin::Signed(AccountKeyring::Ferdie.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Ferdie.to_account_id()),
                 0,
                 pallet_oneshot_account::Account::Normal(MultiAddress::Id(
                     AccountKeyring::Alice.to_account_id()
@@ -1314,7 +1451,7 @@ fn test_oneshot_accounts() {
             );
             assert_noop!(
                 OneshotAccount::consume_oneshot_account(
-                    frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                    RuntimeOrigin::signed(AccountKeyring::Eve.to_account_id()),
                     0,
                     pallet_oneshot_account::Account::Normal(MultiAddress::Id(
                         AccountKeyring::Alice.to_account_id()
@@ -1341,7 +1478,7 @@ fn test_link_account() {
             // Ferdie's account cannot be linked to Alice identity because the account does not exist
             assert_noop!(
                 Identity::link_account(
-                    frame_system::RawOrigin::Signed(alice.clone()).into(),
+                    RuntimeOrigin::signed(alice.clone()),
                     ferdie.clone(),
                     signature.into()
                 ),
@@ -1349,13 +1486,13 @@ fn test_link_account() {
             );
 
             assert_ok!(Balances::transfer_allow_death(
-                frame_system::RawOrigin::Signed(alice.clone()).into(),
+                RuntimeOrigin::signed(alice.clone()),
                 MultiAddress::Id(ferdie.clone()),
                 1_000
             ));
             // Ferdie's account can be linked to Alice identity
             assert_ok!(Identity::link_account(
-                frame_system::RawOrigin::Signed(alice).into(),
+                RuntimeOrigin::signed(alice),
                 ferdie,
                 signature.into()
             ));
@@ -1378,7 +1515,7 @@ fn test_change_owner_key_validator_online() {
         // As an online validator she cannot change key
         assert_noop!(
             Identity::change_owner_key(
-                frame_system::RawOrigin::Signed(alice.clone()).into(),
+                RuntimeOrigin::signed(alice.clone()),
                 ferdie.clone(),
                 signature.into()
             ),
@@ -1419,7 +1556,7 @@ fn test_change_owner_key() {
         );
         // Dave can change his owner key to Ferdie's
         assert_ok!(Identity::change_owner_key(
-            frame_system::RawOrigin::Signed(charlie.clone()).into(),
+            RuntimeOrigin::signed(charlie.clone()),
             ferdie.clone(),
             signature.into()
         ));
@@ -1442,12 +1579,12 @@ fn test_change_owner_key() {
         // Ferdie can set its session_keys and go online
         frame_system::Pallet::<Runtime>::inc_providers(&ferdie);
         assert_ok!(AuthorityMembers::set_session_keys(
-            frame_system::RawOrigin::Signed(AccountKeyring::Ferdie.to_account_id()).into(),
+            RuntimeOrigin::signed(AccountKeyring::Ferdie.to_account_id()),
             create_dummy_session_keys()
         ));
-        assert_ok!(AuthorityMembers::go_online(
-            frame_system::RawOrigin::Signed(AccountKeyring::Ferdie.to_account_id()).into()
-        ));
+        assert_ok!(AuthorityMembers::go_online(RuntimeOrigin::signed(
+            AccountKeyring::Ferdie.to_account_id()
+        )));
 
         // Charlie is still an offline smith
         assert_eq!(
@@ -1491,12 +1628,12 @@ fn test_smith_member_can_revoke_its_idty() {
         // Charlie goes online
         frame_system::Pallet::<Runtime>::inc_providers(&AccountKeyring::Charlie.to_account_id());
         assert_ok!(AuthorityMembers::set_session_keys(
-            frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(),
+            RuntimeOrigin::signed(AccountKeyring::Charlie.to_account_id()),
             create_dummy_session_keys()
         ));
-        assert_ok!(AuthorityMembers::go_online(
-            frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into()
-        ));
+        assert_ok!(AuthorityMembers::go_online(RuntimeOrigin::signed(
+            AccountKeyring::Charlie.to_account_id()
+        )));
 
         run_to_block(25);
 
@@ -1515,7 +1652,7 @@ fn test_smith_member_can_revoke_its_idty() {
             AccountKeyring::Charlie.sign(&(REVOCATION_PAYLOAD_PREFIX, revocation_payload).encode());
 
         assert_ok!(Identity::revoke_identity(
-            frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(),
+            RuntimeOrigin::signed(AccountKeyring::Charlie.to_account_id()),
             3,
             AccountKeyring::Charlie.to_account_id(),
             signature.into()
@@ -1581,9 +1718,9 @@ fn test_unlink_identity() {
         assert_eq!(Identity::identity_index_of(&alice_account), Some(1));
 
         // Alice can unlink her identity from her account
-        assert_ok!(Account::unlink_identity(
-            frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
-        ));
+        assert_ok!(Account::unlink_identity(RuntimeOrigin::signed(
+            AccountKeyring::Alice.to_account_id()
+        ),));
 
         // Alice account has been unlinked
         assert_eq!(
@@ -1610,7 +1747,7 @@ fn test_new_account_linked() {
             );
             // Alice creates identity for Eve
             assert_ok!(Identity::create_identity(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()),
                 eve_account.clone(),
             ));
             // then eve account should be linked to her identity
@@ -1642,7 +1779,7 @@ fn test_killed_account() {
                 Some(2)
             );
             assert_ok!(Balances::transfer_all(
-                frame_system::RawOrigin::Signed(bob_account.clone()).into(),
+                RuntimeOrigin::signed(bob_account.clone()),
                 sp_runtime::MultiAddress::Id(alice_account.clone()),
                 false
             ));
diff --git a/runtime/gdev/tests/xt_tests.rs b/runtime/gdev/tests/xt_tests.rs
index 5bbb96acc988411a73cd0d421eee496a92ee703d..6fcc2060c37257f7f12b5d234a3e864e08bd999b 100644
--- a/runtime/gdev/tests/xt_tests.rs
+++ b/runtime/gdev/tests/xt_tests.rs
@@ -180,7 +180,7 @@ fn test_refund_reaped_linked_account() {
 
             // Ferdie's account can be linked to Alice identity
             assert_ok!(Identity::link_account(
-                frame_system::RawOrigin::Signed(alice.clone()).into(),
+                RuntimeOrigin::signed(alice.clone()),
                 ferdie.clone(),
                 signature.into()
             ));
diff --git a/runtime/gtest/src/weights/pallet_certification.rs b/runtime/gtest/src/weights/pallet_certification.rs
index df9b6216da3df8c692168ee1db4b785b156fbc5b..6c9f58a2c7e403c39c300978ad204a62ba96619e 100644
--- a/runtime/gtest/src/weights/pallet_certification.rs
+++ b/runtime/gtest/src/weights/pallet_certification.rs
@@ -188,4 +188,26 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T>
 			.saturating_add(T::DbWeight::get().reads(4))
 			.saturating_add(T::DbWeight::get().writes(3))
 	}
+	/// Storage: `Certification::CertsByReceiver` (r:1 w:1)
+	/// Proof: `Certification::CertsByReceiver` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `Certification::StorageIdtyCertMeta` (r:1000 w:1000)
+	/// Proof: `Certification::StorageIdtyCertMeta` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `Membership::Membership` (r:1 w:0)
+	/// Proof: `Membership::Membership` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// The range of component `i` is `[2, 1000]`.
+	fn do_remove_all_certs_received_by(i: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `554 + i * (35 ±0)`
+		//  Estimated: `4018 + i * (2511 ±0)`
+		// Minimum execution time: 29_810_000 picoseconds.
+		Weight::from_parts(30_875_000, 0)
+			.saturating_add(Weight::from_parts(0, 4018))
+			// Standard Error: 25_367
+			.saturating_add(Weight::from_parts(8_966_191, 0).saturating_mul(i.into()))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(i.into())))
+			.saturating_add(T::DbWeight::get().writes(1))
+			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into())))
+			.saturating_add(Weight::from_parts(0, 2511).saturating_mul(i.into()))
+	}
 }
diff --git a/runtime/gtest/src/weights/pallet_identity.rs b/runtime/gtest/src/weights/pallet_identity.rs
index 0871837620e15a4717a488b3ba4e6922b3dc8bbd..ece6cef2de8ad008479ac1e986016814eec7b05e 100644
--- a/runtime/gtest/src/weights/pallet_identity.rs
+++ b/runtime/gtest/src/weights/pallet_identity.rs
@@ -135,6 +135,16 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> {
 			.saturating_add(T::DbWeight::get().reads(5))
 			.saturating_add(T::DbWeight::get().writes(5))
 	}
+	fn revoke_identity_legacy() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `697`
+		//  Estimated: `6637`
+		// Minimum execution time: 69_941_000 picoseconds.
+		Weight::from_parts(71_920_000, 0)
+			.saturating_add(Weight::from_parts(0, 6637))
+			.saturating_add(T::DbWeight::get().reads(5))
+			.saturating_add(T::DbWeight::get().writes(5))
+	}
 	/// Storage: `Identity::IdentitiesNames` (r:0 w:999)
 	/// Proof: `Identity::IdentitiesNames` (`max_values`: None, `max_size`: None, mode: `Measured`)
 	/// The range of component `i` is `[2, 1000]`.
diff --git a/xtask/res/templates/runtime-calls-category.md b/xtask/res/templates/runtime-calls-category.md
index 71c6a7bcec12c936d5ccf4dd912227954ab18b3b..823e9a6dd32ab180216e835c9c66642065c0cb4a 100644
--- a/xtask/res/templates/runtime-calls-category.md
+++ b/xtask/res/templates/runtime-calls-category.md
@@ -1,10 +1,12 @@
 There are **{{ calls_counter }}** {{ category_name }} calls from **{{ pallets | length }}** pallets.
 
 {% for pallet in pallets -%}
-### {{ pallet.name }} - {{ pallet.index }}
+
+## {{ pallet.name }} - {{ pallet.index }}
 
 {% for call in pallet.calls -%}
-#### {{ call.name }} - {{ call.index }}
+
+### {{ call.name }} - {{ call.index }}
 
 <details><summary><code>{{ call.name }}(
     {%- for param in call.params -%}
@@ -23,8 +25,15 @@ Taking {{ call.weight }} % of a block.
 ```
 </details>
 
-{# replace markdown sytax in documentation breaking the final result #}
-{{ call.documentation | replace(from="# WARNING:", to="WARNING:") | replace(from="## Complexity", to="**Complexity**") }}
+{# lower heading title to integrate into document hierarchy #}
+{# with a maximum to 6 #}
+{{ call.documentation
+| replace(from="# ", to="## ")
+| replace(from="# ", to="## ")
+| replace(from="# ", to="## ")
+| replace(from="# ", to="## ")
+| replace(from="####### ", to="###### ")
+}}
 
 {% endfor -%}
 {% endfor -%}
diff --git a/xtask/res/templates/runtime-calls.md b/xtask/res/templates/runtime-calls.md
index 4a6d063ade325810511310b7ec92ec455c703e56..17ecc718f6178507ab7d981736f4e25d0d4a7c23 100644
--- a/xtask/res/templates/runtime-calls.md
+++ b/xtask/res/templates/runtime-calls.md
@@ -3,32 +3,16 @@
 Calls are categorized according to the dispatch origin they require:
 
 1. **User calls**: the dispatch origin for this kind of call must be signed by
-the transactor. This is the only call category that can be submitted with an extrinsic.
+   the transactor. This is the only call category that can be submitted with an extrinsic.
 1. **Root calls**: This kind of call requires a special origin that can only be invoked
-through on-chain governance mechanisms.
+   through on-chain governance mechanisms.
 1. **Inherent calls**: This kind of call is invoked by the author of the block itself
-(usually automatically by the node).
+   (usually automatically by the node).
 1. **Disabled calls**: These calls can not be called directly, they are reserved for internal use by other runtime calls.
 
+We only document user calls below.
 
 {% set pallets = user_calls_pallets -%}
 {% set calls_counter = user_calls_counter -%}
 {% set category_name = "user" -%}
-## User calls
-
-{% include "runtime-calls-category.md" %}
-
-{% set pallets = root_calls_pallets -%}
-{% set calls_counter = root_calls_counter -%}
-{% set category_name = "root" -%}
-## Root calls
-
 {% include "runtime-calls-category.md" %}
-
-{% set pallets = disabled_calls_pallets %}
-{% set calls_counter = disabled_calls_counter %}
-{% set category_name = "disabled" %}
-## Disabled calls
-
-{% include "runtime-calls-category.md" -%}
-
diff --git a/xtask/src/gen_doc.rs b/xtask/src/gen_doc.rs
index 91798167d7e54bad9fc8ff6c87548075977f063b..68d77c6117c24adb7ce14e6e552b189f467d3204 100644
--- a/xtask/src/gen_doc.rs
+++ b/xtask/src/gen_doc.rs
@@ -194,56 +194,69 @@ impl From<&scale_info::Variant<PortableForm>> for ErroR {
     }
 }
 
+// classify calls into categories depending on their origin
 enum CallCategory {
+    // calls filtered by runtime
     Disabled,
+    // inherents
     Inherent,
-    OtherOrigin,
+    // ensure_root
     Root,
+    // sudo
     Sudo,
+    // user calls
     User,
+    // other (like a certain proportion of technical comittee)
+    OtherOrigin,
 }
 
 impl CallCategory {
     fn is(pallet_name: &str, call_name: &str) -> Self {
         match (pallet_name, call_name) {
-            ("System", "remark" | "remark_with_event") => Self::Disabled,
+            // substrate "system"
             ("System", _) => Self::Root,
-            ("Babe", "report_equivocation_unsigned") => Self::Inherent,
+            ("Scheduler", _) => Self::Root,
+            ("Babe", "report_equivocation" | "report_equivocation_unsigned") => Self::Inherent,
             ("Babe", "plan_config_change") => Self::Root,
-            ("Timestamp", _) => Self::Inherent,
-            ("Balances", "set_balance" | "force_transfer" | "force_unreserve") => Self::Root,
-            ("AuthorityMembers", "prune_account_id_of" | "remove_member") => Self::Root,
             ("Authorship", _) => Self::Inherent,
             ("Session", _) => Self::Disabled,
-            ("Grandpa", "report_equivocation_unsigned") => Self::Inherent,
+            ("Grandpa", "report_equivocation" | "report_equivocation_unsigned") => Self::Inherent,
             ("Grandpa", "note_stalled") => Self::Root,
-            ("UpgradeOrigin", "dispatch_as_root") => Self::OtherOrigin,
+            ("Timestamp", _) => Self::Inherent,
             ("ImOnline", _) => Self::Inherent,
-            ("Sudo", _) => Self::Sudo,
+            // substrate "common"
             (
-                "Identity",
-                "remove_identity" | "prune_item_identities_names" | "prune_item_identity_index_of",
+                "Balances",
+                "force_set_balance"
+                | "force_transfer"
+                | "force_unreserve"
+                | "force_adjust_total_issuance",
             ) => Self::Root,
-            ("Cert", "del_cert" | "remove_all_certs_received_by") => Self::Root,
-            ("SmithCert", "del_cert" | "remove_all_certs_received_by") => Self::Root,
-            ("TechnicalCommittee", "set_members" | "disapprove_proposal") => Self::Root,
-            ("Utility", "dispatch_as") => Self::Root,
+            ("Balances", "burn") => Self::Disabled,
+            ("Sudo", _) => Self::Sudo,
             ("Treasury", "approve_proposal" | "reject_proposal") => Self::OtherOrigin,
+            ("Utility", "dispatch_as" | "with_weight") => Self::Root,
+            // duniter
+            ("Distance", "force_update_evaluation" | "force_valid_distance_status") => Self::Root,
+            ("Distance", "update_evaluation") => Self::Inherent,
+            ("AuthorityMembers", "remove_member_from_blacklist" | "remove_member") => Self::Root,
+            ("UpgradeOrigin", "dispatch_as_root" | "dispatch_as_root_unchecked_weight") => {
+                Self::OtherOrigin
+            }
+            ("Identity", "remove_identity" | "prune_item_identities_names" | "fix_sufficients") => {
+                Self::Root
+            }
+            ("Certification", "del_cert" | "remove_all_certs_received_by") => Self::Root,
+            ("TechnicalCommittee", "set_members" | "disapprove_proposal") => Self::Root,
+            // if not classified, consider it at a user call
             _ => Self::User,
         }
     }
 
-    fn is_root(pallet_name: &str, call_name: &str) -> bool {
-        matches!(Self::is(pallet_name, call_name), Self::Root)
-    }
-
+    // only user calls
     fn is_user(pallet_name: &str, call_name: &str) -> bool {
         matches!(Self::is(pallet_name, call_name), Self::User)
     }
-
-    fn is_disabled(pallet_name: &str, call_name: &str) -> bool {
-        matches!(Self::is(pallet_name, call_name), Self::Disabled)
-    }
 }
 
 /// generate runtime calls documentation
@@ -477,6 +490,7 @@ fn get_weights(max_weight: u128) -> Result<HashMap<String, HashMap<String, Weigh
 /// use template to render markdown file with runtime calls documentation
 fn print_runtime(pallets: RuntimePallets) -> (String, String, String, String) {
     // init variables
+    // -- user calls
     let mut user_calls_counter = 0;
     let user_calls_pallets: RuntimePallets = pallets
         .iter()
@@ -494,46 +508,14 @@ fn print_runtime(pallets: RuntimePallets) -> (String, String, String, String) {
             }
         })
         .collect();
-    let mut root_calls_counter = 0;
-    let root_calls_pallets: RuntimePallets = pallets
-        .iter()
-        .cloned()
-        .filter_map(|mut pallet| {
-            let pallet_name = pallet.name.clone();
-            pallet
-                .calls
-                .retain(|call| CallCategory::is_root(&pallet_name, &call.name));
-            if pallet.calls.is_empty() {
-                None
-            } else {
-                root_calls_counter += pallet.calls.len();
-                Some(pallet)
-            }
-        })
-        .collect();
-    let mut disabled_calls_counter = 0;
-    let disabled_calls_pallets: RuntimePallets = pallets
-        .iter()
-        .cloned()
-        .filter_map(|mut pallet| {
-            let pallet_name = pallet.name.clone();
-            pallet
-                .calls
-                .retain(|call| CallCategory::is_disabled(&pallet_name, &call.name));
-            if pallet.calls.is_empty() {
-                None
-            } else {
-                disabled_calls_counter += pallet.calls.len();
-                Some(pallet)
-            }
-        })
-        .collect();
 
+    // event counter
     let mut event_counter = 0;
     pallets
         .iter()
         .for_each(|pallet| event_counter += pallet.events.len());
 
+    // error counter
     let mut error_counter = 0;
     pallets
         .iter()
@@ -552,10 +534,6 @@ fn print_runtime(pallets: RuntimePallets) -> (String, String, String, String) {
     let mut context = tera::Context::new();
     context.insert("user_calls_counter", &user_calls_counter);
     context.insert("user_calls_pallets", &user_calls_pallets);
-    context.insert("root_calls_counter", &root_calls_counter);
-    context.insert("root_calls_pallets", &root_calls_pallets);
-    context.insert("disabled_calls_counter", &disabled_calls_counter);
-    context.insert("disabled_calls_pallets", &disabled_calls_pallets);
 
     let call_doc = tera
         .render("runtime-calls.md", &context)
diff --git a/xtask/src/release_runtime.rs b/xtask/src/gitlab.rs
similarity index 92%
rename from xtask/src/release_runtime.rs
rename to xtask/src/gitlab.rs
index 9f9d61548e0e51f47c14ca5b76519a2a2b133940..5cbbf1d6d4d98992f649f445a3fba08590e8fae9 100644
--- a/xtask/src/release_runtime.rs
+++ b/xtask/src/gitlab.rs
@@ -91,14 +91,37 @@ pub(super) async fn release_runtime(
     branch: String,
     milestone: String,
 ) -> Result<()> {
-    let mut release_notes = String::from(
+    release(
+        "Runtime".to_string(),
+        name,
+        Some(network),
+        branch,
+        milestone,
+    )
+    .await
+}
+
+pub(super) async fn release_client(name: String, branch: String, milestone: String) -> Result<()> {
+    release("Client".to_string(), name, None, branch, milestone).await
+}
+
+async fn release(
+    title: String,
+    name: String,
+    network: Option<String>,
+    branch: String,
+    milestone: String,
+) -> Result<()> {
+    let mut release_notes = format!(
         "
-# Runtime
+# {title}
 
-",
+"
     );
 
-    add_srtool_notes(network, &mut release_notes)?;
+    if let Some(network) = network {
+        add_srtool_notes(network.clone(), &mut release_notes)?;
+    }
 
     // Get changes (list of MRs) from gitlab API
     let changes = get_changes::get_changes(milestone.clone()).await?;
diff --git a/xtask/src/release_runtime/create_asset_link.rs b/xtask/src/gitlab/create_asset_link.rs
similarity index 100%
rename from xtask/src/release_runtime/create_asset_link.rs
rename to xtask/src/gitlab/create_asset_link.rs
diff --git a/xtask/src/release_runtime/create_network_release.rs b/xtask/src/gitlab/create_network_release.rs
similarity index 100%
rename from xtask/src/release_runtime/create_network_release.rs
rename to xtask/src/gitlab/create_network_release.rs
diff --git a/xtask/src/release_runtime/create_release.rs b/xtask/src/gitlab/create_release.rs
similarity index 100%
rename from xtask/src/release_runtime/create_release.rs
rename to xtask/src/gitlab/create_release.rs
diff --git a/xtask/src/release_runtime/get_changes.rs b/xtask/src/gitlab/get_changes.rs
similarity index 100%
rename from xtask/src/release_runtime/get_changes.rs
rename to xtask/src/gitlab/get_changes.rs
diff --git a/xtask/src/release_runtime/get_issues.rs b/xtask/src/gitlab/get_issues.rs
similarity index 100%
rename from xtask/src/release_runtime/get_issues.rs
rename to xtask/src/gitlab/get_issues.rs
diff --git a/xtask/src/release_runtime/get_release.rs b/xtask/src/gitlab/get_release.rs
similarity index 100%
rename from xtask/src/release_runtime/get_release.rs
rename to xtask/src/gitlab/get_release.rs
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 07dd4e976bf2ea863422021b4c4f7745855cdb0f..fd574f057c783af78dad2517d42b292c1ac491f3 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -17,7 +17,7 @@
 #![feature(let_chains)]
 
 mod gen_doc;
-mod release_runtime;
+mod gitlab;
 
 use anyhow::{Context, Result};
 use clap::Parser;
@@ -57,9 +57,22 @@ enum DuniterXTaskCommand {
     ReleaseNetwork { network: String, branch: String },
     /// Release a new runtime
     ReleaseRuntime {
+        /// Name of the release + tag to be applied
         name: String,
+        /// Name of the network to be put in the release notes title of the srtool part
         network: String,
+        /// Branch on which the tag `name` will be created during the release
         branch: String,
+        /// Name of the milestone to add this release to
+        milestone: String,
+    },
+    /// Release a new client for a network
+    ReleaseClient {
+        /// Name of the release + tag to be applied
+        name: String,
+        /// Branch on which the tag `name` will be created during the release
+        branch: String,
+        /// Name of the milestone to add this release to
         milestone: String,
     },
     /// Print the chainSpec published on given Network Release
@@ -104,20 +117,25 @@ async fn main() -> Result<()> {
             inject_runtime_code(&raw_spec, &runtime)
         }
         DuniterXTaskCommand::ReleaseNetwork { network, branch } => {
-            release_runtime::release_network(network, branch).await
+            gitlab::release_network(network, branch).await
         }
         DuniterXTaskCommand::ReleaseRuntime {
             name,
             network,
             branch,
             milestone,
-        } => release_runtime::release_runtime(name, network, branch, milestone).await,
-        DuniterXTaskCommand::PrintSpec { network } => release_runtime::print_spec(network).await,
+        } => gitlab::release_runtime(name, network, branch, milestone).await,
+        DuniterXTaskCommand::ReleaseClient {
+            name,
+            branch,
+            milestone,
+        } => gitlab::release_client(name, branch, milestone).await,
+        DuniterXTaskCommand::PrintSpec { network } => gitlab::print_spec(network).await,
         DuniterXTaskCommand::CreateAssetLink {
             tag,
             asset_name,
             asset_url,
-        } => release_runtime::create_asset_link(tag, asset_name, asset_url).await,
+        } => gitlab::create_asset_link(tag, asset_name, asset_url).await,
         DuniterXTaskCommand::Test => test(),
     }
 }