diff --git a/Cargo.lock b/Cargo.lock
index bec05c6ea47a8ece731eaee0e898523d8379466d..3332a6a21e57245130de04a0a4f1616d15182949 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2402,6 +2402,8 @@ dependencies = [
  "num-traits 0.2.19",
  "parity-scale-codec",
  "rayon",
+ "serde",
+ "serde_json",
  "simple_logger",
  "sp-core",
  "sp-distance",
diff --git a/distance-oracle/Cargo.toml b/distance-oracle/Cargo.toml
index 3f7614e3e36e6ddd85b76bf55a5df00e11bd59c1..34a63f798fda9c271d8c1c0140b69aac6598c063 100644
--- a/distance-oracle/Cargo.toml
+++ b/distance-oracle/Cargo.toml
@@ -22,6 +22,7 @@ std = [
 	"sp-core/std",
 	"sp-distance/std",
 	"sp-runtime/std",
+	"serde_json/std",
 ]
 try-runtime = ["sp-distance/try-runtime", "sp-runtime/try-runtime"]
 runtime-benchmarks = []
@@ -47,6 +48,8 @@ tokio = { workspace = true, features = [
 	"rt-multi-thread",
 	"macros",
 ], optional = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
 
 [dev-dependencies]
 bincode = { workspace = true }
diff --git a/distance-oracle/src/bin/distance_precompute.rs b/distance-oracle/src/bin/distance_precompute.rs
index cd07d7b23346628d588804e02a9f34fe37583309..1ffb8727cd8eb183020ce786060e226e76b738e9 100644
--- a/distance-oracle/src/bin/distance_precompute.rs
+++ b/distance-oracle/src/bin/distance_precompute.rs
@@ -1,4 +1,8 @@
+use api::IdtyIndex;
 use distance_oracle::api;
+use fnv::FnvHashMap;
+use log::debug;
+use std::{io::Write, path::PathBuf};
 
 // computes distance result for all identities with status other than Revoked
 // this allows to have a recent estimate of the distance computation
@@ -16,28 +20,114 @@ struct Cli {
     log: log::LevelFilter,
 }
 
+//
+#[derive(serde::Serialize)]
+struct PrecomputationResult {
+    block: sp_core::H256,
+    results: FnvHashMap<IdtyIndex, sp_runtime::Perbill>,
+}
+
 #[tokio::main]
 async fn main() {
+    let cli = Cli::parse();
+
     simple_logger::SimpleLogger::new()
         .with_level(cli.log)
         .init()
         .unwrap();
 
-    let cli = Cli::parse();
-
     let client = &distance_oracle::api::client(cli.rpc_url.clone()).await;
+    let parent_hash = api::parent_hash(client).await; // get hash of recent block
+    let max_depth = api::max_referee_distance(client).await; // get param
+    let evaluation_block = parent_hash;
+
+    // get certs and member iterators
+    let mut certs_iter = api::cert_iter(client, evaluation_block).await;
+    let mut members_iter = api::member_iter(client, evaluation_block).await;
 
-    let parent_hash = api::parent_hash(client).await;
+    // initialize hashmaps
+    // member idty -> issued certs count (will only retain referees)
+    let mut members = FnvHashMap::<IdtyIndex, u32>::default();
 
-    let max_depth = api::max_referee_distance(client).await;
+    // idty -> received certs (collects once from iterator and is passed to distance function)
+    let mut received_certs = FnvHashMap::<IdtyIndex, Vec<IdtyIndex>>::default();
 
-    let current_pool_index = api::current_pool_index(client, parent_hash).await;
+    // collect all members
+    while let Some(member_idty) = members_iter
+        .next()
+        .await
+        .expect("Cannot fetch next members")
+    {
+        members.insert(member_idty, 0);
+    }
 
-    let evaluation_block = parent_hash;
+    // compute certification threshold as a function of the total member count
+    let min_certs_for_referee = (members.len() as f32).powf(1. / (max_depth as f32)).ceil() as u32;
 
-    let mut certs_iter = api::cert_iter(client, evaluation_block).await;
-    let mut members_iter = api::member_iter(client, evaluation_block).await;
+    // collect certifications and updates members map at the same time
+    while let Some((receiver, issuers)) = certs_iter
+        .next()
+        .await
+        .expect("Cannot fetch next certification")
+    {
+        if (issuers.len() as u32) < min_certs_for_referee {
+            // This member is not referee (not enough received certs)
+            members.remove(&receiver);
+        }
+        for (issuer, _removable_on) in issuers.iter() {
+            if let Some(issued_certs) = members.get_mut(issuer) {
+                // update number of issued certs
+                *issued_certs += 1;
+            }
+        }
+        // collect certifications
+        received_certs.insert(
+            receiver,
+            issuers
+                .into_iter()
+                .map(|(issuer, _removable_on)| issuer)
+                .collect(),
+        );
+    }
+
+    // Only retain referees
+    members.retain(|_idty, issued_certs| *issued_certs >= min_certs_for_referee);
+    let referees = members;
+
+    // initialize map of distance results
+    let mut results = FnvHashMap::<IdtyIndex, sp_runtime::Perbill>::default();
+
+    // compute all distances
+    received_certs.keys().into_iter().for_each(|idty| {
+        results.insert(
+            *idty,
+            distance_oracle::distance_rule(&received_certs, &referees, max_depth, *idty),
+        );
+    });
 
-    // TODO
-    unimplemented!()
+    // ---- SAVE
+    let evaluation_result_path =
+        Into::<PathBuf>::into(cli.evaluation_result_dir).join("todo-block-number".to_string());
+    debug!("Saving distance evaluation result to file `{evaluation_result_path:?}`");
+    let mut evaluation_result_file = std::fs::OpenOptions::new()
+        .write(true)
+        .create_new(true)
+        .open(&evaluation_result_path)
+        .unwrap_or_else(|e| {
+            panic!(
+                "Cannot open distance evaluation result file `{evaluation_result_path:?}`: {e:?}"
+            )
+        });
+    evaluation_result_file
+        .write_all(
+            &serde_json::to_vec( &PrecomputationResult {
+                block: evaluation_block,
+                results: results,
+            }).expect("Cannot serialize result"),
+        )
+        .unwrap_or_else(|e| {
+            panic!(
+                "Cannot write distance evaluation result to file `{evaluation_result_path:?}`: {e:?}"
+            )
+        });
 }
diff --git a/distance-oracle/src/lib.rs b/distance-oracle/src/lib.rs
index 965ed8b20ee0d8f79eb261600ae948536b466559..855e1fea9e3a81b98719b6024b62063cf9b0021e 100644
--- a/distance-oracle/src/lib.rs
+++ b/distance-oracle/src/lib.rs
@@ -294,7 +294,7 @@ fn distance_rule_recursive(
 }
 
 /// Returns the fraction `nb_accessible_referees / nb_referees`
-fn distance_rule(
+pub fn distance_rule(
     received_certs: &FnvHashMap<IdtyIndex, Vec<IdtyIndex>>,
     referees: &FnvHashMap<IdtyIndex, u32>,
     depth: u32,