diff --git a/Cargo.lock b/Cargo.lock
index 43b8d55edcf7f7f8fe9c3d2853c1fa33c31c4b5a..ac57f44232606356f4a1fff74c71684a9f5f7ed1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1083,6 +1083,7 @@ dependencies = [
  "log",
  "mockall",
  "once_cell",
+ "paste",
  "rand 0.7.3",
  "serde",
  "serde_json",
@@ -1870,7 +1871,6 @@ dependencies = [
  "cfg-if 0.1.10",
  "criterion",
  "flume",
- "kv_typed_code_gen",
  "leveldb_minimal",
  "lmdb-zero",
  "maybe-async",
@@ -1889,16 +1889,6 @@ dependencies = [
  "zerocopy",
 ]
 
-[[package]]
-name = "kv_typed_code_gen"
-version = "0.1.0"
-dependencies = [
- "Inflector",
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
diff --git a/Cargo.toml b/Cargo.toml
index af836bf382893bdb584dcea94b61ec7106f2a1d9..006d7cf5993f1243c805e8191b9a406b722b96c3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,8 +38,7 @@ members = [
     "rust-libs/duniter-gva",
     "rust-libs/duniter-mempools",
     "rust-libs/duniter-server",
-    "rust-libs/tools/kv_typed",
-    "rust-libs/tools/kv_typed_code_gen"
+    "rust-libs/tools/kv_typed"
 ]
 
 [patch.crates-io]
diff --git a/rust-bins/duniter-dbex/src/main.rs b/rust-bins/duniter-dbex/src/main.rs
index f203b5ddda95b571e1ac54d7e004c7d67771e6e4..bc2f22c077f698c7b57d8d04cd0e72b7a54dcbe7 100644
--- a/rust-bins/duniter-dbex/src/main.rs
+++ b/rust-bins/duniter-dbex/src/main.rs
@@ -269,7 +269,7 @@ fn apply_subcommand<DB: DbExplorable>(
             }
         }
         SubCommand::Schema => {
-            show_db_schema(db.list_collections());
+            show_db_schema(DB::list_collections());
         }
         SubCommand::Migrate => unreachable!(),
     };
diff --git a/rust-libs/duniter-dbs-read-ops/src/lib.rs b/rust-libs/duniter-dbs-read-ops/src/lib.rs
index 489691fb8d899a6797e27bc1c6c10d58a4f27ae3..d98ccbd146d0259f72df8b9bce682cab109de91b 100644
--- a/rust-libs/duniter-dbs-read-ops/src/lib.rs
+++ b/rust-libs/duniter-dbs-read-ops/src/lib.rs
@@ -32,20 +32,8 @@ use dubp::documents::transaction::TransactionDocumentV10;
 use dubp::{common::prelude::BlockNumber, wallet::prelude::SourceAmount};
 use duniter_dbs::bc_v2::BcV2DbReadable;
 use duniter_dbs::{
-    kv_typed::prelude::*,
-    BlockMetaV2, //TxsMpV2DbWritable,
-    //WalletConditionsV2
-    //BlockNumberArrayV2, SourceAmountValV2, UtxosOfScriptV1
-    //GvaV1Db,
-    BlockNumberKeyV2,
-    GvaV1DbReadable,
-    //GvaV1DbWritable,
-    HashKeyV2,
-    //PendingTxDbV2,
-    PubKeyKeyV2,
-    TxDbV2,
-    //TxsMpV2Db,
-    TxsMpV2DbReadable,
+    kv_typed::prelude::*, BlockMetaV2, BlockNumberKeyV2, GvaV1DbReadable, HashKeyV2, PubKeyKeyV2,
+    TxDbV2, TxsMpV2DbReadable,
 };
 use resiter::map::Map;
 use std::ops::{Bound, RangeBounds};
diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs b/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs
index ed42c5014413bae6f5b1d2d291c2e835216e1f49..ccc01d65e643227ad3924bb3549e7695610b1801 100644
--- a/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/bc/identities.rs
@@ -14,12 +14,12 @@
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 use crate::*;
-use duniter_dbs::bc_v2::IdentityEvent;
+use duniter_dbs::bc_v2::IdentitiesEvent;
 use duniter_dbs::IdtyDbV2;
 
 pub(crate) fn update_identities<B: Backend>(
     block: &DubpBlockV10,
-    identities: &mut TxColRw<B::Col, IdentityEvent>,
+    identities: &mut TxColRw<B::Col, IdentitiesEvent>,
 ) -> KvResult<()> {
     for idty in block.identities() {
         let pubkey = idty.issuers()[0];
@@ -61,7 +61,7 @@ pub(crate) fn update_identities<B: Backend>(
 
 pub(crate) fn revert_identities<B: Backend>(
     block: &DubpBlockV10,
-    identities: &mut TxColRw<B::Col, IdentityEvent>,
+    identities: &mut TxColRw<B::Col, IdentitiesEvent>,
 ) -> KvResult<()> {
     for mb in block.joiners() {
         let pubkey = mb.issuers()[0];
diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs b/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs
index 5e4b35a9533f1b08bfe989fc22b8090b14515809..07ddfe1f72bbba9361359b01d79cc474d4892cc7 100644
--- a/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/bc/txs.rs
@@ -14,11 +14,11 @@
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 use crate::*;
-use duniter_dbs::{bc_v2::UdEvent, UdIdV2};
+use duniter_dbs::{bc_v2::UdsEvent, UdIdV2};
 
 pub(crate) fn apply_txs<B: Backend>(
     block_txs: &[TransactionDocumentV10],
-    uds: &mut TxColRw<B::Col, UdEvent>,
+    uds: &mut TxColRw<B::Col, UdsEvent>,
 ) -> KvResult<()> {
     for tx in block_txs {
         for input in tx.get_inputs() {
@@ -36,7 +36,7 @@ pub(crate) fn apply_txs<B: Backend>(
 
 pub(crate) fn revert_txs<B: Backend>(
     block_txs: &[TransactionDocumentV10],
-    uds: &mut TxColRw<B::Col, UdEvent>,
+    uds: &mut TxColRw<B::Col, UdsEvent>,
 ) -> KvResult<()> {
     for tx in block_txs {
         for input in tx.get_inputs() {
@@ -45,7 +45,7 @@ pub(crate) fn revert_txs<B: Backend>(
                 block_number,
             }) = input.id
             {
-                uds.upsert(UdIdV2(issuer, block_number), EmptyValue);
+                uds.upsert(UdIdV2(issuer, block_number), ());
             }
         }
     }
diff --git a/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs b/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs
index 0eac289a62c9f98a0585898b18f4bc323cc0ecc0..140f770c8ac26103bc6a7ebc907bcd6b78eaf3e4 100644
--- a/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/bc/uds.rs
@@ -15,18 +15,18 @@
 
 use crate::*;
 use duniter_dbs::{
-    bc_v2::{IdentityEvent, UdEvent, UdsRevalEvent},
+    bc_v2::{IdentitiesEvent, UdsEvent, UdsRevalEvent},
     UdIdV2,
 };
 
 // ["uds_reval", uds_reval, BlockNumberKeyV2, SourceAmountValV2,],
-// ["uds", uds, UdIdV2, EmptyValue,],
+// ["uds", uds, UdIdV2, (),],
 
 pub(crate) fn create_uds<B: Backend>(
     block_number: BlockNumber,
     dividend: SourceAmount,
-    identities: &mut TxColRw<B::Col, IdentityEvent>,
-    uds: &mut TxColRw<B::Col, UdEvent>,
+    identities: &mut TxColRw<B::Col, IdentitiesEvent>,
+    uds: &mut TxColRw<B::Col, UdsEvent>,
     uds_reval: &mut TxColRw<B::Col, UdsRevalEvent>,
 ) -> KvResult<()> {
     let previous_ud_amount = uds_reval
@@ -41,15 +41,15 @@ pub(crate) fn create_uds<B: Backend>(
             .collect::<KvResult<Vec<_>>>()
     })?;
     for member in members {
-        uds.upsert(UdIdV2(member, block_number), EmptyValue);
+        uds.upsert(UdIdV2(member, block_number), ());
     }
     Ok(())
 }
 
 pub(crate) fn revert_uds<B: Backend>(
     block_number: BlockNumber,
-    identities: &mut TxColRw<B::Col, IdentityEvent>,
-    uds: &mut TxColRw<B::Col, UdEvent>,
+    identities: &mut TxColRw<B::Col, IdentitiesEvent>,
+    uds: &mut TxColRw<B::Col, UdsEvent>,
     uds_reval: &mut TxColRw<B::Col, UdsRevalEvent>,
 ) -> KvResult<()> {
     let previous_reval_block_number = uds_reval
diff --git a/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs b/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs
index cf714942acd5200eaaf7b6dd9cdc926017c35fda..e221091b40219a0a82c7ce61e3a8747a40e41224 100644
--- a/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/gva/tx.rs
@@ -40,7 +40,7 @@ pub(crate) fn write_gva_tx<B: Backend>(
                 // Insert on col `txs_by_issuer`
                 for pubkey in tx.issuers() {
                     let mut hashs = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
-                    hashs.0.insert(tx_hash);
+                    hashs.insert(tx_hash);
                     txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs);
                 }
                 // Insert on col `txs_by_recipient`
@@ -48,7 +48,7 @@ pub(crate) fn write_gva_tx<B: Backend>(
                     let mut hashs = txs_by_recipient
                         .get(&PubKeyKeyV2(pubkey))?
                         .unwrap_or_default();
-                    hashs.0.insert(tx_hash);
+                    hashs.insert(tx_hash);
                     txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs);
                 }
 
@@ -193,14 +193,14 @@ pub(crate) fn revert_tx<B: Backend>(
 fn remove_tx<B: Backend>(
     txs_by_issuer: &mut TxColRw<B::Col, TxsByIssuerEvent>,
     txs_by_recipient: &mut TxColRw<B::Col, TxsByRecipientEvent>,
-    txs: &mut TxColRw<B::Col, TxEvent>,
+    txs: &mut TxColRw<B::Col, TxsEvent>,
     tx_hash: &Hash,
     tx_db: &TxDbV2,
 ) -> KvResult<()> {
     // Remove tx hash in col `txs_by_issuer`
     for pubkey in tx_db.tx.issuers() {
         let mut hashs_ = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
-        hashs_.0.remove(&tx_hash);
+        hashs_.remove(&tx_hash);
         txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs_)
     }
     // Remove tx hash in col `txs_by_recipient`
@@ -208,7 +208,7 @@ fn remove_tx<B: Backend>(
         let mut hashs_ = txs_by_recipient
             .get(&PubKeyKeyV2(pubkey))?
             .unwrap_or_default();
-        hashs_.0.remove(&tx_hash);
+        hashs_.remove(&tx_hash);
         txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs_)
     }
     // Remove tx itself
diff --git a/rust-libs/duniter-dbs-write-ops/src/lib.rs b/rust-libs/duniter-dbs-write-ops/src/lib.rs
index 42026eab3ae931ff56796bfe61ff1cb09253bf54..f03e95980ab9f0871f5f06517fb723aba5f582ec 100644
--- a/rust-libs/duniter-dbs-write-ops/src/lib.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/lib.rs
@@ -37,7 +37,7 @@ use dubp::documents::{
     transaction::TransactionDocumentV10,
 };
 use dubp::wallet::prelude::*;
-use duniter_dbs::gva_v1::{TxEvent, TxsByIssuerEvent, TxsByRecipientEvent};
+use duniter_dbs::gva_v1::{TxsByIssuerEvent, TxsByRecipientEvent, TxsEvent};
 use duniter_dbs::{
     kv_typed::prelude::*, BlockMetaV2, BlockNumberKeyV2, DuniterDbs, GvaV1Db, GvaV1DbReadable,
     GvaV1DbWritable, HashKeyV2, PendingTxDbV2, PubKeyKeyV2, PubKeyValV2, SourceAmountValV2, TxDbV2,
diff --git a/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs b/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs
index 3765a2d4614d41fe6303a68954b797782b0a280f..c5ee49925f4f2385bd61f11f8bf4c786e9bba423 100644
--- a/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs
+++ b/rust-libs/duniter-dbs-write-ops/src/txs_mp.rs
@@ -41,7 +41,7 @@ pub fn add_pending_tx<
     B: Backend,
     F: FnOnce(
         &TransactionDocumentV10,
-        &TxColRw<B::Col, duniter_dbs::txs_mp_v2::TxEvent>,
+        &TxColRw<B::Col, duniter_dbs::txs_mp_v2::TxsEvent>,
     ) -> KvResult<()>,
 >(
     control: F,
@@ -61,12 +61,12 @@ pub fn add_pending_tx<
                 control(&tx, &txs)?;
                 // Insert on col `txs_by_recv_time`
                 let mut hashs = txs_by_recv_time.get(&received_time)?.unwrap_or_default();
-                hashs.0.insert(tx_hash);
+                hashs.insert(tx_hash);
                 txs_by_recv_time.upsert(received_time, hashs);
                 // Insert on col `txs_by_issuer`
                 for pubkey in tx.issuers() {
                     let mut hashs = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
-                    hashs.0.insert(tx.get_hash());
+                    hashs.insert(tx.get_hash());
                     txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs);
                 }
                 // Insert on col `txs_by_recipient`
@@ -74,7 +74,7 @@ pub fn add_pending_tx<
                     let mut hashs = txs_by_recipient
                         .get(&PubKeyKeyV2(pubkey))?
                         .unwrap_or_default();
-                    hashs.0.insert(tx.get_hash());
+                    hashs.insert(tx.get_hash());
                     txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs);
                 }
                 // Insert tx itself
@@ -107,7 +107,7 @@ pub fn trim_expired_non_written_txs<B: Backend>(
     let hashs = txs_mp_db.txs_by_recv_time().iter(..limit_time, |it| {
         it.map_ok(|(k, v)| {
             times.push(k);
-            v.0
+            v
         })
         .flatten_ok()
         .collect::<KvResult<SmallVec<[Hash; 4]>>>()
@@ -133,7 +133,7 @@ fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) ->
                 // Remove tx hash in col `txs_by_issuer`
                 for pubkey in tx.0.issuers() {
                     let mut hashs_ = txs_by_issuer.get(&PubKeyKeyV2(pubkey))?.unwrap_or_default();
-                    hashs_.0.remove(&tx_hash);
+                    hashs_.remove(&tx_hash);
                     txs_by_issuer.upsert(PubKeyKeyV2(pubkey), hashs_)
                 }
                 // Remove tx hash in col `txs_by_recipient`
@@ -141,7 +141,7 @@ fn remove_one_pending_tx<B: Backend>(txs_mp_db: &TxsMpV2Db<B>, tx_hash: Hash) ->
                     let mut hashs_ = txs_by_recipient
                         .get(&PubKeyKeyV2(pubkey))?
                         .unwrap_or_default();
-                    hashs_.0.remove(&tx_hash);
+                    hashs_.remove(&tx_hash);
                     txs_by_recipient.upsert(PubKeyKeyV2(pubkey), hashs_)
                 }
                 // Remove tx itself
diff --git a/rust-libs/duniter-dbs/Cargo.toml b/rust-libs/duniter-dbs/Cargo.toml
index 599a96b5841ec7f46cada88c6c2cac5a2654ccd6..0e00c57504d03ee7629ce193b27966b8825651f1 100644
--- a/rust-libs/duniter-dbs/Cargo.toml
+++ b/rust-libs/duniter-dbs/Cargo.toml
@@ -20,6 +20,7 @@ dubp = { version = "0.30.0" }
 kv_typed = { path = "../tools/kv_typed", default-features = false }
 log = "0.4.8"
 mockall = { version = "0.8.0", optional = true }
+paste = "1.0.2"
 rand = "0.7.3"
 serde = { version = "1.0.105", features = ["derive"] }
 serde_json = "1.0.53"
diff --git a/rust-libs/duniter-dbs/src/bc_v1.rs b/rust-libs/duniter-dbs/src/bc_v1.rs
index 870bea9add36fcb3a3d7b3b3b0510968e0a33c19..bf01ede5e0c999ee807d72737654a11c8d110995 100644
--- a/rust-libs/duniter-dbs/src/bc_v1.rs
+++ b/rust-libs/duniter-dbs/src/bc_v1.rs
@@ -18,139 +18,139 @@ use crate::*;
 db_schema!(
     BcV1,
     [
-        ["level_blockchain", main_blocks, BlockNumberKeyV1, BlockDbV1,],
+        ["level_blockchain", MainBlocks, BlockNumberKeyV1, BlockDbV1],
         [
             "level_blockchain/idty",
-            mb_idty,
+            MbIdty,
             PubKeyKeyV1,
-            BlockNumberArrayV1,
+            BlockNumberArrayV1
         ],
         [
             "level_blockchain/certs",
-            mb_certs,
+            MbCerts,
             PubKeyKeyV1,
             BlockNumberArrayV1
         ],
         [
             "level_blockchain/joiners",
-            mb_joiners,
+            MbJoiners,
             PubKeyKeyV1,
             BlockNumberArrayV1
         ],
         [
             "level_blockchain/actives",
-            mb_actives,
+            MbActives,
             PubKeyKeyV1,
             BlockNumberArrayV1
         ],
         [
             "level_blockchain/leavers",
-            mb_leavers,
+            MbLeavers,
             PubKeyKeyV1,
             BlockNumberArrayV1
         ],
         [
             "level_blockchain/excluded",
-            mb_excluded,
+            MbExcluded,
             PubKeyKeyV1,
             BlockNumberArrayV1
         ],
         [
             "level_blockchain/revoked",
-            mb_revoked,
+            MbRevoked,
             PubKeyAndSigV1,
             BlockNumberArrayV1
         ],
         [
             "level_blockchain/dividends",
-            mb_dividends,
+            MbDividends,
             AllKeyV1,
             BlockNumberArrayV1
         ],
         [
             "level_blockchain/transactions",
-            mb_transactions,
+            MbTransactions,
             AllKeyV1,
             BlockNumberArrayV1
         ],
         [
             "level_blockchain/forks",
-            fork_blocks,
+            ForkBlocks,
             BlockstampKeyV1,
             BlockDbV1
         ],
-        ["level_bindex", bindex, BlockNumberKeyV1, BlockHeadDbV1],
-        ["level_iindex", iindex, PubKeyKeyV1, IIndexDbV1],
+        ["level_bindex", Bindex, BlockNumberKeyV1, BlockHeadDbV1],
+        ["level_iindex", Iindex, PubKeyKeyV1, IIndexDbV1],
         [
             "level_iindex/hash",
-            iindex_hash,
+            IindexHash,
             HashKeyV1,
             PublicKeySingletonDbV1
         ],
-        ["level_iindex/kick", iindex_kick, PubKeyKeyV1, KickDbV1],
+        ["level_iindex/kick", IindexKick, PubKeyKeyV1, KickDbV1],
         [
             "level_iindex/writtenOn",
-            iindex_written_on,
+            IindexWrittenOn,
             BlockNumberKeyV1,
             PublicKeyArrayDbV1
         ],
-        ["level_iindex/uid", uids, UidKeyV1, PublicKeySingletonDbV1],
-        ["level_mindex", mindex, PubKeyKeyV1, MIndexDbV1],
+        ["level_iindex/uid", Uids, UidKeyV1, PublicKeySingletonDbV1],
+        ["level_mindex", Mindex, PubKeyKeyV1, MIndexDbV1],
         [
             "level_mindex/expiresOn",
-            mindex_expires_on,
+            MindexExpiresOn,
             TimestampKeyV1,
             PublicKeyArrayDbV1
         ],
         [
             "level_mindex/revokesOn",
-            mindex_revokes_on,
+            MindexRevokesOn,
             TimestampKeyV1,
             PublicKeyArrayDbV1
         ],
         [
             "level_mindex/writtenOn",
-            mindex_written_on,
+            MindexWrittenOn,
             BlockNumberKeyV1,
             PublicKeyArrayDbV1
         ],
-        ["level_cindex", cindex, PubKeyKeyV1, CIndexDbV1],
+        ["level_cindex", Cindex, PubKeyKeyV1, CIndexDbV1],
         [
             "level_cindex/expiresOn",
-            cindex_expires_on,
+            CindexExpiresOn,
             BlockNumberKeyV1,
             PublicKeyArrayDbV1
         ],
         [
             "level_cindex/writtenOn",
-            cindex_written_on,
+            CindexWrittenOn,
             BlockNumberKeyV1,
             PublicKeyArrayDbV1
         ],
         ["level_wallet", Wallet, WalletConditionsV1, WalletDbV1],
-        ["level_dividend", uds, PubKeyKeyV1, UdEntryDbV1],
+        ["level_dividend", Uds, PubKeyKeyV1, UdEntryDbV1],
         [
             "level_dividend/level_dividend_trim_index",
-            uds_trim,
+            UdsTrim,
             BlockNumberKeyV1,
             PublicKeyArrayDbV1
         ],
-        ["level_sindex", sindex, SourceKeyV1, SIndexDBV1],
+        ["level_sindex", Sindex, SourceKeyV1, SIndexDBV1],
         [
             "level_sindex/written_on",
-            sindex_written_on,
+            SindexWrittenOn,
             BlockNumberKeyV1,
             SourceKeyArrayDbV1
         ],
         [
             "level_sindex/consumed_on",
-            sindex_consumed_on,
+            SindexConsumedOn,
             BlockNumberKeyV1,
             SourceKeyArrayDbV1
         ],
         [
             "level_sindex/conditions",
-            sindex_conditions,
+            SindexConditions,
             WalletConditionsV1,
             SourceKeyArrayDbV1
         ],
diff --git a/rust-libs/duniter-dbs/src/bc_v2.rs b/rust-libs/duniter-dbs/src/bc_v2.rs
index 79db9a3913429ddc6a866a32f24eecde76a26de8..4ab7b559b1cbcb7807c12ee21e7e9adf34f62ca9 100644
--- a/rust-libs/duniter-dbs/src/bc_v2.rs
+++ b/rust-libs/duniter-dbs/src/bc_v2.rs
@@ -18,9 +18,9 @@ use crate::*;
 db_schema!(
     BcV2,
     [
-        ["blocks_meta", blocks_meta, BlockNumberKeyV2, BlockMetaV2,],
-        ["uds_reval", uds_reval, BlockNumberKeyV2, SourceAmountValV2,],
-        ["identities", identities, PubKeyKeyV2, IdtyDbV2,],
-        ["uds", uds, UdIdV2, EmptyValue,],
+        ["blocks_meta", BlocksMeta, BlockNumberKeyV2, BlockMetaV2],
+        ["uds_reval", UdsReval, BlockNumberKeyV2, SourceAmountValV2],
+        ["identities", Identities, PubKeyKeyV2, IdtyDbV2],
+        ["uds", Uds, UdIdV2, ()],
     ]
 );
diff --git a/rust-libs/duniter-dbs/src/cm_v1.rs b/rust-libs/duniter-dbs/src/cm_v1.rs
index 0f3b2b61fd2f7ed8fc2c300d27f9ee07b85abad4..7e1eb4e1e60c8671402cceb93c4f0eb8b96377fd 100644
--- a/rust-libs/duniter-dbs/src/cm_v1.rs
+++ b/rust-libs/duniter-dbs/src/cm_v1.rs
@@ -18,7 +18,7 @@ use crate::*;
 db_schema!(
     CmV1,
     [
-        //["self_pubkey", self_pubkey, EmptyKey, PubKeyValV2,],
-        ["self_peer_card", self_peer_card, EmptyKey, PeerCardDbV1,],
+        //["self_pubkey", self_pubkey, (), PubKeyValV2,],
+        ["self_peer_card", SelfPeerCard, (), PeerCardDbV1],
     ]
 );
diff --git a/rust-libs/duniter-dbs/src/gva_v1.rs b/rust-libs/duniter-dbs/src/gva_v1.rs
index a3623490b836dcb3d61d1b3a4dd7ea9a5ff53637..b796d1fadd1d1ffda7a4a7888e501d0df41d9560 100644
--- a/rust-libs/duniter-dbs/src/gva_v1.rs
+++ b/rust-libs/duniter-dbs/src/gva_v1.rs
@@ -18,26 +18,21 @@ use crate::*;
 db_schema!(
     GvaV1,
     [
-        ["uids_index", uids_index, String, PubKeyValV2,],
-        ["txs", txs, HashKeyV2, TxDbV2,],
-        ["txs_by_issuer", txs_by_issuer, PubKeyKeyV2, HashBTSetV2,],
-        [
-            "txs_by_recipient",
-            txs_by_recipient,
-            PubKeyKeyV2,
-            HashBTSetV2,
-        ],
+        ["uids_index", UidsIndex, String, PubKeyValV2],
+        ["txs", Txs, HashKeyV2, TxDbV2],
+        ["txs_by_issuer", TxsByIssuer, PubKeyKeyV2, BTreeSet<Hash>],
+        ["txs_by_recipient", TxsByRecipient, PubKeyKeyV2, BTreeSet<Hash>],
         [
             "scripts_by_pubkey",
-            scripts_by_pubkey,
+            ScriptsByPubkey,
             PubKeyKeyV2,
-            WalletScriptArrayV2,
+            WalletScriptArrayV2
         ],
         [
             "utxos_by_script",
-            utxos_by_script,
+            UtxosByScript,
             WalletConditionsV2,
-            UtxosOfScriptV1,
+            UtxosOfScriptV1
         ],
     ]
 );
diff --git a/rust-libs/duniter-dbs/src/lib.rs b/rust-libs/duniter-dbs/src/lib.rs
index ebbe6c13c85c65685a13ce8fb5e65815a8e336fd..b43c04d5669aff02e85e73ffd74dca59048a29f0 100644
--- a/rust-libs/duniter-dbs/src/lib.rs
+++ b/rust-libs/duniter-dbs/src/lib.rs
@@ -58,7 +58,7 @@ pub use crate::errors::Result;
 pub use crate::open_dbs::open_dbs;
 
 // Export profession types
-pub use bc_v1::{BcV1Db, BcV1DbReadable, BcV1DbRo, BcV1DbWritable, MainBlockEvent, UidEvent};
+pub use bc_v1::{BcV1Db, BcV1DbReadable, BcV1DbRo, BcV1DbWritable, MainBlocksEvent, UidsEvent};
 pub use gva_v1::{GvaV1Db, GvaV1DbReadable, GvaV1DbRo, GvaV1DbWritable};
 pub use keys::all::AllKeyV1;
 pub use keys::block_number::{BlockNumberKeyV1, BlockNumberKeyV2};
@@ -75,9 +75,8 @@ pub use txs_mp_v2::{TxsMpV2Db, TxsMpV2DbReadable, TxsMpV2DbRo, TxsMpV2DbWritable
 pub use values::block_db::{BlockDbEnum, BlockDbV1, TransactionInBlockDbV1};
 pub use values::block_head_db::BlockHeadDbV1;
 pub use values::block_meta::BlockMetaV2;
-pub use values::block_number_array_db::{BlockNumberArrayV1, BlockNumberArrayV2};
+pub use values::block_number_array_db::BlockNumberArrayV1;
 pub use values::cindex_db::CIndexDbV1;
-pub use values::hash_array_db::HashBTSetV2;
 pub use values::idty_db::IdtyDbV2;
 pub use values::iindex_db::IIndexDbV1;
 pub use values::kick_db::KickDbV1;
@@ -103,6 +102,7 @@ pub(crate) use dubp::common::crypto::keys::ed25519::{PublicKey, Signature};
 pub(crate) use dubp::common::crypto::keys::{PublicKey as _, Signature as _};
 pub(crate) use dubp::common::prelude::*;
 pub(crate) use dubp::documents::dubp_wallet::prelude::*;
+pub(crate) use kv_typed::db_schema;
 pub(crate) use kv_typed::prelude::*;
 pub(crate) use serde::{Deserialize, Serialize};
 pub(crate) use smallvec::SmallVec;
diff --git a/rust-libs/duniter-dbs/src/txs_mp_v2.rs b/rust-libs/duniter-dbs/src/txs_mp_v2.rs
index ceec9ec31a421f63450998907c61205842e1b681..95b6cbaebd94cbfe1e9b463c3c64616282514780 100644
--- a/rust-libs/duniter-dbs/src/txs_mp_v2.rs
+++ b/rust-libs/duniter-dbs/src/txs_mp_v2.rs
@@ -18,14 +18,9 @@ use crate::*;
 db_schema!(
     TxsMpV2,
     [
-        ["txs", txs, HashKeyV2, PendingTxDbV2,],
-        ["txs_by_issuer", txs_by_issuer, PubKeyKeyV2, HashBTSetV2,],
-        [
-            "txs_by_recipient",
-            txs_by_recipient,
-            PubKeyKeyV2,
-            HashBTSetV2,
-        ],
-        ["txs_by_received_time", txs_by_recv_time, i64, HashBTSetV2,],
+        ["txs", Txs, HashKeyV2, PendingTxDbV2],
+        ["txs_by_issuer", TxsByIssuer, PubKeyKeyV2, BTreeSet<Hash>],
+        ["txs_by_recipient", TxsByRecipient, PubKeyKeyV2, BTreeSet<Hash>],
+        ["txs_by_received_time", TxsByRecvTime, i64, BTreeSet<Hash>],
     ]
 );
diff --git a/rust-libs/duniter-dbs/src/values.rs b/rust-libs/duniter-dbs/src/values.rs
index 3c8cb10b24e187a98bcd6ac1a345a0423012537a..8f9369419b83d539d0117b4c00a78566046f04f6 100644
--- a/rust-libs/duniter-dbs/src/values.rs
+++ b/rust-libs/duniter-dbs/src/values.rs
@@ -18,7 +18,6 @@ pub mod block_head_db;
 pub mod block_meta;
 pub mod block_number_array_db;
 pub mod cindex_db;
-pub mod hash_array_db;
 pub mod idty_db;
 pub mod iindex_db;
 pub mod kick_db;
diff --git a/rust-libs/duniter-dbs/src/values/block_number_array_db.rs b/rust-libs/duniter-dbs/src/values/block_number_array_db.rs
index 20286a3e0a0be4d46cb86322db8959fdf16e815f..9fbf01ae23c57cd866ce3429ec07fcead4701f9c 100644
--- a/rust-libs/duniter-dbs/src/values/block_number_array_db.rs
+++ b/rust-libs/duniter-dbs/src/values/block_number_array_db.rs
@@ -52,14 +52,3 @@ impl ExplorableValue for BlockNumberArrayV1 {
         serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e)))
     }
 }
-
-#[derive(Debug, PartialEq)]
-#[repr(transparent)]
-pub struct BlockNumberArrayV2(pub SmallVec<[BlockNumber; 16]>);
-kv_typed::impl_value_for_smallvec_zc!(BlockNumberArrayV2, BlockNumber, 16);
-
-impl ToDumpString for BlockNumberArrayV2 {
-    fn to_dump_string(&self) -> String {
-        todo!()
-    }
-}
diff --git a/rust-libs/duniter-dbs/src/values/hash_array_db.rs b/rust-libs/duniter-dbs/src/values/hash_array_db.rs
deleted file mode 100644
index 8ff946a9ced8042743483b45ed710f18202a909a..0000000000000000000000000000000000000000
--- a/rust-libs/duniter-dbs/src/values/hash_array_db.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-//  Copyright (C) 2020 Éloïs SANCHEZ.
-//
-// This program 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, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
-
-use crate::*;
-
-#[derive(Debug, Default, PartialEq)]
-pub struct HashBTSetV2(pub BTreeSet<Hash>);
-kv_typed::impl_value_for_btreeset_zc!(HashBTSetV2, Hash);
-
-impl ToDumpString for HashBTSetV2 {
-    fn to_dump_string(&self) -> String {
-        todo!()
-    }
-}
diff --git a/rust-libs/duniter-dbs/tests/test_read_write.rs b/rust-libs/duniter-dbs/tests/test_read_write.rs
index ee06bb03097f2a23a8f10dffbadec59b7d51ddaa..60db378d896b4b67b811124e67dadd49d6585cfd 100644
--- a/rust-libs/duniter-dbs/tests/test_read_write.rs
+++ b/rust-libs/duniter-dbs/tests/test_read_write.rs
@@ -18,7 +18,7 @@ use dubp::common::crypto::keys::PublicKey as _;
 use dubp::common::prelude::*;
 use duniter_dbs::kv_typed::prelude::*;
 use duniter_dbs::{
-    BcV1Db, BcV1DbReadable, BcV1DbWritable, BlockDbV1, BlockNumberKeyV1, MainBlockEvent,
+    BcV1Db, BcV1DbReadable, BcV1DbWritable, BlockDbV1, BlockNumberKeyV1, MainBlocksEvent,
     PublicKeySingletonDbV1, Result, UidKeyV1,
 };
 use kv_typed::channel::TryRecvError;
@@ -152,7 +152,7 @@ fn write_read_delete_b0_test<B: Backend>(db: &BcV1Db<B>) -> Result<()> {
         let event = &events[0];
         assert_eq!(
             event,
-            &MainBlockEvent::Upsert {
+            &MainBlocksEvent::Upsert {
                 key: BlockNumberKeyV1(BlockNumber(0)),
                 value: b0,
             },
@@ -184,7 +184,7 @@ fn write_read_delete_b0_test<B: Backend>(db: &BcV1Db<B>) -> Result<()> {
         let event = &events[0];
         assert_eq!(
             event,
-            &MainBlockEvent::Remove {
+            &MainBlocksEvent::Remove {
                 key: BlockNumberKeyV1(BlockNumber(0)),
             },
         );
@@ -388,11 +388,11 @@ fn batch_test<B: Backend>(db: &BcV1Db<B>) -> Result<()> {
         assert!(assert_eq_pairs(
             [&events[0], &events[1]],
             [
-                &MainBlockEvent::Upsert {
+                &MainBlocksEvent::Upsert {
                     key: BlockNumberKeyV1(BlockNumber(0)),
                     value: b0,
                 },
-                &MainBlockEvent::Upsert {
+                &MainBlocksEvent::Upsert {
                     key: BlockNumberKeyV1(BlockNumber(1)),
                     value: b1,
                 }
diff --git a/rust-libs/duniter-gva/src/queries.rs b/rust-libs/duniter-gva/src/queries.rs
index ea08f16cf0718da4cf608e49d62d9d49b882e598..744263987f573a49526b4fd749c5c1ee78b5ba8e 100644
--- a/rust-libs/duniter-gva/src/queries.rs
+++ b/rust-libs/duniter-gva/src/queries.rs
@@ -49,7 +49,7 @@ impl Node {
 
         Ok(data
             .dbs_pool
-            .execute(move |dbs| dbs.cm_db.self_peer_card().get(&EmptyKey))
+            .execute(move |dbs| dbs.cm_db.self_peer_card().get(&()))
             .await??
             .map(Into::into))
     }
diff --git a/rust-libs/duniter-gva/src/subscriptions.rs b/rust-libs/duniter-gva/src/subscriptions.rs
index 3504e5fe0e580827c1bcd99f2c8d4aa0b9ced0ee..5be753736d7d068cfd68a7e57c28150963fbd076 100644
--- a/rust-libs/duniter-gva/src/subscriptions.rs
+++ b/rust-libs/duniter-gva/src/subscriptions.rs
@@ -37,7 +37,7 @@ impl SubscriptionRoot {
         r.into_stream().filter_map(|events| {
             let mut txs = Vec::new();
             for event in events.deref() {
-                if let duniter_dbs::txs_mp_v2::TxEvent::Upsert {
+                if let duniter_dbs::txs_mp_v2::TxsEvent::Upsert {
                     value: ref pending_tx,
                     ..
                 } = event
diff --git a/rust-libs/duniter-server/src/lib.rs b/rust-libs/duniter-server/src/lib.rs
index e99820f34fdbd0a49e55de3de1c58148a11883ba..77bdad24b892da97e3d7a7eeed5d0544881d5237 100644
--- a/rust-libs/duniter-server/src/lib.rs
+++ b/rust-libs/duniter-server/src/lib.rs
@@ -61,7 +61,7 @@ pub struct DuniterServer {
     conf: DuniterServerConf,
     current: Option<BlockMetaV2>,
     dbs_pool: fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>,
-    pending_txs_subscriber: flume::Receiver<Arc<Events<duniter_dbs::txs_mp_v2::TxEvent>>>,
+    pending_txs_subscriber: flume::Receiver<Arc<Events<duniter_dbs::txs_mp_v2::TxsEvent>>>,
     txs_mempool: TxsMempool,
 }
 
@@ -165,10 +165,10 @@ impl DuniterServer {
             use std::ops::Deref as _;
             for event in events.deref() {
                 match event {
-                    duniter_dbs::txs_mp_v2::TxEvent::Upsert { key, value } => {
+                    duniter_dbs::txs_mp_v2::TxsEvent::Upsert { key, value } => {
                         new_pending_txs.insert(key.0, value.0.clone());
                     }
-                    duniter_dbs::txs_mp_v2::TxEvent::Remove { key } => {
+                    duniter_dbs::txs_mp_v2::TxsEvent::Remove { key } => {
                         new_pending_txs.remove(&key.0);
                     }
                     _ => (),
@@ -307,7 +307,7 @@ impl DuniterServer {
                 dbs.cm_db
                     .self_peer_card_write()
                     .upsert(
-                        EmptyKey,
+                        (),
                         duniter_dbs::PeerCardDbV1 {
                             version: new_peer_card.version,
                             currency: new_peer_card.currency,
diff --git a/rust-libs/tools/kv_typed/Cargo.toml b/rust-libs/tools/kv_typed/Cargo.toml
index b7973453a63aac65770c66e0cccdc1fdd7144fe7..d1d549612dab8f0bcb8290d6397a219e5cff5974 100644
--- a/rust-libs/tools/kv_typed/Cargo.toml
+++ b/rust-libs/tools/kv_typed/Cargo.toml
@@ -14,7 +14,6 @@ path = "src/lib.rs"
 [dependencies]
 cfg-if = "0.1.10"
 flume = "0.9.1"
-kv_typed_code_gen = { path = "../kv_typed_code_gen" }
 leveldb_minimal = { version = "0.1.0", optional = true }
 lmdb-zero = { version = "0.4.4", optional = true }
 mockall = { version = "0.8.0", optional = true }
@@ -45,7 +44,7 @@ unwrap = "1.2.1"
 criterion = { version = "0.3.1" }
 
 [features]
-default = ["sled_backend"]
+#default = ["sled_backend"]
 
 async = []
 explorer = ["rayon", "regex", "serde_json"]
@@ -55,5 +54,5 @@ sled_backend = ["sled"]
 
 #mock = ["mockall"]
 
-#default = ["explorer"]
+default = ["sled_backend", "explorer"]
 #default = ["mock"]
diff --git a/rust-libs/tools/kv_typed/benches/compare_backends.rs b/rust-libs/tools/kv_typed/benches/compare_backends.rs
index cce394b8f11cfafd34083256c5374cf0522b121c..d21049b49bfd48fc0a26f5e2e5b5e3c910ec6d5c 100644
--- a/rust-libs/tools/kv_typed/benches/compare_backends.rs
+++ b/rust-libs/tools/kv_typed/benches/compare_backends.rs
@@ -2,7 +2,7 @@ use criterion::{criterion_group, criterion_main, Criterion, /*, AxisScale, PlotC
 use kv_typed::prelude::*;
 use std::{fmt::Debug, path::PathBuf};
 
-db_schema!(Test, [["c1", col_1, u32, String],]);
+kv_typed::db_schema!(Test, [["c1", Col1, u32, String],]);
 //const LEVELDB_DIR_PATH: &str = "/dev/shm/kv_typed/benches/compare_backends/leveldb";
 //const LMDB_DIR_PATH: &str = "/dev/shm/kv_typed/benches/compare_backends/lmdb";
 const LEVELDB_DIR_PATH: &str = "/home/elois/tmp/kv_typed/benches/compare_backends/leveldb";
@@ -13,28 +13,28 @@ static LARGE_VAL: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZab
 
 fn read_n_entries<B: Backend>(db: &TestDb<B>, n: u32, val: String) {
     for i in 0..n {
-        assert_eq!(db.col_1().get(&i).expect("db err"), Some(val.clone()));
+        assert_eq!(db.col1().get(&i).expect("db err"), Some(val.clone()));
     }
-    /*db.col_1().iter(.., |iter| {
+    /*db.col1().iter(.., |iter| {
         let mut iter = iter.values();
         for _ in 0..n {
             assert_eq!(iter.next_res().expect(""), Some(val.clone()));
-            //assert_eq!(db.col_1().get(&i).expect(""), Some(val.clone()));
+            //assert_eq!(db.col1().get(&i).expect(""), Some(val.clone()));
         }
         assert_eq!(iter.next_res().expect(""), None);
     });*/
 }
 fn remove_and_write_n_entries<B: Backend>(db: &TestDb<B>, n: u32, val: String) {
     for i in 0..n {
-        db.col_1_write().remove(i).expect("fail to write");
-        db.col_1_write()
+        db.col1_write().remove(i).expect("fail to write");
+        db.col1_write()
             .upsert(i, val.clone())
             .expect("fail to write");
     }
 }
 fn write_n_entries<B: Backend>(db: &TestDb<B>, n: u32, val: String) {
     for i in 0..n {
-        db.col_1_write()
+        db.col1_write()
             .upsert(i, val.clone())
             .expect("fail to write");
     }
diff --git a/rust-libs/tools/kv_typed/src/as_bytes.rs b/rust-libs/tools/kv_typed/src/as_bytes.rs
index bfa37e87fb1520bdee0fd348876a8fcdaad7fa15..0ba24d8e07edab661687ce0010693134924f3394 100644
--- a/rust-libs/tools/kv_typed/src/as_bytes.rs
+++ b/rust-libs/tools/kv_typed/src/as_bytes.rs
@@ -28,16 +28,7 @@ impl KeyAsBytes for () {
     }
     fn fill_bytes(&self, _: &mut [u8]) {}
 }
-impl KeyAsBytes for EmptyKey {
-    fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T {
-        f(&[])
-    }
-    fn fill_bytes_size(&self) -> Option<usize> {
-        Some(0)
-    }
-    fn fill_bytes(&self, _: &mut [u8]) {}
-}
-impl ValueAsBytes for EmptyValue {
+impl ValueAsBytes for () {
     fn as_bytes<T, F: FnMut(&[u8]) -> Result<T, KvError>>(&self, mut f: F) -> Result<T, KvError> {
         f(&[])
     }
diff --git a/rust-libs/tools/kv_typed/src/db_schema.rs b/rust-libs/tools/kv_typed/src/db_schema.rs
new file mode 100644
index 0000000000000000000000000000000000000000..925c04d7c542a7468d931187815b142ca2291e56
--- /dev/null
+++ b/rust-libs/tools/kv_typed/src/db_schema.rs
@@ -0,0 +1,194 @@
+//  Copyright (C) 2020 Éloïs SANCHEZ.
+//
+// This program 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, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+
+#[macro_export]
+macro_rules! db_schema {
+    ($db_name:ident, [ $([$col_path:literal, $col_name:ident, $K:ty, $V:ty]),*, ]) => {
+        paste::paste! {
+            $(
+                // Define each collection event type
+                #[derive(Debug, PartialEq)]
+                pub enum [<$col_name Event>] {
+                    Upsert { key: $K, value: $V },
+                    Remove { key: $K },
+                    RemoveAll,
+                }
+                impl kv_typed::prelude::EventTrait for [<$col_name Event>] {
+                    type K = $K;
+                    type V = $V;
+
+                    fn clear() -> Self { Self::RemoveAll }
+                    fn upsert(k: Self::K, v: Self::V) -> Self { Self::Upsert { key: k, value: v, } }
+                    fn remove(k: Self::K) -> Self { Self::Remove { key: k } }
+                }
+            )*
+            // Inner module used to hide internals types that must not be exposed on public api
+            pub use __inner::{[<$db_name Db>], [<$db_name DbRo>], [<$db_name DbWritable>], [<$db_name DbReadable>]};
+            mod __inner {
+                use super::*;
+                use kv_typed::prelude::*;
+                // DbCollections
+                #[derive(Clone, Debug)]
+                pub struct [<$db_name ColsRo>]<BC: BackendCol> {
+                    $([<$col_name:snake>]: ColRo<BC, [<$col_name Event>]>,)*
+                }
+                #[derive(Clone, Debug)]
+                pub struct [<$db_name ColsRw>]<BC: BackendCol> {
+                    $([<$col_name:snake>]: ColRw<BC, [<$col_name Event>]>,)*
+                }
+                impl<BC: BackendCol> [<$db_name ColsRw>]<BC> {
+                    fn to_ro(&self) -> [<$db_name ColsRo>]<BC> {
+                        [<$db_name ColsRo>] {
+                            $([<$col_name:snake>]: self.[<$col_name:snake>].to_ro().clone(),)*
+                        }
+                    }
+                }
+                // Db
+                #[derive(Debug)]
+                pub struct [<$db_name Db>]<B: Backend> {
+                    collections: [<$db_name ColsRw>]<B::Col>,
+                }
+                impl<B: Backend> [<$db_name Db>]<B> {
+                    pub const NAME: &'static str = stringify!([<$db_name:snake>]);
+                }
+                impl<B: Backend> Clone for [<$db_name Db>]<B> {
+                    fn clone(&self) -> Self {
+                        [<$db_name Db>] {
+                            collections: self.collections.clone(),
+                        }
+                    }
+                }
+                #[cfg(feature = "explorer")]
+                impl<B: Backend> kv_typed::explorer::DbExplorable for [<$db_name Db>]<B> {
+                    fn explore<'a>(
+                        &self,
+                        collection_name: &str,
+                        action: kv_typed::explorer::ExplorerAction<'a>,
+                        stringify_json_value: fn(serde_json::Value) -> serde_json::Value,
+                    ) -> KvResult<std::result::Result<kv_typed::explorer::ExplorerActionResponse, StringErr>> {
+                        $( if stringify!([<$col_name:snake>]) == collection_name {
+                            return action.exec(&self.collections.[<$col_name:snake>], stringify_json_value);
+                        } )*
+                        Ok(Err(StringErr(format!("collection '{}' not exist in database '{}'.", collection_name, stringify!([<$db_name Db>])))))
+                    }
+                    fn list_collections() -> Vec<(&'static str, &'static str, &'static str)> {
+                        vec![
+                            $((stringify!([<$col_name:snake>]), stringify!($K), stringify!($V)),)*
+                        ]
+                    }
+                }
+                // Batch
+                pub struct [<$db_name DbBatch>]<B: Backend> {
+                    $([<$col_name:snake>]: Batch<B::Col, ColRw<B::Col, [<$col_name Event>]>>,)*
+                }
+                impl<B: Backend> Default for [<$db_name DbBatch>]<B> {
+                    fn default() -> Self {
+                        [<$db_name DbBatch>] {
+                            $([<$col_name:snake>]: Batch::default(),)*
+                        }
+                    }
+                }
+                impl<B: Backend> [<$db_name DbBatch>]<B> {
+                    $(pub fn [<$col_name:snake>](&mut self) -> &mut Batch<B::Col, ColRw<B::Col, [<$col_name Event>]>> { &mut self.[<$col_name:snake>] })*
+                }
+                // DbRo
+                #[derive(Debug)]
+                pub struct [<$db_name DbRo>]<B: Backend> {
+                    collections: [<$db_name ColsRo>]<B::Col>,
+                }
+                impl<B: Backend> [<$db_name DbRo>]<B> {
+                    pub const NAME: &'static str = stringify!([<$db_name:snake>]);
+                }
+                impl<B: Backend> Clone for [<$db_name DbRo>]<B> {
+                    fn clone(&self) -> Self {
+                        [<$db_name DbRo>] {
+                            collections: self.collections.clone(),
+                        }
+                    }
+                }
+                // Read operations
+                pub trait [<$db_name DbReadable>]: Sized {
+                    type Backend: Backend;
+
+                    $(fn [<$col_name:snake>](&self) -> &ColRo<<Self::Backend as Backend>::Col, [<$col_name Event>]>;)*
+                }
+                impl<B: Backend> [<$db_name DbReadable>] for [<$db_name Db>]<B> {
+                    type Backend = B;
+
+                    $(fn [<$col_name:snake>](&self) -> &ColRo<B::Col, [<$col_name Event>]> { &self.collections.[<$col_name:snake>].to_ro() })*
+                }
+                impl<B: Backend> [<$db_name DbReadable>] for [<$db_name DbRo>]<B>{
+                    type Backend = B;
+
+                    $(fn [<$col_name:snake>](&self) -> &ColRo<B::Col, [<$col_name Event>]> { &self.collections.[<$col_name:snake>] })*
+                }
+                // Write operations
+                pub trait [<$db_name DbWritable>]: [<$db_name DbReadable>] {
+                    type Backend: Backend;
+                    type Batch;
+                    $(type [<$col_name ColRw>]: DbCollectionRw;)*
+                    type DbRo: Sized;
+
+                    fn get_ro_handler(&self) -> Self::DbRo;
+                    fn open(
+                        backend_conf: <<Self as [<$db_name DbWritable>]>::Backend as kv_typed::backend::Backend>::Conf,
+                    ) -> KvResult <Self>;
+                    fn new_batch(&self) -> Self::Batch;
+                    fn save(&self) -> KvResult<()>;
+                    fn write_batch(&self, batch: Self::Batch) -> KvResult<()>;
+                    $(fn [<$col_name:snake _write>](&self) -> &Self::[<$col_name ColRw>];)*
+                }
+                impl<B: Backend> [<$db_name DbWritable>] for [<$db_name Db>]<B> {
+                    type Backend = B;
+                    type Batch = [<$db_name DbBatch>]<B>;
+                    $(type [<$col_name ColRw>] = ColRw<B::Col, [<$col_name Event>]>;)*
+                    type DbRo = [<$db_name DbRo>]<B>;
+
+                    #[inline(always)]
+                    fn get_ro_handler(&self) -> Self::DbRo {
+                        [<$db_name DbRo>] {
+                            collections: self.collections.to_ro(),
+                        }
+                    }
+                    #[inline(always)]
+                    fn new_batch(&self) -> Self::Batch {
+                        <[<$db_name DbBatch>]::<B>>::default()
+                    }
+                    fn write_batch(&self, batch: Self::Batch) -> KvResult<()> {
+                        $(self.collections.[<$col_name:snake>].write_batch(batch.[<$col_name:snake>])?;)*
+                        Ok(())
+                    }
+                    fn open(
+                        backend_conf: <<Self as [<$db_name DbWritable>]>::Backend as kv_typed::backend::Backend>::Conf,
+                    ) -> KvResult <Self> {
+                        let mut db = B::open(&backend_conf)?;
+                        Ok([<$db_name Db>] {
+                            collections: [<$db_name ColsRw>] {
+                                $([<$col_name:snake>]: <ColRw<B::Col, [<$col_name Event>]>>::new(
+                                    db.open_col(&backend_conf, $col_path)?
+                                ),)*
+                            },
+                        })
+                    }
+                    fn save(&self) -> KvResult<()> {
+                        $(self.collections.[<$col_name:snake>].save()?;)*
+                        Ok(())
+                    }
+                    $(fn [<$col_name:snake _write>](&self) -> &ColRw<B::Col, [<$col_name Event>]> { &self.collections.[<$col_name:snake>] })*
+                }
+            }
+        }
+    };
+}
diff --git a/rust-libs/tools/kv_typed/src/explorer.rs b/rust-libs/tools/kv_typed/src/explorer.rs
index d07ff02b693c594fa2b2205f3a15a4260fcd76de..e3a4ab089583c1510463b8641c597c52c2294e5c 100644
--- a/rust-libs/tools/kv_typed/src/explorer.rs
+++ b/rust-libs/tools/kv_typed/src/explorer.rs
@@ -9,7 +9,7 @@ pub trait DbExplorable {
         action: ExplorerAction<'a>,
         stringify_json_value: fn(serde_json::Value) -> serde_json::Value,
     ) -> KvResult<Result<ExplorerActionResponse, StringErr>>;
-    fn list_collections(&self) -> Vec<(&'static str, &'static str, &'static str)>;
+    fn list_collections() -> Vec<(&'static str, &'static str, &'static str)>;
 }
 
 pub trait ExplorableKey: Sized {
@@ -27,16 +27,6 @@ impl ExplorableKey for () {
     }
 }
 
-impl ExplorableKey for EmptyKey {
-    fn from_explorer_str(_: &str) -> Result<Self, StringErr> {
-        Ok(EmptyKey)
-    }
-
-    fn to_explorer_string(&self) -> KvResult<String> {
-        Ok(String::with_capacity(0))
-    }
-}
-
 impl ExplorableKey for String {
     fn from_explorer_str(source: &str) -> Result<Self, StringErr> {
         Ok(source.to_owned())
@@ -78,16 +68,6 @@ impl ExplorableValue for () {
     }
 }
 
-impl ExplorableValue for EmptyValue {
-    fn from_explorer_str(_: &str) -> Result<Self, StringErr> {
-        Ok(EmptyValue)
-    }
-
-    fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-        Ok(serde_json::Value::String(String::with_capacity(0)))
-    }
-}
-
 impl ExplorableValue for String {
     fn from_explorer_str(source: &str) -> Result<Self, StringErr> {
         Ok(source.to_owned())
@@ -117,6 +97,107 @@ impl_explorable_value_for_numbers!(
     usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64
 );
 
+impl<T, E> ExplorableValue for Vec<T>
+where
+    T: Display + FromStr<Err = E>,
+    E: Display,
+{
+    fn from_explorer_str(source: &str) -> Result<Vec<T>, StringErr> {
+        if let serde_json::Value::Array(json_array) =
+            serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
+        {
+            let mut vec = Vec::with_capacity(json_array.len());
+            for value in json_array {
+                if let serde_json::Value::String(string) = value {
+                    vec.push(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?);
+                } else {
+                    return Err(StringErr(format!("Expected array of {}.", stringify!(T))));
+                }
+            }
+            Ok(vec)
+        } else {
+            Err(StringErr(format!("Expected array of {}.", stringify!(T))))
+        }
+    }
+
+    fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
+        Ok(serde_json::Value::Array(
+            self.iter()
+                .map(|elem| serde_json::Value::String(format!("{}", elem)))
+                .collect(),
+        ))
+    }
+}
+
+macro_rules! impl_explorable_value_for_smallvec {
+    ($($N:literal),*) => {$(
+        impl<T, E> ExplorableValue for SmallVec<[T; $N]>
+        where
+            T: Display + FromStr<Err = E>,
+            E: Display,
+        {
+            fn from_explorer_str(source: &str) -> Result<SmallVec<[T; $N]>, StringErr> {
+                if let serde_json::Value::Array(json_array) =
+                    serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
+                {
+                    let mut svec = SmallVec::with_capacity(json_array.len());
+                    for value in json_array {
+                        if let serde_json::Value::String(string) = value {
+                            svec.push(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?);
+                        } else {
+                            return Err(StringErr(format!("Expected array of {}.", stringify!(T))));
+                        }
+                    }
+                    Ok(svec)
+                } else {
+                    Err(StringErr(format!("Expected array of {}.", stringify!(T))))
+                }
+            }
+
+            fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
+                Ok(serde_json::Value::Array(
+                    self.iter()
+                        .map(|elem| serde_json::Value::String(format!("{}", elem)))
+                        .collect(),
+                ))
+            }
+        }
+    )*};
+}
+impl_explorable_value_for_smallvec!(2, 4, 8, 16, 32, 64);
+
+impl<T, E> ExplorableValue for BTreeSet<T>
+where
+    T: Display + FromStr<Err = E> + Ord,
+    E: Display,
+{
+    fn from_explorer_str(source: &str) -> Result<BTreeSet<T>, StringErr> {
+        if let serde_json::Value::Array(json_array) =
+            serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
+        {
+            let mut bt_set = BTreeSet::new();
+            for value in json_array {
+                if let serde_json::Value::String(string) = value {
+                    bt_set.insert(<T>::from_str(&string).map_err(|e| StringErr(format!("{}", e)))?);
+                } else {
+                    return Err(StringErr(format!("Expected array of {}.", stringify!(T))));
+                }
+            }
+            Ok(bt_set)
+        } else {
+            Err(StringErr(format!("Expected array of {}.", stringify!(T))))
+        }
+    }
+
+    fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
+        Ok(serde_json::Value::Array(
+            self.iter()
+                .map(|elem| serde_json::Value::String(format!("{}", elem)))
+                .collect(),
+        ))
+    }
+}
+
 #[derive(Debug)]
 pub enum ExplorerAction<'a> {
     Count,
diff --git a/rust-libs/tools/kv_typed/src/from_bytes.rs b/rust-libs/tools/kv_typed/src/from_bytes.rs
index ab05f4a5bcebfbb3cae250e3a7cdeab15c4dc097..a8837d27d53aa461f6516a8f3fbd3e0d2dbaa572 100644
--- a/rust-libs/tools/kv_typed/src/from_bytes.rs
+++ b/rust-libs/tools/kv_typed/src/from_bytes.rs
@@ -14,20 +14,6 @@ impl FromBytes for () {
         Ok(())
     }
 }
-impl FromBytes for EmptyKey {
-    type Err = std::convert::Infallible;
-
-    fn from_bytes(_: &[u8]) -> Result<Self, Self::Err> {
-        Ok(EmptyKey)
-    }
-}
-impl FromBytes for EmptyValue {
-    type Err = std::convert::Infallible;
-
-    fn from_bytes(_: &[u8]) -> Result<Self, Self::Err> {
-        Ok(EmptyValue)
-    }
-}
 
 macro_rules! impl_from_bytes_for_numbers {
     ($($T:ty),*) => {$(
diff --git a/rust-libs/tools/kv_typed/src/key.rs b/rust-libs/tools/kv_typed/src/key.rs
index 91e764c78f4cdbb53ad584ffe32fa320ea0e5202..7d820dfa7b4453454f5a0642e0e76bd8dd96d929 100644
--- a/rust-libs/tools/kv_typed/src/key.rs
+++ b/rust-libs/tools/kv_typed/src/key.rs
@@ -60,6 +60,3 @@ impl<T> Key for T where
         + Sized
 {
 }
-
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-pub struct EmptyKey;
diff --git a/rust-libs/tools/kv_typed/src/lib.rs b/rust-libs/tools/kv_typed/src/lib.rs
index 8d7ff66cc408fa2619d6af54a0a428e86b961865..87eab552096d013a75e5d60b325c5249441ba3a8 100644
--- a/rust-libs/tools/kv_typed/src/lib.rs
+++ b/rust-libs/tools/kv_typed/src/lib.rs
@@ -33,6 +33,7 @@ mod bytes;
 mod collection_inner;
 mod collection_ro;
 mod collection_rw;
+mod db_schema;
 mod error;
 mod event;
 #[cfg(feature = "explorer")]
@@ -81,13 +82,12 @@ pub mod prelude {
     pub use crate::iter::{
         keys::KvIterKeys, values::KvIterValues, EntryIter, KvIter, ResultIter, ReversableIterator,
     };
-    pub use crate::key::{EmptyKey, Key};
+    pub use crate::key::Key;
     pub use crate::subscription::{NewSubscribers, Subscriber, Subscribers};
     pub use crate::transactional_read::{TransactionalRead, TxColRo};
     pub use crate::transactional_write::{DbTxCollectionRw, TransactionalWrite, TxColRw};
     pub use crate::utils::arc::Arc;
-    pub use crate::value::{EmptyValue, Value, ValueSliceZc, ValueZc};
-    pub use kv_typed_code_gen::db_schema;
+    pub use crate::value::{Value, ValueSliceZc, ValueZc};
 }
 
 // Internal crate imports
@@ -109,273 +109,10 @@ pub(crate) use std::{
     collections::{BTreeSet, HashSet},
     convert::TryInto,
     error::Error,
-    fmt::Debug,
+    fmt::{Debug, Display},
     iter::FromIterator,
     marker::PhantomData,
     ops::{Bound, RangeBounds},
+    str::FromStr,
 };
 pub(crate) use thiserror::Error;
-
-#[macro_export]
-/// $Elem must implement Display + FromStr + zerocopy::AsBytes + zerocopy::FromBytes
-macro_rules! impl_value_for_vec_zc {
-    ($T:ty, $Elem:ty) => {
-        impl ValueAsBytes for $T {
-            fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> {
-                self.0.as_bytes(f)
-            }
-        }
-        impl FromBytes for $T {
-            type Err = StringErr;
-
-            fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-                Ok(Self(Vec::<$Elem>::from_bytes(bytes)?))
-            }
-        }
-        impl ValueSliceZc for $T {
-            type Elem = $Elem;
-
-            fn prefix_len() -> usize {
-                0
-            }
-        }
-        #[cfg(feature = "explorer")]
-        use std::str::FromStr as _;
-        #[cfg(feature = "explorer")]
-        impl ExplorableValue for $T {
-            fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-                if let serde_json::Value::Array(json_array) =
-                    serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
-                {
-                    let mut vec = Vec::with_capacity(json_array.len());
-                    for value in json_array {
-                        if let serde_json::Value::String(string) = value {
-                            vec.push(
-                                <$Elem>::from_str(&string)
-                                    .map_err(|e| StringErr(format!("{}", e)))?,
-                            );
-                        } else {
-                            return Err(StringErr(format!(
-                                "Expected array of {}.",
-                                stringify!($Elem)
-                            )));
-                        }
-                    }
-                    Ok(Self(vec))
-                } else {
-                    Err(StringErr(format!(
-                        "Expected array of {}.",
-                        stringify!($Elem)
-                    )))
-                }
-            }
-
-            fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-                Ok(serde_json::Value::Array(
-                    self.0
-                        .iter()
-                        .map(|elem| serde_json::Value::String(format!("{}", elem)))
-                        .collect(),
-                ))
-            }
-        }
-    };
-}
-
-#[macro_export]
-/// $Elem must implement Display + FromStr + zerocopy::AsBytes + zerocopy::FromBytes
-macro_rules! impl_value_for_smallvec_zc {
-    ($T:ty, $Elem:ty, $N:literal) => {
-        impl ValueAsBytes for $T {
-            fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> {
-                self.0.as_bytes(f)
-            }
-        }
-        impl FromBytes for $T {
-            type Err = StringErr;
-
-            fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-                Ok(Self(SmallVec::<[$Elem; $N]>::from_bytes(bytes)?))
-            }
-        }
-        impl ValueSliceZc for $T {
-            type Elem = $Elem;
-
-            fn prefix_len() -> usize {
-                0
-            }
-        }
-        #[cfg(feature = "explorer")]
-        use std::str::FromStr as _;
-        #[cfg(feature = "explorer")]
-        impl ExplorableValue for $T {
-            fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-                if let serde_json::Value::Array(json_array) =
-                    serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
-                {
-                    let mut svec = SmallVec::with_capacity(json_array.len());
-                    for value in json_array {
-                        if let serde_json::Value::String(string) = value {
-                            svec.push(
-                                <$Elem>::from_str(&string)
-                                    .map_err(|e| StringErr(format!("{}", e)))?,
-                            );
-                        } else {
-                            return Err(StringErr(format!(
-                                "Expected array of {}.",
-                                stringify!($Elem)
-                            )));
-                        }
-                    }
-                    Ok(Self(svec))
-                } else {
-                    Err(StringErr(format!(
-                        "Expected array of {}.",
-                        stringify!($Elem)
-                    )))
-                }
-            }
-
-            fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-                Ok(serde_json::Value::Array(
-                    self.0
-                        .iter()
-                        .map(|elem| serde_json::Value::String(format!("{}", elem)))
-                        .collect(),
-                ))
-            }
-        }
-    };
-}
-
-#[macro_export]
-/// $Elem must implement Display + FromStr + Ord + zerocopy::AsBytes + zerocopy::FromBytes
-macro_rules! impl_value_for_btreeset_zc {
-    ($T:ty, $Elem:ty) => {
-        impl ValueAsBytes for $T {
-            fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> {
-                self.0.as_bytes(f)
-            }
-        }
-        impl FromBytes for $T {
-            type Err = StringErr;
-
-            fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-                Ok(Self(BTreeSet::<$Elem>::from_bytes(bytes)?))
-            }
-        }
-        impl ValueSliceZc for $T {
-            type Elem = $Elem;
-
-            fn prefix_len() -> usize {
-                0
-            }
-        }
-        #[cfg(feature = "explorer")]
-        use std::str::FromStr as _;
-        #[cfg(feature = "explorer")]
-        impl ExplorableValue for $T {
-            fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-                if let serde_json::Value::Array(json_array) =
-                    serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
-                {
-                    let mut col = BTreeSet::new();
-                    for value in json_array {
-                        if let serde_json::Value::String(string) = value {
-                            col.insert(
-                                <$Elem>::from_str(&string)
-                                    .map_err(|e| StringErr(format!("{}", e)))?,
-                            );
-                        } else {
-                            return Err(StringErr(format!(
-                                "Expected array of {}.",
-                                stringify!($Elem)
-                            )));
-                        }
-                    }
-                    Ok(Self(col))
-                } else {
-                    Err(StringErr(format!(
-                        "Expected array of {}.",
-                        stringify!($Elem)
-                    )))
-                }
-            }
-
-            fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-                Ok(serde_json::Value::Array(
-                    self.0
-                        .iter()
-                        .map(|elem| serde_json::Value::String(format!("{}", elem)))
-                        .collect(),
-                ))
-            }
-        }
-    };
-}
-
-#[macro_export]
-/// $Elem must implement Display + Eq + FromStr + std::hash::Hash + zerocopy::AsBytes + zerocopy::FromBytes
-macro_rules! impl_value_for_hashset_zc {
-    ($T:ty, $Elem:ty) => {
-        impl ValueAsBytes for $T {
-            fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, f: F) -> KvResult<T> {
-                self.0.as_bytes(f)
-            }
-        }
-        impl FromBytes for $T {
-            type Err = StringErr;
-
-            fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> {
-                Ok(Self(HashSet::<$Elem>::from_bytes(bytes)?))
-            }
-        }
-        impl ValueSliceZc for $T {
-            type Elem = $Elem;
-
-            fn prefix_len() -> usize {
-                0
-            }
-        }
-        #[cfg(feature = "explorer")]
-        use std::str::FromStr as _;
-        #[cfg(feature = "explorer")]
-        impl ExplorableValue for $T {
-            fn from_explorer_str(source: &str) -> std::result::Result<Self, StringErr> {
-                if let serde_json::Value::Array(json_array) =
-                    serde_json::Value::from_str(source).map_err(|e| StringErr(format!("{}", e)))?
-                {
-                    let mut col = HashSet::new();
-                    for value in json_array {
-                        if let serde_json::Value::String(string) = value {
-                            col.insert(
-                                <$Elem>::from_str(&string)
-                                    .map_err(|e| StringErr(format!("{}", e)))?,
-                            );
-                        } else {
-                            return Err(StringErr(format!(
-                                "Expected array of {}.",
-                                stringify!($Elem)
-                            )));
-                        }
-                    }
-                    Ok(Self(col))
-                } else {
-                    Err(StringErr(format!(
-                        "Expected array of {}.",
-                        stringify!($Elem)
-                    )))
-                }
-            }
-
-            fn to_explorer_json(&self) -> KvResult<serde_json::Value> {
-                Ok(serde_json::Value::Array(
-                    self.0
-                        .iter()
-                        .map(|elem| serde_json::Value::String(format!("{}", elem)))
-                        .collect(),
-                ))
-            }
-        }
-    };
-}
diff --git a/rust-libs/tools/kv_typed/src/value.rs b/rust-libs/tools/kv_typed/src/value.rs
index c2d383e67b400c1565a6562a6c3ebc2b04c760f5..92f0afd877a88cbcd55e17501e32dfb0393522f7 100644
--- a/rust-libs/tools/kv_typed/src/value.rs
+++ b/rust-libs/tools/kv_typed/src/value.rs
@@ -82,5 +82,77 @@ impl ValueSliceZc for String {
     }
 }
 
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct EmptyValue;
+impl<T, E> ValueSliceZc for Vec<T>
+where
+    T: 'static
+        + Copy
+        + Debug
+        + Default
+        + Display
+        + FromStr<Err = E>
+        + PartialEq
+        + Send
+        + Sized
+        + Sync
+        + zerocopy::AsBytes
+        + zerocopy::FromBytes,
+    E: Display,
+{
+    type Elem = T;
+
+    fn prefix_len() -> usize {
+        0
+    }
+}
+
+macro_rules! impl_value_slice_zc_for_smallvec {
+    ($($N:literal),*) => {$(
+        impl<T, E> ValueSliceZc for SmallVec<[T; $N]>
+        where
+            T: 'static
+                + Copy
+                + Debug
+                + Default
+                + Display
+                + FromStr<Err = E>
+                + PartialEq
+                + Send
+                + Sized
+                + Sync
+                + zerocopy::AsBytes
+                + zerocopy::FromBytes,
+            E: Display,
+        {
+            type Elem = T;
+
+            fn prefix_len() -> usize {
+                0
+            }
+        }
+    )*};
+}
+impl_value_slice_zc_for_smallvec!(2, 4, 8, 16, 32, 64);
+
+impl<T, E> ValueSliceZc for BTreeSet<T>
+where
+    T: 'static
+        + Copy
+        + Debug
+        + Default
+        + Display
+        + FromStr<Err = E>
+        + Ord
+        + PartialEq
+        + Send
+        + Sized
+        + Sync
+        + zerocopy::AsBytes
+        + zerocopy::FromBytes,
+    E: Display,
+{
+    type Elem = T;
+
+    fn prefix_len() -> usize {
+        0
+    }
+}
diff --git a/rust-libs/tools/kv_typed/tests/db_schema.rs b/rust-libs/tools/kv_typed/tests/db_schema.rs
deleted file mode 100644
index a4b6c135233db811dcc82e381af4f0bb2bce0a6c..0000000000000000000000000000000000000000
--- a/rust-libs/tools/kv_typed/tests/db_schema.rs
+++ /dev/null
@@ -1,216 +0,0 @@
-mod tests {
-    use kv_typed::prelude::*;
-    use smallvec::SmallVec;
-    use std::fmt::Debug;
-
-    #[derive(Clone, Debug, PartialEq)]
-    pub struct VecU128(Vec<u128>);
-    kv_typed::impl_value_for_vec_zc!(VecU128, u128);
-
-    #[derive(Clone, Debug, PartialEq)]
-    pub struct SVecU128(SmallVec<[u128; 4]>);
-    kv_typed::impl_value_for_smallvec_zc!(SVecU128, u128, 4);
-
-    use std::collections::BTreeSet;
-    #[derive(Clone, Debug, PartialEq)]
-    pub struct BTSetU128(BTreeSet<u128>);
-    kv_typed::impl_value_for_btreeset_zc!(BTSetU128, u128);
-
-    use std::collections::HashSet;
-    #[derive(Clone, Debug, PartialEq)]
-    pub struct HashSetU128(HashSet<u128>);
-    kv_typed::impl_value_for_hashset_zc!(HashSetU128, u128);
-
-    db_schema!(
-        TestV1,
-        [
-            ["c1", col_1, i32, String,],
-            ["c2", col_2, usize, EmptyValue,],
-            ["c3", col_3, u64, VecU128],
-            ["c4", col_4, u64, BTSetU128],
-        ]
-    );
-
-    #[cfg(feature = "lmdb_backend")]
-    #[test]
-    fn test_db_schema_lmdb() -> KvResult<()> {
-        let tmp_dir = unwrap::unwrap!(tempdir::TempDir::new("kv_typed_lmdb"));
-        let db = TestV1Db::<kv_typed::backend::lmdb::Lmdb>::open(
-            kv_typed::backend::lmdb::LmdbConf::default().folder_path(tmp_dir.path().to_owned()),
-        )?;
-
-        test_db_schema(&db)
-    }
-
-    #[test]
-    fn test_db_schema_mem() -> KvResult<()> {
-        let db = TestV1Db::<kv_typed::backend::memory::Mem>::open(
-            kv_typed::backend::memory::MemConf::default(),
-        )?;
-
-        test_db_schema(&db)
-    }
-
-    //#[cfg(feature = "sled_backend")]
-    #[test]
-    fn test_db_schema_sled() -> KvResult<()> {
-        let db = TestV1Db::<Sled>::open(SledConf::default().temporary(true))?;
-
-        test_db_schema(&db)
-    }
-
-    fn test_db_schema<B: Backend>(db: &TestV1Db<B>) -> KvResult<()> {
-        let (sender, recv) = kv_typed::channel::unbounded();
-        db.col_1().subscribe(sender)?;
-
-        let db2 = db.clone();
-
-        let handler = std::thread::spawn(move || db2.col_1_write().upsert(3, "toto".to_owned()));
-        handler.join().expect("thread panic")?;
-
-        let expected_events: Events<Col1Event> = smallvec::smallvec![Col1Event::Upsert {
-            key: 3,
-            value: "toto".to_owned(),
-        }];
-        if let Ok(msg) = recv.recv() {
-            assert_eq!(msg.as_ref(), &expected_events,)
-        } else {
-            panic!("must be receive event")
-        }
-
-        assert_eq!(db.col_1().get(&3)?, Some("toto".to_owned()),);
-        let d = db.col_1().get_ref_slice(&3, |bytes| {
-            let str_ = unsafe { core::str::from_utf8_unchecked(bytes) };
-            assert_eq!("toto", str_);
-            assert_eq!(db.col_2().get(&3)?, None,);
-            Ok(str_.to_owned())
-        })?;
-        assert_eq!(d, Some("toto".to_owned()));
-
-        assert_eq!(db.col_2().get(&3)?, None,);
-        db.col_2_write().upsert(3, EmptyValue)?;
-        assert_eq!(db.col_2().get(&3)?, Some(EmptyValue),);
-
-        db.col_1_write().upsert(5, "tutu".to_owned())?;
-
-        db.col_1().iter(.., |mut iter| {
-            assert_eq!(iter.next_res()?, Some((3, "toto".to_owned())));
-            assert_eq!(iter.next_res()?, Some((5, "tutu".to_owned())));
-            assert_eq!(iter.next_res()?, None);
-            Ok::<(), KvError>(())
-        })?;
-
-        db.col_1().iter(.., |it| {
-            let mut iter = it.values().reverse();
-
-            assert_eq!(iter.next_res()?, Some("tutu".to_owned()));
-            assert_eq!(iter.next_res()?, Some("toto".to_owned()));
-            assert_eq!(iter.next_res()?, None);
-            Ok::<(), KvError>(())
-        })?;
-
-        db.col_1_write().upsert(7, "titi".to_owned())?;
-
-        db.col_1().iter(.., |it| {
-            let mut iter = it.values().reverse().step_by(2);
-
-            assert_eq!(iter.next_res()?, Some("titi".to_owned()));
-            assert_eq!(iter.next_res()?, Some("toto".to_owned()));
-            assert_eq!(iter.next_res()?, None);
-
-            Ok::<(), KvError>(())
-        })?;
-
-        db.col_3_write().upsert(4, VecU128(vec![1, 2, 3]))?;
-        db.col_3().get_ref_slice(&4, |numbers| {
-            assert_eq!(numbers, &[1, 2, 3]);
-            Ok(())
-        })?;
-
-        // Test get_ref_slice
-        use std::iter::FromIterator as _;
-        db.col_4_write().upsert(
-            4,
-            BTSetU128(BTreeSet::from_iter((&[3, 2, 4, 1]).iter().copied())),
-        )?;
-        db.col_4().get_ref_slice(&4, |numbers| {
-            assert_eq!(numbers, &[1, 2, 3, 4]);
-            Ok(())
-        })?;
-
-        // Test transactional
-        // A read tx should be opened when write tx not commited
-        let (s1, r1) = flume::bounded::<()>(0);
-        let (s2, r2) = flume::bounded::<()>(0);
-        let db_ro = db.get_ro_handler();
-        let read_task = std::thread::spawn(move || {
-            r1.recv().expect("disconnected");
-            (db_ro.col_3(), db_ro.col_4(), db_ro.col_2()).read(|(c3, c4, _c2)| {
-                c3.get_ref_slice(&4, |numbers| {
-                    assert_eq!(numbers, &[1, 2, 3]);
-                    Ok(())
-                })?;
-                c3.iter(.., |it| {
-                    let iter = it.keys();
-                    s2.send(()).expect("disconnected");
-                    assert_eq!(iter.collect::<KvResult<Vec<_>>>()?, vec![4]);
-                    Ok::<(), KvError>(())
-                })?;
-                c4.get_ref_slice(&4, |numbers| {
-                    assert_eq!(numbers, &[1, 2, 3, 4]);
-                    Ok(())
-                })?;
-                Ok(())
-            })
-        });
-
-        let tres: KvResult<()> = (db.col_3_write(), db.col_4_write(), db.col_2_write()).write(
-            |(mut c3, mut c4, _c2)| {
-                s1.send(()).expect("disconnected");
-                assert_eq!(
-                    c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?,
-                    vec![4]
-                );
-                assert_eq!(
-                    c3.iter(.., |it| it.values().collect::<KvResult<Vec<_>>>())?,
-                    vec![VecU128(vec![1, 2, 3])]
-                );
-                c3.upsert(42, VecU128(vec![5, 4, 6]));
-                assert_eq!(
-                    c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?,
-                    vec![4, 42]
-                );
-                assert_eq!(
-                    c3.iter(.., |it| it.reverse().keys().collect::<KvResult<Vec<_>>>())?,
-                    vec![42, 4]
-                );
-                c3.upsert(8, VecU128(vec![11, 12, 13]));
-                c3.remove(4);
-                assert_eq!(
-                    c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?,
-                    vec![8, 42]
-                );
-                c3.iter(.., |it| {
-                    let iter = it.reverse().keys();
-                    r2.recv().expect("disconnected");
-                    assert_eq!(iter.collect::<KvResult<Vec<_>>>()?, vec![42, 8]);
-
-                    Ok::<(), KvError>(())
-                })?;
-                c4.upsert(
-                    4,
-                    BTSetU128(BTreeSet::from_iter((&[7, 8, 6, 5]).iter().copied())),
-                );
-                Ok(())
-            },
-        );
-        tres?;
-        read_task.join().expect("read task panic")?;
-
-        // Test clear()
-        db.col_4_write().clear()?;
-        assert_eq!(db.col_4().count()?, 0);
-
-        Ok(())
-    }
-}
diff --git a/rust-libs/tools/kv_typed/tests/test_db_schema.rs b/rust-libs/tools/kv_typed/tests/test_db_schema.rs
new file mode 100644
index 0000000000000000000000000000000000000000..75ef7c69868e3d7f2b729050275f0460f8f48d10
--- /dev/null
+++ b/rust-libs/tools/kv_typed/tests/test_db_schema.rs
@@ -0,0 +1,199 @@
+use kv_typed::backend::memory::Mem;
+use kv_typed::db_schema;
+use kv_typed::prelude::*;
+use std::collections::BTreeSet;
+
+db_schema!(
+    TestV1,
+    [
+        ["c1", Col1, i32, String],
+        ["c2", Col2, usize, ()],
+        ["c3", Col3, u64, Vec<u128>],
+        ["c4", Col4, u64, BTreeSet<u128>],
+    ]
+);
+
+#[test]
+fn test_macro_db() {
+    assert_eq!(Col1Event::RemoveAll, Col1Event::RemoveAll);
+
+    #[cfg(feature = "explorer")]
+    {
+        use kv_typed::explorer::DbExplorable as _;
+        assert_eq!(
+            TestV1Db::<Mem>::list_collections(),
+            vec![
+                ("col1", "i32", "String"),
+                ("col2", "usize", "()"),
+                ("col3", "u64", "Vec<u128>"),
+                ("col4", "u64", "BTreeSet<u128>")
+            ]
+        );
+    }
+}
+
+#[test]
+fn test_db_mem() -> KvResult<()> {
+    let db = TestV1Db::<kv_typed::backend::memory::Mem>::open(
+        kv_typed::backend::memory::MemConf::default(),
+    )?;
+
+    test_db(&db)
+}
+
+//#[cfg(feature = "sled_backend")]
+#[test]
+fn test_db_sled() -> KvResult<()> {
+    let db = TestV1Db::<Sled>::open(SledConf::default().temporary(true))?;
+
+    test_db(&db)
+}
+
+fn test_db<B: Backend>(db: &TestV1Db<B>) -> KvResult<()> {
+    let (sender, recv) = kv_typed::channel::unbounded();
+    db.col1().subscribe(sender)?;
+
+    let db2 = db.clone();
+
+    let handler = std::thread::spawn(move || db2.col1_write().upsert(3, "toto".to_owned()));
+    handler.join().expect("thread panic")?;
+
+    let expected_events: Events<Col1Event> = smallvec::smallvec![Col1Event::Upsert {
+        key: 3,
+        value: "toto".to_owned(),
+    }];
+    if let Ok(msg) = recv.recv() {
+        assert_eq!(msg.as_ref(), &expected_events,)
+    } else {
+        panic!("must be receive event")
+    }
+
+    assert_eq!(db.col1().get(&3)?, Some("toto".to_owned()),);
+    let d = db.col1().get_ref_slice(&3, |bytes| {
+        let str_ = unsafe { core::str::from_utf8_unchecked(bytes) };
+        assert_eq!("toto", str_);
+        assert_eq!(db.col2().get(&3)?, None,);
+        Ok(str_.to_owned())
+    })?;
+    assert_eq!(d, Some("toto".to_owned()));
+
+    assert_eq!(db.col2().get(&3)?, None,);
+    db.col2_write().upsert(3, ())?;
+    assert_eq!(db.col2().get(&3)?, Some(()),);
+
+    db.col1_write().upsert(5, "tutu".to_owned())?;
+
+    db.col1().iter(.., |mut iter| {
+        assert_eq!(iter.next_res()?, Some((3, "toto".to_owned())));
+        assert_eq!(iter.next_res()?, Some((5, "tutu".to_owned())));
+        assert_eq!(iter.next_res()?, None);
+        Ok::<(), KvError>(())
+    })?;
+
+    db.col1().iter(.., |it| {
+        let mut iter = it.values().reverse();
+
+        assert_eq!(iter.next_res()?, Some("tutu".to_owned()));
+        assert_eq!(iter.next_res()?, Some("toto".to_owned()));
+        assert_eq!(iter.next_res()?, None);
+        Ok::<(), KvError>(())
+    })?;
+
+    db.col1_write().upsert(7, "titi".to_owned())?;
+
+    db.col1().iter(.., |it| {
+        let mut iter = it.values().reverse().step_by(2);
+
+        assert_eq!(iter.next_res()?, Some("titi".to_owned()));
+        assert_eq!(iter.next_res()?, Some("toto".to_owned()));
+        assert_eq!(iter.next_res()?, None);
+
+        Ok::<(), KvError>(())
+    })?;
+
+    db.col3_write().upsert(4, vec![1, 2, 3])?;
+    db.col3().get_ref_slice(&4, |numbers| {
+        assert_eq!(numbers, &[1, 2, 3]);
+        Ok(())
+    })?;
+
+    // Test get_ref_slice
+    use std::iter::FromIterator as _;
+    db.col4_write()
+        .upsert(4, BTreeSet::from_iter((&[3, 2, 4, 1]).iter().copied()))?;
+    db.col4().get_ref_slice(&4, |numbers| {
+        assert_eq!(numbers, &[1, 2, 3, 4]);
+        Ok(())
+    })?;
+
+    // Test transactional
+    // A read tx should be opened when write tx not commited
+    let (s1, r1) = flume::bounded::<()>(0);
+    let (s2, r2) = flume::bounded::<()>(0);
+    let db_ro = db.get_ro_handler();
+    let read_task = std::thread::spawn(move || {
+        r1.recv().expect("disconnected");
+        (db_ro.col3(), db_ro.col4(), db_ro.col2()).read(|(c3, c4, _c2)| {
+            c3.get_ref_slice(&4, |numbers| {
+                assert_eq!(numbers, &[1, 2, 3]);
+                Ok(())
+            })?;
+            c3.iter(.., |it| {
+                let iter = it.keys();
+                s2.send(()).expect("disconnected");
+                assert_eq!(iter.collect::<KvResult<Vec<_>>>()?, vec![4]);
+                Ok::<(), KvError>(())
+            })?;
+            c4.get_ref_slice(&4, |numbers| {
+                assert_eq!(numbers, &[1, 2, 3, 4]);
+                Ok(())
+            })?;
+            Ok(())
+        })
+    });
+
+    let tres: KvResult<()> =
+        (db.col3_write(), db.col4_write(), db.col2_write()).write(|(mut c3, mut c4, _c2)| {
+            s1.send(()).expect("disconnected");
+            assert_eq!(
+                c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?,
+                vec![4]
+            );
+            assert_eq!(
+                c3.iter(.., |it| it.values().collect::<KvResult<Vec<_>>>())?,
+                vec![vec![1, 2, 3]]
+            );
+            c3.upsert(42, vec![5, 4, 6]);
+            assert_eq!(
+                c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?,
+                vec![4, 42]
+            );
+            assert_eq!(
+                c3.iter(.., |it| it.reverse().keys().collect::<KvResult<Vec<_>>>())?,
+                vec![42, 4]
+            );
+            c3.upsert(8, vec![11, 12, 13]);
+            c3.remove(4);
+            assert_eq!(
+                c3.iter(.., |it| it.keys().collect::<KvResult<Vec<_>>>())?,
+                vec![8, 42]
+            );
+            c3.iter(.., |it| {
+                let iter = it.reverse().keys();
+                r2.recv().expect("disconnected");
+                assert_eq!(iter.collect::<KvResult<Vec<_>>>()?, vec![42, 8]);
+
+                Ok::<(), KvError>(())
+            })?;
+            c4.upsert(4, BTreeSet::from_iter((&[7, 8, 6, 5]).iter().copied()));
+            Ok(())
+        });
+    tres?;
+    read_task.join().expect("read task panic")?;
+
+    // Test clear()
+    db.col4_write().clear()?;
+    assert_eq!(db.col4().count()?, 0);
+
+    Ok(())
+}
diff --git a/rust-libs/tools/kv_typed_code_gen/Cargo.toml b/rust-libs/tools/kv_typed_code_gen/Cargo.toml
deleted file mode 100644
index 6f09757d612f13252c5a3c8aab458c5e680f93cc..0000000000000000000000000000000000000000
--- a/rust-libs/tools/kv_typed_code_gen/Cargo.toml
+++ /dev/null
@@ -1,19 +0,0 @@
-[package]
-name = "kv_typed_code_gen"
-version = "0.1.0"
-authors = ["elois <c@elo.tf>"]
-description = "Macros to generate code for kv_typed users"
-repository = "https://git.duniter.org/nodes/typescript/duniter"
-keywords = ["database", "macro", "key", "sled"]
-license = "AGPL-3.0"
-edition = "2018"
-
-[lib]
-proc-macro = true
-path = "src/lib.rs"
-
-[dependencies]
-Inflector = "0.11.4"
-proc-macro2 = "1.0.20"
-quote = "1.0"
-syn = { version = "1.0.39", features = ["full"] }
diff --git a/rust-libs/tools/kv_typed_code_gen/src/col_schema.rs b/rust-libs/tools/kv_typed_code_gen/src/col_schema.rs
deleted file mode 100644
index 085d0839a085a7ad9dd5ff47c1d853425f5cf697..0000000000000000000000000000000000000000
--- a/rust-libs/tools/kv_typed_code_gen/src/col_schema.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-//  Copyright (C) 2020 Éloïs SANCHEZ.
-//
-// This program 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, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
-
-use crate::*;
-
-pub(crate) struct ColSchema {
-    pub(crate) name_class: Ident,
-    pub(crate) name_snake: Ident,
-    pub(crate) path: LitStr,
-    pub(crate) key_ty: Ident,
-    pub(crate) value_ty: Ident,
-}
-
-impl From<ExprArray> for ColSchema {
-    fn from(expr_array: ExprArray) -> Self {
-        let mut expr_iter = expr_array.elems.into_iter();
-
-        let col_path = if let Some(Expr::Lit(ExprLit { lit, .. })) = expr_iter.next() {
-            if let Lit::Str(lit_str) = lit {
-                lit_str
-            } else {
-                panic!("Collection must be defined by array of one literal followed by three identifiers");
-            }
-        } else {
-            panic!(
-                "Collection must be defined by array of one literal followed by three identifiers"
-            );
-        };
-        let col_name = if let Some(Expr::Path(ExprPath { path, .. })) = expr_iter.next() {
-            path.get_ident()
-                .expect("Collection name must be a plain identifier")
-                .to_owned()
-        } else {
-            panic!(
-                "Collection must be defined by array of one literal followed by three identifiers"
-            );
-        };
-        let key_ty = if let Some(Expr::Path(ExprPath { path, .. })) = expr_iter.next() {
-            path.get_ident()
-                .expect("Collection key type must be a plain identifier")
-                .to_owned()
-        } else {
-            panic!(
-                "Collection must be defined by array of one literal followed by three identifiers"
-            );
-        };
-        let value_ty = if let Some(Expr::Path(ExprPath { path, .. })) = expr_iter.next() {
-            path.get_ident()
-                .expect("Collection value type must be a plain identifier")
-                .to_owned()
-        } else {
-            panic!(
-                "Collection must be defined by array of one literal followed by three identifiers"
-            );
-        };
-        if expr_iter.next().is_some() {
-            panic!(
-                "Collection must be defined by array of one literal followed by three identifiers"
-            );
-        }
-        ColSchema {
-            path: col_path,
-            name_class: format_ident!("{}", ident_to_class_case_string(&col_name)),
-            name_snake: format_ident!("{}", ident_to_snake_case_string(&col_name)),
-            key_ty,
-            value_ty,
-        }
-    }
-}
diff --git a/rust-libs/tools/kv_typed_code_gen/src/db_readable.rs b/rust-libs/tools/kv_typed_code_gen/src/db_readable.rs
deleted file mode 100644
index 0c5977d8e8248c8fafaaac058cc0dd02600b4268..0000000000000000000000000000000000000000
--- a/rust-libs/tools/kv_typed_code_gen/src/db_readable.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-//  Copyright (C) 2020 Éloïs SANCHEZ.
-//
-// This program 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, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
-
-use crate::*;
-
-#[allow(clippy::too_many_arguments)]
-pub(crate) fn impl_db_readable(
-    db: &Ident,
-    db_ro: &Ident,
-    db_readable: &Ident,
-    col_field: &[Ident],
-    col_event_type: &[Ident],
-) -> proc_macro2::TokenStream {
-    quote! {
-        pub trait #db_readable: Sized {
-            type Backend: Backend;
-
-            #(fn #col_field(&self) -> &ColRo<<Self::Backend as Backend>::Col, #col_event_type>;)*
-        }
-        impl<B: Backend> #db_readable for #db<B> {
-            type Backend = B;
-
-            #(fn #col_field(&self) -> &ColRo<B::Col, #col_event_type> { &self.collections.#col_field.to_ro() })*
-        }
-        impl<B: Backend> #db_readable for #db_ro<B>{
-            type Backend = B;
-
-            #(fn #col_field(&self) -> &ColRo<B::Col, #col_event_type> { &self.collections.#col_field })*
-        }
-    }
-}
diff --git a/rust-libs/tools/kv_typed_code_gen/src/db_schema.rs b/rust-libs/tools/kv_typed_code_gen/src/db_schema.rs
deleted file mode 100644
index c60ee621f3f3793847619f36fad00597df205152..0000000000000000000000000000000000000000
--- a/rust-libs/tools/kv_typed_code_gen/src/db_schema.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-//  Copyright (C) 2020 Éloïs SANCHEZ.
-//
-// This program 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, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
-
-use crate::*;
-
-/// Parses the following syntax
-///
-/// DbVersion, [collection_name, ColKeyType, ColValueType]
-///
-pub(crate) struct DbSchema {
-    pub(crate) version: Ident,
-    pub(crate) collections: VecExprArray,
-}
-
-pub(crate) struct VecExprArray(pub(crate) Vec<ExprArray>);
-
-impl Parse for VecExprArray {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let tmp = Punctuated::<ExprArray, Token![,]>::parse_terminated(input)?;
-        Ok(VecExprArray(tmp.into_iter().collect()))
-    }
-}
-
-impl Parse for DbSchema {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let version: Ident = input.parse()?;
-        input.parse::<Token![,]>()?;
-        let collections_group: Group = input.parse()?;
-        let collections: VecExprArray = syn::parse(collections_group.stream().into())?;
-
-        Ok(DbSchema {
-            version,
-            collections,
-        })
-    }
-}
diff --git a/rust-libs/tools/kv_typed_code_gen/src/db_writable.rs b/rust-libs/tools/kv_typed_code_gen/src/db_writable.rs
deleted file mode 100644
index ae66a312983295f69921b5c0140a60f942af2473..0000000000000000000000000000000000000000
--- a/rust-libs/tools/kv_typed_code_gen/src/db_writable.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-//  Copyright (C) 2020 Éloïs SANCHEZ.
-//
-// This program 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, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
-
-use crate::*;
-
-#[allow(clippy::too_many_arguments)]
-pub(crate) fn impl_db_writable(
-    db: &Ident,
-    db_ro: &Ident,
-    db_readable: &Ident,
-    db_writable: &Ident,
-    col_name_class: &[Ident],
-    col_field: &[Ident],
-    col_path: &[LitStr],
-    col_event_type: &[Ident],
-) -> proc_macro2::TokenStream {
-    let db_batch = format_ident!("{}Batch", db);
-    let db_cols_rw = format_ident!("{}ColsRw", db);
-    let col_method_rw: Vec<Ident> = col_field
-        .iter()
-        .map(|field_name| format_ident!("{}_write", field_name))
-        .collect();
-    quote! {
-        pub trait #db_writable: #db_readable {
-            type Backend: Backend;
-            type Batch;
-            #(type #col_name_class: DbCollectionRw;)*
-            type DbRo: Sized;
-
-            fn get_ro_handler(&self) -> Self::DbRo;
-            fn open(
-                backend_conf: <<Self as #db_writable>::Backend as kv_typed::backend::Backend>::Conf,
-            ) -> KvResult <Self>;
-            fn new_batch(&self) -> Self::Batch;
-            fn save(&self) -> KvResult<()>;
-            fn write_batch(&self, batch: Self::Batch) -> KvResult<()>;
-            #(fn #col_method_rw(&self) -> &Self::#col_name_class;)*
-        }
-        impl<B: Backend> #db_writable for #db<B> {
-            type Backend = B;
-            type Batch = #db_batch<B>;
-            #(type #col_name_class = ColRw<B::Col, #col_event_type>;)*
-            type DbRo = #db_ro<B>;
-
-            #[inline(always)]
-            fn get_ro_handler(&self) -> Self::DbRo {
-                #db_ro {
-                    collections: self.collections.to_ro(),
-                }
-            }
-            #[inline(always)]
-            fn new_batch(&self) -> Self::Batch {
-                <#db_batch::<B>>::default()
-            }
-            fn write_batch(&self, batch: Self::Batch) -> KvResult<()> {
-                #(self.collections.#col_field.write_batch(batch.#col_field)?;)*
-                Ok(())
-            }
-            fn open(
-                backend_conf: <<Self as #db_writable>::Backend as kv_typed::backend::Backend>::Conf,
-            ) -> KvResult <Self> {
-                let mut db = B::open(&backend_conf)?;
-                Ok(#db {
-                    collections: #db_cols_rw {
-                        #(#col_field: <ColRw<B::Col, #col_event_type>>::new(
-                            db.open_col(&backend_conf, #col_path)?
-                        ),)*
-                    },
-                })
-            }
-            fn save(&self) -> KvResult<()> {
-                #(self.collections.#col_field.save()?;)*
-                Ok(())
-            }
-            #(fn #col_method_rw(&self) -> &ColRw<B::Col, #col_event_type> { &self.collections.#col_field })*
-        }
-    }
-}
diff --git a/rust-libs/tools/kv_typed_code_gen/src/lib.rs b/rust-libs/tools/kv_typed_code_gen/src/lib.rs
deleted file mode 100644
index f4306e21511fd0b01c865ac3cfb1d00ebb2a73cd..0000000000000000000000000000000000000000
--- a/rust-libs/tools/kv_typed_code_gen/src/lib.rs
+++ /dev/null
@@ -1,229 +0,0 @@
-//  Copyright (C) 2020 Éloïs SANCHEZ.
-//
-// This program 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, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
-
-//! Strongly typed key-value storage
-//!
-//! Macro to generate database code from their schema
-
-mod col_schema;
-mod db_readable;
-mod db_schema;
-mod db_writable;
-
-use inflector::Inflector;
-use proc_macro::TokenStream;
-use proc_macro2::Group;
-use quote::{format_ident, quote};
-use syn::{
-    parse::{Parse, ParseStream, Result},
-    parse_macro_input,
-    punctuated::Punctuated,
-    Expr, ExprArray, ExprLit, ExprPath, Ident, Lit, LitStr, Token,
-};
-
-use crate::col_schema::ColSchema;
-use crate::db_readable::impl_db_readable;
-use crate::db_schema::DbSchema;
-use crate::db_writable::impl_db_writable;
-
-fn ident_to_class_case_string(ident: &Ident) -> String {
-    Inflector::to_class_case(&format!("{}", ident))
-}
-fn ident_to_snake_case_string(ident: &Ident) -> String {
-    Inflector::to_snake_case(&format!("{}", ident))
-}
-
-#[allow(clippy::let_and_return)]
-#[proc_macro]
-pub fn db_schema(input: TokenStream) -> TokenStream {
-    let db_schema = parse_macro_input!(input as DbSchema);
-
-    let version = db_schema.version;
-    let name = ident_to_snake_case_string(&version);
-    let collections: Vec<ColSchema> = db_schema
-        .collections
-        .0
-        .into_iter()
-        .map(Into::into)
-        .collect();
-
-    let db = format_ident!("{}Db", version);
-    let db_batch = format_ident!("{}Batch", db);
-    let db_ro = format_ident!("{}Ro", db);
-    let db_cols_ro = format_ident!("{}ColsRo", db);
-    let db_cols_rw = format_ident!("{}ColsRw", db);
-
-    let col_path: Vec<LitStr> = collections.iter().map(|col| col.path.clone()).collect();
-    let col_field: Vec<Ident> = collections
-        .iter()
-        .map(|col| col.name_snake.clone())
-        .collect();
-    let col_event_type: Vec<Ident> = collections
-        .iter()
-        .map(|col| format_ident!("{}Event", &col.name_class))
-        .collect();
-    let col_key_type: Vec<Ident> = collections.iter().map(|col| col.key_ty.clone()).collect();
-    let col_name_class: Vec<Ident> = collections
-        .iter()
-        .map(|col| col.name_class.clone())
-        .collect();
-    let col_name_class_rw: Vec<Ident> = collections
-        .iter()
-        .map(|col| format_ident!("{}Rw", &col.name_class))
-        .collect();
-    let col_value_type: Vec<Ident> = collections.iter().map(|col| col.value_ty.clone()).collect();
-
-    /*let define_each_db_collection: Vec<proc_macro2::TokenStream> =
-    collections.iter().map(define_db_collection).collect();*/
-    let db_readable = format_ident!("{}Readable", db);
-    let impl_db_readable = impl_db_readable(&db, &db_ro, &db_readable, &col_field, &col_event_type);
-    let db_writable = format_ident!("{}Writable", db);
-    let impl_db_writable = impl_db_writable(
-        &db,
-        &db_ro,
-        &db_readable,
-        &db_writable,
-        &col_name_class_rw,
-        &col_field,
-        &col_path,
-        &col_event_type,
-    );
-
-    let expanded = quote! {
-        pub use __inner::{#db, #db_batch, #db_ro, #db_readable, #db_writable};
-        #(
-            // Define each collection event type
-            #[derive(Debug, PartialEq)]
-            pub enum #col_event_type {
-                Upsert { key: #col_key_type, value: #col_value_type },
-                Remove { key: #col_key_type },
-                RemoveAll,
-            }
-            impl EventTrait for #col_event_type {
-                type K = #col_key_type;
-                type V = #col_value_type;
-
-                fn clear() -> Self { Self::RemoveAll }
-                fn upsert(k: Self::K, v: Self::V) -> Self { Self::Upsert { key: k, value: v, } }
-                fn remove(k: Self::K) -> Self { Self::Remove { key: k } }
-            }
-        )*
-        // Mocks
-        #[cfg(feature = "mock")]
-        mockall::mock! {
-            pub #db_readable {}
-
-            trait #db_readable {
-                type Backend = kv_typed::backend::mock::MockBackend;
-                #(type #col_name_class = kv_typed::prelude::MockColRo<#col_event_type>;)*
-
-                #(fn #col_field(&self) -> &kv_typed::prelude::MockColRo<#col_event_type>;)*
-            }
-        }
-        // Inner module used to hide internals types that must not be exposed on public api
-        mod __inner {
-            use super::*;
-            // DbCollections
-            #[derive(Clone, Debug)]
-            pub struct #db_cols_ro<BC: BackendCol> {
-                #(#col_field: ColRo<BC, #col_event_type>,)*
-            }
-            #[derive(Clone, Debug)]
-            pub struct #db_cols_rw<BC: BackendCol> {
-                #(#col_field: ColRw<BC, #col_event_type>,)*
-            }
-            impl<BC: BackendCol> #db_cols_rw<BC> {
-                fn to_ro(&self) -> #db_cols_ro<BC> {
-                    #db_cols_ro {
-                        #(#col_field: self.#col_field.to_ro().clone(),)*
-                    }
-                }
-            }
-            // Db
-            #[derive(Debug)]
-            pub struct #db<B: Backend> {
-                collections: #db_cols_rw<B::Col>,
-            }
-            impl<B: Backend> #db<B> {
-                pub const NAME: &'static str = #name;
-            }
-            impl<B: Backend> Clone for #db<B> {
-                fn clone(&self) -> Self {
-                    #db {
-                        collections: self.collections.clone(),
-                    }
-                }
-            }
-            #[cfg(feature = "explorer")]
-            use kv_typed::prelude::StringErr;
-            #[cfg(feature = "explorer")]
-            impl<B: Backend> kv_typed::explorer::DbExplorable for #db<B> {
-                fn explore<'a>(
-                    &self,
-                    collection_name: &str,
-                    action: kv_typed::explorer::ExplorerAction<'a>,
-                    stringify_json_value: fn(serde_json::Value) -> serde_json::Value,
-                ) -> KvResult<std::result::Result<kv_typed::explorer::ExplorerActionResponse, StringErr>> {
-                    #( if stringify!(#col_field) == collection_name {
-                        return action.exec(&self.collections.#col_field, stringify_json_value);
-                    } )*
-                    Ok(Err(StringErr(format!("collection '{}' not exist in database '{}'.", collection_name, stringify!(#db)))))
-                }
-                fn list_collections(&self) -> Vec<(&'static str, &'static str, &'static str)> {
-                    vec![
-                        #((stringify!(#col_field), stringify!(#col_key_type), stringify!(#col_value_type)),)*
-                    ]
-                }
-            }
-            pub struct #db_batch<B: Backend> {
-                #(#col_field: Batch<B::Col, ColRw<B::Col, #col_event_type>>,)*
-            }
-            impl<B: Backend> Default for #db_batch<B> {
-                fn default() -> Self {
-                    #db_batch {
-                        #(#col_field: Batch::default(),)*
-                    }
-                }
-            }
-            impl<B: Backend> #db_batch<B> {
-                #(pub fn #col_field(&mut self) -> &mut Batch<B::Col, ColRw<B::Col, #col_event_type>> { &mut self.#col_field })*
-            }
-            // DbRo
-            #[derive(Debug)]
-            pub struct #db_ro<B: Backend> {
-                collections: #db_cols_ro<B::Col>,
-            }
-            impl<B: Backend> #db_ro<B> {
-                pub const NAME: &'static str = #name;
-            }
-            impl<B: Backend> Clone for #db_ro<B> {
-                fn clone(&self) -> Self {
-                    #db_ro {
-                        collections: self.collections.clone(),
-                    }
-                }
-            }
-            // Read operations
-            #impl_db_readable
-            // Write operations
-            #impl_db_writable
-        }
-    };
-
-    //let tokens = TokenStream::from(expanded);
-    //eprintln!("TOKENS: {:#}", tokens);
-
-    TokenStream::from(expanded)
-}