diff --git a/Cargo.lock b/Cargo.lock
index 3cf5426e3c8751e913be36be24df155fcbe934a8..d24218db7ea926a282bb349e7eb5746eae9de826 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1025,6 +1025,7 @@ dependencies = [
  "lz4_flex",
  "maplit",
  "once_cell",
+ "parking_lot",
  "resiter",
  "smallvec",
 ]
diff --git a/db/src/lib.rs b/db/src/lib.rs
index 849241d5266ce154f047a7ec1dddcdf0aee37be0..fca46a2c47d1eef0c31cf52e3905182d3081c34a 100644
--- a/db/src/lib.rs
+++ b/db/src/lib.rs
@@ -45,6 +45,7 @@ pub(crate) use duniter_core::dbs::{
     bincode_db, CorruptedBytes, HashKeyV2, PubKeyKeyV2, SourceAmountValV2, ToDumpString,
     WalletConditionsV2,
 };
+pub(crate) use duniter_core::wot::WotId;
 pub(crate) use serde::{Deserialize, Serialize};
 pub(crate) use std::collections::BTreeSet;
 
diff --git a/db/src/values/gva_idty_db.rs b/db/src/values/gva_idty_db.rs
index 540deab0f0fb81731b00ec6a172b95ece4b3edf7..13332d92d16a119d13f7fdf51898aab13a25c2f9 100644
--- a/db/src/values/gva_idty_db.rs
+++ b/db/src/values/gva_idty_db.rs
@@ -15,12 +15,13 @@
 
 use crate::*;
 
-#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 pub struct GvaIdtyDbV1 {
     pub is_member: bool,
     pub joins: SmallVec<[BlockNumber; 2]>,
     pub leaves: BTreeSet<BlockNumber>,
     pub first_ud: Option<BlockNumber>,
+    pub wot_id: WotId,
 }
 
 impl AsBytes for GvaIdtyDbV1 {
diff --git a/dbs-reader/src/uds_of_pubkey.rs b/dbs-reader/src/uds_of_pubkey.rs
index e92d3411dab1f5be8bf19606ae0136eccb87996b..6e240966a71b8842e8ec10eda5a4d0b9c39cc85d 100644
--- a/dbs-reader/src/uds_of_pubkey.rs
+++ b/dbs-reader/src/uds_of_pubkey.rs
@@ -471,8 +471,8 @@ fn collect_uds<BC: BackendCol, I: Iterator<Item = BlockNumber>>(
 mod tests {
 
     use super::*;
-    use duniter_core::dbs::smallvec::smallvec as svec;
     use duniter_core::dbs::{databases::bc_v2::BcV2DbWritable, SourceAmountValV2, UdIdV2};
+    use duniter_core::{dbs::smallvec::smallvec as svec, wot::WotId};
     use duniter_gva_db::GvaV1DbWritable;
 
     #[test]
@@ -482,6 +482,7 @@ mod tests {
             joins: svec![BlockNumber(26), BlockNumber(51)],
             leaves: [BlockNumber(32)].iter().copied().collect(),
             first_ud: Some(BlockNumber(29)),
+            wot_id: WotId(0),
         };
         let blocks_with_ud = vec![
             BlockNumber(3),
@@ -531,6 +532,7 @@ mod tests {
             joins: svec![BlockNumber(26), BlockNumber(51)],
             leaves: [BlockNumber(32)].iter().copied().collect(),
             first_ud: Some(BlockNumber(29)),
+            wot_id: WotId(0),
         };
 
         let bc_db = duniter_core::dbs::databases::bc_v2::BcV2Db::<Mem>::open(MemConf::default())?;
diff --git a/indexer/Cargo.toml b/indexer/Cargo.toml
index 25d49999a90ad330c341f8d7e30989e58a53f2ef..9e8907ad5bd1377ac8fa62f9d4d2557b516a7e01 100644
--- a/indexer/Cargo.toml
+++ b/indexer/Cargo.toml
@@ -18,12 +18,13 @@ duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core" }
 duniter-gva-db = { path = "../db" }
 dubp = { version = "0.54.1", features = ["duniter"] }
 lz4_flex = { version = "0.7", default-features = false }
-once_cell = "1.5.2"
+once_cell = "1.7"
 resiter = "0.4.0"
 
 [dev-dependencies]
 duniter-core = { git = "https://git.duniter.org/nodes/rust/duniter-core", features = ["mem"] }
 maplit = "1.0.2"
+parking_lot = "0.11"
 smallvec = { version = "1.4.0", features = ["serde", "write"] }
 
 [features]
diff --git a/indexer/src/identities.rs b/indexer/src/identities.rs
index 0a33b376b0113412977700cc337c5eda5b586b5b..2360c7132ea9978c66b60d0ce669f60f7e232e23 100644
--- a/indexer/src/identities.rs
+++ b/indexer/src/identities.rs
@@ -19,13 +19,35 @@ pub(crate) fn update_identities<B: Backend>(
     block: &DubpBlockV10,
     identities: &mut TxColRw<B::Col, GvaIdentitiesEvent>,
 ) -> KvResult<()> {
+    let mut identities_pubkeys = HashSet::new();
+    for idty in block.identities() {
+        let pubkey = idty.issuers()[0];
+        identities_pubkeys.insert(pubkey);
+        let wot_id = crate::get_next_wot_id();
+
+        identities.upsert(
+            PubKeyKeyV2(pubkey),
+            GvaIdtyDbV1 {
+                is_member: true,
+                joins: smallvec![block.number()],
+                leaves: BTreeSet::new(),
+                first_ud: None,
+                wot_id,
+            },
+        );
+    }
     for mb in block.joiners() {
         let pubkey = mb.issuers()[0];
 
-        let mut idty = identities.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
-        idty.is_member = true;
-        idty.joins.push(block.number());
-        identities.upsert(PubKeyKeyV2(pubkey), idty);
+        // Update identity only if the join event concerns an identity already created in the past
+        if !identities_pubkeys.contains(&pubkey) {
+            let mut idty = identities
+                .get(&PubKeyKeyV2(pubkey))?
+                .ok_or_else(|| KvError::DbCorrupted("Joiner without identity".to_owned()))?;
+            idty.is_member = true;
+            idty.joins.push(block.number());
+            identities.upsert(PubKeyKeyV2(pubkey), idty);
+        }
     }
     for revo in block.revoked() {
         let pubkey = revo.issuer;
@@ -52,7 +74,9 @@ pub(crate) fn revert_identities<B: Backend>(
     for mb in block.joiners() {
         let pubkey = mb.issuers()[0];
 
-        let mut idty = identities.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
+        let mut idty = identities
+            .get(&PubKeyKeyV2(pubkey))?
+            .ok_or_else(|| KvError::DbCorrupted("Joiner without identity".to_owned()))?;
         idty.is_member = false;
         idty.joins.pop();
         identities.upsert(PubKeyKeyV2(pubkey), idty);
@@ -60,6 +84,7 @@ pub(crate) fn revert_identities<B: Backend>(
     for idty in block.identities() {
         let pubkey = idty.issuers()[0];
         identities.remove(PubKeyKeyV2(pubkey));
+        crate::revert_wot_id();
     }
     for revo in block.revoked() {
         let pubkey = revo.issuer;
diff --git a/indexer/src/lib.rs b/indexer/src/lib.rs
index 07f8ca9bc34b7f08410e14c2230e2779149a6052..3f03199c642d6236d91526b0a0a22377da579934 100644
--- a/indexer/src/lib.rs
+++ b/indexer/src/lib.rs
@@ -36,21 +36,24 @@ use dubp::{
     },
     wallet::prelude::*,
 };
-use duniter_core::dbs::{
-    bincode_db, kv_typed::prelude::*, prelude::*, FileBackend, HashKeyV2, PubKeyKeyV2,
-    SourceAmountValV2, WalletConditionsV2,
+use duniter_core::{
+    dbs::{
+        bincode_db, kv_typed::prelude::*, prelude::*, smallvec::smallvec, FileBackend, HashKeyV2,
+        PubKeyKeyV2, SourceAmountValV2, WalletConditionsV2,
+    },
+    wot::{WebOfTrust, WotId, MAIN_WOT},
 };
 use duniter_gva_db::*;
+use once_cell::sync::OnceCell;
 use resiter::filter::Filter;
 use std::{
-    collections::{BTreeSet, HashMap},
+    collections::{BTreeSet, HashMap, HashSet},
+    ops::AddAssign,
     path::Path,
 };
 
-static GVA_DB_RO: once_cell::sync::OnceCell<GvaV1DbRo<FileBackend>> =
-    once_cell::sync::OnceCell::new();
-static GVA_DB_RW: once_cell::sync::OnceCell<GvaV1Db<FileBackend>> =
-    once_cell::sync::OnceCell::new();
+static GVA_DB_RO: OnceCell<GvaV1DbRo<FileBackend>> = OnceCell::new();
+static GVA_DB_RW: OnceCell<GvaV1Db<FileBackend>> = OnceCell::new();
 
 pub fn get_gva_db_ro(profile_path_opt: Option<&Path>) -> &'static GvaV1DbRo<FileBackend> {
     GVA_DB_RO.get_or_init(|| get_gva_db_rw(profile_path_opt).get_ro_handler())
@@ -65,6 +68,39 @@ pub fn get_gva_db_rw(profile_path_opt: Option<&Path>) -> &'static GvaV1Db<FileBa
     })
 }
 
+static mut NEXT_WOT_ID: Option<usize> = None;
+
+fn get_next_wot_id() -> WotId {
+    WotId(unsafe {
+        if let Some(ref mut next_wot_id_) = NEXT_WOT_ID {
+            let next_wot_id = *next_wot_id_;
+            next_wot_id_.add_assign(1);
+            next_wot_id
+        } else {
+            NEXT_WOT_ID = Some(if let Some(main_wot) = MAIN_WOT.get() {
+                main_wot.read().size()
+            } else {
+                0
+            });
+            0
+        }
+    })
+}
+fn revert_wot_id() {
+    unsafe {
+        if let Some(ref mut next_wot_id_) = NEXT_WOT_ID {
+            use std::ops::SubAssign as _;
+            next_wot_id_.sub_assign(1);
+        } else {
+            NEXT_WOT_ID = Some(if let Some(main_wot) = MAIN_WOT.get() {
+                main_wot.read().size() - 1
+            } else {
+                unreachable!()
+            });
+        }
+    };
+}
+
 pub struct UtxoV10<'s> {
     pub id: UtxoIdV10,
     pub amount: SourceAmount,
@@ -256,9 +292,13 @@ mod tests {
         documents::transaction::TransactionDocumentV10Stringified,
         documents_parser::prelude::FromStringObject,
     };
+    use once_cell::sync::Lazy;
+
+    static TESTS_MUTEX: Lazy<parking_lot::Mutex<()>> = Lazy::new(|| parking_lot::Mutex::new(()));
 
     #[test]
     fn test_gva_apply_block() -> anyhow::Result<()> {
+        let _ = TESTS_MUTEX.lock();
         let gva_db = GvaV1Db::<Mem>::open(MemConf::default())?;
 
         let s1 = WalletScriptV10::single_sig(PublicKey::from_base58(
@@ -272,6 +312,7 @@ mod tests {
             version: 10,
             median_time: 5_243,
             dividend: Some(1000),
+            identities: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:Ydnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()],
             joiners: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:FFeyrvYio9uYwY5aMcDGswZPNjGLrl8THn9l3EPKSNySD3SDSHjCljSfFEwb87sroyzJQoVzPwER0sW/cbZMDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()],
             inner_hash: Some("0000000A65A12DB95B3153BCD05DB4D5C30CC7F0B1292D9FFBC3DE67F72F6040".to_owned()),
             signature: "7B0hvcfajE2G8nBLp0vLVaQcQdQIyli21Gu8F2l+nimKHRe+fUNi+MWd1e/u29BYZa+RZ1yxhbHIbFzytg7fAA==".to_owned(),
@@ -430,6 +471,7 @@ mod tests {
 
     #[test]
     fn test_gva_revert_block() -> anyhow::Result<()> {
+        let _ = TESTS_MUTEX.lock();
         let gva_db = GvaV1Db::<Mem>::open(MemConf::default())?;
 
         let s1 = WalletScriptV10::single_sig(PublicKey::from_base58(
@@ -443,6 +485,7 @@ mod tests {
             version: 10,
             median_time: 5_243,
             dividend: Some(1000),
+            identities: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:Ydnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()],
             joiners: vec!["D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:FFeyrvYio9uYwY5aMcDGswZPNjGLrl8THn9l3EPKSNySD3SDSHjCljSfFEwb87sroyzJQoVzPwER0sW/cbZMDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:elois".to_owned()],
             inner_hash: Some("0000000A65A12DB95B3153BCD05DB4D5C30CC7F0B1292D9FFBC3DE67F72F6040".to_owned()),
             signature: "7B0hvcfajE2G8nBLp0vLVaQcQdQIyli21Gu8F2l+nimKHRe+fUNi+MWd1e/u29BYZa+RZ1yxhbHIbFzytg7fAA==".to_owned(),