diff --git a/Cargo.lock b/Cargo.lock
index 93ae651610dc06ca7300375ab78ae5041b2b52d7..335797456d9a61fea4f92e07c37f298968ed9c84 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -49,6 +49,12 @@ version = "1.0.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
 
+[[package]]
+name = "arc-swap"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
+
 [[package]]
 name = "arrayref"
 version = "0.3.6"
@@ -389,6 +395,15 @@ dependencies = [
  "vec_map",
 ]
 
+[[package]]
+name = "cloudabi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "cloudabi"
 version = "0.1.0"
@@ -407,6 +422,17 @@ dependencies = [
  "cc",
 ]
 
+[[package]]
+name = "comfy-table"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f97a418f1dee79b100875499e272ea81882c1b931a9c8432e165b595b461752"
+dependencies = [
+ "crossterm",
+ "strum",
+ "strum_macros",
+]
+
 [[package]]
 name = "concurrent-queue"
 version = "1.2.2"
@@ -514,6 +540,31 @@ dependencies = [
  "lazy_static",
 ]
 
+[[package]]
+name = "crossterm"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7"
+dependencies = [
+ "bitflags",
+ "crossterm_winapi",
+ "lazy_static",
+ "libc",
+ "mio",
+ "parking_lot 0.10.2",
+ "signal-hook",
+ "winapi",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "cryptoxide"
 version = "0.2.1"
@@ -679,6 +730,22 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "duniter-dbex"
+version = "0.1.0"
+dependencies = [
+ "arrayvec",
+ "comfy-table",
+ "dirs",
+ "dubp-common",
+ "duniter-dbs",
+ "rayon",
+ "serde",
+ "serde_json",
+ "structopt",
+ "unwrap",
+]
+
 [[package]]
 name = "duniter-dbs"
 version = "0.1.0"
@@ -1065,7 +1132,7 @@ dependencies = [
  "leveldb_minimal",
  "maybe-async",
  "mockall",
- "parking_lot",
+ "parking_lot 0.11.0",
  "rayon",
  "regex",
  "serde_json",
@@ -1118,6 +1185,15 @@ version = "0.2.78"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98"
 
+[[package]]
+name = "lock_api"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
+dependencies = [
+ "scopeguard",
+]
+
 [[package]]
 name = "lock_api"
 version = "0.4.1"
@@ -1190,6 +1266,29 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "mio"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e53a6ea5f38c0a48ca42159868c6d8e1bd56c0451238856cc08d58563643bdc3"
+dependencies = [
+ "libc",
+ "log",
+ "miow",
+ "ntapi",
+ "winapi",
+]
+
+[[package]]
+name = "miow"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
+dependencies = [
+ "socket2",
+ "winapi",
+]
+
 [[package]]
 name = "mockall"
 version = "0.8.1"
@@ -1307,6 +1406,15 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
 
+[[package]]
+name = "ntapi"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "num"
 version = "0.2.1"
@@ -1411,6 +1519,16 @@ version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
 
+[[package]]
+name = "parking_lot"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
+dependencies = [
+ "lock_api 0.3.4",
+ "parking_lot_core 0.7.2",
+]
+
 [[package]]
 name = "parking_lot"
 version = "0.11.0"
@@ -1418,8 +1536,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733"
 dependencies = [
  "instant",
- "lock_api",
- "parking_lot_core",
+ "lock_api 0.4.1",
+ "parking_lot_core 0.8.0",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
+dependencies = [
+ "cfg-if",
+ "cloudabi 0.0.3",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi",
 ]
 
 [[package]]
@@ -1429,7 +1561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
 dependencies = [
  "cfg-if",
- "cloudabi",
+ "cloudabi 0.1.0",
  "instant",
  "libc",
  "redox_syscall",
@@ -1846,6 +1978,27 @@ dependencies = [
  "opaque-debug",
 ]
 
+[[package]]
+name = "signal-hook"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
+dependencies = [
+ "libc",
+ "mio",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
+dependencies = [
+ "arc-swap",
+ "libc",
+]
+
 [[package]]
 name = "slab"
 version = "0.4.2"
@@ -1865,7 +2018,7 @@ dependencies = [
  "fxhash",
  "libc",
  "log",
- "parking_lot",
+ "parking_lot 0.11.0",
 ]
 
 [[package]]
@@ -1898,6 +2051,18 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "socket2"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "winapi",
+]
+
 [[package]]
 name = "spin"
 version = "0.5.2"
@@ -1934,6 +2099,24 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "strum"
+version = "0.19.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b89a286a7e3b5720b9a477b23253bc50debac207c8d21505f8e70b36792f11b5"
+
+[[package]]
+name = "strum_macros"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "syn"
 version = "1.0.42"
diff --git a/Cargo.toml b/Cargo.toml
index 0e0a33e256e2911d8862e2b026ddab3e6477a749..90a051b9c4c06f1dd6f307707e18fe04c1974128 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,6 +29,7 @@ rusty-hook = "0.11.2"
 [workspace]
 members = [
     "neon/native",
+    "rust-bins/duniter-dbex",
     "rust-bins/xtask",
     "rust-libs/dubp-wot",
     "rust-libs/duniter-dbs",
diff --git a/rust-bins/duniter-dbex/Cargo.toml b/rust-bins/duniter-dbex/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..2cb639e6e9a09eb1132b03bd608625cc96d8be98
--- /dev/null
+++ b/rust-bins/duniter-dbex/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "duniter-dbex"
+version = "0.1.0"
+authors = ["elois <elois@duniter.org>"]
+description = "Duniter blockchain DB"
+repository = "https://git.duniter.org/nodes/typescript/duniter/rust-bins/duniter-dbs-explorer"
+readme = "README.md"
+keywords = ["duniter", "database"]
+license = "AGPL-3.0"
+edition = "2018"
+
+[[bin]]
+bench = false
+path = "src/main.rs"
+name = "dex"
+
+[build-dependencies]
+structopt = "0.3.16"
+
+[dependencies]
+arrayvec = "0.5.1"
+comfy-table = "1.0.0"
+dirs = "3.0.1"
+dubp-common = { version = "0.25.2", features = ["crypto_scrypt"] }
+duniter-dbs = { path = "../../rust-libs/duniter-dbs", default-features = false, features = ["explorer", "leveldb_backend", "sync"] }
+rayon = "1.3.1"
+serde_json = "1.0.53"
+structopt = "0.3.16"
+
+[dev-dependencies]
+serde = { version = "1.0.105", features = ["derive"] }
+unwrap = "1.2.1"
diff --git a/rust-bins/duniter-dbex/README.md b/rust-bins/duniter-dbex/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f49c48fd89510f6efa24c7b495396cac545d7d21
--- /dev/null
+++ b/rust-bins/duniter-dbex/README.md
@@ -0,0 +1,25 @@
+# Duniter databases explorer (dex)
+
+## Compile
+
+    git clone https://git.duniter.org/nodes/typescript/duniter.git
+    cd duniter
+    cargo build --release -p duniter-dbex
+
+The binary executable is then here:  `target/release/dex`
+
+## Use
+
+See `dex --help`
+
+## Autocompletion
+
+Bash autocompletion script is available here : `target/release/dex.bash`
+
+**Several others Shell are supported : Zsh, Fish, Powershell and Elvish!**
+
+To generate the autocompletion script for your shell, recompile with env var `COMPLETION_SHELL`.
+
+For exemple for fish : `COMPLETION_SHELL=fish cargo build --release -p duniter-dbex`
+
+The autocompletion script can be found in : `target/release/`
diff --git a/rust-bins/duniter-dbex/build.rs b/rust-bins/duniter-dbex/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..dd655380a11772f3baafcd6d14fcde679dcf07f3
--- /dev/null
+++ b/rust-bins/duniter-dbex/build.rs
@@ -0,0 +1,47 @@
+//  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/>.
+
+extern crate structopt;
+
+include!("src/cli.rs");
+
+use std::env;
+use structopt::clap::Shell;
+
+fn main() {
+    // Define out dir
+    let current_dir = match env::current_dir() {
+        Err(_e) => return,
+        Ok(current_dir) => current_dir,
+    };
+    let out_dir = current_dir.as_path().join(format!(
+        "../../target/{}",
+        env::var("PROFILE").unwrap_or_else(|_| "debug".to_owned())
+    ));
+
+    // Define shell
+    let shell = if let Some(shell_str) = option_env!("COMPLETION_SHELL") {
+        Shell::from_str(shell_str).expect("Unknown shell")
+    } else {
+        Shell::Bash
+    };
+
+    let mut app = Opt::clap();
+    app.gen_completions(
+        "dex", // We need to specify the bin name manually
+        shell, // Then say which shell to build completions for
+        out_dir,
+    ); // Then say where write the completions to
+}
diff --git a/rust-bins/duniter-dbex/src/cli.rs b/rust-bins/duniter-dbex/src/cli.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e5da817361b9acf60b25c2fe70165c2feee7d277
--- /dev/null
+++ b/rust-bins/duniter-dbex/src/cli.rs
@@ -0,0 +1,125 @@
+//  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 std::{num::NonZeroUsize, path::PathBuf, str::FromStr};
+use structopt::StructOpt;
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "duniter-dbex", about = "Duniter databases explorer.")]
+pub struct Opt {
+    /// Duniter profile name
+    #[structopt(short, long)]
+    pub profile: Option<String>,
+
+    /// Duniter home directory
+    #[structopt(short, long, parse(from_os_str))]
+    pub home: Option<PathBuf>,
+
+    /// database
+    #[structopt(default_value = "bc_v1", possible_values = &["bc_v1", "bc_v2", "mp_v1"])]
+    pub database: Database,
+
+    #[structopt(subcommand)]
+    pub cmd: SubCommand,
+}
+
+#[derive(Debug)]
+pub enum Database {
+    BcV1,
+}
+
+impl FromStr for Database {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "bc_v1" => Ok(Self::BcV1),
+            "bc_v2" | "mp_v1" => unimplemented!(),
+            _ => unreachable!(),
+        }
+    }
+}
+
+#[derive(Debug, StructOpt)]
+pub enum SubCommand {
+    /// Count collection entries
+    Count { collection: String },
+    /// Get one value
+    Get { collection: String, key: String },
+    /// Search values by criteria
+    Find {
+        collection: String,
+        #[structopt(short, long)]
+        /// Key min
+        start: Option<String>,
+        #[structopt(short, long)]
+        /// Key max
+        end: Option<String>,
+        /// Filter keys by a regular expression
+        #[structopt(short = "k", long)]
+        key_regex: Option<String>,
+        /// Show keys only
+        #[structopt(long)]
+        keys_only: bool,
+        /// Filter values by a regular expression
+        #[structopt(short = "v", long)]
+        value_regex: Option<String>,
+        /// Maximum number of entries to be found (Slower because force sequential search)
+        #[structopt(short, long)]
+        limit: Option<usize>,
+        /// Browse the collection upside down
+        #[structopt(short, long)]
+        reverse: bool,
+        /// Step by
+        #[structopt(long, default_value = "1")]
+        step: NonZeroUsize,
+        /// Output format
+        #[structopt(short, long, default_value = "table-json", possible_values = &["csv", "json", "table-json", "table-properties"])]
+        output: OutputFormat,
+        /// Pretty json (Only for output format json or table-json)
+        #[structopt(long)]
+        pretty: bool,
+        /// Show only the specified properties
+        #[structopt(short, long)]
+        properties: Vec<String>,
+        /// Export found data to a file
+        #[structopt(short, long, parse(from_os_str))]
+        file: Option<PathBuf>,
+    },
+    /// Show database schema
+    Schema,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum OutputFormat {
+    Table,
+    TableJson,
+    Json,
+    Csv,
+}
+
+impl FromStr for OutputFormat {
+    type Err = String;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "csv" => Ok(Self::Csv),
+            "json" => Ok(Self::Json),
+            "table-properties" => Ok(Self::Table),
+            "table-json" => Ok(Self::TableJson),
+            _ => unreachable!(),
+        }
+    }
+}
diff --git a/rust-bins/duniter-dbex/src/main.rs b/rust-bins/duniter-dbex/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5d6b646645ed63f67953bf2f663e503804e0a44b
--- /dev/null
+++ b/rust-bins/duniter-dbex/src/main.rs
@@ -0,0 +1,305 @@
+//  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/>.
+
+#![deny(
+    clippy::unwrap_used,
+    missing_copy_implementations,
+    trivial_casts,
+    trivial_numeric_casts,
+    unstable_features,
+    unused_import_braces
+)]
+
+mod cli;
+mod print_found_data;
+mod stringify_json_value;
+
+use self::cli::{Database, Opt, OutputFormat, SubCommand};
+use self::stringify_json_value::stringify_json_value;
+use comfy_table::Table;
+use duniter_dbs::kv_typed::prelude::*;
+use duniter_dbs::prelude::*;
+use duniter_dbs::regex::Regex;
+use duniter_dbs::serde_json::{Map, Value};
+use duniter_dbs::smallvec::{smallvec, SmallVec};
+use duniter_dbs::BcV1Db;
+use duniter_dbs::BcV1DbWritable;
+use rayon::prelude::*;
+use std::{
+    collections::{HashMap, HashSet},
+    fs::File,
+    io::{stdin, Write},
+    iter::FromIterator,
+    time::Instant,
+};
+use structopt::StructOpt;
+
+const DATA_DIR: &str = "data";
+const TOO_MANY_ENTRIES_ALERT: usize = 5_000;
+
+fn main() -> Result<(), String> {
+    let opt = Opt::from_args();
+
+    let home = if let Some(home) = opt.home {
+        home
+    } else {
+        dirs::config_dir()
+            .ok_or_else(|| {
+                "Fail to auto find duniter's home directory, please specify it explicitly."
+                    .to_owned()
+            })?
+            .as_path()
+            .join("duniter")
+    };
+
+    let profile_name = if let Some(profile_name) = opt.profile {
+        profile_name
+    } else {
+        "duniter_default".to_owned()
+    };
+
+    let profile_path = home.as_path().join(&profile_name);
+    let data_path = profile_path.as_path().join(DATA_DIR);
+
+    if !data_path.exists() {
+        return Err(format!(
+            "Path '{}' don't exist !",
+            data_path.to_str().expect("non-UTF-8 strings not supported")
+        ));
+    }
+
+    let open_db_start_time = Instant::now();
+    match opt.database {
+        Database::BcV1 => apply_subcommand(
+            BcV1Db::<LevelDb>::open(LevelDbConf {
+                db_path: data_path.as_path().join("leveldb"),
+                ..Default::default()
+            })
+            .map_err(|e| format!("{}", e))?,
+            opt.cmd,
+            open_db_start_time,
+        ),
+    }
+}
+
+fn apply_subcommand<DB: DbExplorable>(
+    db: DB,
+    cmd: SubCommand,
+    open_db_start_time: Instant,
+) -> Result<(), String> {
+    let duration = open_db_start_time.elapsed();
+    println!(
+        "Database opened in {}.{:06} seconds.",
+        duration.as_secs(),
+        duration.subsec_micros()
+    );
+    let start_time = Instant::now();
+
+    match cmd {
+        SubCommand::Count { collection } => {
+            if let ExplorerActionResponse::Count(count) = db
+                .explore(&collection, ExplorerAction::Count, stringify_json_value)
+                .map_err(|e| format!("{}", e))?
+                .map_err(|e| e.0)?
+            {
+                let duration = start_time.elapsed();
+                println!(
+                    "Count operation performed in {}.{:06} seconds.",
+                    duration.as_secs(),
+                    duration.subsec_micros()
+                );
+                println!("\nThis collection contains {} entries.", count);
+            }
+        }
+        SubCommand::Get { collection, key } => {
+            if let ExplorerActionResponse::Get(value_opt) = db
+                .explore(
+                    &collection,
+                    ExplorerAction::Get { key: &key },
+                    stringify_json_value,
+                )
+                .map_err(|e| format!("{}", e))?
+                .map_err(|e| e.0)?
+            {
+                if let Some(value) = value_opt {
+                    println!("\n{}", value)
+                } else {
+                    println!("\nThis collection not contains this key.")
+                }
+            }
+        }
+        SubCommand::Find {
+            collection,
+            start,
+            end,
+            key_regex,
+            keys_only,
+            value_regex,
+            limit,
+            reverse,
+            properties,
+            output: output_format,
+            pretty: pretty_json,
+            file: output_file,
+            step,
+        } => {
+            let value_regex_opt = opt_string_to_res_opt_regex(value_regex)?;
+            let captures_headers = if let Some(ref value_regex) = value_regex_opt {
+                value_regex
+                    .capture_names()
+                    .skip(1)
+                    .enumerate()
+                    .map(|(i, name_opt)| {
+                        if let Some(name) = name_opt {
+                            name.to_owned()
+                        } else {
+                            format!("CAP{}", i + 1)
+                        }
+                    })
+                    .collect()
+            } else {
+                vec![]
+            };
+            if let ExplorerActionResponse::Find(entries) = db
+                .explore(
+                    &collection,
+                    ExplorerAction::Find {
+                        key_min: start,
+                        key_max: end,
+                        key_regex: opt_string_to_res_opt_regex(key_regex)?,
+                        value_regex: value_regex_opt,
+                        limit,
+                        reverse,
+                        step,
+                    },
+                    stringify_json_value,
+                )
+                .map_err(|e| format!("{}", e))?
+                .map_err(|e| e.0)?
+            {
+                let duration = start_time.elapsed();
+                println!(
+                    "Search performed in {}.{:06} seconds.\n\n{} entries found.",
+                    duration.as_secs(),
+                    duration.subsec_micros(),
+                    entries.len()
+                );
+
+                if !too_many_entries(entries.len(), output_file.is_none())
+                    .map_err(|e| format!("{}", e))?
+                {
+                    return Ok(());
+                }
+
+                let start_print = Instant::now();
+                if let Some(output_file) = output_file {
+                    let mut file =
+                        File::create(output_file.as_path()).map_err(|e| format!("{}", e))?;
+
+                    //let mut file_buffer = BufWriter::new(file);
+                    print_found_data::print_found_data(
+                        &mut file,
+                        output_format,
+                        pretty_json,
+                        false,
+                        print_found_data::DataToShow {
+                            entries,
+                            keys_only,
+                            only_properties: properties,
+                        },
+                        captures_headers,
+                    )
+                    .map_err(|e| format!("{}", e))?;
+                    //file_buffer.flush().map_err(|e| format!("{}", e))?;
+
+                    let export_duration = start_print.elapsed();
+                    println!(
+                        "Search results were written to file: '{}' in {}.{:06} seconds.",
+                        output_file
+                            .to_str()
+                            .expect("output-file contains invalid utf8 characters"),
+                        export_duration.as_secs(),
+                        export_duration.subsec_micros(),
+                    );
+                } else {
+                    print_found_data::print_found_data(
+                        &mut std::io::stdout(),
+                        output_format,
+                        pretty_json,
+                        true,
+                        print_found_data::DataToShow {
+                            entries,
+                            keys_only,
+                            only_properties: properties,
+                        },
+                        captures_headers,
+                    )
+                    .map_err(|e| format!("{}", e))?;
+                    let print_duration = start_print.elapsed();
+                    println!(
+                        "Search results were displayed in {}.{:06} seconds.",
+                        print_duration.as_secs(),
+                        print_duration.subsec_micros(),
+                    );
+                };
+            }
+        }
+        SubCommand::Schema => {
+            show_db_schema(db.list_collections());
+        }
+    };
+
+    Ok(())
+}
+
+fn too_many_entries(entries_len: usize, output_in_term: bool) -> std::io::Result<bool> {
+    if entries_len > TOO_MANY_ENTRIES_ALERT {
+        println!(
+            "{} all {} entries ? (Be careful, may crash your system!) [y/N]",
+            if output_in_term { "Display" } else { "Export" },
+            entries_len
+        );
+        let mut buffer = String::new();
+        stdin().read_line(&mut buffer)?;
+        Ok(buffer == "y\n")
+    } else {
+        Ok(true)
+    }
+}
+
+fn show_db_schema(collections_names: Vec<(&'static str, &'static str, &'static str)>) {
+    let mut table = Table::new();
+    table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
+    table.set_header(&["Collection name", "Key type", "Value type"]);
+    for (collection_name, key_full_type_name, value_full_type_name) in collections_names {
+        let key_type_name_opt = key_full_type_name.split(':').last();
+        let value_type_name_opt = value_full_type_name.split(':').last();
+        table.add_row(&[
+            collection_name,
+            key_type_name_opt.unwrap_or("?"),
+            value_type_name_opt.unwrap_or("?"),
+        ]);
+    }
+    println!("{}", table);
+}
+
+#[inline]
+fn opt_string_to_res_opt_regex(str_regex_opt: Option<String>) -> Result<Option<Regex>, String> {
+    if let Some(str_regex) = str_regex_opt {
+        Ok(Some(Regex::new(&str_regex).map_err(|e| format!("{}", e))?))
+    } else {
+        Ok(None)
+    }
+}
diff --git a/rust-bins/duniter-dbex/src/print_found_data.rs b/rust-bins/duniter-dbex/src/print_found_data.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0c600ef0b2ab22b7831a79a1ec0f83d4558ebaa0
--- /dev/null
+++ b/rust-bins/duniter-dbex/src/print_found_data.rs
@@ -0,0 +1,383 @@
+//  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::*;
+
+const KEY_COLUMN_NAME: &str = "Key";
+const VALUE_COLUMN_NAME: &str = "Value";
+
+pub struct DataToShow {
+    pub entries: Vec<EntryFound>,
+    pub keys_only: bool,
+    pub only_properties: Vec<String>,
+}
+
+pub fn print_found_data<W: Write>(
+    output: &mut W,
+    output_format: OutputFormat,
+    pretty_json: bool,
+    dynamic_content_arrangement: bool,
+    data_to_show: DataToShow,
+    captures_names: Vec<String>,
+) -> std::io::Result<()> {
+    let DataToShow {
+        entries,
+        keys_only,
+        only_properties,
+    } = data_to_show;
+    if entries.is_empty() {
+        return Ok(());
+    }
+
+    let only_properties_set = if !only_properties.is_empty() {
+        HashSet::from_iter(only_properties.into_iter())
+    } else {
+        HashSet::with_capacity(0)
+    };
+
+    match output_format {
+        OutputFormat::Json => {
+            let json_array = if keys_only {
+                entries
+                    .into_par_iter()
+                    .map(|entry| key_to_json(entry.key))
+                    .collect()
+            } else {
+                entries
+                    .into_par_iter()
+                    .map(|entry| entry_to_json(&only_properties_set, &captures_names, entry))
+                    .collect()
+            };
+            if pretty_json {
+                writeln!(output, "{:#}", Value::Array(json_array))
+            } else {
+                writeln!(output, "{}", Value::Array(json_array))
+            }
+        }
+        OutputFormat::Table => {
+            // If value is not an object or an array of objects, force raw output format
+            let mut entries_iter = entries.iter();
+            let first_object_opt = if keys_only {
+                None
+            } else {
+                loop {
+                    if let Some(EntryFound { value, .. }) = entries_iter.next() {
+                        if let Value::Array(ref json_array) = value {
+                            if json_array.is_empty() {
+                                continue;
+                            } else {
+                                break json_array[0].as_object();
+                            }
+                        } else {
+                            break value.as_object();
+                        }
+                    } else {
+                        // All values are empty array, force raw output format
+                        break None;
+                    }
+                }
+            };
+
+            let properties_names = if let Some(first_object) = first_object_opt {
+                if only_properties_set.is_empty() {
+                    first_object.keys().cloned().collect::<HashSet<String>>()
+                } else {
+                    first_object
+                        .keys()
+                        .filter(|property_name| {
+                            only_properties_set.contains(property_name.as_str())
+                        })
+                        .cloned()
+                        .collect::<HashSet<String>>()
+                }
+            } else {
+                return print_found_data(
+                    output,
+                    OutputFormat::TableJson,
+                    pretty_json,
+                    dynamic_content_arrangement,
+                    print_found_data::DataToShow {
+                        entries,
+                        keys_only,
+                        only_properties: vec![],
+                    },
+                    captures_names,
+                );
+            };
+
+            // Create table
+            let mut table = Table::new();
+            if dynamic_content_arrangement {
+                table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
+            }
+
+            // Map data by property
+            let entries_map: Vec<HashMap<String, String>> = entries
+                .into_par_iter()
+                .map(|entry| entry_to_rows(&only_properties_set, &captures_names, entry))
+                .flatten()
+                .collect();
+
+            // Define table headers
+            let mut headers = Vec::with_capacity(1 + properties_names.len() + captures_names.len());
+            headers.push(KEY_COLUMN_NAME.to_owned());
+            if !keys_only {
+                for property_name in properties_names {
+                    headers.push(property_name);
+                }
+                headers.sort_by(|a, b| {
+                    if a == KEY_COLUMN_NAME {
+                        std::cmp::Ordering::Less
+                    } else if b == KEY_COLUMN_NAME {
+                        std::cmp::Ordering::Greater
+                    } else {
+                        a.cmp(b)
+                    }
+                });
+                headers.extend(captures_names);
+            }
+            table.set_header(&headers);
+
+            // Fill table
+            for properties_values in entries_map {
+                let mut row = SmallVec::<[&str; 8]>::new();
+                for column_name in &headers {
+                    if let Some(property_value) = properties_values.get(column_name) {
+                        row.push(property_value.as_str());
+                    } else {
+                        row.push("")
+                    }
+                }
+                table.add_row(row);
+            }
+
+            // Print table
+            writeln!(output, "{}", table)
+        }
+        OutputFormat::TableJson => {
+            let mut table = Table::new();
+            if dynamic_content_arrangement {
+                table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
+            }
+            let mut headers = Vec::with_capacity(2 + captures_names.len());
+            headers.push(KEY_COLUMN_NAME);
+            if !keys_only {
+                headers.push(VALUE_COLUMN_NAME);
+            }
+            headers.extend(captures_names.iter().map(String::as_str));
+            table.set_header(headers);
+            for EntryFound {
+                key,
+                value,
+                captures: captures_opt,
+            } in entries
+            {
+                let mut row = Vec::with_capacity(2 + captures_names.len());
+                row.push(key);
+                if keys_only {
+                    table.add_row(row);
+                } else {
+                    if pretty_json {
+                        row.push(format!("{:#}", value));
+                    } else {
+                        row.push(value.to_string());
+                    }
+                    let rows = caps_to_rows(row, &captures_names[..], captures_opt);
+                    for row in rows {
+                        table.add_row(row);
+                    }
+                }
+            }
+            writeln!(output, "{}", table)
+        }
+        _ => todo!(),
+    }
+}
+
+#[inline(always)]
+fn key_to_json(key: String) -> Value {
+    Value::String(key)
+}
+
+fn entry_to_json(
+    only_properties_set: &HashSet<String>,
+    captures_names: &[String],
+    entry: EntryFound,
+) -> Value {
+    let EntryFound {
+        key,
+        mut value,
+        captures: captures_opt,
+    } = entry;
+    if !only_properties_set.is_empty() {
+        match value {
+            Value::Object(ref mut json_map) => {
+                let mut properties_to_rm = SmallVec::<[String; 64]>::new();
+                for property_name in json_map.keys() {
+                    if !only_properties_set.contains(property_name) {
+                        properties_to_rm.push(property_name.clone());
+                    }
+                }
+                for property_name in properties_to_rm {
+                    json_map.remove(&property_name);
+                }
+            }
+            Value::Array(ref mut json_array) => {
+                for sub_value in json_array {
+                    if let Value::Object(ref mut json_map) = sub_value {
+                        let mut properties_to_rm = SmallVec::<[String; 64]>::new();
+                        for property_name in json_map.keys() {
+                            if !only_properties_set.contains(property_name) {
+                                properties_to_rm.push(property_name.clone());
+                            }
+                        }
+                        for property_name in properties_to_rm {
+                            json_map.remove(&property_name);
+                        }
+                    }
+                }
+            }
+            _ => (),
+        }
+    }
+    let mut json_map = Map::with_capacity(2);
+    json_map.insert("key".to_owned(), Value::String(key));
+    json_map.insert("value".to_owned(), value);
+    if !captures_names.is_empty() {
+        let mut captures_objects = Vec::new();
+        if let Some(ValueCaptures(captures)) = captures_opt {
+            for capture in captures {
+                let mut capture_object = Map::with_capacity(captures_names.len());
+                for (i, capture_group_value_opt) in capture.into_iter().enumerate() {
+                    if let Some(capture_group_value) = capture_group_value_opt {
+                        capture_object.insert(
+                            captures_names[i].to_owned(),
+                            Value::String(capture_group_value),
+                        );
+                    }
+                }
+                captures_objects.push(Value::Object(capture_object));
+            }
+        }
+        json_map.insert("captures".to_owned(), Value::Array(captures_objects));
+    }
+    Value::Object(json_map)
+}
+
+fn entry_to_rows(
+    only_properties_set: &HashSet<String>,
+    captures_names: &[String],
+    entry: EntryFound,
+) -> Vec<HashMap<String, String>> {
+    let EntryFound {
+        key,
+        value,
+        captures: captures_opt,
+    } = entry;
+    match value {
+        Value::Object(value_json_map) => {
+            let row_map = map_entry_by_properties(&only_properties_set, key, value_json_map);
+            caps_to_rows_maps(row_map, captures_names, captures_opt)
+        }
+        Value::Array(json_array) => json_array
+            .into_iter()
+            .map(|sub_value| {
+                if let Value::Object(sub_value_json_map) = sub_value {
+                    map_entry_by_properties(&only_properties_set, key.clone(), sub_value_json_map)
+                } else {
+                    unreachable!()
+                }
+            })
+            .collect(),
+        _ => unreachable!(),
+    }
+}
+
+fn map_entry_by_properties(
+    only_properties_set: &HashSet<String>,
+    k: String,
+    value_json_map: Map<String, Value>,
+) -> HashMap<String, String> {
+    let mut row_map = HashMap::with_capacity(1 + value_json_map.len());
+    row_map.insert(KEY_COLUMN_NAME.to_owned(), k);
+    for (property_name, property_value) in value_json_map {
+        if only_properties_set.is_empty() || only_properties_set.contains(&property_name) {
+            if let Value::String(property_value_string) = property_value {
+                row_map.insert(property_name, property_value_string);
+            } else {
+                row_map.insert(property_name, property_value.to_string());
+            }
+        }
+    }
+    row_map
+}
+
+fn caps_to_rows(
+    mut first_row_begin: Vec<String>,
+    captures_names: &[String],
+    captures_opt: Option<ValueCaptures>,
+) -> SmallVec<[Vec<String>; 2]> {
+    if !captures_names.is_empty() {
+        if let Some(ValueCaptures(captures)) = captures_opt {
+            let first_row_begin_len = first_row_begin.len();
+            let mut rows = SmallVec::with_capacity(captures.len());
+            let mut current_row = first_row_begin;
+            for capture in captures {
+                for capture_group_value_opt in capture.into_iter() {
+                    if let Some(capture_group_value) = capture_group_value_opt {
+                        current_row.push(capture_group_value);
+                    } else {
+                        current_row.push(String::new());
+                    }
+                }
+                rows.push(current_row);
+                current_row = (0..first_row_begin_len).map(|_| String::new()).collect();
+            }
+            rows
+        } else {
+            first_row_begin.extend((0..captures_names.len()).map(|_| String::new()));
+            smallvec![first_row_begin]
+        }
+    } else {
+        smallvec![first_row_begin]
+    }
+}
+
+fn caps_to_rows_maps(
+    first_row_map_begin: HashMap<String, String>,
+    captures_names: &[String],
+    captures_opt: Option<ValueCaptures>,
+) -> Vec<HashMap<String, String>> {
+    if !captures_names.is_empty() {
+        if let Some(ValueCaptures(captures)) = captures_opt {
+            let mut rows = Vec::with_capacity(captures.len());
+            let mut current_row_map = first_row_map_begin;
+            for capture in captures {
+                for (i, capture_group_value_opt) in capture.into_iter().enumerate() {
+                    if let Some(capture_group_value) = capture_group_value_opt {
+                        current_row_map.insert(captures_names[i].to_owned(), capture_group_value);
+                    }
+                }
+                rows.push(current_row_map);
+                current_row_map = HashMap::with_capacity(captures_names.len());
+            }
+            rows
+        } else {
+            vec![first_row_map_begin]
+        }
+    } else {
+        vec![first_row_map_begin]
+    }
+}
diff --git a/rust-bins/duniter-dbex/src/stringify_json_value.rs b/rust-bins/duniter-dbex/src/stringify_json_value.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7070c166c5baa8cd8b7b0fda2be5f66f321ad853
--- /dev/null
+++ b/rust-bins/duniter-dbex/src/stringify_json_value.rs
@@ -0,0 +1,219 @@
+use arrayvec::ArrayVec;
+use dubp_common::crypto::bases::b58::ToBase58 as _;
+use dubp_common::crypto::hashs::Hash;
+use dubp_common::crypto::keys::ed25519::{PublicKey, Signature};
+use dubp_common::crypto::keys::Signature as _;
+use std::convert::TryFrom;
+
+pub fn stringify_json_value(mut json_value: serde_json::Value) -> serde_json::Value {
+    match json_value {
+        serde_json::Value::Object(ref mut json_obj) => stringify_json_object(json_obj),
+        serde_json::Value::Array(ref mut json_array) => {
+            for json_array_cell in json_array {
+                if let serde_json::Value::Object(json_obj) = json_array_cell {
+                    stringify_json_object(json_obj)
+                }
+            }
+        }
+        _ => (),
+    }
+
+    json_value
+}
+
+fn stringify_json_object(json_object: &mut serde_json::Map<String, serde_json::Value>) {
+    let mut stringified_values: Vec<(String, serde_json::Value)> = Vec::new();
+    for (k, v) in json_object.iter_mut() {
+        match k.as_str() {
+            "pub" | "pubkey" | "issuer" => {
+                if let serde_json::Value::Object(json_pubkey) = v {
+                    let json_pubkey_data = json_pubkey.get("datas").expect("corrupted db");
+                    if let serde_json::Value::Array(json_array) = json_pubkey_data {
+                        let pubkey_string =
+                            PublicKey::try_from(&json_array_to_32_bytes(json_array)[..])
+                                .expect("corrupted db")
+                                .to_base58();
+                        stringified_values
+                            .push((k.to_owned(), serde_json::Value::String(pubkey_string)));
+                    } else {
+                        panic!("corrupted db");
+                    }
+                }
+            }
+            "hash" | "inner_hash" | "previous_hash" => {
+                if let serde_json::Value::Array(json_array) = v {
+                    let hash_string = Hash(json_array_to_32_bytes(json_array)).to_hex();
+                    stringified_values.push((k.to_owned(), serde_json::Value::String(hash_string)));
+                }
+            }
+            "sig" | "signature" => {
+                if let serde_json::Value::Array(json_array) = v {
+                    let sig_string = Signature(json_array_to_64_bytes(json_array)).to_base64();
+                    stringified_values.push((k.to_owned(), serde_json::Value::String(sig_string)));
+                }
+            }
+            _ => {
+                if let serde_json::Value::Object(ref mut json_sub_object) = v {
+                    stringify_json_object(json_sub_object)
+                }
+            }
+        }
+    }
+    for (k, v) in stringified_values {
+        json_object.insert(k, v);
+    }
+}
+
+#[inline]
+fn json_array_to_32_bytes(json_array: &[serde_json::Value]) -> [u8; 32] {
+    let bytes = json_array
+        .iter()
+        .map(|jv| {
+            if let serde_json::Value::Number(jn) = jv {
+                jn.as_u64().unwrap_or_default() as u8
+            } else {
+                panic!("corrupted db")
+            }
+        })
+        .collect::<ArrayVec<[u8; 32]>>();
+    bytes.into_inner().expect("corrupted db")
+}
+
+#[inline]
+fn json_array_to_64_bytes(json_array: &[serde_json::Value]) -> [u8; 64] {
+    let bytes = json_array
+        .iter()
+        .map(|jv| {
+            if let serde_json::Value::Number(jn) = jv {
+                jn.as_u64().unwrap_or_default() as u8
+            } else {
+                panic!("corrupted db")
+            }
+        })
+        .collect::<ArrayVec<[u8; 64]>>();
+    bytes.into_inner().expect("corrupted db")
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use dubp_common::crypto::keys::PublicKey as _;
+    use serde_json::Number;
+    use serde_json::Value;
+    use unwrap::unwrap;
+
+    #[derive(serde::Serialize)]
+    struct JsonObjectTest {
+        pubkey: PublicKey,
+        hash: Hash,
+        other: usize,
+        inner: JsonSubObjectTest,
+    }
+
+    #[derive(serde::Serialize)]
+    struct JsonSubObjectTest {
+        issuer: PublicKey,
+    }
+
+    #[test]
+    fn test_stringify_json_object() {
+        let mut json_value = unwrap!(serde_json::to_value(JsonObjectTest {
+            pubkey: unwrap!(PublicKey::from_base58(
+                "A2C6cVJnkkT2n4ivMPiLH2njQHeHSZcVf1cSTwZYScQ6"
+            )),
+            hash: unwrap!(Hash::from_hex(
+                "51DF2FCAB8809596253CD98594D0DBCEECAAF3A88A43C6EDD285B6B24FB9D50D"
+            )),
+            other: 3,
+            inner: JsonSubObjectTest {
+                issuer: unwrap!(PublicKey::from_base58(
+                    "4agK3ycEQNahuRGoFJDXA2aQGt4iV2YSMPKcoMeR6ZfA"
+                ))
+            }
+        }));
+
+        if let serde_json::Value::Object(ref mut json_obj) = json_value {
+            stringify_json_object(json_obj);
+
+            assert_eq!(
+                json_obj.get("pubkey"),
+                Some(Value::String(
+                    "A2C6cVJnkkT2n4ivMPiLH2njQHeHSZcVf1cSTwZYScQ6".to_owned()
+                ))
+                .as_ref()
+            );
+            assert_eq!(
+                json_obj.get("hash"),
+                Some(Value::String(
+                    "51DF2FCAB8809596253CD98594D0DBCEECAAF3A88A43C6EDD285B6B24FB9D50D".to_owned()
+                ))
+                .as_ref()
+            );
+            assert_eq!(
+                json_obj.get("other"),
+                Some(Value::Number(Number::from(3))).as_ref()
+            );
+
+            let json_sub_obj = unwrap!(json_obj.get("inner"));
+            if let serde_json::Value::Object(json_sub_obj) = json_sub_obj {
+                assert_eq!(
+                    json_sub_obj.get("issuer"),
+                    Some(Value::String(
+                        "4agK3ycEQNahuRGoFJDXA2aQGt4iV2YSMPKcoMeR6ZfA".to_owned()
+                    ))
+                    .as_ref()
+                );
+            } else {
+                panic!("json_sub_obj must be an abject");
+            }
+        } else {
+            panic!("json_value must be an abject");
+        }
+    }
+
+    #[test]
+    fn test_json_array_to_32_bytes() {
+        let json_array = vec![
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+            Value::Number(Number::from(2)),
+            Value::Number(Number::from(0)),
+            Value::Number(Number::from(1)),
+        ];
+
+        assert_eq!(
+            [
+                0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0,
+                1, 2, 0, 1
+            ],
+            json_array_to_32_bytes(json_array.as_ref()),
+        )
+    }
+}