diff --git a/end2end-tests/cucumber-genesis/default.json b/end2end-tests/cucumber-genesis/default.json
index 300b99f219a987ee2eadac2a579fea0d39b881a4..7e1c8ffda3e8c11ccd06de484ccc982bf41f49a7 100644
--- a/end2end-tests/cucumber-genesis/default.json
+++ b/end2end-tests/cucumber-genesis/default.json
@@ -79,7 +79,7 @@
     "Bob",
     "Charlie"
   ],
-  "treasury_funder": "FHNpKmJrUtusuvKPGomAygQqeiks98bdV6yD61Stb6vg",
+  "treasury_funder_pubkey": "FHNpKmJrUtusuvKPGomAygQqeiks98bdV6yD61Stb6vg",
   "ud": 1000,
   "initial_monetary_mass": 3000
 }
\ No newline at end of file
diff --git a/end2end-tests/cucumber-genesis/wot.json b/end2end-tests/cucumber-genesis/wot.json
index 9e59cbbdb724c811c9de48994f50c19435502d3e..948b34aaa99e7ac2a747efa31b1ec68ba0af55aa 100644
--- a/end2end-tests/cucumber-genesis/wot.json
+++ b/end2end-tests/cucumber-genesis/wot.json
@@ -88,7 +88,7 @@
     "Bob",
     "Charlie"
   ],
-  "treasury_funder": "FHNpKmJrUtusuvKPGomAygQqeiks98bdV6yD61Stb6vg",
+  "treasury_funder_pubkey": "FHNpKmJrUtusuvKPGomAygQqeiks98bdV6yD61Stb6vg",
   "ud": 1000,
   "initial_monetary_mass": 4000
 }
\ No newline at end of file
diff --git a/node/src/chain_spec/gen_genesis_data.rs b/node/src/chain_spec/gen_genesis_data.rs
index 613f5cb947fd2dbfad7f306061c885efe5c4bb72..a49285935bac7f0254e635915dfd42a7407eb721 100644
--- a/node/src/chain_spec/gen_genesis_data.rs
+++ b/node/src/chain_spec/gen_genesis_data.rs
@@ -99,10 +99,11 @@ struct GenesisInput<Parameters> {
     #[serde(default)]
     parameters: Option<Parameters>,
     #[serde(rename = "smiths")]
-    smith_identities: Option<BTreeMap<String, SmithData>>,
+    smith_identities: Option<BTreeMap<String, RawSmith>>,
     clique_smiths: Option<Vec<CliqueSmith>>,
     sudo_key: Option<AccountId>,
-    treasury_funder: PubkeyV1,
+    treasury_funder_pubkey: Option<PubkeyV1>,
+    treasury_funder_address: Option<AccountId>,
     technical_committee: Vec<String>,
     ud: u64,
 }
@@ -199,6 +200,17 @@ struct IdentityV2 {
     certs_received: HashMap<String, u32>,
 }
 
+#[derive(Clone, Deserialize, Serialize)]
+struct RawSmith {
+    name: String,
+    /// optional pre-set session keys (at least for the smith bootstraping the blockchain)
+    session_keys: Option<String>,
+    /// optional pre-set account migration
+    migration_address: Option<AccountId>,
+    #[serde(default)]
+    certs_received: Vec<String>,
+}
+
 #[derive(Clone, Deserialize, Serialize)]
 struct SmithData {
     idty_index: u32,
@@ -213,6 +225,7 @@ struct SmithData {
 #[derive(Clone, Deserialize, Serialize)]
 struct CliqueSmith {
     name: String,
+    migration_address: Option<AccountId>,
     session_keys: Option<String>,
 }
 
@@ -239,28 +252,38 @@ where
 {
     let genesis_timestamp: u64 = get_genesis_timestamp()?;
 
+    // Per network input
     let GenesisInput {
         sudo_key,
-        treasury_funder,
+        treasury_funder_pubkey,
+        treasury_funder_address,
         first_ud,
         first_ud_reeval,
         parameters,
-        mut smith_identities,
-        mut clique_smiths,
+        smith_identities,
+        clique_smiths,
         technical_committee,
         ud,
     } = get_genesis_input::<P>(
         std::env::var("DUNITER_GENESIS_CONFIG").unwrap_or_else(|_| json_file_path.to_owned()),
     )?;
-    let treasury_funder = v1_pubkey_to_account_id(treasury_funder)
-        .expect("treasury founder must have a valid public key");
+
+    // Per network parameters
     let common_parameters = get_common_parameters(&parameters);
-    check_smiths_vs_clique_smiths(&smith_identities, &clique_smiths)?;
+
+    // Per network smiths (without link to an account yet — identified by their pseudonym)
+    let mut smiths = build_smiths_wot(&clique_smiths, smith_identities)?;
+
+    // G1 data migration (common to all networks)
     let mut genesis_data = get_genesis_migration_data()?;
     check_parameters_consistency(&genesis_data.wallets, &first_ud, &first_ud_reeval, &ud)?;
-    check_genesis_data_filter_certs(&mut genesis_data, genesis_timestamp, &common_parameters);
+    check_genesis_data_and_filter_expired_certs_since_export(
+        &mut genesis_data,
+        genesis_timestamp,
+        &common_parameters,
+    );
     let mut identities_v2: HashMap<String, IdentityV2> =
-        genesis_data_to_identities_v2(genesis_data.identities, genesis_timestamp);
+        genesis_data_to_identities_v2(genesis_data.identities, genesis_timestamp, &smiths);
     check_identities_v2(&identities_v2, &common_parameters);
 
     // MONEY AND WOT //
@@ -281,14 +304,10 @@ where
     let mut technical_committee_members: Vec<AccountId> = Vec::new();
     // memberships
     let mut memberships = BTreeMap::new();
-    // identities
-    let mut identities: Vec<GenesisMemberIdentity> = Vec::new();
     // certifications
     let mut certs_by_receiver = BTreeMap::new();
     // initial authorities
     let mut initial_authorities = BTreeMap::new();
-    // session keys
-    let mut session_keys_map = BTreeMap::new();
     //let mut total_dust = 0;
 
     // FORCED AUTHORITY //
@@ -297,8 +316,7 @@ where
     if let Some(authority_name) = &maybe_force_authority {
         make_authority_exist::<SessionKeys, SKP>(
             &mut identities_v2,
-            &mut smith_identities,
-            &mut clique_smiths,
+            &mut smiths,
             &common_parameters,
             authority_name,
         );
@@ -324,8 +342,7 @@ where
     }
 
     // IDENTITIES //
-    let was_fatal = feed_identities(
-        &mut identities,
+    let (was_fatal, identities) = feed_identities(
         &mut accounts,
         &mut identity_index,
         &mut monetary_mass,
@@ -348,20 +365,10 @@ where
 
     // Authorities
     if let Some(name) = &maybe_force_authority {
-        check_authority_exists_in_both_wots(
-            name,
-            &identities_v2,
-            &clique_smiths,
-            &smith_identities,
-        );
+        check_authority_exists_in_both_wots(name, &identities_v2, &smiths);
     }
 
-    let smiths = build_smiths_wot(
-        &clique_smiths,
-        &identity_index,
-        &identities_v2,
-        smith_identities,
-    );
+    let smiths = decorate_smiths_with_identity(smiths, &identity_index, &identities_v2);
 
     // counter for online authorities at genesis
     let (
@@ -370,9 +377,9 @@ where
         counter_smith_cert,
         smith_certs_by_receiver,
         smith_memberships,
+        session_keys_map,
     ) = create_smith_wot(
         &mut initial_authorities,
-        &mut session_keys_map,
         &identities_v2,
         &smiths,
         &common_parameters,
@@ -433,6 +440,13 @@ where
     }
 
     // treasury balance must come from existing money
+    let treasury_funder: AccountId = match (treasury_funder_address, treasury_funder_pubkey) {
+        (Some(address), None) => address,
+        (None, Some(pubkey)) => {
+            v1_pubkey_to_account_id(pubkey).expect("treasury founder must have a valid public key")
+        }
+        _ => panic!("One of treasury_funder_address or treasury_funder_pubkey must be set"),
+    };
     if let Some(existing_account) = accounts.get_mut(&treasury_funder) {
         existing_account.balance = existing_account
             .balance
@@ -639,7 +653,6 @@ fn some_more_checks<SK: Decode>(
 
 fn create_smith_wot<SK: Decode>(
     initial_authorities: &mut BTreeMap<u32, (AccountId32, bool)>,
-    session_keys_map: &mut BTreeMap<AccountId32, SK>,
     identities_v2: &HashMap<String, IdentityV2>,
     smiths: &Vec<SmithData>,
     common_parameters: &CommonParameters,
@@ -651,6 +664,7 @@ fn create_smith_wot<SK: Decode>(
         u32,
         BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
         BTreeMap<u32, sp_membership::MembershipData<u32>>,
+        BTreeMap<<<MultiSignature as Verify>::Signer as IdentifyAccount>::AccountId, SK>,
     ),
     String,
 > {
@@ -661,15 +675,17 @@ fn create_smith_wot<SK: Decode>(
     let mut smith_certs_by_receiver = BTreeMap::new();
     // smith memberships
     let mut smith_memberships = BTreeMap::new();
+    let mut session_keys_map = BTreeMap::new();
     // Then create the smith WoT
     for smith in smiths {
         // check that smith exists
-        if let Some(identity) = &identities_v2.get(&smith.name.clone()) {
-            counter_online_authorities = set_session_keys_and_authority_status(
+        let identities_v2_clone = identities_v2.clone();
+        if let Some(identity) = identities_v2.get(&smith.name.clone()) {
+            counter_online_authorities = set_smith_session_keys_and_authority_status(
                 initial_authorities,
-                session_keys_map,
+                &mut session_keys_map,
                 &smith,
-                &identity,
+                identity,
             )?;
 
             // smith certifications
@@ -677,8 +693,8 @@ fn create_smith_wot<SK: Decode>(
                 &mut smith_certs_by_receiver,
                 &clique_smiths,
                 &smith,
-                &identity,
-                &identities_v2,
+                identity,
+                &identities_v2_clone,
                 &common_parameters,
             )?;
 
@@ -703,6 +719,7 @@ fn create_smith_wot<SK: Decode>(
         counter_smith_cert,
         smith_certs_by_receiver,
         smith_memberships,
+        session_keys_map,
     ))
 }
 
@@ -957,18 +974,6 @@ fn v1_wallets_to_v2_accounts(
     (fatal, monetary_mass, accounts, invalid_wallets)
 }
 
-fn check_smiths_vs_clique_smiths(
-    smith_identities: &Option<BTreeMap<String, SmithData>>,
-    clique_smiths: &Option<Vec<CliqueSmith>>,
-) -> Result<(), String> {
-    if smith_identities.is_some() && clique_smiths.is_some() {
-        return Err(
-            "'smiths' and 'clique_smiths' cannot be both defined at the same time".to_string(),
-        );
-    }
-    Ok(())
-}
-
 fn check_identities_v2(
     identities_v2: &HashMap<String, IdentityV2>,
     common_parameters: &CommonParameters,
@@ -1001,7 +1006,7 @@ fn check_identities_v2(
         });
 }
 
-fn check_genesis_data_filter_certs(
+fn check_genesis_data_and_filter_expired_certs_since_export(
     genesis_data: &mut GenesisMigrationData,
     genesis_timestamp: u64,
     common_parameters: &CommonParameters,
@@ -1055,26 +1060,49 @@ fn check_genesis_data_filter_certs(
 fn genesis_data_to_identities_v2(
     genesis_identities: BTreeMap<String, IdentityV1>,
     genesis_timestamp: u64,
+    smiths: &Vec<RawSmith>,
 ) -> HashMap<String, IdentityV2> {
+    let key_migrations: HashMap<String, AccountId> = smiths
+        .iter()
+        .filter(|s| s.migration_address.is_some())
+        .map(|s| {
+            (
+                s.name.clone(),
+                s.migration_address.clone().expect("already filtered"),
+            )
+        })
+        .collect();
     genesis_identities
         .into_iter()
         .map(|(name, i)| {
+            let legacy_account = i
+                .owner_pubkey
+                .map(|pubkey| {
+                    v1_pubkey_to_account_id(pubkey)
+                        .expect("a G1 identity necessarily has a valid pubkey")
+                })
+                .unwrap_or_else(|| {
+                    i.owner_address.unwrap_or_else(|| {
+                        panic!("neither pubkey nor address is defined for {}", name)
+                    })
+                });
+            let migration = key_migrations.get(name.clone().as_str());
+            let owner_key = if let Some(migrated_account) = migration {
+                migrated_account.clone()
+            } else {
+                legacy_account.clone()
+            };
+            let old_owner_key = if migration.is_none() {
+                None
+            } else {
+                Some(legacy_account)
+            };
             (
                 name.clone(),
                 IdentityV2 {
                     index: i.index,
-                    owner_key: i
-                        .owner_pubkey
-                        .map(|pubkey| {
-                            v1_pubkey_to_account_id(pubkey)
-                                .expect("a G1 identity necessarily has a valid pubkey")
-                        })
-                        .unwrap_or_else(|| {
-                            i.owner_address.unwrap_or_else(|| {
-                                panic!("neither pubkey nor address is defined for {}", name)
-                            })
-                        }),
-                    old_owner_key: None,
+                    owner_key: owner_key.clone(),
+                    old_owner_key,
                     membership_expire_on: timestamp_to_relative_blocs(
                         i.membership_expire_on,
                         genesis_timestamp,
@@ -1102,8 +1130,7 @@ fn genesis_data_to_identities_v2(
 
 fn make_authority_exist<SessionKeys: Encode, SKP: SessionKeysProvider<SessionKeys>>(
     identities_v2: &mut HashMap<String, IdentityV2>,
-    smith_identities: &mut Option<BTreeMap<String, SmithData>>,
-    clique_smiths: &mut Option<Vec<CliqueSmith>>,
+    smiths: &mut Vec<RawSmith>,
     common_parameters: &CommonParameters,
     authority_name: &String,
 ) {
@@ -1152,43 +1179,29 @@ fn make_authority_exist<SessionKeys: Encode, SKP: SessionKeysProvider<SessionKey
     let sk: SessionKeys = SKP::session_keys(&get_authority_keys_from_seed(authority_name.as_str()));
     let forced_authority_session_keys = format!("0x{}", hex::encode(sk.encode()));
     // Add forced authority to smiths (whether explicit smith WoT or clique)
-    if let Some(smith_identities) = smith_identities {
-        smith_identities.insert(
-            authority_name.clone(),
-            SmithData {
-                idty_index: authority.index,
-                name: authority_name.clone(),
-                account: authority.owner_key.clone(),
-                session_keys: Some(forced_authority_session_keys.clone()),
-                certs_received: vec![],
-            },
-        );
-    }
-    if let Some(clique_smiths) = clique_smiths {
-        let existing = clique_smiths.iter_mut().find(|s| &s.name == authority_name);
-        if let Some(smith) = existing {
-            // Forced authority is already set in smiths: but we force its session keys
-            smith.session_keys = Some(forced_authority_session_keys);
-        } else {
-            // Forced authority is not known in smiths: we add it with its session keys
-            clique_smiths.push(CliqueSmith {
-                name: authority_name.clone(),
-                session_keys: Some(forced_authority_session_keys),
-            });
-        }
+    if let Some(smith) = smiths.iter_mut().find(|s| &s.name == authority_name) {
+        smith.session_keys = Some(forced_authority_session_keys.clone());
+        smith.migration_address = None;
+    } else {
+        smiths.push(RawSmith {
+            name: authority_name.clone(),
+            session_keys: Some(forced_authority_session_keys),
+            migration_address: None,
+            certs_received: vec![],
+        })
     }
 }
 
 fn feed_identities(
-    identities: &mut Vec<GenesisMemberIdentity>,
     accounts: &mut BTreeMap<AccountId32, GenesisAccountData<u64>>,
     identity_index: &mut HashMap<u32, String>,
     monetary_mass: &mut u64,
     inactive_identities: &mut HashMap<u32, String>,
     memberships: &mut BTreeMap<u32, MembershipData>,
     identities_v2: &HashMap<String, IdentityV2>,
-) -> Result<bool, String> {
+) -> Result<(bool, Vec<GenesisMemberIdentity>), String> {
     let mut fatal = false;
+    let mut identities: Vec<GenesisMemberIdentity> = Vec::new();
     for (name, identity) in identities_v2 {
         // identity name
         if !validate_idty_name(name) {
@@ -1278,10 +1291,10 @@ fn feed_identities(
     // sort the identities by index for reproducibility (should have been a vec in json)
     identities.sort_unstable_by(|a, b| a.index.cmp(&b.index));
 
-    Ok(fatal)
+    Ok((fatal, identities))
 }
 
-fn set_session_keys_and_authority_status<SK>(
+fn set_smith_session_keys_and_authority_status<SK>(
     initial_authorities: &mut BTreeMap<
         u32,
         (
@@ -1294,7 +1307,7 @@ fn set_session_keys_and_authority_status<SK>(
         SK,
     >,
     smith: &&SmithData,
-    identity: &&&IdentityV2,
+    identity: &IdentityV2,
 ) -> Result<u32, String>
 where
     SK: Decode,
@@ -1332,7 +1345,7 @@ fn feed_smith_certs_by_receiver(
     smith_certs_by_receiver: &mut BTreeMap<u32, BTreeMap<u32, Option<u32>>>,
     clique_smiths: &Option<Vec<CliqueSmith>>,
     smith: &&SmithData,
-    identity: &&&IdentityV2,
+    identity: &IdentityV2,
     identities_v2: &HashMap<String, IdentityV2>,
     common_parameters: &CommonParameters,
 ) -> Result<u32, String> {
@@ -1396,64 +1409,71 @@ fn feed_certs_by_receiver(
 fn check_authority_exists_in_both_wots(
     name: &String,
     identities_v2: &HashMap<String, IdentityV2>,
-    clique_smiths: &Option<Vec<CliqueSmith>>,
-    smith_identities: &Option<BTreeMap<String, SmithData>>,
+    smiths: &Vec<RawSmith>,
 ) {
     identities_v2
         .get(name)
         .ok_or(format!("Identity '{}' not exist", name))
         .expect("Initial authority must have an identity");
-    if let Some(smiths) = &clique_smiths {
-        smiths
-            .iter()
-            .find(|s| s.name == *name)
-            .expect("Forced authority must be present in smiths clique");
-    }
-    if let Some(smiths) = &smith_identities {
-        smiths
-            .iter()
-            .find(|(other_name, _)| *other_name == name)
-            .expect("Forced authority must be present in smiths");
-    }
+    smiths
+        .iter()
+        .find(|smith| &smith.name == name)
+        .expect("Forced authority must be present in smiths");
 }
 
 fn build_smiths_wot(
     clique_smiths: &Option<Vec<CliqueSmith>>,
-    identity_index: &HashMap<u32, String>,
-    identities_v2: &HashMap<String, IdentityV2>,
-    smith_identities: Option<BTreeMap<String, SmithData>>,
-) -> Vec<SmithData> {
+    smith_identities: Option<BTreeMap<String, RawSmith>>,
+) -> Result<Vec<RawSmith>, String> {
+    if smith_identities.is_some() && clique_smiths.is_some() {
+        return Err(
+            "'smiths' and 'clique_smiths' cannot be both defined at the same time".to_string(),
+        );
+    }
     // Create a single source of smiths
     let smiths = if let Some(clique) = &clique_smiths {
         // From a clique
         clique
             .iter()
-            .map(|smith| {
-                let idty_index = identity_index
-                    .iter()
-                    .find(|(_, v)| ***v == smith.name)
-                    .map(|(k, _)| *k)
-                    .expect("smith must have an identity");
-                SmithData {
-                    idty_index,
-                    name: smith.name.clone(),
-                    account: identities_v2
-                        .get(smith.name.as_str())
-                        .map(|i| i.owner_key.clone())
-                        .expect("identity must exist"),
-                    session_keys: smith.session_keys.clone(),
-                    certs_received: vec![],
-                }
+            .map(|smith| RawSmith {
+                name: smith.name.clone(),
+                session_keys: smith.session_keys.clone(),
+                certs_received: vec![],
+                migration_address: smith.migration_address.clone(),
             })
-            .collect::<Vec<SmithData>>()
+            .collect::<Vec<RawSmith>>()
     } else {
         // From explicit smith WoT
         smith_identities
             .expect("existence has been tested earlier")
             .into_values()
-            .collect::<Vec<SmithData>>()
+            .collect::<Vec<RawSmith>>()
     };
+    Ok(smiths)
+}
+
+fn decorate_smiths_with_identity(
+    smiths: Vec<RawSmith>,
+    identity_index: &HashMap<u32, String>,
+    identities_v2: &HashMap<String, IdentityV2>,
+) -> Vec<SmithData> {
     smiths
+        .into_iter()
+        .map(|smith| SmithData {
+            idty_index: identity_index
+                .iter()
+                .find(|(_, v)| ***v == smith.name)
+                .map(|(k, _)| *k)
+                .expect("smith must have an identity"),
+            account: identities_v2
+                .get(smith.name.as_str())
+                .map(|i| i.owner_key.clone())
+                .expect("identity must exist"),
+            name: smith.name,
+            session_keys: smith.session_keys,
+            certs_received: smith.certs_received,
+        })
+        .collect()
 }
 
 pub fn generate_genesis_data_for_local_chain<P, SK, SessionKeys: Encode, SKP>(
diff --git a/pallets/certification/src/lib.rs b/pallets/certification/src/lib.rs
index f1bcb550a16eb71cd6acf5a9bc64b448b9324f27..ef561430a7bb36a001551e462f9b136a5f32ae12 100644
--- a/pallets/certification/src/lib.rs
+++ b/pallets/certification/src/lib.rs
@@ -80,6 +80,7 @@ pub mod pallet {
         type MaxByIssuer: Get<u32>;
         /// Minimum number of certifications that must be received to be able to issue
         /// certifications.
+        #[pallet::constant]
         type MinReceivedCertToBeAbleToIssueCert: Get<u32>;
         /// Handler for NewCert event
         type OnNewcert: OnNewcert<Self::IdtyIndex>;
diff --git a/resources/gdev.json b/resources/gdev.json
index 3b41116eb0e5cc3e50f6c76328c9a60f8bc69924..d012c9a5d85cef73eec08c17147e00572b1d954c 100644
--- a/resources/gdev.json
+++ b/resources/gdev.json
@@ -34,10 +34,11 @@
     { "name": "1000i100" },
     { "name": "vit" },
     { "name": "cgeek",
-      "session_keys": "0xc7cd153c0f8cb4d21e1f02e8bd56a0586ad36f4d4d10274f070364dea355551e2634b8ede99cb0dde4566fec9804b5c84f7923ce8f992070206b4063242c21482634b8ede99cb0dde4566fec9804b5c84f7923ce8f992070206b4063242c21482634b8ede99cb0dde4566fec9804b5c84f7923ce8f992070206b4063242c2148"
+      "migration_address": "5E6q47RRGZU15LjUiBTm2DZjpqFKAjRNafYS8YV8AzTQZtLG",
+      "session_keys": "0x3090c9a6974f1a4749038ee75eb47da7c72ba4972e4809f2f71e8730567c1c5a7acfdc6e9b470e8d2d334edc662d755aeec93d2bb140e1c9f7db724b046dd54b7acfdc6e9b470e8d2d334edc662d755aeec93d2bb140e1c9f7db724b046dd54b7acfdc6e9b470e8d2d334edc662d755aeec93d2bb140e1c9f7db724b046dd54b"
     }
   ],
   "sudo_key": "5Hm8sBbwuLAU99dBezvgtnRmZCrUy9mhqmbQMFyGTaeATYg7",
-  "treasury_funder": "2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ",
+  "treasury_funder_address": "5E6q47RRGZU15LjUiBTm2DZjpqFKAjRNafYS8YV8AzTQZtLG",
   "technical_committee": ["Pini", "moul", "HugoTrentesaux", "tuxmain", "1000i100", "vit", "cgeek"]
 }
\ No newline at end of file