diff --git a/docs/api/runtime-calls.md b/docs/api/runtime-calls.md
index 033203a40f859cd2c557e50e9e8ef5d2fbebadcc..3c96e8358dfda38ca1dc6434860180de1e675926 100644
--- a/docs/api/runtime-calls.md
+++ b/docs/api/runtime-calls.md
@@ -13,7 +13,7 @@ through on-chain governance mechanisms.
 
 ## User calls
 
-There are **79** user calls from **22** pallets.
+There are **77** user calls from **21** pallets.
 
 ### Account - 1
 
@@ -831,51 +831,6 @@ payload_sig: T::Signature
 
 Link an account to an identity
 
-### Membership - 42
-
-#### claim_membership - 1
-
-<details><summary><code>claim_membership()</code></summary>
-
-Taking 0.0213 % of a block.
-
-```rust
-```
-</details>
-
-
-claim membership
-it must fullfill the requirements (certs, distance)
-TODO #159 for main wot claim_membership is called automatically when distance is evaluated positively
-for smith wot, it means joining the authority members
-
-#### renew_membership - 2
-
-<details><summary><code>renew_membership()</code></summary>
-
-Taking 0.0164 % of a block.
-
-```rust
-```
-</details>
-
-
-extend the validity period of an active membership
-
-#### revoke_membership - 3
-
-<details><summary><code>revoke_membership()</code></summary>
-
-Taking 0.0586 % of a block.
-
-```rust
-```
-</details>
-
-
-revoke an active membership
-(only available for sub wot, automatic for main wot)
-
 ### Certification - 43
 
 #### add_cert - 0
@@ -932,20 +887,37 @@ remove all certifications received by an identity (only root)
 
 <details><summary><code>request_distance_evaluation()</code></summary>
 
-Taking 0.0187 % of a block.
+Taking 0.06 % of a block.
+
+```rust
+```
+</details>
+
+
+Request caller identity to be evaluated
+positive evaluation will result in claim/renew membership
+negative evaluation will result in slash for caller
+
+#### request_distance_evaluation_for - 4
+
+<details><summary><code>request_distance_evaluation_for(target)</code></summary>
+
+Taking 0.0805 % of a block.
 
 ```rust
+target: T::IdtyIndex
 ```
 </details>
 
 
-Request an identity to be evaluated
+Request target identity to be evaluated
+only possible for unvalidated identity
 
 #### update_evaluation - 1
 
 <details><summary><code>update_evaluation(computation_result)</code></summary>
 
-Taking 0.0195 % of a block.
+Taking 0.0914 % of a block.
 
 ```rust
 computation_result: ComputationResult
@@ -954,12 +926,13 @@ computation_result: ComputationResult
 
 
 (Inherent) Push an evaluation result to the pool
+this is called internally by validators (= inherent)
 
 #### force_update_evaluation - 2
 
 <details><summary><code>force_update_evaluation(evaluator, computation_result)</code></summary>
 
-Taking 0.0122 % of a block.
+Taking 0.0759 % of a block.
 
 ```rust
 evaluator: <T as frame_system::Config>::AccountId
@@ -968,28 +941,21 @@ computation_result: ComputationResult
 </details>
 
 
-Push an evaluation result to the pool
+Force push an evaluation result to the pool
 
-#### force_set_distance_status - 3
+#### force_valid_distance_status - 3
 
-<details><summary><code>force_set_distance_status(identity, status)</code></summary>
+<details><summary><code>force_valid_distance_status(identity)</code></summary>
 
-Taking 0.011 % of a block.
+Taking 0.074 % of a block.
 
 ```rust
 identity: <T as pallet_identity::Config>::IdtyIndex
-status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>
 ```
 </details>
 
 
-Set the distance evaluation status of an identity
-
-Removes the status if `status` is `None`.
-
-* `status.0` is the account for whom the price will be unreserved or slashed
-  when the evaluation completes.
-* `status.1` is the status of the evaluation.
+Force set the distance evaluation status of an identity
 
 ### AtomicSwap - 50
 
diff --git a/docs/api/runtime-errors.md b/docs/api/runtime-errors.md
index ebb10eb1a66022ba5bb640d7dc65cbf89a34d0b5..620c1674c34a0644c650d1e06969d0c6dbe56460 100644
--- a/docs/api/runtime-errors.md
+++ b/docs/api/runtime-errors.md
@@ -1,6 +1,6 @@
 # Runtime errors
 
-There are **178** errors from **35** pallets.
+There are **176** errors from **35** pallets.
 
 <ul>
 <li>System - 0
@@ -386,21 +386,21 @@ A smith has a limited stock of certifications
 <details>
 <summary>
 <code>AlreadyIncoming</code> - 0</summary>
-Member already incoming
+Member already incoming.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>AlreadyOnline</code> - 1</summary>
-Member already online
+Member already online.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>AlreadyOutgoing</code> - 2</summary>
-Member already outgoing
+Member already outgoing.
 </details>
 </li>
 <li>
@@ -414,42 +414,42 @@ Owner key is invalid as a member.
 <details>
 <summary>
 <code>MemberBlacklisted</code> - 4</summary>
-Member is blacklisted
+Member is blacklisted.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>MemberNotBlacklisted</code> - 5</summary>
-Member is not blacklisted
+Member is not blacklisted.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>MemberNotFound</code> - 6</summary>
-Member not found
+Member not found.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>NotOnlineNorIncoming</code> - 7</summary>
-Neither online nor scheduled
+Neither online nor scheduled.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>NotMember</code> - 8</summary>
-Not member
+Not member.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>SessionKeysNotProvided</code> - 9</summary>
-Session keys not provided
+Session keys not provided.
 </details>
 </li>
 <li>
@@ -740,113 +740,57 @@ This account is not allowed to claim UDs.
 <li>
 <details>
 <summary>
-<code>NotEnoughCertsToClaimMembership</code> - 0</summary>
-Insufficient certifications received to claim membership.
-</details>
-</li>
-<li>
-<details>
-<summary>
-<code>DistanceIsInvalid</code> - 1</summary>
-Distance is invalid.
-</details>
-</li>
-<li>
-<details>
-<summary>
-<code>DistanceNotEvaluated</code> - 2</summary>
-Distance is not evaluated.
-</details>
-</li>
-<li>
-<details>
-<summary>
-<code>DistanceEvaluationPending</code> - 3</summary>
-Distance evaluation has been requested but is still pending
-</details>
-</li>
-<li>
-<details>
-<summary>
-<code>DistanceEvaluationNotRequested</code> - 4</summary>
-Distance evaluation has not been requested
-</details>
-</li>
-<li>
-<details>
-<summary>
-<code>IdtyNotAllowedToRequestMembership</code> - 5</summary>
-Identity is not allowed to request membership.
+<code>NotEnoughCerts</code> - 0</summary>
+Insufficient certifications received.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>IdtyNotAllowedToRenewMembership</code> - 6</summary>
-Identity not allowed to renew membership.
+<code>TargetStatusInvalid</code> - 1</summary>
+Target status is incompatible with this operation.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>IdtyCreationPeriodNotRespected</code> - 7</summary>
+<code>IdtyCreationPeriodNotRespected</code> - 2</summary>
 Identity creation period not respected.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>NotEnoughReceivedCertsToCreateIdty</code> - 8</summary>
+<code>NotEnoughReceivedCertsToCreateIdty</code> - 3</summary>
 Insufficient received certifications to create identity.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>MaxEmittedCertsReached</code> - 9</summary>
+<code>MaxEmittedCertsReached</code> - 4</summary>
 Maximum number of emitted certifications reached.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>NotAllowedToChangeIdtyAddress</code> - 10</summary>
-Not allowed to change identity address.
-</details>
-</li>
-<li>
-<details>
-<summary>
-<code>NotAllowedToRemoveIdty</code> - 11</summary>
-Not allowed to remove identity.
-</details>
-</li>
-<li>
-<details>
-<summary>
-<code>IssuerNotMember</code> - 12</summary>
+<code>IssuerNotMember</code> - 5</summary>
 Issuer cannot emit a certification because it is not member.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>CertToUnconfirmed</code> - 13</summary>
-Cannot issue a certification to an unconfirmed identity
-</details>
-</li>
-<li>
-<details>
-<summary>
-<code>CertToRevoked</code> - 14</summary>
-Cannot issue a certification to a revoked identity
+<code>IdtyNotFound</code> - 6</summary>
+Issuer or receiver not found.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>IdtyNotFound</code> - 15</summary>
-Issuer or receiver not found.
+<code>MembershipRenewalPeriodNotRespected</code> - 7</summary>
+Membership can only be renewed after an antispam delay.
 </details>
 </li>
 </ul>
@@ -1011,6 +955,13 @@ Membership already acquired.
 Membership not found.
 </details>
 </li>
+<li>
+<details>
+<summary>
+<code>AlreadyMember</code> - 3</summary>
+Already member, can not claim membership.
+</details>
+</li>
 </ul>
 </li>
 <li>Certification - 43
@@ -1019,21 +970,21 @@ Membership not found.
 <details>
 <summary>
 <code>CannotCertifySelf</code> - 0</summary>
-Identity cannot certify itself
+Identity cannot certify itself.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>IssuedTooManyCert</code> - 1</summary>
-Identity has already issued the maximum number of certifications
+Identity has already issued the maximum number of certifications.
 </details>
 </li>
 <li>
 <details>
 <summary>
 <code>IssuerNotFound</code> - 2</summary>
-Issuer not found
+Issuer not found.
 </details>
 </li>
 <li>
@@ -1085,31 +1036,66 @@ No author for this block.
 <li>
 <details>
 <summary>
-<code>NoIdentity</code> - 4</summary>
+<code>CallerHasNoIdentity</code> - 4</summary>
 Caller has no identity.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>QueueFull</code> - 5</summary>
+<code>CallerIdentityNotFound</code> - 5</summary>
+Caller identity not found.
+</details>
+</li>
+<li>
+<details>
+<summary>
+<code>CallerNotMember</code> - 6</summary>
+Caller not member.
+</details>
+</li>
+<li>
+<details>
+<summary>
+<code>CallerStatusInvalid</code> - 7</summary>
+
+</details>
+</li>
+<li>
+<details>
+<summary>
+<code>TargetIdentityNotFound</code> - 8</summary>
+Target identity not found.
+</details>
+</li>
+<li>
+<details>
+<summary>
+<code>QueueFull</code> - 9</summary>
 Evaluation queue is full.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>TooManyEvaluators</code> - 6</summary>
+<code>TooManyEvaluators</code> - 10</summary>
 Too many evaluators in the current evaluation pool.
 </details>
 </li>
 <li>
 <details>
 <summary>
-<code>WrongResultLength</code> - 7</summary>
+<code>WrongResultLength</code> - 11</summary>
 Evaluation result has a wrong length.
 </details>
 </li>
+<li>
+<details>
+<summary>
+<code>TargetMustBeUnvalidated</code> - 12</summary>
+Targeted distance evaluation request is only possible for an unvalidated identity.
+</details>
+</li>
 </ul>
 </li>
 <li>AtomicSwap - 50
diff --git a/docs/api/runtime-events.md b/docs/api/runtime-events.md
index 9aa80926367bb4a66c4f20f066063c6975b15769..566e7383905fab4996865f7a71390aefa0b0c6cb 100644
--- a/docs/api/runtime-events.md
+++ b/docs/api/runtime-events.md
@@ -1,6 +1,6 @@
 # Runtime events
 
-There are **127** events from **35** pallets.
+There are **128** events from **35** pallets.
 
 <ul>
 <li>System - 0
@@ -650,7 +650,7 @@ no args
 <details>
 <summary>
 <code>InvitationSent(idty_index, invited_by)</code> - 0</summary>
-An identity is being inivited to become a smith
+An identity is being inivited to become a smith.
 
 ```rust
 idty_index: T::IdtyIndex
@@ -663,7 +663,7 @@ invited_by: T::IdtyIndex
 <details>
 <summary>
 <code>InvitationAccepted(idty_index)</code> - 1</summary>
-The invitation has been accepted
+The invitation has been accepted.
 
 ```rust
 idty_index: T::IdtyIndex
@@ -700,7 +700,7 @@ idty_index: T::IdtyIndex
 <details>
 <summary>
 <code>SmithExcluded(idty_index)</code> - 4</summary>
-A smith has been removed from the smiths set
+A smith has been removed from the smiths set.
 
 ```rust
 idty_index: T::IdtyIndex
@@ -1286,7 +1286,20 @@ expire_on: BlockNumberFor<T>
 <li>
 <details>
 <summary>
-<code>MembershipRemoved(member, reason)</code> - 1</summary>
+<code>MembershipRenewed(member, expire_on)</code> - 1</summary>
+A membership was renewed.
+
+```rust
+member: T::IdtyId
+expire_on: BlockNumberFor<T>
+```
+
+</details>
+</li>
+<li>
+<details>
+<summary>
+<code>MembershipRemoved(member, reason)</code> - 2</summary>
 A membership was removed.
 
 ```rust
@@ -1360,11 +1373,11 @@ who: T::AccountId
 <li>
 <details>
 <summary>
-<code>EvaluationUpdated(evaluator)</code> - 1</summary>
-A distance evaluation was updated.
+<code>EvaluatedValid(idty_index)</code> - 1</summary>
+Distance rule was found valid.
 
 ```rust
-evaluator: T::AccountId
+idty_index: T::IdtyIndex
 ```
 
 </details>
@@ -1372,12 +1385,11 @@ evaluator: T::AccountId
 <li>
 <details>
 <summary>
-<code>EvaluationStatusForced(idty_index, status)</code> - 2</summary>
-A distance status was forced.
+<code>EvaluatedInvalid(idty_index)</code> - 2</summary>
+Distance rule was found invalid.
 
 ```rust
 idty_index: T::IdtyIndex
-status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>
 ```
 
 </details>
diff --git a/end2end-tests/cucumber-features/identity_creation.feature b/end2end-tests/cucumber-features/identity_creation.feature
index f8bffeacc3017112a3b02485a693d5c56ec3f893..7de97f182dd43c222dd6e737b53693ccabd4ccc1 100644
--- a/end2end-tests/cucumber-features/identity_creation.feature
+++ b/end2end-tests/cucumber-features/identity_creation.feature
@@ -29,6 +29,4 @@ Feature: Identity creation
     Then dave should have distance result in 1 session
     When alice runs distance oracle
     When 30 blocks later
-    Then dave should have distance ok
-    When dave claims membership
     Then dave identity should be member
diff --git a/end2end-tests/cucumber-genesis/default.json b/end2end-tests/cucumber-genesis/default.json
index 245e91e312e06afca31bf3a5023a5e423c8d2e31..cfd66ba939a20409e5c577a657138dd211f69ec7 100644
--- a/end2end-tests/cucumber-genesis/default.json
+++ b/end2end-tests/cucumber-genesis/default.json
@@ -53,7 +53,7 @@
     "idty_confirm_period": 40,
     "idty_creation_period": 50,
     "membership_period": 1000,
-    "pending_membership_period": 500,
+    "membership_renewal_period": 500,
     "ud_creation_period": 60000,
     "ud_reeval_period": 600000,
     "smith_cert_max_by_issuer": 8,
@@ -64,9 +64,15 @@
     "wot_min_cert_for_membership": 2
   },
   "clique_smiths": [
-    { "name":  "Alice" },
-    { "name":  "Bob" },
-    { "name":  "Charlie" }
+    {
+      "name": "Alice"
+    },
+    {
+      "name": "Bob"
+    },
+    {
+      "name": "Charlie"
+    }
   ],
   "sudo_key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
   "technical_committee": [
diff --git a/end2end-tests/cucumber-genesis/wot.json b/end2end-tests/cucumber-genesis/wot.json
index 6b1fad67065eb3323c083cc7e0e80ae8ce6098ef..813fc6fc13b2a9082f716734835b6b83f1aa27ad 100644
--- a/end2end-tests/cucumber-genesis/wot.json
+++ b/end2end-tests/cucumber-genesis/wot.json
@@ -62,7 +62,7 @@
     "idty_confirm_period": 40,
     "idty_creation_period": 50,
     "membership_period": 1000,
-    "pending_membership_period": 500,
+    "membership_renewal_period": 500,
     "ud_creation_period": 60000,
     "ud_reeval_period": 600000,
     "smith_cert_max_by_issuer": 8,
@@ -73,9 +73,15 @@
     "wot_min_cert_for_membership": 2
   },
   "clique_smiths": [
-    { "name":  "Alice" },
-    { "name":  "Bob" },
-    { "name":  "Charlie" }
+    {
+      "name": "Alice"
+    },
+    {
+      "name": "Bob"
+    },
+    {
+      "name": "Charlie"
+    }
   ],
   "sudo_key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
   "technical_committee": [
diff --git a/end2end-tests/tests/common/membership.rs b/end2end-tests/tests/common/membership.rs
deleted file mode 100644
index d800f3387f756240b64645bda3c2859391b706db..0000000000000000000000000000000000000000
--- a/end2end-tests/tests/common/membership.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2021 Axiom-Team
-//
-// This file is part of Substrate-Libre-Currency.
-//
-// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, version 3 of the License.
-//
-// Substrate-Libre-Currency is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
-
-use super::*;
-use sp_keyring::AccountKeyring;
-use subxt::tx::PairSigner;
-
-pub async fn claim_membership(client: &Client, from: AccountKeyring) -> Result<()> {
-    let from = PairSigner::new(from.pair());
-
-    let _events = create_block_with_extrinsic(
-        client,
-        client
-            .tx()
-            .create_signed(
-                &gdev::tx().membership().claim_membership(),
-                &from,
-                BaseExtrinsicParamsBuilder::new(),
-            )
-            .await
-            .unwrap(),
-    )
-    .await
-    .unwrap();
-
-    Ok(())
-}
diff --git a/end2end-tests/tests/common/mod.rs b/end2end-tests/tests/common/mod.rs
index 6dbbbda5b6a216c7447b10d8168bb7bf2316a097..d69a00c155f3cfc1694811945447a74d86d8d492 100644
--- a/end2end-tests/tests/common/mod.rs
+++ b/end2end-tests/tests/common/mod.rs
@@ -20,7 +20,6 @@ pub mod balances;
 pub mod cert;
 pub mod distance;
 pub mod identity;
-pub mod membership;
 pub mod oneshot;
 
 #[subxt::subxt(
@@ -234,9 +233,9 @@ fn spawn_full_node(
 
     let timeout =
         if let Ok(duration_string) = std::env::var("DUNITER_END2END_TESTS_SPAWN_NODE_TIMEOUT") {
-            duration_string.parse().unwrap_or(4)
+            duration_string.parse().unwrap_or(10)
         } else {
-            4
+            10
         };
 
     wait_until_log_line(
diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs
index 7832a4a3d89048a033d61245d2a99cbaa508ea13..48d7c30be44ce6ef8a1c7bfba5cc4a487f4d83c3 100644
--- a/end2end-tests/tests/cucumber_tests.rs
+++ b/end2end-tests/tests/cucumber_tests.rs
@@ -333,14 +333,6 @@ async fn confirm_identity(world: &mut DuniterWorld, from: String, pseudo: String
     common::identity::confirm_identity(world.client(), from, pseudo).await
 }
 
-#[allow(clippy::needless_pass_by_ref_mut)]
-#[when(regex = r#"([a-zA-Z]+) claims membership"#)]
-async fn claim_membership(world: &mut DuniterWorld, from: String) -> Result<()> {
-    // input names to keyrings
-    let from = AccountKeyring::from_str(&from).expect("unknown from");
-    common::membership::claim_membership(world.client(), from).await
-}
-
 #[allow(clippy::needless_pass_by_ref_mut)]
 #[when(regex = r#"([a-zA-Z]+) requests distance evaluation"#)]
 async fn request_distance_evaluation(world: &mut DuniterWorld, who: String) -> Result<()> {
@@ -541,33 +533,6 @@ async fn should_have_distance_result_in_sessions(
     Err(anyhow::anyhow!("no evaluation in given pool").into())
 }
 
-#[allow(clippy::needless_pass_by_ref_mut)]
-#[then(regex = r"([a-zA-Z]+) should have distance ok")]
-async fn should_have_distance_ok(world: &mut DuniterWorld, who: String) -> Result<()> {
-    let who = AccountKeyring::from_str(&who).unwrap().to_account_id();
-
-    let idty_id = world
-        .read(&gdev::storage().identity().identity_index_of(&who.into()))
-        .await
-        .await?
-        .unwrap();
-
-    match world
-        .read(&gdev::storage().distance().identity_distance_status(idty_id))
-        .await
-        .await?
-    {
-        Some((_, gdev::runtime_types::pallet_distance::types::DistanceStatus::Valid)) => Ok(()),
-        Some((_, gdev::runtime_types::pallet_distance::types::DistanceStatus::Invalid)) => {
-            Err(anyhow::anyhow!("invalid distance status").into())
-        }
-        Some((_, gdev::runtime_types::pallet_distance::types::DistanceStatus::Pending)) => {
-            Err(anyhow::anyhow!("pending distance status").into())
-        }
-        None => Err(anyhow::anyhow!("no distance status").into()),
-    }
-}
-
 use gdev::runtime_types::pallet_identity::types::IdtyStatus;
 
 // status from string
diff --git a/live-tests/tests/sanity_gdev.rs b/live-tests/tests/sanity_gdev.rs
index d0ae6434cea09c3cb995e0b308d25eb71fee32c1..e326b7191eed13d800e1cadca7e0d04632dc4828 100644
--- a/live-tests/tests/sanity_gdev.rs
+++ b/live-tests/tests/sanity_gdev.rs
@@ -156,8 +156,6 @@ mod verifier {
             Self { errors: Vec::new() }
         }
 
-        // FIXME why async functions when called with await?
-
         /// method to run all storage tests
         pub(super) async fn verify_storage(&mut self, storage: &Storage) -> anyhow::Result<()> {
             self.verify_accounts(&storage.accounts).await;
diff --git a/node/src/chain_spec/gdev.rs b/node/src/chain_spec/gdev.rs
index e927d483bc777d7b21c647ca7d07e3303ed464da..a66be70d21a9446bfd6833e250dab394db805260 100644
--- a/node/src/chain_spec/gdev.rs
+++ b/node/src/chain_spec/gdev.rs
@@ -87,7 +87,7 @@ fn get_parameters(parameters_from_file: &Option<GenesisParameters>) -> CommonPar
         identity_change_owner_key_period: parameters::ChangeOwnerKeyPeriod::get(),
         identity_idty_creation_period: parameters_from_file.idty_creation_period,
         membership_membership_period: parameters_from_file.membership_period,
-        membership_pending_membership_period: parameters_from_file.pending_membership_period,
+        membership_membership_renewal_period: parameters_from_file.membership_renewal_period,
         cert_max_by_issuer: parameters_from_file.cert_max_by_issuer,
         cert_min_received_cert_to_be_able_to_issue_cert: parameters_from_file
             .cert_min_received_cert_to_issue_cert,
@@ -373,6 +373,7 @@ fn get_local_chain_parameters() -> Option<GenesisParameters> {
     let babe_epoch_duration = get_env("DUNITER_BABE_EPOCH_DURATION", 30) as u64;
     let cert_validity_period = get_env("DUNITER_CERT_VALIDITY_PERIOD", 1_000);
     let membership_period = get_env("DUNITER_MEMBERSHIP_PERIOD", 1_000);
+    let membership_renewal_period = get_env("DUNITER_MEMBERSHIP_RENEWAL_PERIOD", 1_000);
     let ud_creation_period = get_env("DUNITER_UD_CREATION_PERIOD", 60_000);
     let ud_reeval_period = get_env("DUNITER_UD_REEEVAL_PERIOD", 1_200_000);
     Some(GenesisParameters {
@@ -384,7 +385,7 @@ fn get_local_chain_parameters() -> Option<GenesisParameters> {
         idty_confirm_period: 40,
         idty_creation_period: 50,
         membership_period,
-        pending_membership_period: 500,
+        membership_renewal_period,
         ud_creation_period,
         ud_reeval_period,
         smith_cert_max_by_issuer: 8,
diff --git a/node/src/chain_spec/gen_genesis_data.rs b/node/src/chain_spec/gen_genesis_data.rs
index 56481245eb257f0fadc84ce85b7969d83b8c6520..2565f3d4a273fdb7999e5be179372fdfc0ac18d4 100644
--- a/node/src/chain_spec/gen_genesis_data.rs
+++ b/node/src/chain_spec/gen_genesis_data.rs
@@ -553,6 +553,15 @@ where
                 G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32
             )
         }
+        if common_parameters.membership_membership_renewal_period / DAYS
+            != G1_DUNITER_V1_MSVALIDITY / DUNITER_V1_DAYS
+        {
+            warn!(
+                "parameter `membership_renewal_period` ({} days) is different from Äž1's ({} days)",
+                common_parameters.membership_membership_renewal_period as f32 / DAYS as f32,
+                G1_DUNITER_V1_MSVALIDITY as f32 / DUNITER_V1_DAYS as f32
+            )
+        }
         if common_parameters.cert_cert_period / DAYS != G1_DUNITER_V1_SIGPERIOD / DUNITER_V1_DAYS {
             warn!(
                 "parameter `cert_period` ({} days) is different from Äž1's ({} days)",
@@ -807,8 +816,8 @@ fn dump_genesis_info(info: GenesisInfo) {
         get_best_unit_and_diviser_for_blocks(p.identity_idty_creation_period);
     let (membership_membership_period, membership_membership_period_unit) =
         get_best_unit_and_diviser_for_blocks(p.membership_membership_period);
-    let (membership_pending_membership_period, membership_pending_membership_period_unit) =
-        get_best_unit_and_diviser_for_blocks(p.membership_pending_membership_period);
+    let (membership_membership_renewal_period, membership_membership_renewal_period_unit) =
+        get_best_unit_and_diviser_for_blocks(p.membership_membership_renewal_period);
     let (cert_cert_period, cert_cert_period_unit) =
         get_best_unit_and_diviser_for_blocks(p.cert_cert_period);
     let (cert_max_by_issuer, cert_max_by_issuer_unit) =
@@ -857,7 +866,7 @@ fn dump_genesis_info(info: GenesisInfo) {
         - identity.change_owner_key_period: {} {}
         - identity.idty_creation_period: {} {}
         - membership.membership_period: {} {}
-        - membership.pending_membership_period: {} {}
+        - membership.membership_renewal_period: {} {}
         - cert.cert_period: {} {}
         - cert.max_by_issuer: {} {}
         - cert.min_received_cert_to_be_able_to_issue_cert: {} {}
@@ -907,8 +916,8 @@ fn dump_genesis_info(info: GenesisInfo) {
         identity_idty_creation_period_unit,
         membership_membership_period,
         membership_membership_period_unit,
-        membership_pending_membership_period,
-        membership_pending_membership_period_unit,
+        membership_membership_renewal_period,
+        membership_membership_renewal_period_unit,
         cert_cert_period,
         cert_cert_period_unit,
         cert_max_by_issuer,
@@ -1980,7 +1989,7 @@ pub struct CommonParameters {
     pub identity_change_owner_key_period: u32,
     pub identity_idty_creation_period: u32,
     pub membership_membership_period: u32,
-    pub membership_pending_membership_period: u32,
+    pub membership_membership_renewal_period: u32,
     pub cert_cert_period: u32,
     pub cert_max_by_issuer: u32,
     pub cert_min_received_cert_to_be_able_to_issue_cert: u32,
diff --git a/node/src/chain_spec/gtest.rs b/node/src/chain_spec/gtest.rs
index d01ee0ccea092ce251aa1f631b40fd6ac56625a2..e7814f6d0fb4e1e72f37e4610601ec5441e700da 100644
--- a/node/src/chain_spec/gtest.rs
+++ b/node/src/chain_spec/gtest.rs
@@ -91,7 +91,7 @@ fn get_parameters(_: &Option<GenesisParameters>) -> CommonParameters {
         identity_change_owner_key_period: parameters::ChangeOwnerKeyPeriod::get(),
         identity_idty_creation_period: parameters::IdtyCreationPeriod::get(),
         membership_membership_period: parameters::MembershipPeriod::get(),
-        membership_pending_membership_period: parameters::PendingMembershipPeriod::get(),
+        membership_membership_renewal_period: parameters::MembershipRenewalPeriod::get(),
         cert_max_by_issuer: parameters::MaxByIssuer::get(),
         cert_min_received_cert_to_be_able_to_issue_cert:
             parameters::MinReceivedCertToBeAbleToIssueCert::get(),
diff --git a/pallets/authority-members/src/lib.rs b/pallets/authority-members/src/lib.rs
index d4ebf6b3fd4a919e055e2f5e72e8c2692e992cf7..3494331ad3f026c8fbbda12283aed225cceb9816 100644
--- a/pallets/authority-members/src/lib.rs
+++ b/pallets/authority-members/src/lib.rs
@@ -177,25 +177,25 @@ pub mod pallet {
 
     #[pallet::error]
     pub enum Error<T> {
-        /// Member already incoming
+        /// Member already incoming.
         AlreadyIncoming,
-        /// Member already online
+        /// Member already online.
         AlreadyOnline,
-        /// Member already outgoing
+        /// Member already outgoing.
         AlreadyOutgoing,
         /// Owner key is invalid as a member.
         MemberIdNotFound,
-        /// Member is blacklisted
+        /// Member is blacklisted.
         MemberBlacklisted,
-        /// Member is not blacklisted
+        /// Member is not blacklisted.
         MemberNotBlacklisted,
-        /// Member not found
+        /// Member not found.
         MemberNotFound,
-        /// Neither online nor scheduled
+        /// Neither online nor scheduled.
         NotOnlineNorIncoming,
-        /// Not member
+        /// Not member.
         NotMember,
-        /// Session keys not provided
+        /// Session keys not provided.
         SessionKeysNotProvided,
         /// Too many authorities.
         TooManyAuthorities,
diff --git a/pallets/certification/src/benchmarking.rs b/pallets/certification/src/benchmarking.rs
index 553e34846ae9c1a99e68c5ee677ffbd126c50979..add49d64470f60b9dbc3bfdefcd37e4d08c4acc5 100644
--- a/pallets/certification/src/benchmarking.rs
+++ b/pallets/certification/src/benchmarking.rs
@@ -80,7 +80,7 @@ benchmarks! {
         assert!(CertsByReceiver::<T>::get(receiver).is_empty() );
     }
     on_initialize {
-        assert!(StorageCertsRemovableOn::<T>::try_get(T::BlockNumber::zero()).is_err());
+        assert!(CertsRemovableOn::<T>::try_get(T::BlockNumber::zero()).is_err());
     }: {Pallet::<T>::on_initialize(T::BlockNumber::zero());}
     do_remove_cert_noop {
     }: {Pallet::<T>::do_remove_cert(100.into(), 101.into(), Some(T::BlockNumber::zero()));}
diff --git a/pallets/certification/src/lib.rs b/pallets/certification/src/lib.rs
index aab81a26417bc6e1c7a4e4459537e121c840e140..f3cc3c0c4783b266154a8b3f8a1b39d0855807e0 100644
--- a/pallets/certification/src/lib.rs
+++ b/pallets/certification/src/lib.rs
@@ -58,7 +58,7 @@ pub mod pallet {
     #[pallet::config]
     pub trait Config: frame_system::Config {
         #[pallet::constant]
-        /// Minimum duration between two certifications issued by the same issuer
+        /// Minimum duration between two certifications issued by the same issuer.
         type CertPeriod: Get<Self::BlockNumber>;
         /// A short identity index.
         type IdtyIndex: Parameter
@@ -70,27 +70,26 @@ pub mod pallet {
             + MaybeSerializeDeserialize
             + Debug
             + MaxEncodedLen;
-        /// Something that give the owner key of an identity
+        /// Something that give the owner key of an identity.
         type OwnerKeyOf: Convert<Self::IdtyIndex, Option<Self::AccountId>>;
-        ///
+        /// Provide method to check that cert is allowed.
         type CheckCertAllowed: CheckCertAllowed<Self::IdtyIndex>;
         #[pallet::constant]
-        /// Maximum number of active certifications by issuer
+        /// Maximum number of active certifications by issuer.
         type MaxByIssuer: Get<u32>;
-        /// Minimum number of certifications that must be received to be able to issue
-        /// certifications.
+        /// Minimum number of certifications received to be allowed to issue a certification.
         #[pallet::constant]
         type MinReceivedCertToBeAbleToIssueCert: Get<u32>;
-        /// Handler for NewCert event
+        /// Handler for NewCert event.
         type OnNewcert: OnNewcert<Self::IdtyIndex>;
-        /// Handler for Removed event
+        /// Handler for Removed event.
         type OnRemovedCert: OnRemovedCert<Self::IdtyIndex>;
         /// Because this pallet emits events, it depends on the runtime's definition of an event.
         type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
-        /// Type representing the weight of this pallet
+        /// Type representing the weight of this pallet.
         type WeightInfo: WeightInfo;
         #[pallet::constant]
-        /// Duration of validity of a certification
+        /// Duration of validity of a certification.
         type ValidityPeriod: Get<Self::BlockNumber>;
     }
 
@@ -191,31 +190,31 @@ pub mod pallet {
                 );
                 StorageIdtyCertMeta::<T>::insert(issuer, cert_meta);
             }
-            // Write storage StorageCertsRemovableOn
+            // Write storage CertsRemovableOn
             for (removable_on, certs) in certs_removable_on {
-                StorageCertsRemovableOn::<T>::insert(removable_on, certs);
+                CertsRemovableOn::<T>::insert(removable_on, certs);
             }
         }
     }
 
     // STORAGE //
 
-    /// Certifications metada by issuer
+    /// Certifications metada by issuer.
     #[pallet::storage]
     #[pallet::getter(fn idty_cert_meta)]
     pub type StorageIdtyCertMeta<T: Config> =
         StorageMap<_, Twox64Concat, T::IdtyIndex, IdtyCertMeta<T::BlockNumber>, ValueQuery>;
 
-    /// Certifications by receiver
+    /// Certifications by receiver.
     #[pallet::storage]
     #[pallet::getter(fn certs_by_receiver)]
     pub type CertsByReceiver<T: Config> =
         StorageMap<_, Twox64Concat, T::IdtyIndex, Vec<(T::IdtyIndex, T::BlockNumber)>, ValueQuery>;
 
-    /// Certifications removable on
+    /// Certifications removable on.
     #[pallet::storage]
     #[pallet::getter(fn certs_removable_on)]
-    pub type StorageCertsRemovableOn<T: Config> =
+    pub type CertsRemovableOn<T: Config> =
         StorageMap<_, Twox64Concat, T::BlockNumber, Vec<(T::IdtyIndex, T::IdtyIndex)>, OptionQuery>;
 
     // EVENTS //
@@ -245,11 +244,11 @@ pub mod pallet {
 
     #[pallet::error]
     pub enum Error<T> {
-        /// Identity cannot certify itself
+        /// Identity cannot certify itself.
         CannotCertifySelf,
-        /// Identity has already issued the maximum number of certifications
+        /// Identity has already issued the maximum number of certifications.
         IssuedTooManyCert,
-        /// Issuer not found
+        /// Issuer not found.
         IssuerNotFound,
         /// Insufficient certifications received.
         NotEnoughCertReceived,
@@ -282,9 +281,16 @@ pub mod pallet {
         ) -> DispatchResultWithPostInfo {
             let who = ensure_signed(origin)?;
 
+            // Verify caller ownership
+            let issuer_owner_key =
+                T::OwnerKeyOf::convert(issuer).ok_or(Error::<T>::IssuerNotFound)?;
+            ensure!(issuer_owner_key == who, DispatchError::BadOrigin);
+
             let block_number = frame_system::pallet::Pallet::<T>::block_number();
-            Self::check_cert_allowed(who, issuer, receiver, block_number)?;
-            Self::do_add_cert(block_number, issuer, receiver)
+            Self::check_add_cert(issuer, receiver, block_number)?;
+            Self::do_add_cert(block_number, issuer, receiver);
+
+            Ok(().into())
         }
 
         /// remove a certification (only root)
@@ -324,44 +330,23 @@ pub mod pallet {
             receiver: T::IdtyIndex,
             verify_rules: bool,
         ) -> DispatchResultWithPostInfo {
-            // Forbid self cert
-            ensure!(issuer != receiver, Error::<T>::CannotCertifySelf);
-
             let block_number = frame_system::pallet::Pallet::<T>::block_number();
 
             if verify_rules {
-                // Verify rule MinReceivedCertToBeAbleToIssueCert
-                let issuer_idty_cert_meta = StorageIdtyCertMeta::<T>::get(issuer);
-                ensure!(
-                    issuer_idty_cert_meta.received_count
-                        >= T::MinReceivedCertToBeAbleToIssueCert::get(),
-                    Error::<T>::NotEnoughCertReceived
-                );
-
-                // Verify rule MaxByIssuer
-                ensure!(
-                    issuer_idty_cert_meta.issued_count < T::MaxByIssuer::get(),
-                    Error::<T>::IssuedTooManyCert
-                );
-
-                // Verify rule CertPeriod
-                ensure!(
-                    block_number >= issuer_idty_cert_meta.next_issuable_on,
-                    Error::<T>::NotRespectCertPeriod
-                );
+                // only verify internal rules if asked
+                Self::check_add_cert_internal(issuer, receiver, block_number)?;
             };
 
-            Self::do_add_cert(block_number, issuer, receiver)
+            Self::do_add_cert(block_number, issuer, receiver);
+
+            Ok(().into())
         }
+
         /// perform cert addition or renewal
-        fn do_add_cert(
-            block_number: T::BlockNumber,
-            issuer: T::IdtyIndex,
-            receiver: T::IdtyIndex,
-        ) -> DispatchResultWithPostInfo {
-            // Write StorageCertsRemovableOn
+        fn do_add_cert(block_number: T::BlockNumber, issuer: T::IdtyIndex, receiver: T::IdtyIndex) {
+            // Write CertsRemovableOn
             let removable_on = block_number + T::ValidityPeriod::get();
-            <StorageCertsRemovableOn<T>>::append(removable_on, (issuer, receiver));
+            <CertsRemovableOn<T>>::append(removable_on, (issuer, receiver));
 
             // Write CertsByReceiver
             let mut created = false;
@@ -414,17 +399,16 @@ pub mod pallet {
                 });
                 // emit CertRenewed event
                 Self::deposit_event(Event::CertRenewed { issuer, receiver });
-            }
-
-            Ok(().into())
+            };
         }
+
         /// remove the certifications due to expire on the given block
         // (run at on_initialize step)
         fn prune_certifications(block_number: T::BlockNumber) -> Weight {
             // See on initialize for the overhead weight accounting
             let mut total_weight = Weight::zero();
 
-            if let Some(certs) = StorageCertsRemovableOn::<T>::take(block_number) {
+            if let Some(certs) = CertsRemovableOn::<T>::take(block_number) {
                 for (issuer, receiver) in certs {
                     total_weight += Self::do_remove_cert(issuer, receiver, Some(block_number));
                 }
@@ -432,6 +416,7 @@ pub mod pallet {
 
             total_weight
         }
+
         /// perform the certification removal
         /// if block number is given only remove cert if still set to expire at this block number
         pub fn do_remove_cert(
@@ -495,24 +480,19 @@ pub mod pallet {
         }
 
         /// check cert allowed
-        // first internal checks
-        // then external checks
-        fn check_cert_allowed(
-            caller_key: T::AccountId,
+        // 1. no self cert
+        // 2. issuer received cert count
+        // 3. issuer max emitted cert
+        // 4. issuer cert period
+        fn check_add_cert_internal(
             issuer: T::IdtyIndex,
             receiver: T::IdtyIndex,
             block_number: T::BlockNumber,
         ) -> DispatchResult {
-            // --- first internal checks
             // 1. Forbid self cert
             ensure!(issuer != receiver, Error::<T>::CannotCertifySelf);
 
-            // 2. Verify caller ownership
-            let issuer_owner_key =
-                T::OwnerKeyOf::convert(issuer).ok_or(Error::<T>::IssuerNotFound)?;
-            ensure!(issuer_owner_key == caller_key, DispatchError::BadOrigin);
-
-            // 3. Verify rule MinReceivedCertToBeAbleToIssueCert
+            // 2. Verify rule MinReceivedCertToBeAbleToIssueCert
             // (this number can differ from the one necessary to be member)
             let issuer_idty_cert_meta = <StorageIdtyCertMeta<T>>::get(issuer);
             ensure!(
@@ -521,18 +501,32 @@ pub mod pallet {
                 Error::<T>::NotEnoughCertReceived
             );
 
-            // 4. Verify rule MaxByIssuer
+            // 3. Verify rule MaxByIssuer
             ensure!(
                 issuer_idty_cert_meta.issued_count < T::MaxByIssuer::get(),
                 Error::<T>::IssuedTooManyCert
             );
 
-            // 5. Verify rule CertPeriod
+            // 4. Verify rule CertPeriod
             ensure!(
                 block_number >= issuer_idty_cert_meta.next_issuable_on,
                 Error::<T>::NotRespectCertPeriod
             );
 
+            Ok(())
+        }
+
+        /// check cert allowed
+        // first internal checks
+        // then external checks
+        fn check_add_cert(
+            issuer: T::IdtyIndex,
+            receiver: T::IdtyIndex,
+            block_number: T::BlockNumber,
+        ) -> DispatchResult {
+            // internal checks
+            Self::check_add_cert_internal(issuer, receiver, block_number)?;
+
             // --- then external checks
             // - issuer is member
             // - receiver is confirmed
diff --git a/pallets/certification/src/weights.rs b/pallets/certification/src/weights.rs
index 4a5bc2f404d0341a3d6607d68b51e4b25bfdb513..1f5918b2a6abdad08e259e4448dd7368db486774 100644
--- a/pallets/certification/src/weights.rs
+++ b/pallets/certification/src/weights.rs
@@ -33,7 +33,7 @@ impl WeightInfo for () {
     // Storage: Identity Identities (r:2 w:0)
     // Storage: Cert StorageIdtyCertMeta (r:2 w:2)
     // Storage: Parameters ParametersStorage (r:1 w:0)
-    // Storage: Cert StorageCertsRemovableOn (r:1 w:1)
+    // Storage: Cert CertsRemovableOn (r:1 w:1)
     // Storage: Cert CertsByReceiver (r:1 w:1)
     fn add_cert() -> Weight {
         // Minimum execution time: 259_247 nanoseconds.
diff --git a/pallets/distance/src/benchmarking.rs b/pallets/distance/src/benchmarking.rs
index 33ba9419b1f9aeb7b4837948bf47e1ce166742a0..c762bde19753caa3c506c546772f495a98d620f9 100644
--- a/pallets/distance/src/benchmarking.rs
+++ b/pallets/distance/src/benchmarking.rs
@@ -50,16 +50,36 @@ benchmarks! {
         T: pallet_balances::Config, T::Balance: From<u64>,
         T::BlockNumber: From<u32>,
     }
+
+    // request distance evaluation
     request_distance_evaluation {
-            let idty = T::IdtyIndex::one();
-            let caller: T::AccountId  = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key;
-            let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
-            let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value());
+        let idty = T::IdtyIndex::one();
+        let caller: T::AccountId  = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key;
+        let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
+        let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value());
     }: _<T::RuntimeOrigin>(caller_origin.clone())
     verify {
-        assert!(IdentityDistanceStatus::<T>::get(idty) == Some((caller.clone(), DistanceStatus::Pending)), "Request not added");
+        assert!(PendingEvaluationRequest::<T>::get(idty) == Some(caller.clone()), "Request not added");
         assert_has_event::<T>(Event::<T>::EvaluationRequested { idty_index: idty, who: caller }.into());
     }
+
+    // request distance evaluation for
+    request_distance_evaluation_for {
+        let idty = T::IdtyIndex::one();
+        let caller: T::AccountId  = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key;
+        let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
+        let _ = <Balances<T> as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value());
+        let target = 2u32;
+        // set target status since targeted distance evaluation only allowed for unvalidated
+        pallet_identity::Identities::<T>::mutate(target,
+            |idty_val| idty_val.as_mut().unwrap().status = pallet_identity::IdtyStatus::Unvalidated);
+    }: _<T::RuntimeOrigin>(caller_origin.clone(), target)
+    verify {
+        assert!(PendingEvaluationRequest::<T>::get(target) == Some(caller.clone()), "Request not added");
+        assert_has_event::<T>(Event::<T>::EvaluationRequested { idty_index: target, who: caller }.into());
+    }
+
+    // update evaluation
     update_evaluation {
         let digest_data = sp_consensus_babe::digests::PreDigest::SecondaryPlain(
         sp_consensus_babe::digests::SecondaryPlainPreDigest { authority_index: 0u32, slot: Default::default() });
@@ -71,27 +91,25 @@ benchmarks! {
         let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
         let i in 1 .. MAX_EVALUATIONS_PER_SESSION => populate_pool::<T>(i)?;
     }: _<T::RuntimeOrigin>(RawOrigin::None.into(), ComputationResult{distances: vec![Perbill::one(); i as usize]})
-    verify {
-        assert_has_event::<T>(Event::<T>::EvaluationUpdated { evaluator: caller }.into());
-    }
+
+    // force update evaluation
     force_update_evaluation {
             let idty = T::IdtyIndex::one();
             let caller: T::AccountId  = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key;
             let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
             let i in 1 .. MAX_EVALUATIONS_PER_SESSION => populate_pool::<T>(i)?;
     }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), caller.clone(), ComputationResult{distances: vec![Perbill::one(); i as usize]})
+
+    // force valid distance status
+    force_valid_distance_status {
+        let idty = T::IdtyIndex::one();
+        let caller: T::AccountId  = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key;
+    }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), idty)
     verify {
-        assert_has_event::<T>(Event::<T>::EvaluationUpdated { evaluator: caller }.into());
-    }
-    force_set_distance_status {
-            let idty = T::IdtyIndex::one();
-            let caller: T::AccountId  = pallet_identity::Identities::<T>::get(idty).unwrap().owner_key;
-            let status = Some((caller.clone(), DistanceStatus::Valid));
-    }: _<T::RuntimeOrigin>(RawOrigin::Root.into(), idty, status.clone())
-    verify {
-        assert!(IdentityDistanceStatus::<T>::get(idty) == Some((caller, DistanceStatus::Valid)), "Status not set");
-        assert_has_event::<T>(Event::<T>::EvaluationStatusForced { idty_index: idty, status }.into());
+        assert_has_event::<T>(Event::<T>::EvaluatedValid { idty_index: idty }.into());
     }
+
+    // on finalize
     on_finalize {
         DidUpdate::<T>::set(true);
     }: { Pallet::<T>::on_finalize(Default::default()); }
diff --git a/pallets/distance/src/lib.rs b/pallets/distance/src/lib.rs
index d7d131cd8f00e99d7bcef9b4470ae512a1caa07e..1790134a02e738b41eb2588b7b39b77e9131fc02 100644
--- a/pallets/distance/src/lib.rs
+++ b/pallets/distance/src/lib.rs
@@ -17,7 +17,7 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
 mod median;
-mod traits;
+pub mod traits;
 mod types;
 mod weights;
 
@@ -26,6 +26,8 @@ pub mod benchmarking;
 
 #[cfg(test)]
 mod mock;
+#[cfg(test)]
+mod tests;
 
 pub use pallet::*;
 pub use traits::*;
@@ -37,6 +39,7 @@ use pallet_authority_members::SessionIndex;
 use sp_distance::{InherentError, INHERENT_IDENTIFIER};
 use sp_inherents::{InherentData, InherentIdentifier};
 use sp_std::convert::TryInto;
+use sp_std::prelude::*;
 
 type IdtyIndex = u32;
 
@@ -66,6 +69,7 @@ pub mod pallet {
         + pallet_identity::Config<IdtyIndex = IdtyIndex>
         + pallet_session::Config
     {
+        /// Currency type used in this pallet (used for reserve/slash)
         type Currency: ReservableCurrency<Self::AccountId>;
         /// Amount reserved during evaluation
         #[pallet::constant]
@@ -79,12 +83,14 @@ pub mod pallet {
         /// Minimum ratio of accessible referees
         #[pallet::constant]
         type MinAccessibleReferees: Get<Perbill>;
-        /// Number of session to keep a positive evaluation result
-        type ResultExpiration: Get<u32>;
         /// The overarching event type.
         type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
         /// Type representing the weight of this pallet
         type WeightInfo: WeightInfo;
+        /// Handler for successful distance evaluation
+        type OnValidDistanceStatus: OnValidDistanceStatus<Self>;
+        /// Trait to check that distance evaluation request is allowed
+        type CheckRequestDistanceEvaluation: CheckRequestDistanceEvaluation<Self>;
     }
 
     // STORAGE //
@@ -128,35 +134,20 @@ pub mod pallet {
     pub type EvaluationBlock<T: Config> =
         StorageValue<_, <T as frame_system::Config>::Hash, ValueQuery>;
 
-    /// Distance evaluation status by identity
+    /// Pending evaluation requesters
     ///
-    /// * `.0` is the account who requested an evaluation and reserved the price,
+    /// account who requested an evaluation and reserved the price,
     ///   for whom the price will be unreserved or slashed when the evaluation completes.
-    /// * `.1` is the status of the evaluation.
     #[pallet::storage]
-    #[pallet::getter(fn identity_distance_status)]
-    pub type IdentityDistanceStatus<T: Config> = StorageMap<
+    #[pallet::getter(fn pending_evaluation_request)]
+    pub type PendingEvaluationRequest<T: Config> = StorageMap<
         _,
         Twox64Concat,
         <T as pallet_identity::Config>::IdtyIndex,
-        (<T as frame_system::Config>::AccountId, DistanceStatus),
+        <T as frame_system::Config>::AccountId,
         OptionQuery,
     >;
 
-    /// Identities by distance status expiration session index
-    #[pallet::storage]
-    #[pallet::getter(fn distance_status_expire_on)]
-    pub type DistanceStatusExpireOn<T: Config> = StorageMap<
-        _,
-        Twox64Concat,
-        u32,
-        BoundedVec<
-            <T as pallet_identity::Config>::IdtyIndex,
-            ConstU32<MAX_EVALUATIONS_PER_SESSION>,
-        >,
-        ValueQuery,
-    >;
-
     /// Did evaluation get updated in this block?
     #[pallet::storage]
     pub(super) type DidUpdate<T: Config> = StorageValue<_, bool, ValueQuery>;
@@ -176,13 +167,10 @@ pub mod pallet {
             idty_index: T::IdtyIndex,
             who: T::AccountId,
         },
-        /// A distance evaluation was updated.
-        EvaluationUpdated { evaluator: T::AccountId },
-        /// A distance status was forced.
-        EvaluationStatusForced {
-            idty_index: T::IdtyIndex,
-            status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>,
-        },
+        /// Distance rule was found valid.
+        EvaluatedValid { idty_index: T::IdtyIndex },
+        /// Distance rule was found invalid.
+        EvaluatedInvalid { idty_index: T::IdtyIndex },
     }
 
     // ERRORS //
@@ -198,13 +186,23 @@ pub mod pallet {
         /// No author for this block.
         NoAuthor,
         /// Caller has no identity.
-        NoIdentity,
+        CallerHasNoIdentity,
+        /// Caller identity not found.
+        CallerIdentityNotFound,
+        /// Caller not member.
+        CallerNotMember,
+        // Caller status can only be Unvalidated, Member or NotMember.
+        CallerStatusInvalid,
+        /// Target identity not found.
+        TargetIdentityNotFound,
         /// Evaluation queue is full.
         QueueFull,
         /// Too many evaluators in the current evaluation pool.
         TooManyEvaluators,
         /// Evaluation result has a wrong length.
         WrongResultLength,
+        /// Targeted distance evaluation request is only possible for an unvalidated identity.
+        TargetMustBeUnvalidated,
     }
 
     #[pallet::hooks]
@@ -228,32 +226,45 @@ pub mod pallet {
 
     #[pallet::call]
     impl<T: Config> Pallet<T> {
-        /// Request an identity to be evaluated
+        /// Request caller identity to be evaluated
+        /// positive evaluation will result in claim/renew membership
+        /// negative evaluation will result in slash for caller
         #[pallet::call_index(0)]
         #[pallet::weight(<T as pallet::Config>::WeightInfo::request_distance_evaluation())]
         pub fn request_distance_evaluation(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
             let who = ensure_signed(origin)?;
 
-            let idty =
-                pallet_identity::IdentityIndexOf::<T>::get(&who).ok_or(Error::<T>::NoIdentity)?;
+            let idty = Self::check_request_distance_evaluation_self(&who)?;
 
-            ensure!(
-                IdentityDistanceStatus::<T>::get(idty)
-                    != Some((who.clone(), DistanceStatus::Pending)),
-                Error::<T>::AlreadyInEvaluation
-            );
+            Pallet::<T>::do_request_distance_evaluation(&who, idty)?;
+            Ok(().into())
+        }
+
+        /// Request target identity to be evaluated
+        /// only possible for unvalidated identity
+        #[pallet::call_index(4)]
+        #[pallet::weight(<T as pallet::Config>::WeightInfo::request_distance_evaluation_for())]
+        pub fn request_distance_evaluation_for(
+            origin: OriginFor<T>,
+            target: T::IdtyIndex,
+        ) -> DispatchResultWithPostInfo {
+            let who = ensure_signed(origin)?;
+
+            Self::check_request_distance_evaluation_for(&who, target)?;
 
-            Pallet::<T>::do_request_distance_evaluation(who, idty)?;
+            Pallet::<T>::do_request_distance_evaluation(&who, target)?;
             Ok(().into())
         }
 
         /// (Inherent) Push an evaluation result to the pool
+        /// this is called internally by validators (= inherent)
         #[pallet::call_index(1)]
         #[pallet::weight(<T as pallet::Config>::WeightInfo::update_evaluation(MAX_EVALUATIONS_PER_SESSION))]
         pub fn update_evaluation(
             origin: OriginFor<T>,
             computation_result: ComputationResult,
         ) -> DispatchResult {
+            // no origin = inherent
             ensure_none(origin)?;
             ensure!(
                 !DidUpdate::<T>::exists(),
@@ -267,7 +278,8 @@ pub mod pallet {
             Ok(())
         }
 
-        /// Push an evaluation result to the pool
+        /// Force push an evaluation result to the pool
+        // (it is convenient to have this call in end2end tests)
         #[pallet::call_index(2)]
         #[pallet::weight(<T as pallet::Config>::WeightInfo::force_update_evaluation(MAX_EVALUATIONS_PER_SESSION))]
         pub fn force_update_evaluation(
@@ -280,60 +292,21 @@ pub mod pallet {
             Pallet::<T>::do_update_evaluation(evaluator, computation_result)
         }
 
-        /// Set the distance evaluation status of an identity
-        ///
-        /// Removes the status if `status` is `None`.
-        ///
-        /// * `status.0` is the account for whom the price will be unreserved or slashed
-        ///   when the evaluation completes.
-        /// * `status.1` is the status of the evaluation.
+        /// Force set the distance evaluation status of an identity
+        // (it is convenient to have this in test network)
         #[pallet::call_index(3)]
-        #[pallet::weight(<T as pallet::Config>::WeightInfo::force_set_distance_status())]
-        pub fn force_set_distance_status(
+        #[pallet::weight(<T as pallet::Config>::WeightInfo::force_valid_distance_status())]
+        pub fn force_valid_distance_status(
             origin: OriginFor<T>,
             identity: <T as pallet_identity::Config>::IdtyIndex,
-            status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>,
         ) -> DispatchResult {
             ensure_root(origin)?;
 
-            IdentityDistanceStatus::<T>::set(identity, status.clone());
-            DistanceStatusExpireOn::<T>::mutate(
-                pallet_session::CurrentIndex::<T>::get() + T::ResultExpiration::get(),
-                move |identities| {
-                    identities
-                        .try_push(identity)
-                        .map_err(|_| Error::<T>::TooManyEvaluationsInBlock)
-                },
-            )?;
-            Self::deposit_event(Event::EvaluationStatusForced {
-                idty_index: identity,
-                status,
-            });
+            Self::do_valid_distance_status(identity);
             Ok(())
         }
     }
 
-    // BENCHMARK FUNCTIONS //
-
-    impl<T: Config> Pallet<T> {
-        /// Force the distance status using IdtyIndex and AccountId
-        /// only to prepare identity for benchmarking.
-        pub fn set_distance_status(
-            identity: <T as pallet_identity::Config>::IdtyIndex,
-            status: Option<(<T as frame_system::Config>::AccountId, DistanceStatus)>,
-        ) -> DispatchResult {
-            IdentityDistanceStatus::<T>::set(identity, status);
-            DistanceStatusExpireOn::<T>::mutate(
-                pallet_session::CurrentIndex::<T>::get() + T::ResultExpiration::get(),
-                move |identities| {
-                    identities
-                        .try_push(identity)
-                        .map_err(|_| Error::<T>::TooManyEvaluationsInBlock.into())
-                },
-            )
-        }
-    }
-
     // INTERNAL FUNCTIONS //
 
     impl<T: Config> Pallet<T> {
@@ -398,55 +371,119 @@ pub mod pallet {
             }
         }
 
+        /// check that request distance evaluation is allowed
+        fn check_request_distance_evaluation_self(
+            who: &T::AccountId,
+        ) -> Result<<T as pallet_identity::Config>::IdtyIndex, DispatchError> {
+            // caller has an identity
+            let idty_index = pallet_identity::IdentityIndexOf::<T>::get(who)
+                .ok_or(Error::<T>::CallerHasNoIdentity)?;
+            let idty = pallet_identity::Identities::<T>::get(idty_index)
+                .ok_or(Error::<T>::CallerIdentityNotFound)?;
+            // caller is (Unvalidated, Member, NotMember)
+            ensure!(
+                idty.status == pallet_identity::IdtyStatus::Unvalidated
+                    || idty.status == pallet_identity::IdtyStatus::Member
+                    || idty.status == pallet_identity::IdtyStatus::NotMember,
+                Error::<T>::CallerStatusInvalid
+            );
+            Self::check_request_distance_evaluation_common(idty_index)?;
+            Ok(idty_index)
+        }
+
+        /// check that targeted request distance evaluation is allowed
+        fn check_request_distance_evaluation_for(
+            who: &T::AccountId,
+            target: <T as pallet_identity::Config>::IdtyIndex,
+        ) -> Result<(), DispatchError> {
+            // caller has an identity
+            let caller_idty_index = pallet_identity::IdentityIndexOf::<T>::get(who)
+                .ok_or(Error::<T>::CallerHasNoIdentity)?;
+            let caller_idty = pallet_identity::Identities::<T>::get(caller_idty_index)
+                .ok_or(Error::<T>::CallerIdentityNotFound)?;
+            // caller is member
+            ensure!(
+                caller_idty.status == pallet_identity::IdtyStatus::Member,
+                Error::<T>::CallerNotMember
+            );
+            // target has an identity
+            let target_idty = pallet_identity::Identities::<T>::get(target)
+                .ok_or(Error::<T>::TargetIdentityNotFound)?;
+            // target is unvalidated
+            ensure!(
+                target_idty.status == pallet_identity::IdtyStatus::Unvalidated,
+                Error::<T>::TargetMustBeUnvalidated
+            );
+            Self::check_request_distance_evaluation_common(target)?;
+            Ok(())
+        }
+
+        // common checks between check_request_distance_evaluation _self and _for
+        fn check_request_distance_evaluation_common(
+            target: <T as pallet_identity::Config>::IdtyIndex,
+        ) -> Result<(), DispatchError> {
+            // no pending evaluation request
+            ensure!(
+                PendingEvaluationRequest::<T>::get(target).is_none(),
+                Error::<T>::AlreadyInEvaluation
+            );
+            // external validation (wot)
+            // - membership renewal antispam
+            // - target has received enough certifications
+            T::CheckRequestDistanceEvaluation::check_request_distance_evaluation(target)
+        }
+
+        /// request distance evaluation in current pool
         fn do_request_distance_evaluation(
-            who: T::AccountId,
+            who: &T::AccountId,
             idty_index: <T as pallet_identity::Config>::IdtyIndex,
         ) -> Result<(), DispatchError> {
             Pallet::<T>::mutate_current_pool(
                 pallet_session::CurrentIndex::<T>::get(),
                 |current_pool| {
+                    // extrinsics are transactional by default, this check might not be needed
                     ensure!(
                         current_pool.evaluations.len() < (MAX_EVALUATIONS_PER_SESSION as usize),
                         Error::<T>::QueueFull
                     );
 
-                    T::Currency::reserve(&who, <T as Config>::EvaluationPrice::get())?;
+                    T::Currency::reserve(who, <T as Config>::EvaluationPrice::get())?;
 
                     current_pool
                         .evaluations
                         .try_push((idty_index, median::MedianAcc::new()))
                         .map_err(|_| Error::<T>::QueueFull)?;
 
-                    IdentityDistanceStatus::<T>::insert(
-                        idty_index,
-                        (&who, DistanceStatus::Pending),
-                    );
+                    PendingEvaluationRequest::<T>::insert(idty_index, who);
 
-                    DistanceStatusExpireOn::<T>::mutate(
-                        pallet_session::CurrentIndex::<T>::get() + T::ResultExpiration::get(),
-                        move |identities| identities.try_push(idty_index).ok(),
-                    );
-                    Self::deposit_event(Event::EvaluationRequested { idty_index, who });
+                    Self::deposit_event(Event::EvaluationRequested {
+                        idty_index,
+                        who: who.clone(),
+                    });
                     Ok(())
                 },
             )
         }
 
+        /// update distance evaluation in next pool
         fn do_update_evaluation(
             evaluator: <T as frame_system::Config>::AccountId,
             computation_result: ComputationResult,
         ) -> DispatchResult {
             Pallet::<T>::mutate_next_pool(pallet_session::CurrentIndex::<T>::get(), |result_pool| {
+                // evaluation must be provided for all identities (no more, no less)
                 ensure!(
                     computation_result.distances.len() == result_pool.evaluations.len(),
                     Error::<T>::WrongResultLength
                 );
 
+                // insert the evaluator if not already there
                 if result_pool
                     .evaluators
                     .try_insert(evaluator.clone())
                     .map_err(|_| Error::<T>::TooManyEvaluators)?
                 {
+                    // update the median accumulator with the new result
                     for (distance_value, (_identity, median_acc)) in computation_result
                         .distances
                         .into_iter()
@@ -454,23 +491,28 @@ pub mod pallet {
                     {
                         median_acc.push(distance_value);
                     }
-
-                    Self::deposit_event(Event::EvaluationUpdated { evaluator });
                     Ok(())
                 } else {
+                    // one author can only submit one evaluation
                     Err(Error::<T>::TooManyEvaluationsByAuthor.into())
                 }
             })
         }
+
+        /// Set the distance status using IdtyIndex and AccountId
+        pub fn do_valid_distance_status(idty: <T as pallet_identity::Config>::IdtyIndex) {
+            // callback
+            T::OnValidDistanceStatus::on_valid_distance_status(idty);
+            // deposit event
+            Self::deposit_event(Event::EvaluatedValid { idty_index: idty });
+        }
     }
 
     impl<T: Config> pallet_authority_members::OnNewSession for Pallet<T> {
         fn on_new_session(index: SessionIndex) {
+            // set evaluation block
             EvaluationBlock::<T>::set(frame_system::Pallet::<T>::parent_hash());
 
-            // Make results expire
-            DistanceStatusExpireOn::<T>::remove(index);
-
             // Apply the results from the current pool (which was previous session's result pool)
             // We take the results so the pool is left empty for the new session.
             #[allow(clippy::type_complexity)]
@@ -479,35 +521,48 @@ pub mod pallet {
                 <T as pallet_identity::Config>::IdtyIndex,
             > = Pallet::<T>::take_current_pool(index);
             for (idty, median_acc) in current_pool.evaluations.into_iter() {
+                // distance result
+                let mut distance_result: Option<bool> = None;
+
+                // get result of the computation
                 if let Some(median_result) = median_acc.get_median() {
                     let median = match median_result {
                         MedianResult::One(m) => m,
                         MedianResult::Two(m1, m2) => m1 + (m2 - m1) / 2, // Avoid overflow (since max is 1)
                     };
-                    IdentityDistanceStatus::<T>::mutate(idty, |entry| {
-                        if let Some((account_id, status)) = entry.as_mut() {
-                            if median >= T::MinAccessibleReferees::get() {
-                                T::Currency::unreserve(
-                                    account_id,
-                                    <T as Config>::EvaluationPrice::get(),
-                                );
-                                *status = DistanceStatus::Valid;
-                            } else {
-                                T::Currency::slash_reserved(
-                                    account_id,
-                                    <T as Config>::EvaluationPrice::get(),
-                                );
-                                *status = DistanceStatus::Invalid;
-                            }
+                    // update distance result
+                    distance_result = Some(median >= T::MinAccessibleReferees::get());
+                }
+
+                // take requester and perform unreserve or slash
+                if let Some(requester) = PendingEvaluationRequest::<T>::take(idty) {
+                    match distance_result {
+                        None => {
+                            // no result, unreserve
+                            T::Currency::unreserve(
+                                &requester,
+                                <T as Config>::EvaluationPrice::get(),
+                            );
                         }
-                    });
-                } else if let Some((account_id, _status)) = IdentityDistanceStatus::<T>::take(idty)
-                {
-                    <T as Config>::Currency::unreserve(
-                        &account_id,
-                        <T as Config>::EvaluationPrice::get(),
-                    );
+                        Some(true) => {
+                            // positive result, unreserve and apply
+                            T::Currency::unreserve(
+                                &requester,
+                                <T as Config>::EvaluationPrice::get(),
+                            );
+                            Self::do_valid_distance_status(idty);
+                        }
+                        Some(false) => {
+                            // negative result, slash and deposit event
+                            T::Currency::slash_reserved(
+                                &requester,
+                                <T as Config>::EvaluationPrice::get(),
+                            );
+                            Self::deposit_event(Event::EvaluatedInvalid { idty_index: idty });
+                        }
+                    }
                 }
+                // if evaluation happened without request, it's ok to do nothing
             }
         }
     }
diff --git a/pallets/distance/src/mock.rs b/pallets/distance/src/mock.rs
index ef477a8c8fb954bf324b2b0936b092492ff05831..6861b3c43ad8a817ba9b04f521203a21d9f2bccd 100644
--- a/pallets/distance/src/mock.rs
+++ b/pallets/distance/src/mock.rs
@@ -19,7 +19,7 @@ use crate::{self as pallet_distance};
 use core::marker::PhantomData;
 use frame_support::{
     parameter_types,
-    traits::{Everything, GenesisBuild},
+    traits::{Everything, GenesisBuild, OnFinalize, OnInitialize},
 };
 use frame_system as system;
 use pallet_balances::AccountData;
@@ -256,9 +256,10 @@ impl pallet_distance::Config for Test {
     type EvaluationPrice = frame_support::traits::ConstU64<1000>;
     type MaxRefereeDistance = frame_support::traits::ConstU32<5>;
     type MinAccessibleReferees = MinAccessibleReferees;
-    type ResultExpiration = frame_support::traits::ConstU32<720>;
     type RuntimeEvent = RuntimeEvent;
     type WeightInfo = ();
+    type OnValidDistanceStatus = ();
+    type CheckRequestDistanceEvaluation = ();
 }
 
 // Build genesis storage according to the mock runtime.
@@ -290,3 +291,14 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
 
     sp_io::TestExternalities::new(t)
 }
+
+pub fn run_to_block(n: u64) {
+    while System::block_number() < n {
+        Session::on_finalize(System::block_number());
+        System::on_finalize(System::block_number());
+        System::reset_events();
+        System::set_block_number(System::block_number() + 1);
+        System::on_initialize(System::block_number());
+        Session::on_initialize(System::block_number());
+    }
+}
diff --git a/pallets/distance/src/tests.rs b/pallets/distance/src/tests.rs
new file mode 100644
index 0000000000000000000000000000000000000000..18302be75c61c8ed2718d33d353e7a49b8148253
--- /dev/null
+++ b/pallets/distance/src/tests.rs
@@ -0,0 +1,108 @@
+// Copyright 2023 Axiom-Team
+//
+// This file is part of Duniter-v2S.
+//
+// Duniter-v2S is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// Duniter-v2S is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU 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/>.
+
+use crate::mock::*;
+use crate::*;
+use frame_support::traits::Currency;
+use frame_support::{assert_noop, assert_ok};
+
+// allow request distance evaluation for oneself
+#[test]
+fn test_request_distance_evaluation() {
+    new_test_ext().execute_with(|| {
+        run_to_block(1);
+        // give enough for reserve
+        Balances::make_free_balance_be(&1, 10_000);
+
+        // call request
+        assert_ok!(Distance::request_distance_evaluation(
+            RuntimeOrigin::signed(1)
+        ));
+        System::assert_has_event(RuntimeEvent::Distance(Event::EvaluationRequested {
+            idty_index: 1,
+            who: 1,
+        }));
+
+        // currency was reserved
+        assert_eq!(Balances::reserved_balance(1), 1000);
+    });
+}
+
+// allow request distance evaluation for an unvalidated identity
+#[test]
+fn test_request_distance_evaluation_for() {
+    new_test_ext().execute_with(|| {
+        run_to_block(1);
+        // give enough for reserve
+        Balances::make_free_balance_be(&1, 10_000);
+        assert_ok!(Identity::create_identity(RuntimeOrigin::signed(1), 5));
+        assert_ok!(Identity::confirm_identity(
+            RuntimeOrigin::signed(5),
+            "Eeeve".into()
+        ));
+
+        // call request
+        assert_ok!(Distance::request_distance_evaluation_for(
+            RuntimeOrigin::signed(1),
+            5
+        ));
+        System::assert_has_event(RuntimeEvent::Distance(Event::EvaluationRequested {
+            idty_index: 5,
+            who: 1,
+        }));
+
+        // currency was reserved
+        assert_eq!(Balances::reserved_balance(1), 1000);
+    });
+}
+
+// non member can not request distance evaluation
+#[test]
+fn test_request_distance_evaluation_non_member() {
+    new_test_ext().execute_with(|| {
+        run_to_block(1);
+        // give enough for reserve
+        Balances::make_free_balance_be(&5, 10_000);
+
+        assert_noop!(
+            Distance::request_distance_evaluation_for(RuntimeOrigin::signed(5), 1),
+            Error::<Test>::CallerHasNoIdentity
+        );
+        assert_ok!(Identity::create_identity(RuntimeOrigin::signed(1), 5));
+        assert_noop!(
+            Distance::request_distance_evaluation_for(RuntimeOrigin::signed(5), 1),
+            Error::<Test>::CallerNotMember
+        );
+    });
+}
+
+// can not request distance eval if already in evaluation
+#[test]
+fn test_request_distance_evaluation_twice() {
+    new_test_ext().execute_with(|| {
+        run_to_block(1);
+        // give enough for reserve
+        Balances::make_free_balance_be(&1, 10_000);
+
+        assert_ok!(Distance::request_distance_evaluation(
+            RuntimeOrigin::signed(1)
+        ));
+        assert_noop!(
+            Distance::request_distance_evaluation(RuntimeOrigin::signed(1)),
+            Error::<Test>::AlreadyInEvaluation
+        );
+    });
+}
diff --git a/pallets/distance/src/traits.rs b/pallets/distance/src/traits.rs
index de9d532d64f4ce64a16fd20fab28455523f18e69..6849d9c250c29421bffb67f4d52c0cf626271a59 100644
--- a/pallets/distance/src/traits.rs
+++ b/pallets/distance/src/traits.rs
@@ -14,8 +14,24 @@
 // 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/>.
 
-pub trait HandleNegativeEvaluation<T: crate::Config> {
-    /// Do something with the reserved amount on the account,
-    /// when distance evaluation result is negative.
-    fn handle_negative_evaluation(account_id: T::AccountId);
+use crate::*;
+use frame_support::pallet_prelude::*;
+
+pub trait OnValidDistanceStatus<T: Config> {
+    /// Handler for valid distance evaluation
+    fn on_valid_distance_status(idty_index: T::IdtyIndex);
+}
+
+impl<T: Config> OnValidDistanceStatus<T> for () {
+    fn on_valid_distance_status(_idty_index: T::IdtyIndex) {}
+}
+
+pub trait CheckRequestDistanceEvaluation<T: Config> {
+    fn check_request_distance_evaluation(idty_index: T::IdtyIndex) -> Result<(), DispatchError>;
+}
+
+impl<T: Config> CheckRequestDistanceEvaluation<T> for () {
+    fn check_request_distance_evaluation(_idty_index: T::IdtyIndex) -> Result<(), DispatchError> {
+        Ok(())
+    }
 }
diff --git a/pallets/distance/src/weights.rs b/pallets/distance/src/weights.rs
index b0808361a6b8d1810b51c159390fe3d8d640c268..0e8f34c45944c330c446ce4ac047c395f157702e 100644
--- a/pallets/distance/src/weights.rs
+++ b/pallets/distance/src/weights.rs
@@ -20,102 +20,82 @@ use frame_support::weights::{constants::RocksDbWeight, Weight};
 
 pub trait WeightInfo {
     fn request_distance_evaluation() -> Weight;
+    fn request_distance_evaluation_for() -> Weight;
     fn update_evaluation(i: u32) -> Weight;
     fn force_update_evaluation(i: u32) -> Weight;
-    fn force_set_distance_status() -> Weight;
+    fn force_valid_distance_status() -> Weight;
     fn on_finalize() -> Weight;
 }
 
 // Insecure weights implementation, use it for tests only!
 impl WeightInfo for () {
-    /// Storage: Identity IdentityIndexOf (r:1 w:0)
-    /// Proof Skipped: Identity IdentityIndexOf (max_values: None, max_size: None, mode: Measured)
-    /// Storage: Distance IdentityDistanceStatus (r:1 w:1)
-    /// Proof Skipped: Distance IdentityDistanceStatus (max_values: None, max_size: None, mode: Measured)
-    /// Storage: Session CurrentIndex (r:1 w:0)
-    /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured)
-    /// Storage: Distance EvaluationPool2 (r:1 w:1)
-    /// Proof Skipped: Distance EvaluationPool2 (max_values: Some(1), max_size: None, mode: Measured)
-    /// Storage: System Account (r:1 w:1)
-    /// Proof: System Account (max_values: None, max_size: Some(121), added: 2596, mode: MaxEncodedLen)
-    /// Storage: Distance DistanceStatusExpireOn (r:1 w:1)
-    /// Proof Skipped: Distance DistanceStatusExpireOn (max_values: None, max_size: None, mode: Measured)
     fn request_distance_evaluation() -> Weight {
         // Proof Size summary in bytes:
-        //  Measured:  `935`
-        //  Estimated: `4400`
-        // Minimum execution time: 28_469_000 picoseconds.
-        Weight::from_parts(30_905_000, 0)
-            .saturating_add(Weight::from_parts(0, 4400))
-            .saturating_add(RocksDbWeight::get().reads(6))
-            .saturating_add(RocksDbWeight::get().writes(4))
+        //  Measured:  `1280`
+        //  Estimated: `4745`
+        // Minimum execution time: 876_053_000 picoseconds.
+        Weight::from_parts(898_445_000, 0)
+            .saturating_add(Weight::from_parts(0, 4745))
+            .saturating_add(RocksDbWeight::get().reads(8))
+            .saturating_add(RocksDbWeight::get().writes(3))
+    }
+
+    fn request_distance_evaluation_for() -> Weight {
+        // Proof Size summary in bytes:
+        //  Measured:  `1485`
+        //  Estimated: `7425`
+        // Minimum execution time: 1_118_982_000 picoseconds.
+        Weight::from_parts(1_292_782_000, 0)
+            .saturating_add(Weight::from_parts(0, 7425))
+            .saturating_add(RocksDbWeight::get().reads(10))
+            .saturating_add(RocksDbWeight::get().writes(3))
     }
-    /// Storage: Distance DidUpdate (r:1 w:1)
-    /// Proof Skipped: Distance DidUpdate (max_values: Some(1), max_size: None, mode: Measured)
-    /// Storage: Authorship Author (r:1 w:1)
-    /// Proof: Authorship Author (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen)
-    /// Storage: System Digest (r:1 w:0)
-    /// Proof Skipped: System Digest (max_values: Some(1), max_size: None, mode: Measured)
-    /// Storage: Session CurrentIndex (r:1 w:0)
-    /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured)
-    /// Storage: Distance EvaluationPool0 (r:1 w:1)
-    /// Proof Skipped: Distance EvaluationPool0 (max_values: Some(1), max_size: None, mode: Measured)
-    /// The range of component `i` is `[1, 600]`.
+
     fn update_evaluation(i: u32) -> Weight {
         // Proof Size summary in bytes:
-        //  Measured:  `744 + i * (10 ±0)`
-        //  Estimated: `2228 + i * (10 ±0)`
-        // Minimum execution time: 13_870_000 picoseconds.
-        Weight::from_parts(17_116_748, 0)
-            .saturating_add(Weight::from_parts(0, 2228))
-            // Standard Error: 684
-            .saturating_add(Weight::from_parts(128_989, 0).saturating_mul(i.into()))
-            .saturating_add(RocksDbWeight::get().reads(5))
+        //  Measured:  `773 + i * (10 ±0)`
+        //  Estimated: `2256 + i * (10 ±0)`
+        // Minimum execution time: 463_878_000 picoseconds.
+        Weight::from_parts(743_823_548, 0)
+            .saturating_add(Weight::from_parts(0, 2256))
+            // Standard Error: 292_144
+            .saturating_add(Weight::from_parts(1_326_639, 0).saturating_mul(i.into()))
+            .saturating_add(RocksDbWeight::get().reads(6))
             .saturating_add(RocksDbWeight::get().writes(3))
             .saturating_add(Weight::from_parts(0, 10).saturating_mul(i.into()))
     }
-    /// Storage: Session CurrentIndex (r:1 w:0)
-    /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured)
-    /// Storage: Distance EvaluationPool0 (r:1 w:1)
-    /// Proof Skipped: Distance EvaluationPool0 (max_values: Some(1), max_size: None, mode: Measured)
-    /// The range of component `i` is `[1, 600]`.
+
     fn force_update_evaluation(i: u32) -> Weight {
         // Proof Size summary in bytes:
         //  Measured:  `612 + i * (10 ±0)`
-        //  Estimated: `2096 + i * (10 ±0)`
-        // Minimum execution time: 8_392_000 picoseconds.
-        Weight::from_parts(10_825_908, 0)
-            .saturating_add(Weight::from_parts(0, 2096))
-            // Standard Error: 326
-            .saturating_add(Weight::from_parts(123_200, 0).saturating_mul(i.into()))
+        //  Estimated: `2095 + i * (10 ±0)`
+        // Minimum execution time: 208_812_000 picoseconds.
+        Weight::from_parts(257_150_521, 0)
+            .saturating_add(Weight::from_parts(0, 2095))
+            // Standard Error: 53_366
+            .saturating_add(Weight::from_parts(1_841_329, 0).saturating_mul(i.into()))
             .saturating_add(RocksDbWeight::get().reads(2))
             .saturating_add(RocksDbWeight::get().writes(1))
             .saturating_add(Weight::from_parts(0, 10).saturating_mul(i.into()))
     }
-    /// Storage: Session CurrentIndex (r:1 w:0)
-    /// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured)
-    /// Storage: Distance DistanceStatusExpireOn (r:1 w:1)
-    /// Proof Skipped: Distance DistanceStatusExpireOn (max_values: None, max_size: None, mode: Measured)
-    /// Storage: Distance IdentityDistanceStatus (r:0 w:1)
-    /// Proof Skipped: Distance IdentityDistanceStatus (max_values: None, max_size: None, mode: Measured)
-    fn force_set_distance_status() -> Weight {
+
+    fn force_valid_distance_status() -> Weight {
         // Proof Size summary in bytes:
-        //  Measured:  `586`
-        //  Estimated: `4051`
-        // Minimum execution time: 8_099_000 picoseconds.
-        Weight::from_parts(8_786_000, 0)
-            .saturating_add(Weight::from_parts(0, 4051))
-            .saturating_add(RocksDbWeight::get().reads(2))
-            .saturating_add(RocksDbWeight::get().writes(2))
+        //  Measured:  `1181`
+        //  Estimated: `7121`
+        // Minimum execution time: 873_892_000 picoseconds.
+        Weight::from_parts(1_081_510_000, 0)
+            .saturating_add(Weight::from_parts(0, 7121))
+            .saturating_add(RocksDbWeight::get().reads(7))
+            .saturating_add(RocksDbWeight::get().writes(5))
     }
-    /// Storage: Distance DidUpdate (r:1 w:1)
-    /// Proof Skipped: Distance DidUpdate (max_values: Some(1), max_size: None, mode: Measured)
+
     fn on_finalize() -> Weight {
         // Proof Size summary in bytes:
         //  Measured:  `170`
         //  Estimated: `1655`
-        // Minimum execution time: 3_904_000 picoseconds.
-        Weight::from_parts(4_132_000, 0)
+        // Minimum execution time: 93_595_000 picoseconds.
+        Weight::from_parts(109_467_000, 0)
             .saturating_add(Weight::from_parts(0, 1655))
             .saturating_add(RocksDbWeight::get().reads(1))
             .saturating_add(RocksDbWeight::get().writes(1))
diff --git a/pallets/duniter-test-parameters/src/lib.rs b/pallets/duniter-test-parameters/src/lib.rs
index 4da26974ba7202c4dce8476d415e965d54276878..55ca1c612e69e681ef2f76bb877b92f209cca87a 100644
--- a/pallets/duniter-test-parameters/src/lib.rs
+++ b/pallets/duniter-test-parameters/src/lib.rs
@@ -45,7 +45,7 @@ pub mod types {
         pub idty_confirm_period: BlockNumber,
         pub idty_creation_period: BlockNumber,
         pub membership_period: BlockNumber,
-        pub pending_membership_period: BlockNumber,
+        pub membership_renewal_period: BlockNumber,
         pub ud_creation_period: PeriodCount,
         pub ud_reeval_period: PeriodCount,
         pub smith_cert_max_by_issuer: CertCount,
diff --git a/pallets/duniter-wot/src/lib.rs b/pallets/duniter-wot/src/lib.rs
index eb127c0cc0979c1064910b791c89d70df89f44b5..7051c922c6eb5f8179a6650a6ea6b1c54147e182 100644
--- a/pallets/duniter-wot/src/lib.rs
+++ b/pallets/duniter-wot/src/lib.rs
@@ -30,8 +30,6 @@ mod benchmarking;*/
 
 pub use pallet::*;
 
-use traits::*;
-
 use frame_support::pallet_prelude::*;
 use pallet_certification::traits::SetNextIssuableOn;
 use pallet_identity::{IdtyEvent, IdtyStatus};
@@ -61,8 +59,6 @@ pub mod pallet {
         + pallet_identity::Config<IdtyIndex = IdtyIndex>
         + pallet_membership::Config<IdtyId = IdtyIndex>
     {
-        /// Distance evaluation provider
-        type IsDistanceOk: IsDistanceOk<IdtyIndex>;
         #[pallet::constant]
         type FirstIssuableOn: Get<Self::BlockNumber>;
         #[pallet::constant]
@@ -87,38 +83,24 @@ pub mod pallet {
 
     #[pallet::error]
     pub enum Error<T> {
-        /// Insufficient certifications received to claim membership.
-        NotEnoughCertsToClaimMembership,
-        /// Distance is invalid.
-        DistanceIsInvalid,
-        /// Distance is not evaluated.
-        DistanceNotEvaluated,
-        /// Distance evaluation has been requested but is still pending
-        DistanceEvaluationPending,
-        /// Distance evaluation has not been requested
-        DistanceEvaluationNotRequested,
-        /// Identity is not allowed to request membership.
-        IdtyNotAllowedToRequestMembership,
-        /// Identity not allowed to renew membership.
-        IdtyNotAllowedToRenewMembership,
+        /// Insufficient certifications received.
+        NotEnoughCerts,
+        /// Target status is incompatible with this operation.
+        // - Membership can not be added/renewed with this status
+        // - Certification can not be added to identity with this status
+        TargetStatusInvalid,
         /// Identity creation period not respected.
         IdtyCreationPeriodNotRespected,
         /// Insufficient received certifications to create identity.
         NotEnoughReceivedCertsToCreateIdty,
         /// Maximum number of emitted certifications reached.
         MaxEmittedCertsReached,
-        /// Not allowed to change identity address.
-        NotAllowedToChangeIdtyAddress,
-        /// Not allowed to remove identity.
-        NotAllowedToRemoveIdty,
         /// Issuer cannot emit a certification because it is not member.
         IssuerNotMember,
-        /// Cannot issue a certification to an unconfirmed identity
-        CertToUnconfirmed,
-        /// Cannot issue a certification to a revoked identity
-        CertToRevoked,
         /// Issuer or receiver not found.
         IdtyNotFound,
+        /// Membership can only be renewed after an antispam delay.
+        MembershipRenewalPeriodNotRespected,
     }
 }
 
@@ -162,55 +144,53 @@ impl<T: Config> pallet_certification::traits::CheckCertAllowed<IdtyIndex> for Pa
     fn check_cert_allowed(issuer: IdtyIndex, receiver: IdtyIndex) -> Result<(), DispatchError> {
         // issuer checks
         // ensure issuer is member
-        if let Some(issuer_data) = pallet_identity::Pallet::<T>::identity(issuer) {
-            ensure!(
-                issuer_data.status == IdtyStatus::Member,
-                Error::<T>::IssuerNotMember
-            );
-        } else {
-            return Err(Error::<T>::IdtyNotFound.into());
-        }
+        let issuer_data =
+            pallet_identity::Pallet::<T>::identity(issuer).ok_or(Error::<T>::IdtyNotFound)?;
+        ensure!(
+            issuer_data.status == IdtyStatus::Member,
+            Error::<T>::IssuerNotMember
+        );
 
         // receiver checks
         // ensure receiver identity is confirmed and not revoked
-        if let Some(receiver_data) = pallet_identity::Pallet::<T>::identity(receiver) {
-            match receiver_data.status {
-                // able to receive cert
-                IdtyStatus::Unvalidated | IdtyStatus::Member | IdtyStatus::NotMember => {}
-                IdtyStatus::Unconfirmed => return Err(Error::<T>::CertToUnconfirmed.into()),
-                IdtyStatus::Revoked => return Err(Error::<T>::CertToRevoked.into()),
-            };
-        } else {
-            return Err(Error::<T>::IdtyNotFound.into());
-        }
+        let receiver_data =
+            pallet_identity::Pallet::<T>::identity(receiver).ok_or(Error::<T>::IdtyNotFound)?;
+        ensure!(
+            receiver_data.status == IdtyStatus::Unvalidated
+                || receiver_data.status == IdtyStatus::Member
+                || receiver_data.status == IdtyStatus::NotMember,
+            Error::<T>::TargetStatusInvalid
+        );
         Ok(())
     }
 }
 
 // implement membership call checks
-impl<T: Config> sp_membership::traits::CheckMembershipCallAllowed<IdtyIndex> for Pallet<T> {
-    // membership claim is only possible when enough certs are received (both wots) and distance is ok
-    fn check_idty_allowed_to_claim_membership(idty_index: &IdtyIndex) -> Result<(), DispatchError> {
-        let idty_cert_meta = pallet_certification::Pallet::<T>::idty_cert_meta(idty_index);
+impl<T: Config> sp_membership::traits::CheckMembershipOpAllowed<IdtyIndex> for Pallet<T> {
+    fn check_add_membership(idty_index: IdtyIndex) -> Result<(), DispatchError> {
+        // check identity status
+        let idty_value =
+            pallet_identity::Pallet::<T>::identity(idty_index).ok_or(Error::<T>::IdtyNotFound)?;
         ensure!(
-            idty_cert_meta.received_count >= T::MinCertForMembership::get(),
-            Error::<T>::NotEnoughCertsToClaimMembership
+            idty_value.status == IdtyStatus::Unvalidated
+                || idty_value.status == IdtyStatus::NotMember,
+            Error::<T>::TargetStatusInvalid
         );
-        T::IsDistanceOk::is_distance_ok(idty_index)?;
+        // check cert count
+        check_cert_count::<T>(idty_index)?;
         Ok(())
     }
 
     // membership renewal is only possible when identity is member (otherwise it should claim again)
-    fn check_idty_allowed_to_renew_membership(idty_index: &IdtyIndex) -> Result<(), DispatchError> {
-        if let Some(idty_value) = pallet_identity::Pallet::<T>::identity(idty_index) {
-            ensure!(
-                idty_value.status == IdtyStatus::Member,
-                Error::<T>::IdtyNotAllowedToRenewMembership
-            );
-            T::IsDistanceOk::is_distance_ok(idty_index)?;
-        } else {
-            return Err(Error::<T>::IdtyNotFound.into());
-        }
+    fn check_renew_membership(idty_index: IdtyIndex) -> Result<(), DispatchError> {
+        // check identity status
+        let idty_value =
+            pallet_identity::Pallet::<T>::identity(idty_index).ok_or(Error::<T>::IdtyNotFound)?;
+        ensure!(
+            idty_value.status == IdtyStatus::Member,
+            Error::<T>::TargetStatusInvalid
+        );
+        // no need to check certification count since loosing certifications make membership expire
         Ok(())
     }
 }
@@ -250,7 +230,7 @@ impl<T: Config> pallet_identity::traits::OnIdtyChange<T> for Pallet<T> {
                     }
                 }
             }
-            // TODO split in removed / revoked in two events:
+            // we could split this event in removed / revoked:
             // if identity is revoked keep it
             // if identity is removed also remove certs
             IdtyEvent::Removed { status } => {
@@ -321,3 +301,91 @@ impl<T: Config> pallet_certification::traits::OnRemovedCert<IdtyIndex> for Palle
         }
     }
 }
+
+/// valid distance status handler
+impl<T: Config + pallet_distance::Config> pallet_distance::traits::OnValidDistanceStatus<T>
+    for Pallet<T>
+{
+    fn on_valid_distance_status(idty_index: IdtyIndex) {
+        if let Some(identity) = pallet_identity::Identities::<T>::get(idty_index) {
+            match identity.status {
+                IdtyStatus::Unconfirmed | IdtyStatus::Revoked => {
+                    // IdtyStatus::Unconfirmed
+                    // distance evaluation request should never happen for unconfirmed identity
+                    // IdtyStatus::Revoked
+                    // the identity can have been revoked during distance evaluation by the oracle
+                }
+
+                IdtyStatus::Unvalidated | IdtyStatus::NotMember => {
+                    // IdtyStatus::Unvalidated
+                    // normal scenario for first entry
+                    // IdtyStatus::NotMember
+                    // normal scenario for re-entry
+                    // the following can fail if a certification expired during distance evaluation
+                    // otherwise it should succeed
+                    let _ = pallet_membership::Pallet::<T>::try_add_membership(idty_index);
+                    // sp_std::if_std! {
+                    //     if let Err(e) = r {
+                    //         print!("failed to claim identity when distance status was found ok: ");
+                    //         println!("{:?}", idty_index);
+                    //         println!("reason: {:?}", e);
+                    //     }
+                    // }
+                }
+                IdtyStatus::Member => {
+                    // IdtyStatus::Member
+                    // normal scenario for renewal
+                    // should succeed
+                    let _ = pallet_membership::Pallet::<T>::try_renew_membership(idty_index);
+                    // sp_std::if_std! {
+                    //     if let Err(e) = r {
+                    //         print!("failed to renew identity when distance status was found ok: ");
+                    //         println!("{:?}", idty_index);
+                    //         println!("reason: {:?}", e);
+                    //     }
+                    // }
+                }
+            }
+        } else {
+            // identity was removed before distance status was found
+            // so it's ok to do nothing
+            sp_std::if_std! {
+                println!("identity was removed before distance status was found: {:?}", idty_index);
+            }
+        }
+    }
+}
+
+/// distance evaluation request allowed check
+impl<T: Config + pallet_distance::Config> pallet_distance::traits::CheckRequestDistanceEvaluation<T>
+    for Pallet<T>
+{
+    fn check_request_distance_evaluation(idty_index: IdtyIndex) -> Result<(), DispatchError> {
+        // check membership renewal antispam
+        let maybe_membership_data = pallet_membership::Pallet::<T>::membership(idty_index);
+        if let Some(membership_data) = maybe_membership_data {
+            // if membership data exists, this is for a renewal, apply antispam
+            ensure!(
+                // current_block > expiration block - membership period + renewal period
+                membership_data.expire_on
+                    + <T as pallet_membership::Config>::MembershipRenewalPeriod::get()
+                    < frame_system::Pallet::<T>::block_number()
+                        + <T as pallet_membership::Config>::MembershipPeriod::get(),
+                Error::<T>::MembershipRenewalPeriodNotRespected
+            );
+        };
+        // check cert count
+        check_cert_count::<T>(idty_index)?;
+        Ok(())
+    }
+}
+
+/// check certification count
+fn check_cert_count<T: Config>(idty_index: IdtyIndex) -> Result<(), DispatchError> {
+    let idty_cert_meta = pallet_certification::Pallet::<T>::idty_cert_meta(idty_index);
+    ensure!(
+        idty_cert_meta.received_count >= T::MinCertForMembership::get(),
+        Error::<T>::NotEnoughCerts
+    );
+    Ok(())
+}
diff --git a/pallets/duniter-wot/src/mock.rs b/pallets/duniter-wot/src/mock.rs
index c043ef76448f72bcf18013fd168b8d9c232c1bee..3ecb6c2332c42f019958a347a45aef79148f7fba 100644
--- a/pallets/duniter-wot/src/mock.rs
+++ b/pallets/duniter-wot/src/mock.rs
@@ -48,7 +48,7 @@ frame_support::construct_runtime!(
         System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
         DuniterWot: pallet_duniter_wot::{Pallet},
         Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>},
-        Membership: pallet_membership::{Pallet, Call, Config<T>, Storage, Event<T>},
+        Membership: pallet_membership::{Pallet, Config<T>, Storage, Event<T>},
         Cert: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>},
     }
 );
@@ -97,7 +97,6 @@ impl pallet_duniter_wot::Config for Test {
     type MinCertForMembership = MinCertForMembership;
     type MinCertForCreateIdtyRight = MinCertForCreateIdtyRight;
     type FirstIssuableOn = FirstIssuableOn;
-    type IsDistanceOk = crate::traits::DistanceAlwaysOk;
 }
 
 // Identity
@@ -139,14 +138,16 @@ impl pallet_identity::Config for Test {
 // Membership
 parameter_types! {
     pub const MembershipPeriod: u64 = 8;
+    pub const MembershipRenewalPeriod: u64 = 2;
 }
 
 impl pallet_membership::Config for Test {
-    type CheckMembershipCallAllowed = DuniterWot;
+    type CheckMembershipOpAllowed = DuniterWot;
     type IdtyId = IdtyIndex;
     type IdtyIdOf = IdentityIndexOf<Self>;
     type AccountIdOf = ();
     type MembershipPeriod = MembershipPeriod;
+    type MembershipRenewalPeriod = MembershipRenewalPeriod;
     type OnEvent = DuniterWot;
     type RuntimeEvent = RuntimeEvent;
     type WeightInfo = ();
diff --git a/pallets/duniter-wot/src/tests.rs b/pallets/duniter-wot/src/tests.rs
index 2b86ee97f6dd1258126a035a3ca166a95b13eb69..f1718d1402c3c1835cf00691af39f54855a91631 100644
--- a/pallets/duniter-wot/src/tests.rs
+++ b/pallets/duniter-wot/src/tests.rs
@@ -123,7 +123,7 @@ fn test_new_idty_validation() {
 
         // Ferdie should be able to claim membership
         run_to_block(5);
-        assert_ok!(Membership::claim_membership(RuntimeOrigin::signed(6)),);
+        assert_ok!(Membership::try_add_membership(6));
         System::assert_has_event(RuntimeEvent::Membership(
             pallet_membership::Event::MembershipAdded {
                 member: 6,
@@ -242,9 +242,9 @@ fn test_idty_membership_expire() {
         run_to_block(4);
 
         // Alice renews her membership
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1)));
+        assert_ok!(Membership::try_renew_membership(1));
         // Bob renews his membership
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2)));
+        assert_ok!(Membership::try_renew_membership(2));
 
         run_to_block(5);
         // renew certifications so that Alice can still issue cert at block 22
@@ -253,7 +253,7 @@ fn test_idty_membership_expire() {
 
         // Charlie's membership should expire at block #8
         run_to_block(8);
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1)));
+        assert_ok!(Membership::try_renew_membership(1));
         assert!(Membership::membership(3).is_none());
 
         System::assert_has_event(RuntimeEvent::Membership(
@@ -276,7 +276,7 @@ fn test_idty_membership_expire() {
         // check that identity is added to auto-revoke list (currently IdentityChangeSchedule)
         assert_eq!(Identity::next_scheduled(14), vec!(3));
         run_to_block(14);
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1)));
+        assert_ok!(Membership::try_renew_membership(1));
         // Charlie's identity should be auto-revoked at block #11 (8 + 3)
         System::assert_has_event(RuntimeEvent::Identity(
             pallet_identity::Event::IdtyRevoked {
@@ -298,7 +298,7 @@ fn test_idty_membership_expire() {
         // Alice can't certify revoked identity
         assert_noop!(
             Cert::add_cert(RuntimeOrigin::signed(1), 1, 3),
-            pallet_duniter_wot::Error::<Test>::CertToRevoked
+            pallet_duniter_wot::Error::<Test>::TargetStatusInvalid
         );
 
         run_to_block(21);
@@ -356,15 +356,15 @@ fn test_certification_expire() {
         assert_ok!(Cert::add_cert(RuntimeOrigin::signed(3), 3, 2));
         // --- BLOCK 7 ---
         run_to_block(7);
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1)));
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2)));
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3)));
+        assert_ok!(Membership::try_renew_membership(1));
+        assert_ok!(Membership::try_renew_membership(2));
+        assert_ok!(Membership::try_renew_membership(3));
 
         // --- BLOCK 14 ---
         run_to_block(14);
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1)));
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2)));
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3)));
+        assert_ok!(Membership::try_renew_membership(1));
+        assert_ok!(Membership::try_renew_membership(2));
+        assert_ok!(Membership::try_renew_membership(3));
 
         // normal cert Bob → Alice expires at block 20
         run_to_block(20);
@@ -387,19 +387,19 @@ fn test_certification_expire() {
         // --- BLOCK 21 ---
         // Bob and Charlie can renew their membership
         run_to_block(21);
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2)));
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3)));
+        assert_ok!(Membership::try_renew_membership(2));
+        assert_ok!(Membership::try_renew_membership(3));
 
         // Alice can not renew her membership which does not exist
         assert_noop!(
-            Membership::renew_membership(RuntimeOrigin::signed(1)),
+            Membership::try_renew_membership(1),
             pallet_membership::Error::<Test>::MembershipNotFound
         );
 
         // Alice can not claim her membership because she does not have enough certifications
         assert_noop!(
-            Membership::claim_membership(RuntimeOrigin::signed(1)),
-            pallet_duniter_wot::Error::<Test>::NotEnoughCertsToClaimMembership
+            Membership::try_add_membership(1),
+            pallet_duniter_wot::Error::<Test>::NotEnoughCerts
         );
 
         // --- BLOCK 23 ---
@@ -436,16 +436,16 @@ fn test_cert_can_not_be_issued() {
         assert_ok!(Cert::add_cert(RuntimeOrigin::signed(4), 4, 3)); // +20
                                                                     // --- BLOCK 7 ---
         run_to_block(7);
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); // + 8
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); // + 8
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3))); // + 8
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(4))); // + 8
+        assert_ok!(Membership::try_renew_membership(1)); // + 8
+        assert_ok!(Membership::try_renew_membership(2)); // + 8
+        assert_ok!(Membership::try_renew_membership(3)); // + 8
+        assert_ok!(Membership::try_renew_membership(4)); // + 8
 
         run_to_block(14);
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(1))); // + 8
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(2))); // + 8
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(3))); // + 8
-        assert_ok!(Membership::renew_membership(RuntimeOrigin::signed(4))); // + 8
+        assert_ok!(Membership::try_renew_membership(1)); // + 8
+        assert_ok!(Membership::try_renew_membership(2)); // + 8
+        assert_ok!(Membership::try_renew_membership(3)); // + 8
+        assert_ok!(Membership::try_renew_membership(4)); // + 8
 
         run_to_block(20);
         // println!("{:?}", System::events());
diff --git a/pallets/duniter-wot/src/traits.rs b/pallets/duniter-wot/src/traits.rs
index 6934ce454bdf2c1cbc318158ba8647671804dca7..f3474822b34656f1c99a9b12d34a548316c87941 100644
--- a/pallets/duniter-wot/src/traits.rs
+++ b/pallets/duniter-wot/src/traits.rs
@@ -13,17 +13,3 @@
 //
 // 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 crate::DispatchError;
-
-pub trait IsDistanceOk<IdtyId> {
-    fn is_distance_ok(idty_id: &IdtyId) -> Result<(), DispatchError>;
-}
-
-pub struct DistanceAlwaysOk;
-
-impl<IdtyId> IsDistanceOk<IdtyId> for DistanceAlwaysOk {
-    fn is_distance_ok(_idty_id: &IdtyId) -> Result<(), DispatchError> {
-        Ok(())
-    }
-}
diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs
index 01998e0c5dc056155f3059575c3939b71c320c16..dbe7167c5875c110b41eb90e552a3d7288debf5a 100644
--- a/pallets/identity/src/lib.rs
+++ b/pallets/identity/src/lib.rs
@@ -82,18 +82,18 @@ pub mod pallet {
         /// Period after which a revoked identity is removed and the keys are freed.
         #[pallet::constant]
         type DeletionPeriod: Get<Self::BlockNumber>;
-        /// Minimum duration between two owner key changes
+        /// Minimum duration between two owner key changes.
         // to avoid stealing the identity without means to revoke
         #[pallet::constant]
         type ChangeOwnerKeyPeriod: Get<Self::BlockNumber>;
-        /// Minimum duration between the creation of 2 identities by the same creator
+        /// Minimum duration between the creation of 2 identities by the same creator.
         // it should be greater or equal than the certification period in certification pallet
         #[pallet::constant]
         type IdtyCreationPeriod: Get<Self::BlockNumber>;
         /// Management of the authorizations of the different calls.
         /// The default implementation allows everything.
         type CheckIdtyCallAllowed: CheckIdtyCallAllowed<Self>;
-        /// Custom data to store in each identity
+        /// Custom data to store in each identity.
         type IdtyData: Clone
             + Codec
             + Default
diff --git a/pallets/membership/README.md b/pallets/membership/README.md
index 4bcd6020fa90de4d182c84c1e0145d80a95fc04e..c6dc0da834a30d0766609fc20773da17e1fd3136 100644
--- a/pallets/membership/README.md
+++ b/pallets/membership/README.md
@@ -4,20 +4,4 @@ Duniter membership is related to duniter Web of Trust and more specific than [pa
 
 ## Main Web of Trust
 
-When used in conjunction with the main Web of Trust, the membership pallet is combined with the Duniter-WoT pallet and the identity pallet, resulting in the following functionality:
-
-- `claim_membership` requires enough certifications, and a valid distance
-- `renew_membership` requires a valid membership and a valid distance status to extend the validity period of a membership.
-- `revoke_membership` (to be removed?)
-
-In practice, a new user creates an account, confirms identity using the `confirm_identity` call from the identity pallet. The user then validates its identity using `claim_membership`. If the certification and distance requirements are met, the identity is granted membership.
-
-## Sub Web of Trust Smith
-
-Functionality related to the Smith Web of Trust involves the following:
-
-- `claim_membership` requires to be member of the main wot and have enough smith certifications
-- `renew_membership` needs a valid membership to extend the membership's validity period.
-- `revoke_membership` requires a valid origin to revoke the membership.
-
-In practice, a user must complete all steps to gain membership in the main Web Of Trust. They can call `claim_membership` to be added to the authority members.
+Membership pallet manages all events related to web of trust membership of an identity. It exposes no calls to the user and its features are only available trough distance evaluation provided by distance oracle.
diff --git a/pallets/membership/src/benchmarking.rs b/pallets/membership/src/benchmarking.rs
index 1bdf5edf6c75666c3c2fc1acd901d2b3ad642733..84216da894acd5049049f33b48ec40bf6d756317 100644
--- a/pallets/membership/src/benchmarking.rs
+++ b/pallets/membership/src/benchmarking.rs
@@ -20,17 +20,15 @@ use super::*;
 
 use frame_benchmarking::benchmarks;
 use frame_system::pallet_prelude::BlockNumberFor;
-use frame_system::RawOrigin;
-use sp_runtime::traits::{Convert, One};
 
 #[cfg(test)]
 use maplit::btreemap;
 
 use crate::Pallet;
 
-fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
-    frame_system::Pallet::<T>::assert_has_event(generic_event.into());
-}
+// fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
+//     frame_system::Pallet::<T>::assert_has_event(generic_event.into());
+// }
 
 benchmarks! {
     where_clause {
@@ -39,39 +37,7 @@ benchmarks! {
             <T as frame_system::Config>::BlockNumber: From<u32>,
     }
 
-    // claim membership
-    claim_membership {
-        let idty: T::IdtyId = 3.into();
-        Membership::<T>::take(idty);
-        let caller: T::AccountId = T::AccountIdOf::convert(idty).unwrap();
-        let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
-        T::BenchmarkSetupHandler::force_status_ok(&idty, &caller);
-    }: _<T::RuntimeOrigin>(caller_origin)
-    verify {
-        assert_has_event::<T>(Event::<T>::MembershipAdded{member: idty, expire_on: BlockNumberFor::<T>::one() + T::MembershipPeriod::get()}.into());
-    }
-
-    // renew membership
-    renew_membership {
-        let idty: T::IdtyId = 3.into();
-        let caller: T::AccountId = T::AccountIdOf::convert(idty).unwrap();
-        let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
-        T::BenchmarkSetupHandler::force_status_ok(&idty, &caller);
-    }: _<T::RuntimeOrigin>(caller_origin)
-    verify {
-        assert_has_event::<T>(Event::<T>::MembershipAdded{member: idty, expire_on: BlockNumberFor::<T>::one() + T::MembershipPeriod::get()}.into());
-    }
-
-    // revoke membership
-    revoke_membership {
-        let idty: T::IdtyId = 3.into();
-        let caller: T::AccountId = T::AccountIdOf::convert(idty).unwrap();
-        let caller_origin: <T as frame_system::Config>::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into();
-        frame_system::pallet::Pallet::<T>::set_block_number(10_000_000.into()); // Arbitrarily high, to be in the worst case of wot instance.
-    }: _<T::RuntimeOrigin>(caller_origin)
-    verify {
-        assert_has_event::<T>(Event::<T>::MembershipRemoved{member: idty, reason: MembershipRemovalReason::Revoked}.into());
-    }
+    // TODO membership add and renewal should be included to distance on_new_session as worst case scenario
 
     // Base weight of an empty initialize
     on_initialize {
@@ -98,7 +64,7 @@ benchmarks! {
 
     impl_benchmark_test_suite!(
         Pallet,
-        crate::mock::new_test_ext(crate::mock::DefaultMembershipConfig {
+        crate::mock::new_test_ext(crate::mock::MembershipConfig {
         memberships: btreemap![
             3 => MembershipData {
                 expire_on: 3,
diff --git a/pallets/membership/src/lib.rs b/pallets/membership/src/lib.rs
index cba39e37ee5abb5fc058fcdea588e74f7dced747..c23c07a867257e885fde4eee0420369a8b318b5b 100644
--- a/pallets/membership/src/lib.rs
+++ b/pallets/membership/src/lib.rs
@@ -32,9 +32,7 @@ pub use pallet::*;
 pub use weights::WeightInfo;
 
 use frame_support::dispatch::Weight;
-use frame_support::error::BadOrigin;
 use frame_support::pallet_prelude::*;
-use frame_system::RawOrigin;
 use sp_membership::traits::*;
 use sp_membership::MembershipData;
 use sp_runtime::traits::Zero;
@@ -44,13 +42,13 @@ use std::collections::BTreeMap;
 
 #[cfg(feature = "runtime-benchmarks")]
 pub trait SetupBenchmark<IdtyId, AccountId> {
-    fn force_status_ok(idty_index: &IdtyId, account: &AccountId);
+    fn force_valid_distance_status(idty_index: &IdtyId);
     fn add_cert(_issuer: &IdtyId, _receiver: &IdtyId);
 }
 
 #[cfg(feature = "runtime-benchmarks")]
 impl<IdtyId, AccountId> SetupBenchmark<IdtyId, AccountId> for () {
-    fn force_status_ok(_idty_id: &IdtyId, _account: &AccountId) {}
+    fn force_valid_distance_status(_idty_id: &IdtyId) {}
     fn add_cert(_issuer: &IdtyId, _receiver: &IdtyId) {}
 }
 
@@ -86,16 +84,21 @@ pub mod pallet {
     #[pallet::config]
     pub trait Config: frame_system::Config {
         /// Ask the runtime whether the identity can perform membership operations
-        type CheckMembershipCallAllowed: CheckMembershipCallAllowed<Self::IdtyId>;
+        type CheckMembershipOpAllowed: CheckMembershipOpAllowed<Self::IdtyId>;
         /// Something that identifies an identity
         type IdtyId: Copy + MaybeSerializeDeserialize + Parameter + Ord;
         /// Something that gives the IdtyId of an AccountId
         type IdtyIdOf: Convert<Self::AccountId, Option<Self::IdtyId>>;
         /// Something that gives the AccountId of an IdtyId
         type AccountIdOf: Convert<Self::IdtyId, Option<Self::AccountId>>;
+        /// Maximum life span of a single membership (in number of blocks)
+        // (this could be renamed "validity" or "duration")
         #[pallet::constant]
-        /// Maximum life span of a non-renewable membership (in number of blocks)
         type MembershipPeriod: Get<Self::BlockNumber>;
+        /// Minimum delay to wait before renewing membership
+        // i.e. asking for distance evaluation
+        #[pallet::constant]
+        type MembershipRenewalPeriod: Get<Self::BlockNumber>;
         /// On event handler
         type OnEvent: OnEvent<Self::IdtyId>;
         /// Because this pallet emits events, it depends on the runtime's definition of an event.
@@ -156,6 +159,11 @@ pub mod pallet {
             member: T::IdtyId,
             expire_on: BlockNumberFor<T>,
         },
+        /// A membership was renewed.
+        MembershipRenewed {
+            member: T::IdtyId,
+            expire_on: BlockNumberFor<T>,
+        },
         /// A membership was removed.
         MembershipRemoved {
             member: T::IdtyId,
@@ -173,6 +181,8 @@ pub mod pallet {
         MembershipAlreadyAcquired,
         /// Membership not found.
         MembershipNotFound,
+        /// Already member, can not claim membership.
+        AlreadyMember,
     }
 
     // HOOKS //
@@ -188,61 +198,13 @@ pub mod pallet {
         }
     }
 
-    // CALLS //
-
-    #[pallet::call]
-    impl<T: Config> Pallet<T> {
-        /// claim membership
-        /// it must fullfill the requirements (certs, distance)
-        /// TODO #159 for main wot claim_membership is called automatically when distance is evaluated positively
-        /// for smith wot, it means joining the authority members
-        #[pallet::call_index(1)]
-        #[pallet::weight(T::WeightInfo::claim_membership())]
-        pub fn claim_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
-            // get identity
-            let idty_id = Self::get_idty_id(origin)?;
-
-            Self::check_allowed_to_claim(idty_id)?;
-            Self::do_add_membership(idty_id);
-            Ok(().into())
-        }
-
-        /// extend the validity period of an active membership
-        #[pallet::call_index(2)]
-        #[pallet::weight(T::WeightInfo::renew_membership())]
-        pub fn renew_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
-            // Verify phase
-            let idty_id = Self::get_idty_id(origin)?;
-            let membership_data =
-                Membership::<T>::get(idty_id).ok_or(Error::<T>::MembershipNotFound)?;
-
-            T::CheckMembershipCallAllowed::check_idty_allowed_to_renew_membership(&idty_id)?;
-
-            // apply phase
-            Self::unschedule_membership_expiry(idty_id, membership_data.expire_on);
-            Self::insert_membership_and_schedule_expiry(idty_id);
-            T::OnEvent::on_event(&sp_membership::Event::MembershipRenewed(idty_id));
-
-            Ok(().into())
-        }
-
-        /// revoke an active membership
-        /// (only available for sub wot, automatic for main wot)
-        #[pallet::call_index(3)]
-        #[pallet::weight(T::WeightInfo::revoke_membership())]
-        pub fn revoke_membership(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
-            // Verify phase
-            let idty_id = Self::get_idty_id(origin)?;
-
-            // Apply phase
-            Self::do_remove_membership(idty_id, MembershipRemovalReason::Revoked);
-
-            Ok(().into())
-        }
-    }
+    // // CALLS //
+    // #[pallet::call]
+    // impl<T: Config> Pallet<T> {
+    //     // no calls for membership pallet
+    // }
 
     // INTERNAL FUNCTIONS //
-
     impl<T: Config> Pallet<T> {
         /// unschedule membership expiry
         fn unschedule_membership_expiry(idty_id: T::IdtyId, block_number: T::BlockNumber) {
@@ -254,31 +216,78 @@ pub mod pallet {
             }
         }
         /// schedule membership expiry
-        fn insert_membership_and_schedule_expiry(idty_id: T::IdtyId) {
+        fn insert_membership_and_schedule_expiry(idty_id: T::IdtyId) -> T::BlockNumber {
             let block_number = frame_system::pallet::Pallet::<T>::block_number();
             let expire_on = block_number + T::MembershipPeriod::get();
 
             Membership::<T>::insert(idty_id, MembershipData { expire_on });
             MembershipsExpireOn::<T>::append(expire_on, idty_id);
-            Self::deposit_event(Event::MembershipAdded {
-                member: idty_id,
-                expire_on,
-            });
+            expire_on
         }
 
         /// check that membership can be claimed
-        pub fn check_allowed_to_claim(idty_id: T::IdtyId) -> Result<(), DispatchError> {
+        pub fn check_add_membership(idty_id: T::IdtyId) -> Result<(), DispatchError> {
+            // no-op is error
+            ensure!(
+                Membership::<T>::get(idty_id).is_none(),
+                Error::<T>::AlreadyMember
+            );
+
             // enough certifications and distance rule for example
-            T::CheckMembershipCallAllowed::check_idty_allowed_to_claim_membership(&idty_id)?;
+            T::CheckMembershipOpAllowed::check_add_membership(idty_id)?;
+            Ok(())
+        }
+
+        /// check that membership can be renewed
+        pub fn check_renew_membership(
+            idty_id: T::IdtyId,
+        ) -> Result<MembershipData<T::BlockNumber>, DispatchError> {
+            let membership_data =
+                Membership::<T>::get(idty_id).ok_or(Error::<T>::MembershipNotFound)?;
+
+            // enough certifications and distance rule for example
+            T::CheckMembershipOpAllowed::check_renew_membership(idty_id)?;
+            Ok(membership_data)
+        }
+
+        /// try claim membership
+        pub fn try_add_membership(idty_id: T::IdtyId) -> Result<(), DispatchError> {
+            Self::check_add_membership(idty_id)?;
+            Self::do_add_membership(idty_id);
+            Ok(())
+        }
+
+        /// try renew membership
+        pub fn try_renew_membership(idty_id: T::IdtyId) -> Result<(), DispatchError> {
+            let membership_data = Self::check_renew_membership(idty_id)?;
+            Self::do_renew_membership(idty_id, membership_data);
             Ok(())
         }
 
         /// perform membership addition
         fn do_add_membership(idty_id: T::IdtyId) {
-            Self::insert_membership_and_schedule_expiry(idty_id);
+            let expire_on = Self::insert_membership_and_schedule_expiry(idty_id);
+            Self::deposit_event(Event::MembershipAdded {
+                member: idty_id,
+                expire_on,
+            });
             T::OnEvent::on_event(&sp_membership::Event::MembershipAdded(idty_id));
         }
 
+        /// perform membership renewal
+        fn do_renew_membership(
+            idty_id: T::IdtyId,
+            membership_data: MembershipData<T::BlockNumber>,
+        ) {
+            Self::unschedule_membership_expiry(idty_id, membership_data.expire_on);
+            let expire_on = Self::insert_membership_and_schedule_expiry(idty_id);
+            Self::deposit_event(Event::MembershipRenewed {
+                member: idty_id,
+                expire_on,
+            });
+            T::OnEvent::on_event(&sp_membership::Event::MembershipRenewed(idty_id));
+        }
+
         /// perform membership removal
         pub fn do_remove_membership(idty_id: T::IdtyId, reason: MembershipRemovalReason) {
             if let Some(membership_data) = Membership::<T>::take(idty_id) {
@@ -291,15 +300,6 @@ pub mod pallet {
             }
         }
 
-        /// check the origin and get identity id if valid
-        fn get_idty_id(origin: OriginFor<T>) -> Result<T::IdtyId, DispatchError> {
-            if let Ok(RawOrigin::Signed(account_id)) = origin.into() {
-                T::IdtyIdOf::convert(account_id).ok_or_else(|| Error::<T>::IdtyIdNotFound.into())
-            } else {
-                Err(BadOrigin.into())
-            }
-        }
-
         /// perform the membership expiry scheduled at given block
         pub fn expire_memberships(block_number: T::BlockNumber) -> Weight {
             let mut expired_idty_count = 0u32;
diff --git a/pallets/membership/src/mock.rs b/pallets/membership/src/mock.rs
index 5173ff716833722a3269e9c65bc5a04e721fbaab..f197691276be7446d36a9a5fa910222c5337adb9 100644
--- a/pallets/membership/src/mock.rs
+++ b/pallets/membership/src/mock.rs
@@ -41,7 +41,7 @@ frame_support::construct_runtime!(
         UncheckedExtrinsic = UncheckedExtrinsic,
     {
         System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
-        DefaultMembership: pallet_membership::{Pallet, Call, Event<T>, Storage, Config<T>},
+        Membership: pallet_membership::{Pallet, Event<T>, Storage, Config<T>},
     }
 );
 
@@ -79,15 +79,16 @@ impl system::Config for Test {
 
 parameter_types! {
     pub const MembershipPeriod: BlockNumber = 5;
-    pub const PendingMembershipPeriod: BlockNumber = 3;
+    pub const MembershipRenewalPeriod: BlockNumber = 2;
 }
 
 impl pallet_membership::Config for Test {
-    type CheckMembershipCallAllowed = ();
+    type CheckMembershipOpAllowed = ();
     type IdtyId = IdtyId;
     type IdtyIdOf = ConvertInto;
     type AccountIdOf = ConvertInto;
     type MembershipPeriod = MembershipPeriod;
+    type MembershipRenewalPeriod = MembershipRenewalPeriod;
     type OnEvent = ();
     type RuntimeEvent = RuntimeEvent;
     type WeightInfo = ();
@@ -99,7 +100,7 @@ impl pallet_membership::Config for Test {
 pub fn new_test_ext(gen_conf: pallet_membership::GenesisConfig<Test>) -> sp_io::TestExternalities {
     GenesisConfig {
         system: SystemConfig::default(),
-        default_membership: gen_conf,
+        membership: gen_conf,
     }
     .build_storage()
     .unwrap()
@@ -108,11 +109,11 @@ pub fn new_test_ext(gen_conf: pallet_membership::GenesisConfig<Test>) -> sp_io::
 
 pub fn run_to_block(n: u64) {
     while System::block_number() < n {
-        DefaultMembership::on_finalize(System::block_number());
+        Membership::on_finalize(System::block_number());
         System::on_finalize(System::block_number());
         System::reset_events();
         System::set_block_number(System::block_number() + 1);
         System::on_initialize(System::block_number());
-        DefaultMembership::on_initialize(System::block_number());
+        Membership::on_initialize(System::block_number());
     }
 }
diff --git a/pallets/membership/src/tests.rs b/pallets/membership/src/tests.rs
index 53da60336bb6cefa94a5897836a38871929f7503..4f22c32135af2cd04f215261307b0ad5278dfba2 100644
--- a/pallets/membership/src/tests.rs
+++ b/pallets/membership/src/tests.rs
@@ -22,11 +22,8 @@ use maplit::btreemap;
 use sp_membership::traits::*;
 use sp_membership::MembershipData;
 
-// alias
-type RtEvent = RuntimeEvent;
-
-fn default_gen_conf() -> DefaultMembershipConfig {
-    DefaultMembershipConfig {
+fn default_gen_conf() -> MembershipConfig {
+    MembershipConfig {
         memberships: btreemap![
             0 => MembershipData {
                 expire_on: 3,
@@ -41,10 +38,10 @@ fn test_genesis_build() {
         run_to_block(1);
         // Verify state
         assert_eq!(
-            DefaultMembership::membership(0),
+            Membership::membership(0),
             Some(MembershipData { expire_on: 3 })
         );
-        assert_eq!(DefaultMembership::members_count(), 1);
+        assert_eq!(Membership::members_count(), 1);
     });
 }
 
@@ -55,42 +52,39 @@ fn test_membership_expiration() {
     new_test_ext(default_gen_conf()).execute_with(|| {
         // Membership 0 should not expired on block #2
         run_to_block(2);
-        assert!(DefaultMembership::is_member(&0));
+        assert!(Membership::is_member(&0));
         // Membership 0 should expire on block #3
         run_to_block(3);
-        assert!(!DefaultMembership::is_member(&0));
-        System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRemoved {
+        assert!(!Membership::is_member(&0));
+        System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRemoved {
             member: 0,
             reason: MembershipRemovalReason::Expired,
         }));
     });
 }
 
-/// test membership renewal
-// there is no limit for membership renewal outside wot rules (number of certs, distance rule)
+/// test membership renewal (triggered automatically after distance evaluation)
 #[test]
 fn test_membership_renewal() {
     new_test_ext(default_gen_conf()).execute_with(|| {
         // membership still valid at block 2
         run_to_block(2);
-        assert!(DefaultMembership::is_member(&0));
+        assert!(Membership::is_member(&0));
         // Membership 0 can be renewed
-        assert_ok!(DefaultMembership::renew_membership(RuntimeOrigin::signed(
-            0
-        ),));
-        System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipAdded {
+        assert_ok!(Membership::try_renew_membership(0));
+        System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRenewed {
             member: 0,
             expire_on: 2 + <Test as crate::Config>::MembershipPeriod::get(),
         }));
         // membership should not expire at block 3 to 6 because it has been renewed
         run_to_block(3);
-        assert!(DefaultMembership::is_member(&0));
+        assert!(Membership::is_member(&0));
         run_to_block(6);
-        assert!(DefaultMembership::is_member(&0));
+        assert!(Membership::is_member(&0));
         // membership should expire at block 7 (2+5)
         run_to_block(7);
-        assert!(!DefaultMembership::is_member(&0));
-        System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRemoved {
+        assert!(!Membership::is_member(&0));
+        System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRemoved {
             member: 0,
             reason: MembershipRemovalReason::Expired,
         }));
@@ -102,14 +96,14 @@ fn test_membership_renewal() {
 fn test_membership_renewal_nope() {
     new_test_ext(default_gen_conf()).execute_with(|| {
         run_to_block(2);
-        assert!(!DefaultMembership::is_member(&1));
+        assert!(!Membership::is_member(&1));
         // Membership 1 can not be renewed
         assert_noop!(
-            DefaultMembership::renew_membership(RuntimeOrigin::signed(1)),
+            Membership::try_renew_membership(1),
             Error::<Test>::MembershipNotFound,
         );
         run_to_block(3);
-        assert!(!DefaultMembership::is_member(&1));
+        assert!(!Membership::is_member(&1));
     });
 }
 
@@ -119,21 +113,17 @@ fn test_membership_revocation() {
     new_test_ext(default_gen_conf()).execute_with(|| {
         run_to_block(1);
         // Membership 0 can be revocable on block #1
-        assert_ok!(DefaultMembership::revoke_membership(RuntimeOrigin::signed(
-            0
-        ),));
-        System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRemoved {
+        Membership::do_remove_membership(0, MembershipRemovalReason::Revoked);
+        System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRemoved {
             member: 0,
             reason: MembershipRemovalReason::Revoked,
         }));
-        assert_eq!(DefaultMembership::membership(0), None);
+        assert_eq!(Membership::membership(0), None);
 
         // Membership 0 can re-claim membership
         run_to_block(5);
-        assert_ok!(DefaultMembership::claim_membership(RuntimeOrigin::signed(
-            0
-        ),));
-        System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipAdded {
+        assert_ok!(Membership::try_add_membership(0));
+        System::assert_has_event(RuntimeEvent::Membership(Event::MembershipAdded {
             member: 0,
             expire_on: 5 + <Test as crate::Config>::MembershipPeriod::get(),
         }));
@@ -149,28 +139,24 @@ fn test_membership_workflow() {
     new_test_ext(Default::default()).execute_with(|| {
         // - Then, idty 0 claim membership
         run_to_block(2);
-        assert_ok!(DefaultMembership::claim_membership(RuntimeOrigin::signed(
-            0
-        ),));
-        System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipAdded {
+        assert_ok!(Membership::try_add_membership(0));
+        System::assert_has_event(RuntimeEvent::Membership(Event::MembershipAdded {
             member: 0,
             expire_on: 2 + <Test as crate::Config>::MembershipPeriod::get(),
         }));
 
         // - Then, idty 0 claim renewal, should success
         run_to_block(2);
-        assert_ok!(DefaultMembership::renew_membership(RuntimeOrigin::signed(
-            0
-        ),));
+        assert_ok!(Membership::try_renew_membership(0));
 
         // idty 0 should still be member until membership period ended
         run_to_block(6); // 2 + 5 - 1
-        assert!(DefaultMembership::is_member(&0));
+        assert!(Membership::is_member(&0));
 
         // - Then, idty 0 should expire after membership period
         run_to_block(7); // 2 + 5
-        assert!(!DefaultMembership::is_member(&0));
-        System::assert_has_event(RtEvent::DefaultMembership(Event::MembershipRemoved {
+        assert!(!Membership::is_member(&0));
+        System::assert_has_event(RuntimeEvent::Membership(Event::MembershipRemoved {
             member: 0,
             reason: MembershipRemovalReason::Expired,
         }));
diff --git a/pallets/smith-members/src/lib.rs b/pallets/smith-members/src/lib.rs
index 95dd31177915c5dd19b8872c363c7c524ee39d52..8d4df3fa6a46d2538098ee734f2b944b3fd8394e 100644
--- a/pallets/smith-members/src/lib.rs
+++ b/pallets/smith-members/src/lib.rs
@@ -128,12 +128,12 @@ pub mod pallet {
     #[pallet::event]
     #[pallet::generate_deposit(pub(super) fn deposit_event)]
     pub enum Event<T: Config> {
-        /// An identity is being inivited to become a smith
+        /// An identity is being inivited to become a smith.
         InvitationSent {
             idty_index: T::IdtyIndex,
             invited_by: T::IdtyIndex,
         },
-        /// The invitation has been accepted
+        /// The invitation has been accepted.
         InvitationAccepted { idty_index: T::IdtyIndex },
         /// Certification received
         CertificationReceived {
@@ -142,7 +142,7 @@ pub mod pallet {
         },
         /// A smith gathered enough certifications to become an authority (can call `go_online()`).
         PromotedToSmith { idty_index: T::IdtyIndex },
-        /// A smith has been removed from the smiths set
+        /// A smith has been removed from the smiths set.
         SmithExcluded { idty_index: T::IdtyIndex },
     }
 
diff --git a/pallets/universal-dividend/src/lib.rs b/pallets/universal-dividend/src/lib.rs
index 25de8fb027cd88c57244465d9a0292b9c937f591..b524827091f2c4031c4ec7f8509970c5029b25a6 100644
--- a/pallets/universal-dividend/src/lib.rs
+++ b/pallets/universal-dividend/src/lib.rs
@@ -61,8 +61,8 @@ pub mod pallet {
         type MomentIntoBalance: Convert<Self::Moment, BalanceOf<Self>>;
         // The currency
         type Currency: Currency<Self::AccountId>;
-        #[pallet::constant]
         /// Maximum number of past UD revaluations to keep in storage.
+        #[pallet::constant]
         type MaxPastReeval: Get<u32>;
         /// Somethings that must provide the number of accounts allowed to create the universal dividend
         type MembersCount: Get<BalanceOf<Self>>;
@@ -70,19 +70,19 @@ pub mod pallet {
         type MembersStorage: frame_support::traits::StoredMap<Self::AccountId, FirstEligibleUd>;
         /// Because this pallet emits events, it depends on the runtime's definition of an event.
         type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
-        #[pallet::constant]
         /// Square of the money growth rate per ud reevaluation period
-        type SquareMoneyGrowthRate: Get<Perbill>;
         #[pallet::constant]
+        type SquareMoneyGrowthRate: Get<Perbill>;
         /// Universal dividend creation period (ms)
-        type UdCreationPeriod: Get<Self::Moment>;
         #[pallet::constant]
+        type UdCreationPeriod: Get<Self::Moment>;
         /// Universal dividend reevaluation period (ms)
-        type UdReevalPeriod: Get<Self::Moment>;
         #[pallet::constant]
+        type UdReevalPeriod: Get<Self::Moment>;
         /// The number of units to divide the amounts expressed in number of UDs
         /// Example: If you wish to express the UD amounts with a maximum precision of the order
         /// of the milliUD, choose 1000
+        #[pallet::constant]
         type UnitsPerUd: Get<BalanceOf<Self>>;
         /// Pallet weights info
         type WeightInfo: WeightInfo;
@@ -97,12 +97,14 @@ pub mod pallet {
     #[pallet::getter(fn current_ud)]
     pub type CurrentUd<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
 
+    // default value for number of the next UD
     #[pallet::type_value]
     pub fn DefaultForCurrentUdIndex() -> UdIndex {
         1
     }
 
     /// Current UD index
+    // (more like the index of the ongoing UD = the next one)
     #[pallet::storage]
     #[pallet::getter(fn ud_index)]
     pub type CurrentUdIndex<T: Config> =
@@ -110,6 +112,9 @@ pub mod pallet {
 
     #[cfg(test)]
     #[pallet::storage]
+    // UD should be linked to idtyid instead of accountid
+    // if it is convenient in test, why not have it in runtime also?
+    // storing it in idty_value.data is strange
     pub type TestMembers<T: Config> = StorageMap<
         _,
         Blake2_128Concat,
diff --git a/primitives/membership/src/traits.rs b/primitives/membership/src/traits.rs
index abb5961cd3d7d76cdba4f845a79b458ce03e326e..9e54c148b7dd06be47a76c9ca114f0e51ab6cc5b 100644
--- a/primitives/membership/src/traits.rs
+++ b/primitives/membership/src/traits.rs
@@ -16,16 +16,16 @@
 
 use frame_support::pallet_prelude::*;
 
-pub trait CheckMembershipCallAllowed<IdtyId> {
-    fn check_idty_allowed_to_claim_membership(idty_id: &IdtyId) -> Result<(), DispatchError>;
-    fn check_idty_allowed_to_renew_membership(idty_id: &IdtyId) -> Result<(), DispatchError>;
+pub trait CheckMembershipOpAllowed<IdtyId> {
+    fn check_add_membership(idty_id: IdtyId) -> Result<(), DispatchError>;
+    fn check_renew_membership(idty_id: IdtyId) -> Result<(), DispatchError>;
 }
 
-impl<IdtyId> CheckMembershipCallAllowed<IdtyId> for () {
-    fn check_idty_allowed_to_claim_membership(_: &IdtyId) -> Result<(), DispatchError> {
+impl<IdtyId> CheckMembershipOpAllowed<IdtyId> for () {
+    fn check_add_membership(_: IdtyId) -> Result<(), DispatchError> {
         Ok(())
     }
-    fn check_idty_allowed_to_renew_membership(_: &IdtyId) -> Result<(), DispatchError> {
+    fn check_renew_membership(_: IdtyId) -> Result<(), DispatchError> {
         Ok(())
     }
 }
diff --git a/resources/gdev.yaml b/resources/gdev.yaml
index a901457535966732d744522220a1b26ce5dfe1ee..fcfd98034cb67c96d53cdc9991f43184fdffd02d 100644
--- a/resources/gdev.yaml
+++ b/resources/gdev.yaml
@@ -27,8 +27,8 @@ parameters:
   cert_validity_period: 2102400
   # Validity duration of a membership. 1051200 blocks = 73 days.
   membership_period: 1051200
-  # Validity duration of a pending membership. 172800 blocks = 12 days.
-  pending_membership_period: 172800
+  # Period to wait before membership renewal. 1051200 blocks = 1 days.
+  membership_renewal_period: 14400
   # Delay a new member must observe before being able to emit a certification
   wot_first_cert_issuable_on: 0
   # Number of required received certs to become a member
@@ -68,4 +68,5 @@ sudo_key: "5CfodrEFe64MJtWvfhTHYBuUySr4WXLv2B41mZFucTXSGMFA"
 treasury_funder_address: "5E6q47RRGZU15LjUiBTm2DZjpqFKAjRNafYS8YV8AzTQZtLG"
 
 # The technical committee members, to act as sudo
-technical_committee: ["Pini", "moul", "HugoTrentesaux", "tuxmain", "1000i100", "vit", "cgeek"]
\ No newline at end of file
+technical_committee:
+  ["Pini", "moul", "HugoTrentesaux", "tuxmain", "1000i100", "vit", "cgeek"]
diff --git a/resources/metadata.scale b/resources/metadata.scale
index 15ad56874e147a9b94c30468250afd196b8504ec..820374cd2073ee49d2ba67359255742635e037a9 100644
Binary files a/resources/metadata.scale and b/resources/metadata.scale differ
diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs
index 6bae43eb554b952e97e8f7e23b226f4002bf0b70..d70ece043f1a29ed2c9f1802b39eb8a23c0be56c 100644
--- a/runtime/common/src/pallets_config.rs
+++ b/runtime/common/src/pallets_config.rs
@@ -453,7 +453,6 @@ macro_rules! pallets_config {
 
         impl pallet_duniter_wot::Config for Runtime {
             type FirstIssuableOn = WotFirstCertIssuableOn;
-            type IsDistanceOk = common_runtime::providers::MainWotIsDistanceOk<Runtime>;
             type MinCertForMembership = WotMinCertForMembership;
             type MinCertForCreateIdtyRight = WotMinCertForCreateIdtyRight;
         }
@@ -483,11 +482,12 @@ macro_rules! pallets_config {
         }
 
         impl pallet_membership::Config for Runtime {
-            type CheckMembershipCallAllowed = Wot;
+            type CheckMembershipOpAllowed = Wot;
             type IdtyId = IdtyIndex;
             type IdtyIdOf = common_runtime::providers::IdentityIndexOf<Self>;
             type AccountIdOf = common_runtime::providers::IdentityAccountIdProvider<Self>;
             type MembershipPeriod = MembershipPeriod;
+            type MembershipRenewalPeriod = MembershipRenewalPeriod;
             type OnEvent = (OnMembershipEventHandler<Wot, Runtime>, Wot);
             type RuntimeEvent = RuntimeEvent;
             type WeightInfo = common_runtime::weights::pallet_membership::WeightInfo<Runtime>;
@@ -516,9 +516,10 @@ macro_rules! pallets_config {
             type EvaluationPrice = frame_support::traits::ConstU64<1000>;
             type MaxRefereeDistance = frame_support::traits::ConstU32<5>;
             type MinAccessibleReferees = MinAccessibleReferees;
-            type ResultExpiration = frame_support::traits::ConstU32<720>;
             type RuntimeEvent = RuntimeEvent;
             type WeightInfo = common_runtime::weights::pallet_distance::WeightInfo<Runtime>;
+            type OnValidDistanceStatus = Wot;
+            type CheckRequestDistanceEvaluation = Wot;
         }
 
         // SMITH-MEMBERS
diff --git a/runtime/common/src/providers.rs b/runtime/common/src/providers.rs
index ca631988e74cfd3b19417be348605c4bd58d5574..504cf92f8db91313aa780b7e8c5b2f927bc35863 100644
--- a/runtime/common/src/providers.rs
+++ b/runtime/common/src/providers.rs
@@ -17,7 +17,6 @@
 use crate::{entities::IdtyData, AccountId, IdtyIndex};
 use core::marker::PhantomData;
 use pallet_universal_dividend::FirstEligibleUd;
-use sp_runtime::DispatchError;
 
 pub struct IdentityAccountIdProvider<Runtime>(PhantomData<Runtime>);
 
@@ -71,31 +70,6 @@ where
     }
 }
 
-pub struct MainWotIsDistanceOk<T>(PhantomData<T>);
-
-impl<T> pallet_duniter_wot::traits::IsDistanceOk<<T as pallet_identity::Config>::IdtyIndex>
-    for MainWotIsDistanceOk<T>
-where
-    T: pallet_distance::Config + pallet_duniter_wot::Config,
-{
-    fn is_distance_ok(
-        idty_id: &<T as pallet_identity::Config>::IdtyIndex,
-    ) -> Result<(), DispatchError> {
-        match pallet_distance::Pallet::<T>::identity_distance_status(idty_id) {
-            Some((_, status)) => match status {
-                pallet_distance::DistanceStatus::Valid => Ok(()),
-                pallet_distance::DistanceStatus::Invalid => {
-                    Err(pallet_duniter_wot::Error::<T>::DistanceIsInvalid.into())
-                }
-                pallet_distance::DistanceStatus::Pending => {
-                    Err(pallet_duniter_wot::Error::<T>::DistanceEvaluationPending.into())
-                }
-            },
-            None => Err(pallet_duniter_wot::Error::<T>::DistanceEvaluationNotRequested.into()),
-        }
-    }
-}
-
 pub struct IsWoTMemberProvider<T>(PhantomData<T>);
 impl<T: pallet_smith_members::Config>
     sp_runtime::traits::IsMember<<T as pallet_membership::Config>::IdtyId>
@@ -121,14 +95,8 @@ macro_rules! impl_benchmark_setup_handler {
             T: pallet_certification::Config,
             <T as pallet_certification::Config>::IdtyIndex: From<u32>,
         {
-            fn force_status_ok(
-                idty_id: &IdtyIndex,
-                account: &<T as frame_system::Config>::AccountId,
-            ) -> () {
-                let _ = pallet_distance::Pallet::<T>::set_distance_status(
-                    *idty_id,
-                    Some((account.clone(), pallet_distance::DistanceStatus::Valid)),
-                );
+            fn force_valid_distance_status(idty_id: &IdtyIndex) -> () {
+                let _ = pallet_distance::Pallet::<T>::do_valid_distance_status(*idty_id);
             }
             fn add_cert(issuer: &IdtyIndex, receiver: &IdtyIndex) {
                 let _ = pallet_certification::Pallet::<T>::do_add_cert_checked(
diff --git a/runtime/common/src/weights/pallet_certification.rs b/runtime/common/src/weights/pallet_certification.rs
index 3e98b656d34399f3c2954a667f791db2168eca30..4252be4605eae790017201d5e58f1b79ea3267c5 100644
--- a/runtime/common/src/weights/pallet_certification.rs
+++ b/runtime/common/src/weights/pallet_certification.rs
@@ -54,8 +54,8 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T>
 	/// Proof Skipped: Cert StorageIdtyCertMeta (max_values: None, max_size: None, mode: Measured)
 	/// Storage: Parameters ParametersStorage (r:1 w:0)
 	/// Proof Skipped: Parameters ParametersStorage (max_values: Some(1), max_size: None, mode: Measured)
-	/// Storage: Cert StorageCertsRemovableOn (r:1 w:1)
-	/// Proof Skipped: Cert StorageCertsRemovableOn (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Cert CertsRemovableOn (r:1 w:1)
+	/// Proof Skipped: Cert CertsRemovableOn (max_values: None, max_size: None, mode: Measured)
 	/// Storage: Cert CertsByReceiver (r:1 w:1)
 	/// Proof Skipped: Cert CertsByReceiver (max_values: None, max_size: None, mode: Measured)
 	fn add_cert() -> Weight {
@@ -110,8 +110,8 @@ impl<T: frame_system::Config> pallet_certification::WeightInfo for WeightInfo<T>
 			.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into())))
 			.saturating_add(Weight::from_parts(0, 2511).saturating_mul(i.into()))
 	}
-	/// Storage: Cert StorageCertsRemovableOn (r:1 w:0)
-	/// Proof Skipped: Cert StorageCertsRemovableOn (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Cert CertsRemovableOn (r:1 w:0)
+	/// Proof Skipped: Cert CertsRemovableOn (max_values: None, max_size: None, mode: Measured)
 	fn on_initialize() -> Weight {
 		// Proof Size summary in bytes:
 		//  Measured:  `181`
diff --git a/runtime/common/src/weights/pallet_distance.rs b/runtime/common/src/weights/pallet_distance.rs
index 27aa944753e7c172da0a2c05b5493358dcd3008c..c1c3c8c557f2b26e50632388bc65d06780c999fd 100644
--- a/runtime/common/src/weights/pallet_distance.rs
+++ b/runtime/common/src/weights/pallet_distance.rs
@@ -17,19 +17,19 @@
 //! Autogenerated weights for `pallet_distance`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2024-01-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2023-12-20, STEPS: `8`, REPEAT: `4`, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! WORST CASE MAP SIZE: `1000000`
-//! HOSTNAME: `bgallois-ms7d43`, CPU: `12th Gen Intel(R) Core(TM) i3-12100F`
+//! HOSTNAME: `squirrel`, CPU: `Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz`
 //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
 
 // Executed Command:
-// target/release/duniter
+// ./target/debug/duniter
 // benchmark
 // pallet
 // --chain=dev
-// --steps=50
-// --repeat=20
-// --pallet=*
+// --steps=8
+// --repeat=4
+// --pallet=pallet-distance
 // --extrinsic=*
 // --execution=wasm
 // --wasm-execution=compiled
@@ -50,25 +50,57 @@ pub struct WeightInfo<T>(PhantomData<T>);
 impl<T: frame_system::Config> pallet_distance::WeightInfo for WeightInfo<T> {
 	/// Storage: Identity IdentityIndexOf (r:1 w:0)
 	/// Proof Skipped: Identity IdentityIndexOf (max_values: None, max_size: None, mode: Measured)
-	/// Storage: Distance IdentityDistanceStatus (r:1 w:1)
-	/// Proof Skipped: Distance IdentityDistanceStatus (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Distance PendingEvaluationRequest (r:1 w:1)
+	/// Proof Skipped: Distance PendingEvaluationRequest (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Distance ValidEvaluationResult (r:1 w:0)
+	/// Proof Skipped: Distance ValidEvaluationResult (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Cert StorageIdtyCertMeta (r:1 w:0)
+	/// Proof Skipped: Cert StorageIdtyCertMeta (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Parameters ParametersStorage (r:1 w:0)
+	/// Proof Skipped: Parameters ParametersStorage (max_values: Some(1), max_size: None, mode: Measured)
 	/// Storage: Session CurrentIndex (r:1 w:0)
 	/// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured)
 	/// Storage: Distance EvaluationPool2 (r:1 w:1)
 	/// Proof Skipped: Distance EvaluationPool2 (max_values: Some(1), max_size: None, mode: Measured)
 	/// Storage: System Account (r:1 w:1)
 	/// Proof: System Account (max_values: None, max_size: Some(126), added: 2601, mode: MaxEncodedLen)
-	/// Storage: Distance DistanceStatusExpireOn (r:1 w:1)
-	/// Proof Skipped: Distance DistanceStatusExpireOn (max_values: None, max_size: None, mode: Measured)
 	fn request_distance_evaluation() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `939`
-		//  Estimated: `4404`
-		// Minimum execution time: 33_043_000 picoseconds.
-		Weight::from_parts(33_977_000, 0)
-			.saturating_add(Weight::from_parts(0, 4404))
-			.saturating_add(T::DbWeight::get().reads(6))
-			.saturating_add(T::DbWeight::get().writes(4))
+		//  Measured:  `1280`
+		//  Estimated: `4745`
+		// Minimum execution time: 876_053_000 picoseconds.
+		Weight::from_parts(898_445_000, 0)
+			.saturating_add(Weight::from_parts(0, 4745))
+			.saturating_add(T::DbWeight::get().reads(8))
+			.saturating_add(T::DbWeight::get().writes(3))
+	}
+	/// Storage: Identity IdentityIndexOf (r:1 w:0)
+	/// Proof Skipped: Identity IdentityIndexOf (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Identity Identities (r:2 w:0)
+	/// Proof Skipped: Identity Identities (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Distance PendingEvaluationRequest (r:1 w:1)
+	/// Proof Skipped: Distance PendingEvaluationRequest (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Distance ValidEvaluationResult (r:1 w:0)
+	/// Proof Skipped: Distance ValidEvaluationResult (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Cert StorageIdtyCertMeta (r:1 w:0)
+	/// Proof Skipped: Cert StorageIdtyCertMeta (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Parameters ParametersStorage (r:1 w:0)
+	/// Proof Skipped: Parameters ParametersStorage (max_values: Some(1), max_size: None, mode: Measured)
+	/// Storage: Session CurrentIndex (r:1 w:0)
+	/// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured)
+	/// Storage: Distance EvaluationPool2 (r:1 w:1)
+	/// Proof Skipped: Distance EvaluationPool2 (max_values: Some(1), max_size: None, mode: Measured)
+	/// Storage: System Account (r:1 w:1)
+	/// Proof: System Account (max_values: None, max_size: Some(126), added: 2601, mode: MaxEncodedLen)
+	fn request_distance_evaluation_for() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `1485`
+		//  Estimated: `7425`
+		// Minimum execution time: 1_118_982_000 picoseconds.
+		Weight::from_parts(1_292_782_000, 0)
+			.saturating_add(Weight::from_parts(0, 7425))
+			.saturating_add(T::DbWeight::get().reads(10))
+			.saturating_add(T::DbWeight::get().writes(3))
 	}
 	/// Storage: Distance DidUpdate (r:1 w:1)
 	/// Proof Skipped: Distance DidUpdate (max_values: Some(1), max_size: None, mode: Measured)
@@ -85,13 +117,13 @@ impl<T: frame_system::Config> pallet_distance::WeightInfo for WeightInfo<T> {
 	/// The range of component `i` is `[1, 600]`.
 	fn update_evaluation(i: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `772 + i * (10 ±0)`
+		//  Measured:  `773 + i * (10 ±0)`
 		//  Estimated: `2256 + i * (10 ±0)`
-		// Minimum execution time: 22_135_000 picoseconds.
-		Weight::from_parts(27_043_883, 0)
+		// Minimum execution time: 463_878_000 picoseconds.
+		Weight::from_parts(743_823_548, 0)
 			.saturating_add(Weight::from_parts(0, 2256))
-			// Standard Error: 1_421
-			.saturating_add(Weight::from_parts(125_435, 0).saturating_mul(i.into()))
+			// Standard Error: 292_144
+			.saturating_add(Weight::from_parts(1_326_639, 0).saturating_mul(i.into()))
 			.saturating_add(T::DbWeight::get().reads(6))
 			.saturating_add(T::DbWeight::get().writes(3))
 			.saturating_add(Weight::from_parts(0, 10).saturating_mul(i.into()))
@@ -103,32 +135,40 @@ impl<T: frame_system::Config> pallet_distance::WeightInfo for WeightInfo<T> {
 	/// The range of component `i` is `[1, 600]`.
 	fn force_update_evaluation(i: u32, ) -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `611 + i * (10 ±0)`
+		//  Measured:  `612 + i * (10 ±0)`
 		//  Estimated: `2095 + i * (10 ±0)`
-		// Minimum execution time: 13_033_000 picoseconds.
-		Weight::from_parts(15_741_933, 0)
+		// Minimum execution time: 208_812_000 picoseconds.
+		Weight::from_parts(257_150_521, 0)
 			.saturating_add(Weight::from_parts(0, 2095))
-			// Standard Error: 693
-			.saturating_add(Weight::from_parts(121_989, 0).saturating_mul(i.into()))
+			// Standard Error: 53_366
+			.saturating_add(Weight::from_parts(1_841_329, 0).saturating_mul(i.into()))
 			.saturating_add(T::DbWeight::get().reads(2))
 			.saturating_add(T::DbWeight::get().writes(1))
 			.saturating_add(Weight::from_parts(0, 10).saturating_mul(i.into()))
 	}
 	/// Storage: Session CurrentIndex (r:1 w:0)
 	/// Proof Skipped: Session CurrentIndex (max_values: Some(1), max_size: None, mode: Measured)
-	/// Storage: Distance DistanceStatusExpireOn (r:1 w:1)
-	/// Proof Skipped: Distance DistanceStatusExpireOn (max_values: None, max_size: None, mode: Measured)
-	/// Storage: Distance IdentityDistanceStatus (r:0 w:1)
-	/// Proof Skipped: Distance IdentityDistanceStatus (max_values: None, max_size: None, mode: Measured)
-	fn force_set_distance_status() -> Weight {
+	/// Storage: Distance ValidEvaluationExpireOn (r:1 w:1)
+	/// Proof Skipped: Distance ValidEvaluationExpireOn (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Identity Identities (r:1 w:0)
+	/// Proof Skipped: Identity Identities (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Membership Membership (r:1 w:1)
+	/// Proof Skipped: Membership Membership (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Membership MembershipsExpireOn (r:2 w:2)
+	/// Proof Skipped: Membership MembershipsExpireOn (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Parameters ParametersStorage (r:1 w:0)
+	/// Proof Skipped: Parameters ParametersStorage (max_values: Some(1), max_size: None, mode: Measured)
+	/// Storage: Distance ValidEvaluationResult (r:0 w:1)
+	/// Proof Skipped: Distance ValidEvaluationResult (max_values: None, max_size: None, mode: Measured)
+	fn force_valid_distance_status() -> Weight {
 		// Proof Size summary in bytes:
-		//  Measured:  `585`
-		//  Estimated: `4050`
-		// Minimum execution time: 12_886_000 picoseconds.
-		Weight::from_parts(13_465_000, 0)
-			.saturating_add(Weight::from_parts(0, 4050))
-			.saturating_add(T::DbWeight::get().reads(2))
-			.saturating_add(T::DbWeight::get().writes(2))
+		//  Measured:  `1181`
+		//  Estimated: `7121`
+		// Minimum execution time: 873_892_000 picoseconds.
+		Weight::from_parts(1_081_510_000, 0)
+			.saturating_add(Weight::from_parts(0, 7121))
+			.saturating_add(T::DbWeight::get().reads(7))
+			.saturating_add(T::DbWeight::get().writes(5))
 	}
 	/// Storage: Distance DidUpdate (r:1 w:1)
 	/// Proof Skipped: Distance DidUpdate (max_values: Some(1), max_size: None, mode: Measured)
@@ -136,8 +176,8 @@ impl<T: frame_system::Config> pallet_distance::WeightInfo for WeightInfo<T> {
 		// Proof Size summary in bytes:
 		//  Measured:  `170`
 		//  Estimated: `1655`
-		// Minimum execution time: 3_830_000 picoseconds.
-		Weight::from_parts(4_065_000, 0)
+		// Minimum execution time: 93_595_000 picoseconds.
+		Weight::from_parts(109_467_000, 0)
 			.saturating_add(Weight::from_parts(0, 1655))
 			.saturating_add(T::DbWeight::get().reads(1))
 			.saturating_add(T::DbWeight::get().writes(1))
diff --git a/runtime/common/src/weights/pallet_identity.rs b/runtime/common/src/weights/pallet_identity.rs
index 159094ef918d118978f8caefcf26e22ed75472f4..16189190a6bbb51687c61eea057452de22587d48 100644
--- a/runtime/common/src/weights/pallet_identity.rs
+++ b/runtime/common/src/weights/pallet_identity.rs
@@ -64,8 +64,8 @@ impl<T: frame_system::Config> pallet_identity::WeightInfo for WeightInfo<T> {
 	/// Proof Skipped: Identity IdentityChangeSchedule (max_values: None, max_size: None, mode: Measured)
 	/// Storage: Identity CounterForIdentities (r:1 w:1)
 	/// Proof: Identity CounterForIdentities (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen)
-	/// Storage: Cert StorageCertsRemovableOn (r:1 w:1)
-	/// Proof Skipped: Cert StorageCertsRemovableOn (max_values: None, max_size: None, mode: Measured)
+	/// Storage: Cert CertsRemovableOn (r:1 w:1)
+	/// Proof Skipped: Cert CertsRemovableOn (max_values: None, max_size: None, mode: Measured)
 	/// Storage: Cert CertsByReceiver (r:1 w:1)
 	/// Proof Skipped: Cert CertsByReceiver (max_values: None, max_size: None, mode: Measured)
 	/// Storage: Quota IdtyQuota (r:0 w:1)
diff --git a/runtime/g1/src/lib.rs b/runtime/g1/src/lib.rs
index 40a3025acac0603b7069edfef33d2c977b0497b5..8c9bca67c4336aaf69d36a807bf07a27cb6f6657 100644
--- a/runtime/g1/src/lib.rs
+++ b/runtime/g1/src/lib.rs
@@ -174,9 +174,6 @@ impl Contains<RuntimeCall> for BaseCallFilter {
             call,
             RuntimeCall::System(
                 frame_system::Call::remark { .. } | frame_system::Call::remark_with_event { .. }
-            ) | RuntimeCall::Membership(
-                pallet_membership::Call::claim_membership { .. }
-                    | pallet_membership::Call::revoke_membership { .. }
             ) | RuntimeCall::Session(_)
         )
     }
@@ -287,7 +284,7 @@ construct_runtime!(
         // Web Of Trust
         Wot: pallet_duniter_wot::{Pallet} = 40,
         Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>} = 41,
-        Membership: pallet_membership::{Pallet, Call, Config<T>, Storage, Event<T>} = 42,
+        Membership: pallet_membership::{Pallet, Config<T>, Storage, Event<T>} = 42,
         Certification: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>} = 43,
         Distance: pallet_distance::{Pallet, Call, Storage, Inherent, Event<T>} = 44,
 
diff --git a/runtime/g1/src/parameters.rs b/runtime/g1/src/parameters.rs
index 22ef4efa43e1bf4424bbd86c5b6bf00daeb62586..adfc0183ad11ca025a18b29e335966fbdb9257ff 100644
--- a/runtime/g1/src/parameters.rs
+++ b/runtime/g1/src/parameters.rs
@@ -105,7 +105,7 @@ parameter_types! {
 // Membership
 parameter_types! {
     pub const MembershipPeriod: BlockNumber = YEARS;
-    pub const PendingMembershipPeriod: BlockNumber = 2 * MONTHS;
+    pub const MembershipRenewalPeriod: BlockNumber = 2 * MONTHS;
 }
 
 // Certification
diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs
index 04366d5dbe9b44698365b1531d33653819e914bd..d64153d645e659cc59d1a789cbfd253115618e62 100644
--- a/runtime/gdev/src/lib.rs
+++ b/runtime/gdev/src/lib.rs
@@ -177,10 +177,9 @@ impl Contains<RuntimeCall> for BaseCallFilter {
     fn contains(call: &RuntimeCall) -> bool {
         !matches!(
             call,
-            // in main web of trust, membership request and revoke are handeled through identity pallet
-            // the user can not call them directly
-            RuntimeCall::Membership(pallet_membership::Call::revoke_membership { .. })
-                | RuntimeCall::Session(_)
+            // session calls can not be called directly
+            // it should be done through authority-members pallet
+            RuntimeCall::Session(_)
         )
     }
 }
@@ -257,6 +256,7 @@ common_runtime::pallets_config! {
     pub type ConfirmPeriod = pallet_duniter_test_parameters::IdtyConfirmPeriod<Runtime>;
     pub type IdtyCreationPeriod = pallet_duniter_test_parameters::IdtyCreationPeriod<Runtime>;
     pub type MembershipPeriod = pallet_duniter_test_parameters::MembershipPeriod<Runtime>;
+    pub type MembershipRenewalPeriod = pallet_duniter_test_parameters::MembershipRenewalPeriod<Runtime>;
     pub type UdCreationPeriod = pallet_duniter_test_parameters::UdCreationPeriod<Runtime>;
     pub type UdReevalPeriod = pallet_duniter_test_parameters::UdReevalPeriod<Runtime>;
     pub type WotFirstCertIssuableOn = pallet_duniter_test_parameters::WotFirstCertIssuableOn<Runtime>;
@@ -329,7 +329,7 @@ construct_runtime!(
         // Web Of Trust
         Wot: pallet_duniter_wot::{Pallet} = 40,
         Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>} = 41,
-        Membership: pallet_membership::{Pallet, Call, Config<T>, Storage, Event<T>} = 42,
+        Membership: pallet_membership::{Pallet, Config<T>, Storage, Event<T>} = 42,
         Certification: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>} = 43,
         Distance: pallet_distance::{Pallet, Call, Storage, Inherent, Event<T>} = 44,
 
diff --git a/runtime/gdev/src/parameters.rs b/runtime/gdev/src/parameters.rs
index 7dc4e8177245114e7df93fb3cd8a2d4e7e7e29c2..abd164fe652d74a58e2d90d8cfd61b3c224534ac 100644
--- a/runtime/gdev/src/parameters.rs
+++ b/runtime/gdev/src/parameters.rs
@@ -90,6 +90,11 @@ frame_support::parameter_types! {
     pub const ChangeOwnerKeyPeriod: BlockNumber = 7 * DAYS;
 }
 
+// Membership
+frame_support::parameter_types! {
+    pub const SmithMembershipRenewalPeriod: BlockNumber = MONTHS;
+}
+
 /*************/
 /* UTILITIES */
 /*************/
diff --git a/runtime/gdev/tests/common/mod.rs b/runtime/gdev/tests/common/mod.rs
index 261755f759209283c2acbbfa4dd0de41c26f24c8..0ca8aa74cc02be2f068724ebafd8811ce6b58cb1 100644
--- a/runtime/gdev/tests/common/mod.rs
+++ b/runtime/gdev/tests/common/mod.rs
@@ -105,7 +105,7 @@ impl ExtBuilder {
                 idty_confirm_period: 40,
                 idty_creation_period: 50,
                 membership_period: 100,
-                pending_membership_period: 500,
+                membership_renewal_period: 10,
                 ud_creation_period: 60_000,
                 ud_reeval_period: 60_000 * 20,
                 smith_cert_max_by_issuer: 8,
diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs
index 49755e0c49b7b054aa61f21cceed8cd5ca94a649..cfc3cb05d3e533518bb32eb7768a12de8cd09a55 100644
--- a/runtime/gdev/tests/integration_tests.rs
+++ b/runtime/gdev/tests/integration_tests.rs
@@ -311,7 +311,7 @@ fn test_remove_identity() {
     });
 }
 
-/// test identity is validated when membership is claimed
+/// test identity is "validated" (= membership is claimed) when distance is evaluated positively
 #[test]
 fn test_validate_identity_when_claim() {
     ExtBuilder::new(1, 3, 4)
@@ -346,11 +346,14 @@ fn test_validate_identity_when_claim() {
                 5
             ));
 
+            // eve request distance evaluation for herself
             assert_ok!(Distance::request_distance_evaluation(
                 frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
             ));
 
-            run_to_block(51); // Pass 2 sessions
+            // Pass 2 sessions
+            run_to_block(51);
+            // simulate an evaluation published by smith Alice
             assert_ok!(Distance::force_update_evaluation(
                 frame_system::RawOrigin::Root.into(),
                 AccountKeyring::Alice.to_account_id(),
@@ -358,20 +361,161 @@ fn test_validate_identity_when_claim() {
                     distances: vec![Perbill::one()],
                 }
             ));
-            run_to_block(76); // Pass 1 session
+            run_to_block(75); // Pass 1 session
+            System::assert_has_event(RuntimeEvent::Distance(
+                pallet_distance::Event::EvaluatedValid { idty_index: 5 },
+            ));
+
+            // eve can not claim her membership manually because it is done automatically
+            // the following call does not exist anymore
+            // assert_noop!(
+            //     Membership::claim_membership(
+            //         frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+            //     ),
+            //     pallet_membership::Error::<Runtime>::AlreadyMember
+            // );
 
-            // eve can claim her membership
-            assert_ok!(Membership::claim_membership(
+            // println!("{:?}", System::events());
+            System::assert_has_event(RuntimeEvent::Membership(
+                pallet_membership::Event::MembershipAdded {
+                    member: 5,
+                    expire_on: 75 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(),
+                },
+            ));
+        });
+}
+
+/// test identity creation workflow
+// with distance requested by last certifier
+#[test]
+fn test_identity_creation_workflow() {
+    ExtBuilder::new(1, 3, 4)
+        .with_initial_balances(vec![
+            (AccountKeyring::Charlie.to_account_id(), 10_000), // necessary for evalation distance reserve
+            (AccountKeyring::Eve.to_account_id(), 2_000),
+            (AccountKeyring::Ferdie.to_account_id(), 1_000),
+        ])
+        .build()
+        .execute_with(|| {
+            run_to_block(1);
+            // alice create identity for Eve
+            assert_ok!(Identity::create_identity(
+                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                AccountKeyring::Eve.to_account_id(),
+            ));
+            assert_eq!(
+                Identity::identity(5),
+                Some(pallet_identity::IdtyValue {
+                    data: Default::default(),
+                    next_creatable_identity_on: 0u32,
+                    old_owner_key: None,
+                    owner_key: AccountKeyring::Eve.to_account_id(),
+                    next_scheduled: 1 + 40,
+                    status: pallet_identity::IdtyStatus::Unconfirmed,
+                })
+            );
+            run_to_block(2);
+            // eve confirms her identity
+            assert_ok!(Identity::confirm_identity(
                 frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+                "Eeeeeveeeee".into(),
+            ));
+            assert_eq!(
+                Identity::identity(5),
+                Some(pallet_identity::IdtyValue {
+                    data: Default::default(),
+                    next_creatable_identity_on: 0u32,
+                    old_owner_key: None,
+                    owner_key: AccountKeyring::Eve.to_account_id(),
+                    next_scheduled: 2 + 876600,
+                    status: pallet_identity::IdtyStatus::Unvalidated,
+                })
+            );
+            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(),
+                2,
+                5
+            ));
+            assert_ok!(Certification::add_cert(
+                frame_system::RawOrigin::Signed(AccountKeyring::Charlie.to_account_id()).into(),
+                3,
+                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(),
+                5
+            ));
+            // then the evaluation is pending
+            assert_eq!(
+                Distance::pending_evaluation_request(5),
+                Some(AccountKeyring::Charlie.to_account_id(),)
+            );
+
+            // Pass 2 sessions
+            run_to_block(51);
+            // simulate evaluation published by smith Alice
+            assert_ok!(Distance::force_update_evaluation(
+                frame_system::RawOrigin::Root.into(),
+                AccountKeyring::Alice.to_account_id(),
+                pallet_distance::ComputationResult {
+                    distances: vec![Perbill::one()],
+                }
             ));
+            // Pass 1 session
+            run_to_block(75);
 
+            // eve should not even have to claim her membership
             System::assert_has_event(RuntimeEvent::Membership(
                 pallet_membership::Event::MembershipAdded {
                     member: 5,
-                    expire_on: 76 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(),
+                    expire_on: 75 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(),
+                },
+            ));
+
+            // test state coherence
+            assert_eq!(
+                Identity::identity(5),
+                Some(pallet_identity::IdtyValue {
+                    data: IdtyData {
+                        // block time is 6_000 ms
+                        // ud creation period is 60_000 ms ~ 10 blocks
+                        // first ud is at 24_000 ms ~ 4 blocks
+                        // at block 75 this is UD number 8, so next is 9
+                        first_eligible_ud: pallet_universal_dividend::FirstEligibleUd(Some(
+                            sp_std::num::NonZeroU16::new(9).unwrap()
+                        ))
+                    },
+                    next_creatable_identity_on: 0u32,
+                    old_owner_key: None,
+                    owner_key: AccountKeyring::Eve.to_account_id(),
+                    next_scheduled: 0,
+                    status: pallet_identity::IdtyStatus::Member,
+                })
+            );
+
+            run_to_block(84);
+            System::assert_has_event(RuntimeEvent::UniversalDividend(
+                pallet_universal_dividend::Event::NewUdCreated {
+                    amount: 1000,
+                    index: 9,
+                    monetary_mass: 50_000, // 13_000 (initial) + 4 * 1000 * 8 (produced) + 5000
+                    members_count: 5,
+                },
+            ));
+            assert_ok!(UniversalDividend::claim_uds(
+                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+            ));
+            System::assert_has_event(RuntimeEvent::UniversalDividend(
+                pallet_universal_dividend::Event::UdsClaimed {
+                    count: 1,
+                    total: 1000,
+                    who: AccountKeyring::Eve.to_account_id(),
                 },
             ));
-            // not possible anymore to validate identity of someone else
         });
 }
 
@@ -424,6 +568,16 @@ fn test_membership_renewal() {
         .with_initial_balances(vec![(AccountKeyring::Alice.to_account_id(), 2000)])
         .build()
         .execute_with(|| {
+            // can not renew membership immediately
+            assert_noop!(
+                Distance::request_distance_evaluation(
+                    frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
+                ),
+                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(),
             ));
@@ -436,33 +590,27 @@ fn test_membership_renewal() {
                     distances: vec![Perbill::one()],
                 }
             ));
-            run_to_block(76); // Pass 1 session
-
-            // renew at block 76
-            assert_ok!(Membership::renew_membership(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
-            ));
+            // Pass 1 session, membership is renewed automatically
+            run_to_block(75);
             System::assert_has_event(RuntimeEvent::Membership(
-                pallet_membership::Event::MembershipAdded {
+                pallet_membership::Event::MembershipRenewed {
                     member: 1,
-                    expire_on: 76 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(),
+                    expire_on: 75 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(),
                 },
             ));
 
-            // renew at block 77
-            run_to_block(77);
-            assert_ok!(Membership::renew_membership(
-                frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
-            ));
-            System::assert_has_event(RuntimeEvent::Membership(
-                pallet_membership::Event::MembershipAdded {
-                    member: 1,
-                    expire_on: 77 + <Runtime as pallet_membership::Config>::MembershipPeriod::get(),
-                },
-            ));
+            run_to_block(76);
+            // 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(),
+                ),
+                pallet_duniter_wot::Error::<Runtime>::MembershipRenewalPeriodNotRespected,
+            );
 
-            // should expire at block 177 = 77+100
-            run_to_block(177);
+            // should expire at block 175 = 75+100
+            run_to_block(175);
             System::assert_has_event(RuntimeEvent::Membership(
                 pallet_membership::Event::MembershipRemoved {
                     member: 1,
@@ -583,18 +731,19 @@ fn test_ud_claimed_membership_on_and_off() {
             },
         ));
 
-        // alice claims back her membership
-        assert_ok!(Distance::force_set_distance_status(
+        // alice claims back her membership through distance evaluation
+        assert_ok!(Distance::force_valid_distance_status(
             frame_system::RawOrigin::Root.into(),
             1,
-            Some((
-                AccountKeyring::Alice.to_account_id(),
-                pallet_distance::DistanceStatus::Valid
-            ))
-        ));
-        assert_ok!(Membership::claim_membership(
-            frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into()
         ));
+        // 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(),
+        //     ),
+        //     pallet_membership::Error::<Runtime>::AlreadyMember
+        // );
         System::assert_has_event(RuntimeEvent::Membership(
             pallet_membership::Event::MembershipAdded {
                 member: 1,
@@ -963,12 +1112,22 @@ fn test_create_new_idty_without_founds() {
         .build()
         .execute_with(|| {
             run_to_block(2);
+            assert_eq!(
+                Balances::free_balance(AccountKeyring::Eve.to_account_id()),
+                0
+            );
 
             // Should be able to create an identity without founds
             assert_ok!(Identity::create_identity(
                 frame_system::RawOrigin::Signed(AccountKeyring::Alice.to_account_id()).into(),
                 AccountKeyring::Eve.to_account_id(),
             ));
+            System::assert_has_event(RuntimeEvent::Identity(
+                pallet_identity::Event::IdtyCreated {
+                    idty_index: 5,
+                    owner_key: AccountKeyring::Eve.to_account_id(),
+                },
+            ));
 
             // At next block, nothing should be preleved
             run_to_block(3);
@@ -1029,24 +1188,26 @@ fn test_validate_new_idty_after_few_uds() {
                 pallet_identity::IdtyName::from("Eve"),
             ));
 
-            // At next block, Bob should be able to certify and validate the new identity
+            // 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(),
                 2,
                 5,
             ));
-            assert_ok!(Distance::force_set_distance_status(
+            // valid distance status should trigger identity validation
+            assert_ok!(Distance::force_valid_distance_status(
                 frame_system::RawOrigin::Root.into(),
                 5,
-                Some((
-                    AccountKeyring::Bob.to_account_id(),
-                    pallet_distance::DistanceStatus::Valid
-                ))
-            ));
-            assert_ok!(Membership::claim_membership(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
             ));
+            // 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(),
+            //     ),
+            //     pallet_membership::Error::<Runtime>::AlreadyMember
+            // );
 
             // The new member should have first_eligible_ud equal to three
             assert!(Identity::identity(5).is_some());
@@ -1095,18 +1256,19 @@ fn test_claim_memberhsip_after_few_uds() {
                 5,
             ));
 
-            // eve should be able to claim her membership
-            assert_ok!(Distance::force_set_distance_status(
+            // eve membership should be able to be claimed through distance evaluation
+            assert_ok!(Distance::force_valid_distance_status(
                 frame_system::RawOrigin::Root.into(),
                 5,
-                Some((
-                    AccountKeyring::Eve.to_account_id(),
-                    pallet_distance::DistanceStatus::Valid
-                ))
-            ));
-            assert_ok!(Membership::claim_membership(
-                frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
             ));
+            // but not manually
+            // because the call does not exist
+            // assert_noop!(
+            //     Membership::claim_membership(
+            //         frame_system::RawOrigin::Signed(AccountKeyring::Eve.to_account_id()).into(),
+            //     ),
+            //     pallet_membership::Error::<Runtime>::AlreadyMember
+            // );
 
             // The new member should have first_eligible_ud equal to three
             assert!(Identity::identity(5).is_some());
@@ -1451,16 +1613,6 @@ fn test_new_account_linked() {
         );
     })
 }
-#[test]
-#[ignore = "what was this test supposed to do?"]
-fn smith_data_problem() {
-    ExtBuilder::new(1, 3, 4)
-        .change_parameters(|_parameters| {})
-        .build()
-        .execute_with(|| {
-            run_to_block(4);
-        });
-}
 
 /// test killed account
 // The only way to kill an account is to kill  the identity
diff --git a/runtime/gdev/tests/xt_tests.rs b/runtime/gdev/tests/xt_tests.rs
index 1663d6221475aaea797b388399d8f0d1364aeeca..4635571e3dd7e4709d10593d38c12fe9baa4b7e9 100644
--- a/runtime/gdev/tests/xt_tests.rs
+++ b/runtime/gdev/tests/xt_tests.rs
@@ -15,6 +15,7 @@
 // along with Duniter-v2S. If not, see <https://www.gnu.org/licenses/>.
 
 // these integration tests aim to test fees and extrinsic-related externalities
+// they need constant-fees feature to work
 
 mod common;
 
diff --git a/runtime/gtest/src/lib.rs b/runtime/gtest/src/lib.rs
index a4b6e010a2133efd043cbb7a05ae5f6dd9fa3e13..7fe0cfeba60b0c77c6746d931b272e13e582f3d3 100644
--- a/runtime/gtest/src/lib.rs
+++ b/runtime/gtest/src/lib.rs
@@ -292,7 +292,7 @@ construct_runtime!(
         // Web Of Trust
         Wot: pallet_duniter_wot::{Pallet} = 40,
         Identity: pallet_identity::{Pallet, Call, Config<T>, Storage, Event<T>} = 41,
-        Membership: pallet_membership::{Pallet, Call, Config<T>, Storage, Event<T>} = 42,
+        Membership: pallet_membership::{Pallet, Config<T>, Storage, Event<T>} = 42,
         Certification: pallet_certification::{Pallet, Call, Config<T>, Storage, Event<T>} = 43,
         Distance: pallet_distance::{Pallet, Call, Storage, Inherent, Event<T>} = 44,
 
diff --git a/runtime/gtest/src/parameters.rs b/runtime/gtest/src/parameters.rs
index 252ae211a2571ead87fa67ab4d13f64b98ae8c88..c7731b0a9950fcee4ae933a7c43438949d980b29 100644
--- a/runtime/gtest/src/parameters.rs
+++ b/runtime/gtest/src/parameters.rs
@@ -107,7 +107,7 @@ parameter_types! {
 // Membership
 parameter_types! {
     pub const MembershipPeriod: BlockNumber = 73 * DAYS;
-    pub const PendingMembershipPeriod: BlockNumber = 12 * DAYS;
+    pub const MembershipRenewalPeriod: BlockNumber = 56 * DAYS;
 }
 
 // Certification