From 52ebb5ef47cf29536c156b6b93151c1421cd6657 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=89lo=C3=AFs?= <c@elo.tf>
Date: Tue, 5 Jul 2022 14:33:14 +0200
Subject: [PATCH] =?UTF-8?q?feat(runtime):=C2=A0create=20UDs=20manually=20w?=
 =?UTF-8?q?ith=20new=20call=20universalDividend.claim=5Fuds=20(!83)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* ref: move IdtyDataIter in common runtime

* bench real weights for pallet ud

* bench claim_uds

* weights constants

* weights: ParametersStorage is whitelisted

* pallet ud on_initialize weights

* manual ud: not emit event if 0 UDs claimed

* tests(gdev): add int test test_remove_identity_after_one_ud

* auto claim uds at member removal

* migrate pallet id integration tests to manual ud

* remove all try_get in pallet ud

* write PastReevals

* add call claim_uds

* remove pallet ud_accounts_storage

* update pallet ud interface

* create type FirstEligibleUd

* add custom IdtyData
---
 .cargo/config                                 |   1 +
 .vscode/launch.json                           |  19 --
 Cargo.lock                                    |  21 --
 Cargo.toml                                    |   1 -
 node/src/chain_spec/gdev.rs                   |   9 +-
 node/src/chain_spec/gen_genesis_data.rs       |   6 +-
 pallets/duniter-wot/src/mock.rs               |   2 +
 pallets/duniter-wot/src/tests.rs              |   1 +
 pallets/identity/src/lib.rs                   |  64 ++++-
 pallets/identity/src/mock.rs                  |   1 +
 pallets/identity/src/tests.rs                 |   3 +-
 pallets/identity/src/types.rs                 |   3 +-
 pallets/ud-accounts-storage/Cargo.toml        |  83 -------
 pallets/ud-accounts-storage/src/lib.rs        | 155 -------------
 pallets/universal-dividend/Cargo.toml         |  21 +-
 .../universal-dividend/src/benchmarking.rs    |  90 ++++++--
 .../src/compute_claim_uds.rs                  |  96 ++++++++
 pallets/universal-dividend/src/lib.rs         | 218 ++++++++++++++----
 pallets/universal-dividend/src/mock.rs        |  43 +++-
 pallets/universal-dividend/src/tests.rs       | 215 +++++++++++++----
 pallets/universal-dividend/src/types.rs       |  97 ++++++++
 pallets/universal-dividend/src/weights.rs     |  35 +++
 runtime/common/Cargo.toml                     |   3 -
 runtime/common/src/constants.rs               |  24 +-
 runtime/common/src/handlers.rs                |  37 +--
 runtime/common/src/lib.rs                     |   9 +-
 runtime/common/src/pallets_config.rs          |  16 +-
 runtime/common/src/providers.rs               |  40 +++-
 .../src/weights/pallet_universal_dividend.rs  |  38 ++-
 runtime/g1/Cargo.toml                         |   1 -
 runtime/g1/src/lib.rs                         |   6 +-
 runtime/gdev/Cargo.toml                       |   1 -
 runtime/gdev/src/lib.rs                       |   9 +-
 runtime/gdev/tests/common/mod.rs              |   7 +-
 runtime/gdev/tests/integration_tests.rs       |  62 ++++-
 runtime/gtest/Cargo.toml                      |   1 -
 runtime/gtest/src/lib.rs                      |   6 +-
 37 files changed, 947 insertions(+), 497 deletions(-)
 delete mode 100644 pallets/ud-accounts-storage/Cargo.toml
 delete mode 100644 pallets/ud-accounts-storage/src/lib.rs
 create mode 100644 pallets/universal-dividend/src/compute_claim_uds.rs
 create mode 100644 pallets/universal-dividend/src/types.rs

diff --git a/.cargo/config b/.cargo/config
index e07889471..3a03e9a4f 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -2,5 +2,6 @@
 cucumber = "test -p duniter-end2end-tests --test cucumber_tests --"
 sanity-gdev = "test -p duniter-live-tests --test sanity_gdev -- --nocapture"
 tu = "test --workspace --exclude duniter-end2end-tests"
+tb = "test --features runtime-benchmarks -p"
 xtask = "run --package xtask --"
 
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 23b696a3d..3fb767dac 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -88,25 +88,6 @@
       "args": [],
       "cwd": "${workspaceFolder}"
     },
-    {
-      "type": "lldb",
-      "request": "launch",
-      "name": "Debug unit tests in library 'pallet-ud-accounts-storage'",
-      "cargo": {
-        "args": [
-          "test",
-          "--no-run",
-          "--lib",
-          "--package=pallet-ud-accounts-storage"
-        ],
-        "filter": {
-          "name": "pallet-ud-accounts-storage",
-          "kind": "lib"
-        }
-      },
-      "args": [],
-      "cwd": "${workspaceFolder}"
-    },
     {
       "type": "lldb",
       "request": "launch",
diff --git a/Cargo.lock b/Cargo.lock
index fb1b1b03e..2bb67beb8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -895,7 +895,6 @@ dependencies = [
  "pallet-session",
  "pallet-timestamp",
  "pallet-treasury",
- "pallet-ud-accounts-storage",
  "pallet-universal-dividend",
  "pallet-upgrade-origin",
  "parity-scale-codec",
@@ -2325,7 +2324,6 @@ dependencies = [
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
  "pallet-treasury",
- "pallet-ud-accounts-storage",
  "pallet-universal-dividend",
  "pallet-upgrade-origin",
  "pallet-utility",
@@ -2394,7 +2392,6 @@ dependencies = [
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
  "pallet-treasury",
- "pallet-ud-accounts-storage",
  "pallet-universal-dividend",
  "pallet-upgrade-origin",
  "pallet-utility",
@@ -2666,7 +2663,6 @@ dependencies = [
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
  "pallet-treasury",
- "pallet-ud-accounts-storage",
  "pallet-universal-dividend",
  "pallet-upgrade-origin",
  "pallet-utility",
@@ -5501,23 +5497,6 @@ dependencies = [
  "sp-std",
 ]
 
-[[package]]
-name = "pallet-ud-accounts-storage"
-version = "3.0.0"
-dependencies = [
- "frame-benchmarking",
- "frame-support",
- "frame-system",
- "pallet-balances",
- "parity-scale-codec",
- "scale-info",
- "serde",
- "sp-core",
- "sp-io",
- "sp-runtime",
- "sp-std",
-]
-
 [[package]]
 name = "pallet-universal-dividend"
 version = "3.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index b054d2a4e..ae17782bb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -131,7 +131,6 @@ members = [
     'pallets/identity',
     'pallets/membership',
     'pallets/authority-members',
-    'pallets/ud-accounts-storage',
     'pallets/universal-dividend',
     'pallets/upgrade-origin',
     'primitives/membership',
diff --git a/node/src/chain_spec/gdev.rs b/node/src/chain_spec/gdev.rs
index 18695102a..52bea218a 100644
--- a/node/src/chain_spec/gdev.rs
+++ b/node/src/chain_spec/gdev.rs
@@ -21,7 +21,7 @@ use gdev_runtime::{
     opaque::SessionKeys, AccountConfig, AccountId, AuthorityMembersConfig, BabeConfig,
     BalancesConfig, CertConfig, GenesisConfig, IdentityConfig, ImOnlineId, MembershipConfig,
     ParametersConfig, SessionConfig, SmithsCertConfig, SmithsMembershipConfig, SudoConfig,
-    SystemConfig, UdAccountsStorageConfig, UniversalDividendConfig, WASM_BINARY,
+    SystemConfig, UniversalDividendConfig, WASM_BINARY,
 };
 use sc_service::ChainType;
 use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
@@ -377,6 +377,7 @@ fn gen_genesis_for_local_chain(
                         owner_key: owner_key.clone(),
                         removable_on: 0,
                         status: IdtyStatus::Validated,
+                        data: IdtyData::min(),
                     },
                 })
                 .collect(),
@@ -416,9 +417,6 @@ fn gen_genesis_for_local_chain(
             certs_by_issuer: clique_wot(initial_smiths_len, smith_cert_validity_period),
         },
         smiths_collective: Default::default(),
-        ud_accounts_storage: UdAccountsStorageConfig {
-            ud_accounts: initial_identities.values().cloned().collect(),
-        },
         universal_dividend: UniversalDividendConfig {
             first_reeval: 100,
             first_ud,
@@ -466,7 +464,6 @@ fn genesis_data_to_gdev_genesis_conf(
         smiths_certs_by_issuer,
         smiths_memberships,
         sudo_key,
-        ud_accounts,
     } = genesis_data;
 
     gdev_runtime::GenesisConfig {
@@ -506,6 +503,7 @@ fn genesis_data_to_gdev_genesis_conf(
                         owner_key: pubkey,
                         removable_on: 0,
                         status: IdtyStatus::Validated,
+                        data: IdtyData::min(),
                     },
                 })
                 .collect(),
@@ -523,7 +521,6 @@ fn genesis_data_to_gdev_genesis_conf(
             memberships: smiths_memberships,
         },
         smiths_collective: Default::default(),
-        ud_accounts_storage: UdAccountsStorageConfig { ud_accounts },
         universal_dividend: UniversalDividendConfig {
             first_reeval: first_ud_reeval,
             first_ud,
diff --git a/node/src/chain_spec/gen_genesis_data.rs b/node/src/chain_spec/gen_genesis_data.rs
index 437f691fe..d9c66c76a 100644
--- a/node/src/chain_spec/gen_genesis_data.rs
+++ b/node/src/chain_spec/gen_genesis_data.rs
@@ -17,7 +17,7 @@
 use common_runtime::*;
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use sp_core::{blake2_256, Decode, Encode, H256};
-use std::collections::{BTreeMap, BTreeSet};
+use std::collections::BTreeMap;
 
 type MembershipData = sp_membership::MembershipData<u32>;
 
@@ -38,7 +38,6 @@ pub struct GenesisData<Parameters: DeserializeOwned, SessionKeys: Decode> {
     pub smiths_certs_by_issuer: BTreeMap<u32, BTreeMap<u32, u32>>,
     pub smiths_memberships: BTreeMap<u32, MembershipData>,
     pub sudo_key: Option<AccountId>,
-    pub ud_accounts: BTreeSet<AccountId>,
 }
 
 #[derive(Default)]
@@ -151,7 +150,6 @@ where
     let mut initial_monetary_mass = 0;
     let mut memberships = BTreeMap::new();
     //let mut total_dust = 0;
-    let mut ud_accounts = BTreeSet::new();
 
     // SIMPLE WALLETS //
 
@@ -194,7 +192,6 @@ where
         // We must count the money under the existential deposit because what we count is
         // the monetary mass created (for the revaluation of the DU)
         initial_monetary_mass += identity.balance;
-        ud_accounts.insert(identity.pubkey.clone());
 
         // Wot
         identities_.push((idty_name.clone(), identity.pubkey.clone()));
@@ -329,7 +326,6 @@ where
         smiths_certs_by_issuer,
         smiths_memberships,
         sudo_key,
-        ud_accounts,
     };
 
     Ok(f(genesis_data))
diff --git a/pallets/duniter-wot/src/mock.rs b/pallets/duniter-wot/src/mock.rs
index ae08de65f..5646650d9 100644
--- a/pallets/duniter-wot/src/mock.rs
+++ b/pallets/duniter-wot/src/mock.rs
@@ -113,6 +113,7 @@ impl pallet_identity::Config for Test {
     type Event = Event;
     type EnsureIdtyCallAllowed = DuniterWot;
     type IdtyCreationPeriod = IdtyCreationPeriod;
+    type IdtyData = ();
     type IdtyNameValidator = IdtyNameValidatorTestImpl;
     type IdtyIndex = IdtyIndex;
     type IdtyValidationOrigin = system::EnsureRoot<AccountId>;
@@ -248,6 +249,7 @@ pub fn new_test_ext(
                     owner_key: i as u64,
                     removable_on: 0,
                     status: pallet_identity::IdtyStatus::Validated,
+                    data: (),
                 },
             })
             .collect(),
diff --git a/pallets/duniter-wot/src/tests.rs b/pallets/duniter-wot/src/tests.rs
index 3e237c609..870f29ccd 100644
--- a/pallets/duniter-wot/src/tests.rs
+++ b/pallets/duniter-wot/src/tests.rs
@@ -217,6 +217,7 @@ fn test_new_idty_validation() {
                 owner_key: 6,
                 removable_on: 0,
                 status: IdtyStatus::Validated,
+                data: ()
             })
         );
     });
diff --git a/pallets/identity/src/lib.rs b/pallets/identity/src/lib.rs
index 79c16b06e..0cc9fc089 100644
--- a/pallets/identity/src/lib.rs
+++ b/pallets/identity/src/lib.rs
@@ -68,6 +68,14 @@ pub mod pallet {
         type EnsureIdtyCallAllowed: EnsureIdtyCallAllowed<Self>;
         /// Minimum duration between the creation of 2 identities by the same creator
         type IdtyCreationPeriod: Get<Self::BlockNumber>;
+        /// Custom data to store in each identity
+        type IdtyData: Clone
+            + Codec
+            + Default
+            + Eq
+            + TypeInfo
+            + MaybeSerializeDeserialize
+            + MaxEncodedLen;
         /// A short identity index.
         type IdtyIndex: Parameter
             + Member
@@ -102,7 +110,7 @@ pub mod pallet {
     pub struct GenesisIdty<T: Config> {
         pub index: T::IdtyIndex,
         pub name: IdtyName,
-        pub value: IdtyValue<T::BlockNumber, T::AccountId>,
+        pub value: IdtyValue<T::BlockNumber, T::AccountId, T::IdtyData>,
     }
 
     #[pallet::genesis_config]
@@ -159,7 +167,7 @@ pub mod pallet {
         _,
         Twox64Concat,
         T::IdtyIndex,
-        IdtyValue<T::BlockNumber, T::AccountId>,
+        IdtyValue<T::BlockNumber, T::AccountId, T::IdtyData>,
         OptionQuery,
     >;
 
@@ -280,6 +288,7 @@ pub mod pallet {
                     owner_key: owner_key.clone(),
                     removable_on,
                     status: IdtyStatus::Created,
+                    data: Default::default(),
                 },
             );
             IdentitiesRemovableOn::<T>::append(removable_on, (idty_index, IdtyStatus::Created));
@@ -560,3 +569,54 @@ impl<T: Config> sp_runtime::traits::Convert<T::AccountId, Option<T::IdtyIndex>>
         Self::identity_index_of(account_id)
     }
 }
+
+impl<T> frame_support::traits::StoredMap<T::AccountId, T::IdtyData> for Pallet<T>
+where
+    T: Config,
+{
+    fn get(key: &T::AccountId) -> T::IdtyData {
+        if let Some(idty_index) = Self::identity_index_of(key) {
+            if let Some(idty_val) = Identities::<T>::get(idty_index) {
+                idty_val.data
+            } else {
+                Default::default()
+            }
+        } else {
+            Default::default()
+        }
+    }
+    fn try_mutate_exists<R, E: From<sp_runtime::DispatchError>>(
+        key: &T::AccountId,
+        f: impl FnOnce(&mut Option<T::IdtyData>) -> Result<R, E>,
+    ) -> Result<R, E> {
+        let maybe_idty_index = Self::identity_index_of(key);
+        let mut maybe_idty_data = if let Some(idty_index) = maybe_idty_index {
+            if let Some(idty_val) = Identities::<T>::get(idty_index) {
+                Some(idty_val.data)
+            } else {
+                None
+            }
+        } else {
+            None
+        };
+        let result = f(&mut maybe_idty_data)?;
+        if let Some(idty_index) = maybe_idty_index {
+            Identities::<T>::mutate_exists(idty_index, |idty_val_opt| {
+                if let Some(ref mut idty_val) = idty_val_opt {
+                    idty_val.data = maybe_idty_data.unwrap_or_default();
+                } else if maybe_idty_data.is_some() {
+                    return Err(sp_runtime::DispatchError::Other(
+                        "Tring to set IdtyData for a non-existing identity!",
+                    ));
+                }
+                Ok(())
+            })?;
+        } else if maybe_idty_data.is_some() {
+            return Err(sp_runtime::DispatchError::Other(
+                "Tring to set IdtyData for a non-existing identity!",
+            )
+            .into());
+        }
+        Ok(result)
+    }
+}
diff --git a/pallets/identity/src/mock.rs b/pallets/identity/src/mock.rs
index 420bfa10b..36e24dca7 100644
--- a/pallets/identity/src/mock.rs
+++ b/pallets/identity/src/mock.rs
@@ -102,6 +102,7 @@ impl pallet_identity::Config for Test {
     type Event = Event;
     type EnsureIdtyCallAllowed = ();
     type IdtyCreationPeriod = IdtyCreationPeriod;
+    type IdtyData = ();
     type IdtyNameValidator = IdtyNameValidatorTestImpl;
     type IdtyIndex = u64;
     type IdtyValidationOrigin = system::EnsureRoot<AccountId>;
diff --git a/pallets/identity/src/tests.rs b/pallets/identity/src/tests.rs
index 3a25afc57..f2f50efb1 100644
--- a/pallets/identity/src/tests.rs
+++ b/pallets/identity/src/tests.rs
@@ -22,7 +22,7 @@ use frame_support::assert_ok;
 use frame_system::{EventRecord, Phase};
 use sp_runtime::testing::TestSignature;
 
-type IdtyVal = IdtyValue<u64, u64>;
+type IdtyVal = IdtyValue<u64, u64, ()>;
 
 fn alice() -> GenesisIdty<Test> {
     GenesisIdty {
@@ -33,6 +33,7 @@ fn alice() -> GenesisIdty<Test> {
             owner_key: 1,
             removable_on: 0,
             status: crate::IdtyStatus::Validated,
+            data: (),
         },
     }
 }
diff --git a/pallets/identity/src/types.rs b/pallets/identity/src/types.rs
index 529fb0bab..708b2ed93 100644
--- a/pallets/identity/src/types.rs
+++ b/pallets/identity/src/types.rs
@@ -79,11 +79,12 @@ impl Default for IdtyStatus {
 
 #[cfg_attr(feature = "std", derive(Debug, Deserialize, Serialize))]
 #[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)]
-pub struct IdtyValue<BlockNumber, AccountId> {
+pub struct IdtyValue<BlockNumber, AccountId, IdtyData> {
     pub next_creatable_identity_on: BlockNumber,
     pub owner_key: AccountId,
     pub removable_on: BlockNumber,
     pub status: IdtyStatus,
+    pub data: IdtyData,
 }
 
 #[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo, RuntimeDebug)]
diff --git a/pallets/ud-accounts-storage/Cargo.toml b/pallets/ud-accounts-storage/Cargo.toml
deleted file mode 100644
index 3a7dc7135..000000000
--- a/pallets/ud-accounts-storage/Cargo.toml
+++ /dev/null
@@ -1,83 +0,0 @@
-[package]
-authors = ['librelois <c@elo.tf>']
-description = 'FRAME pallet universal dividend accounts storage.'
-edition = '2018'
-homepage = 'https://substrate.dev'
-license = 'AGPL-3.0'
-name = 'pallet-ud-accounts-storage'
-readme = 'README.md'
-repository = 'https://git.duniter.org/nodes/rust/duniter-v2s'
-version = '3.0.0'
-
-[features]
-default = ['std']
-runtime-benchmarks = ['frame-benchmarking']
-std = [
-    'codec/std',
-    'frame-support/std',
-    'frame-system/std',
-    'frame-benchmarking/std',
-    "sp-std/std",
-]
-try-runtime = ['frame-support/try-runtime']
-
-[dependencies]
-
-# substrate
-scale-info = { version = "1.0", default-features = false, features = ["derive"] }
-
-[dependencies.codec]
-default-features = false
-features = ['derive']
-package = 'parity-scale-codec'
-version = '2.3.1'
-
-[dependencies.frame-benchmarking]
-default-features = false
-git = 'https://github.com/librelois/substrate.git'
-optional = true
-branch = 'duniter-monthly-2022-02'
-
-[dependencies.frame-support]
-default-features = false
-git = 'https://github.com/librelois/substrate.git'
-branch = 'duniter-monthly-2022-02'
-
-[dependencies.frame-system]
-default-features = false
-git = 'https://github.com/librelois/substrate.git'
-branch = 'duniter-monthly-2022-02'
-
-[dependencies.sp-std]
-default-features = false
-git = 'https://github.com/librelois/substrate.git'
-branch = 'duniter-monthly-2022-02'
-
-### DOC ###
-
-[package.metadata.docs.rs]
-targets = ['x86_64-unknown-linux-gnu']
-[dev-dependencies.serde]
-version = '1.0.119'
-
-### DEV ###
-
-[dev-dependencies.pallet-balances]
-default-features = false
-git = 'https://github.com/librelois/substrate.git'
-branch = 'duniter-monthly-2022-02'
-
-[dev-dependencies.sp-core]
-default-features = false
-git = 'https://github.com/librelois/substrate.git'
-branch = 'duniter-monthly-2022-02'
-
-[dev-dependencies.sp-io]
-default-features = false
-git = 'https://github.com/librelois/substrate.git'
-branch = 'duniter-monthly-2022-02'
-
-[dev-dependencies.sp-runtime]
-default-features = false
-git = 'https://github.com/librelois/substrate.git'
-branch = 'duniter-monthly-2022-02'
diff --git a/pallets/ud-accounts-storage/src/lib.rs b/pallets/ud-accounts-storage/src/lib.rs
deleted file mode 100644
index d73c89fed..000000000
--- a/pallets/ud-accounts-storage/src/lib.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2021 Axiom-Team
-//
-// This file is part of Substrate-Libre-Currency.
-//
-// Substrate-Libre-Currency 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, version 3 of the License.
-//
-// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
-
-#![cfg_attr(not(feature = "std"), no_std)]
-#![allow(clippy::unused_unit)]
-
-pub use pallet::*;
-
-/*#[cfg(test)]
-mod mock;
-
-#[cfg(test)]
-mod tests;
-
-#[cfg(feature = "runtime-benchmarks")]
-mod benchmarking;*/
-
-use sp_std::prelude::*;
-
-#[frame_support::pallet]
-pub mod pallet {
-    use super::*;
-    use frame_support::pallet_prelude::*;
-    use frame_support::traits::StorageVersion;
-
-    /// The current storage version.
-    const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
-
-    #[pallet::pallet]
-    #[pallet::generate_store(pub(super) trait Store)]
-    #[pallet::storage_version(STORAGE_VERSION)]
-    #[pallet::without_storage_info]
-    pub struct Pallet<T>(_);
-
-    // CONFIG //
-
-    #[pallet::config]
-    pub trait Config: frame_system::Config {}
-
-    // STORAGE //
-
-    #[pallet::storage]
-    #[pallet::getter(fn ud_accounts)]
-    pub type UdAccounts<T: Config> =
-        CountedStorageMap<_, Blake2_128Concat, T::AccountId, (), ValueQuery>;
-
-    // GENESIS //
-
-    #[pallet::genesis_config]
-    pub struct GenesisConfig<T: Config> {
-        pub ud_accounts: sp_std::collections::btree_set::BTreeSet<T::AccountId>,
-    }
-
-    #[cfg(feature = "std")]
-    impl<T: Config> Default for GenesisConfig<T> {
-        fn default() -> Self {
-            Self {
-                ud_accounts: Default::default(),
-            }
-        }
-    }
-
-    #[pallet::genesis_build]
-    impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
-        fn build(&self) {
-            for account in &self.ud_accounts {
-                <UdAccounts<T>>::insert(account, ());
-            }
-        }
-    }
-
-    // PUBLIC FUNCTIONS //
-
-    impl<T: Config> Pallet<T> {
-        pub fn accounts_len() -> u32 {
-            <UdAccounts<T>>::count()
-        }
-        pub fn accounts_list() -> Vec<T::AccountId> {
-            <UdAccounts<T>>::iter_keys().collect()
-        }
-        pub fn replace_account(
-            old_account_opt: Option<T::AccountId>,
-            new_account_opt: Option<T::AccountId>,
-        ) -> Weight {
-            if let Some(old_account) = old_account_opt {
-                if let Some(new_account) = new_account_opt {
-                    Self::replace_account_inner(old_account, new_account)
-                } else {
-                    Self::del_account(old_account)
-                }
-            } else if let Some(new_account) = new_account_opt {
-                Self::add_account(new_account)
-            } else {
-                0
-            }
-        }
-        pub fn remove_account(account_id: T::AccountId) -> Weight {
-            Self::del_account(account_id)
-        }
-        fn replace_account_inner(old_account: T::AccountId, new_account: T::AccountId) -> Weight {
-            if <UdAccounts<T>>::contains_key(&old_account) {
-                if !<UdAccounts<T>>::contains_key(&new_account) {
-                    <UdAccounts<T>>::remove(&old_account);
-                    <UdAccounts<T>>::insert(&new_account, ());
-                } else {
-                    frame_support::runtime_print!(
-                        "ERROR: replace_account(): new_account {:?} already added",
-                        new_account
-                    );
-                }
-            } else {
-                frame_support::runtime_print!(
-                    "ERROR: replace_account(): old_account {:?} already deleted",
-                    old_account
-                );
-            }
-            0
-        }
-        fn add_account(account: T::AccountId) -> Weight {
-            if !<UdAccounts<T>>::contains_key(&account) {
-                <UdAccounts<T>>::insert(&account, ());
-            } else {
-                frame_support::runtime_print!(
-                    "ERROR: add_account(): account {:?} already added",
-                    account
-                );
-            }
-            0
-        }
-        fn del_account(account: T::AccountId) -> Weight {
-            if <UdAccounts<T>>::contains_key(&account) {
-                <UdAccounts<T>>::remove(&account);
-            } else {
-                frame_support::runtime_print!(
-                    "ERROR: del_account(): account {:?} already deleted",
-                    account
-                );
-            }
-            0
-        }
-    }
-}
diff --git a/pallets/universal-dividend/Cargo.toml b/pallets/universal-dividend/Cargo.toml
index 8b939e5f7..f6ab06ac8 100644
--- a/pallets/universal-dividend/Cargo.toml
+++ b/pallets/universal-dividend/Cargo.toml
@@ -12,13 +12,14 @@ version = '3.0.0'
 default = ['std']
 runtime-benchmarks = [
 	"frame-benchmarking/runtime-benchmarks",
-	"pallet-balances"
+	"pallet-balances",
 ]
 std = [
     'codec/std',
     'frame-support/std',
     'frame-system/std',
     'frame-benchmarking/std',
+	"serde",
     "sp-arithmetic/std",
     "sp-io/std",
     "sp-std/std",
@@ -26,20 +27,15 @@ std = [
 try-runtime = ['frame-support/try-runtime']
 
 [dependencies]
-
-# substrate
+# crates.io
+codec = { package = 'parity-scale-codec', version = '2.3.1', default-features = false, features = ["derive", "max-encoded-len"] }
 scale-info = { version = "1.0", default-features = false, features = ["derive"] }
+serde = { version = "1.0.101", features = ["derive"], optional = true }
 
 # substrate bencharks
 frame-benchmarking = { git = 'https://github.com/librelois/substrate.git', branch = 'duniter-monthly-2022-02', optional = true, default-features = false }
 pallet-balances = { git = 'https://github.com/librelois/substrate.git', branch = 'duniter-monthly-2022-02', optional = true, default-features = false }
 
-[dependencies.codec]
-default-features = false
-features = ['derive']
-package = 'parity-scale-codec'
-version = '2.3.1'
-
 [dependencies.frame-support]
 default-features = false
 git = 'https://github.com/librelois/substrate.git'
@@ -78,14 +74,13 @@ targets = ['x86_64-unknown-linux-gnu']
 
 ### DEV ###
 
+[dev-dependencies]
+serde = { version = "1.0.101", features = ["derive"] }
+
 [dev-dependencies.pallet-balances]
 git = 'https://github.com/librelois/substrate.git'
 branch = 'duniter-monthly-2022-02'
 
-[dev-dependencies.serde]
-features = ["derive"]
-version = '1.0.119'
-
 [dev-dependencies.sp-core]
 git = 'https://github.com/librelois/substrate.git'
 branch = 'duniter-monthly-2022-02'
diff --git a/pallets/universal-dividend/src/benchmarking.rs b/pallets/universal-dividend/src/benchmarking.rs
index 530065d03..0dd6b662d 100644
--- a/pallets/universal-dividend/src/benchmarking.rs
+++ b/pallets/universal-dividend/src/benchmarking.rs
@@ -18,8 +18,8 @@
 
 use super::*;
 
-use frame_benchmarking::{account, benchmarks, whitelisted_caller};
-use frame_support::pallet_prelude::IsType;
+use frame_benchmarking::{account, benchmarks, whitelist_account, whitelisted_caller};
+use frame_support::pallet_prelude::{BoundedVec, IsType};
 use frame_support::traits::{Get, OnInitialize};
 use frame_system::RawOrigin;
 use pallet_balances::Pallet as Balances;
@@ -27,28 +27,89 @@ use sp_runtime::traits::Bounded;
 
 use crate::Pallet;
 
-const BLOCK_NUMBER: u32 = 2;
 const ED_MULTIPLIER: u32 = 10;
 const SEED: u32 = 0;
 
-/*fn fill_storage<T: Config>(
-    members_count: u32,
-) -> Result<(), &'static str> {
-    Ok(())
-}*/
-
 benchmarks! {
-    // TODO add on_initialize_ud_created and on_initialize_ud_reevalued (after manual UD impl)
+    where_clause {
+        where
+        T: pallet_balances::Config, T::Balance: From<u64>,
+        <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance>
+    }
     on_initialize {
-        //let n in 1 .. 10_000;
-        //fill_storage::<T>(n)?;
-    }: { Pallet::<T>::on_initialize(BLOCK_NUMBER.into()); }
+        let total_money_created = Pallet::<T>::total_money_created();
+    }: { Pallet::<T>::on_initialize(1_u32.into()); }
+    verify {
+        assert_eq!(Pallet::<T>::total_money_created(), total_money_created);
+    }
+    where_clause {
+        where
+        T: pallet_balances::Config, T::Balance: From<u64>,
+        <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance>
+    }
+    on_initialize_ud_created {
+        let block_number = T::UdCreationPeriod::get();
+        let block_number_plus_one: T::BlockNumber = block_number + One::one();
+        NextReeval::<T>::put(block_number_plus_one);
+    }: { Pallet::<T>::on_initialize(block_number.into()); }
+    verify {
+    }
+    where_clause {
+        where
+        T: pallet_balances::Config, T::Balance: From<u64>,
+        <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance>
+    }
+    on_initialize_ud_reevalued {
+        let block_number = T::UdCreationPeriod::get();
+        let block_number_plus_one: T::BlockNumber = block_number + One::one();
+        NextReeval::<T>::put(block_number_plus_one);
+        Pallet::<T>::on_initialize(block_number.into());
+        NextReeval::<T>::put(block_number);
+    }: { Pallet::<T>::on_initialize(block_number.into()); }
+    verify {
+    }
+    // Benchmark `claim_uds` extrinsic with the worst possible conditions:
+    // * UDs have never been claimed
+    // * The maximum number of revaluations has taken place since
+    where_clause {
+        where
+        T: pallet_balances::Config, T::Balance: From<u64>,
+        <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance>
+    }
+    claim_uds {
+        let n in 1 .. T::MaxPastReeval::get();
+
+        // Caller should be a member
+        let caller: T::AccountId = T::MembersStorageIter::from(None)
+            .next()
+            .expect("we need at least one member")
+            .0;
+
+        // Simulate n reevals
+        let mut past_reevals = BoundedVec::default();
+        for i in 0..n {
+            past_reevals
+                .try_push((((3 * i) + 1) as u16, ((1_000 + (100 * i)) as u32).into()))
+                .expect("unreachable");
+        }
+        PastReevals::<T>::put(past_reevals);
+
+        // Simulate 3n+2 UDs
+        CurrentUdIndex::<T>::put(((3 * n) + 2) as u16);
+
+        whitelist_account!(caller);
+    }: claim_uds(RawOrigin::Signed(caller))
     verify {
     }
+
     // Benchmark `transfer_ud` extrinsic with the worst possible conditions:
     // * Transfer will kill the sender account.
     // * Transfer will create the recipient account.
-    where_clause { where T: pallet_balances::Config, T::Balance: From<u64>, <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance> }
+    where_clause {
+        where
+        T: pallet_balances::Config, T::Balance: From<u64>,
+        <T::Currency as Currency<T::AccountId>>::Balance: IsType<T::Balance>
+    }
     transfer_ud {
         let existential_deposit = T::ExistentialDeposit::get();
         let caller = whitelisted_caller();
@@ -94,6 +155,7 @@ benchmarks! {
             first_reeval: 8,
             first_ud: 1_000,
             initial_monetary_mass: 0,
+            initial_members: vec![1],
         }),
         crate::mock::Test
     );
diff --git a/pallets/universal-dividend/src/compute_claim_uds.rs b/pallets/universal-dividend/src/compute_claim_uds.rs
new file mode 100644
index 000000000..5e214b977
--- /dev/null
+++ b/pallets/universal-dividend/src/compute_claim_uds.rs
@@ -0,0 +1,96 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Substrate-Libre-Currency.
+//
+// Substrate-Libre-Currency 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, version 3 of the License.
+//
+// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
+
+use super::UdIndex;
+use core::iter::DoubleEndedIterator;
+use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero};
+
+pub(super) fn compute_claim_uds<Balance: AtLeast32BitUnsigned>(
+    mut current_ud_index: UdIndex,
+    first_ud_index: UdIndex,
+    past_reevals: impl DoubleEndedIterator<Item = (UdIndex, Balance)>,
+) -> (UdIndex, Balance) {
+    let mut total_amount = Zero::zero();
+    let mut total_count = 0;
+    for (ud_index, ud_amount) in past_reevals.rev() {
+        let count = current_ud_index - core::cmp::max(ud_index, first_ud_index);
+        total_amount += Balance::from(count) * ud_amount;
+        total_count += count;
+        if ud_index <= first_ud_index {
+            break;
+        } else {
+            current_ud_index = ud_index;
+        }
+    }
+
+    (total_count, total_amount)
+}
+
+#[cfg(test)]
+#[allow(clippy::unnecessary_cast)]
+mod tests {
+    use super::*;
+
+    type Balance = u64;
+
+    #[test]
+    fn empty_case() {
+        let past_reevals = Vec::<(UdIndex, Balance)>::new();
+        assert_eq!(compute_claim_uds(11, 1, past_reevals.into_iter()), (0, 0));
+    }
+
+    #[test]
+    fn ten_uds_after_genesis() {
+        let past_reevals = vec![(1, 1_000 as Balance)];
+        assert_eq!(
+            compute_claim_uds(11, 1, past_reevals.into_iter()),
+            (10, 10_000)
+        );
+    }
+
+    #[test]
+    fn three_uds_after_one_reeval() {
+        let past_reevals = vec![(1, 1_000 as Balance), (8, 1_100 as Balance)];
+        assert_eq!(
+            compute_claim_uds(11, 1, past_reevals.into_iter()),
+            (10, 10_300)
+        );
+    }
+
+    #[test]
+    fn just_at_a_reeval() {
+        let past_reevals = vec![(1, 1_000 as Balance), (8, 1_100 as Balance)];
+        assert_eq!(
+            compute_claim_uds(9, 1, past_reevals.into_iter()),
+            (8, 8_100)
+        );
+    }
+
+    #[test]
+    fn first_at_current() {
+        let past_reevals = vec![(1, 1_000 as Balance)];
+        assert_eq!(compute_claim_uds(1, 1, past_reevals.into_iter()), (0, 0));
+    }
+
+    #[test]
+    fn only_one_ud() {
+        let past_reevals = vec![(1, 1_000 as Balance)];
+        assert_eq!(
+            compute_claim_uds(2, 1, past_reevals.into_iter()),
+            (1, 1_000)
+        );
+    }
+}
diff --git a/pallets/universal-dividend/src/lib.rs b/pallets/universal-dividend/src/lib.rs
index ab6fa5361..faf5749d2 100644
--- a/pallets/universal-dividend/src/lib.rs
+++ b/pallets/universal-dividend/src/lib.rs
@@ -17,13 +17,16 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
 mod benchmarking;
+mod compute_claim_uds;
 #[cfg(test)]
 mod mock;
 #[cfg(test)]
 mod tests;
+mod types;
 mod weights;
 
 pub use pallet::*;
+pub use types::*;
 pub use weights::WeightInfo;
 
 use frame_support::traits::{tokens::ExistenceRequirement, Currency};
@@ -32,17 +35,15 @@ use sp_arithmetic::{
     traits::{One, Saturating, Zero},
 };
 use sp_runtime::traits::StaticLookup;
-use sp_std::prelude::*;
-
-const OFFCHAIN_PREFIX_UD_HISTORY: &[u8] = b"ud::history::";
 
 #[frame_support::pallet]
 pub mod pallet {
     use super::*;
     use frame_support::pallet_prelude::*;
-    use frame_support::traits::StorageVersion;
+    use frame_support::traits::{StorageVersion, StoredMap};
     use frame_system::pallet_prelude::*;
     use sp_runtime::traits::Convert;
+    use sp_std::vec::Vec;
 
     pub type BalanceOf<T> =
         <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
@@ -53,7 +54,7 @@ pub mod pallet {
     #[pallet::pallet]
     #[pallet::generate_store(pub(super) trait Store)]
     #[pallet::storage_version(STORAGE_VERSION)]
-    #[pallet::without_storage_info]
+    //#[pallet::without_storage_info]
     pub struct Pallet<T>(_);
 
     #[pallet::config]
@@ -64,10 +65,16 @@ pub mod pallet {
         type Currency: Currency<Self::AccountId>;
         /// Because this pallet emits events, it depends on the runtime's definition of an event.
         type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
+        #[pallet::constant]
+        /// Maximum number of past UD revaluations to keep in storage.
+        type MaxPastReeval: Get<u32>;
         /// Somethings that must provide the number of accounts allowed to create the universal dividend
         type MembersCount: Get<BalanceOf<Self>>;
         /// Somethings that must provide the list of accounts ids allowed to create the universal dividend
-        type MembersIds: Get<Vec<<Self as frame_system::Config>::AccountId>>;
+        type MembersStorage: frame_support::traits::StoredMap<Self::AccountId, FirstEligibleUd>;
+        /// An iterator over all members
+        type MembersStorageIter: From<Option<Vec<u8>>>
+            + Iterator<Item = (Self::AccountId, FirstEligibleUd)>;
         #[pallet::constant]
         /// Square of the money growth rate per ud reevaluation period
         type SquareMoneyGrowthRate: Get<Perbill>;
@@ -93,6 +100,29 @@ pub mod pallet {
     #[pallet::getter(fn current_ud)]
     pub type CurrentUd<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
 
+    #[pallet::type_value]
+    pub fn DefaultForCurrentUdIndex() -> UdIndex {
+        1
+    }
+
+    /// Current UD index
+    #[pallet::storage]
+    #[pallet::getter(fn ud_index)]
+    pub type CurrentUdIndex<T: Config> =
+        StorageValue<_, UdIndex, ValueQuery, DefaultForCurrentUdIndex>;
+
+    #[cfg(test)]
+    #[pallet::storage]
+    pub type TestMembers<T: Config> = StorageMap<
+        _,
+        Blake2_128Concat,
+        T::AccountId,
+        FirstEligibleUd,
+        ValueQuery,
+        GetDefault,
+        ConstU32<300_000>,
+    >;
+
     /// Total quantity of money created by universal dividend (does not take into account the possible destruction of money)
     #[pallet::storage]
     #[pallet::getter(fn total_money_created)]
@@ -103,6 +133,12 @@ pub mod pallet {
     #[pallet::getter(fn next_reeval)]
     pub type NextReeval<T: Config> = StorageValue<_, T::BlockNumber, ValueQuery>;
 
+    /// Past UD reevaluations
+    #[pallet::storage]
+    #[pallet::getter(fn past_reevals)]
+    pub type PastReevals<T: Config> =
+        StorageValue<_, BoundedVec<(UdIndex, BalanceOf<T>), T::MaxPastReeval>, ValueQuery>;
+
     // GENESIS
 
     #[pallet::genesis_config]
@@ -110,6 +146,8 @@ pub mod pallet {
         pub first_reeval: T::BlockNumber,
         pub first_ud: BalanceOf<T>,
         pub initial_monetary_mass: BalanceOf<T>,
+        #[cfg(test)]
+        pub initial_members: Vec<T::AccountId>,
     }
 
     #[cfg(feature = "std")]
@@ -119,6 +157,8 @@ pub mod pallet {
                 first_reeval: Default::default(),
                 first_ud: Default::default(),
                 initial_monetary_mass: Default::default(),
+                #[cfg(test)]
+                initial_members: Default::default(),
             }
         }
     }
@@ -132,6 +172,18 @@ pub mod pallet {
             <CurrentUd<T>>::put(self.first_ud);
             <MonetaryMass<T>>::put(self.initial_monetary_mass);
             NextReeval::<T>::put(self.first_reeval);
+            let mut past_reevals = BoundedVec::default();
+            past_reevals
+                .try_push((1, self.first_ud))
+                .expect("MaxPastReeval should be greather than zero");
+            PastReevals::<T>::put(past_reevals);
+
+            #[cfg(test)]
+            {
+                for member in &self.initial_members {
+                    TestMembers::<T>::insert(member, FirstEligibleUd::min());
+                }
+            }
         }
     }
 
@@ -140,21 +192,21 @@ pub mod pallet {
     #[pallet::hooks]
     impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
         fn on_initialize(n: T::BlockNumber) -> Weight {
-            let mut total_weight = T::WeightInfo::on_initialize();
             if (n % T::UdCreationPeriod::get()).is_zero() {
                 let current_members_count = T::MembersCount::get();
                 let next_reeval = NextReeval::<T>::get();
                 if n >= next_reeval {
                     NextReeval::<T>::put(next_reeval.saturating_add(T::UdReevalPeriod::get()));
-                    total_weight += Self::reeval_ud(current_members_count)
-                        + Self::create_ud(current_members_count, n)
-                        + T::DbWeight::get().reads_writes(2, 1);
+                    Self::reeval_ud(current_members_count);
+                    Self::create_ud(current_members_count);
+                    T::WeightInfo::on_initialize_ud_reevalued()
                 } else {
-                    total_weight +=
-                        Self::create_ud(current_members_count, n) + T::DbWeight::get().reads(2);
+                    Self::create_ud(current_members_count);
+                    T::WeightInfo::on_initialize_ud_created()
                 }
+            } else {
+                T::WeightInfo::on_initialize()
             }
-            total_weight
         }
     }
 
@@ -165,45 +217,92 @@ pub mod pallet {
     #[pallet::event]
     #[pallet::generate_deposit(pub(super) fn deposit_event)]
     pub enum Event<T: Config> {
-        /// A new universal dividend is created
-        /// [amout, members_count]
+        /// A new universal dividend is created.
         NewUdCreated {
             amount: BalanceOf<T>,
+            index: UdIndex,
             monetary_mass: BalanceOf<T>,
             members_count: BalanceOf<T>,
         },
-        /// The universal dividend has been re-evaluated
-        /// [new_ud_amount, monetary_mass, members_count]
+        /// The universal dividend has been re-evaluated.
         UdReevalued {
             new_ud_amount: BalanceOf<T>,
             monetary_mass: BalanceOf<T>,
             members_count: BalanceOf<T>,
         },
+        /// DUs were automatically transferred as part of a member removal.
+        UdsAutoPaidAtRemoval {
+            count: UdIndex,
+            total: BalanceOf<T>,
+            who: T::AccountId,
+        },
+        /// A member claimed his UDs.
+        UdsClaimed {
+            count: UdIndex,
+            total: BalanceOf<T>,
+            who: T::AccountId,
+        },
+    }
+
+    // ERRORS //
+
+    #[pallet::error]
+    pub enum Error<T> {
+        /// This account is not allowed to claim UDs.
+        AccountNotAllowedToClaimUds,
     }
 
     // INTERNAL FUNCTIONS //
     impl<T: Config> Pallet<T> {
-        fn create_ud(members_count: BalanceOf<T>, n: T::BlockNumber) -> Weight {
-            let total_weight: Weight = 0;
+        fn create_ud(members_count: BalanceOf<T>) {
+            let ud_amount = <CurrentUd<T>>::get();
+            let monetary_mass = <MonetaryMass<T>>::get();
 
-            let ud_amount = <CurrentUd<T>>::try_get().expect("corrupted storage");
-            let monetary_mass = <MonetaryMass<T>>::try_get().expect("corrupted storage");
-
-            for account_id in T::MembersIds::get() {
-                T::Currency::deposit_creating(&account_id, ud_amount);
-                Self::write_ud_history(n, account_id, ud_amount);
-            }
+            // Increment ud index
+            let ud_index = CurrentUdIndex::<T>::mutate(|next_ud_index| {
+                core::mem::replace(next_ud_index, next_ud_index.saturating_add(1))
+            });
 
             let new_monetary_mass =
                 monetary_mass.saturating_add(ud_amount.saturating_mul(members_count));
             MonetaryMass::<T>::put(new_monetary_mass);
             Self::deposit_event(Event::NewUdCreated {
                 amount: ud_amount,
+                index: ud_index,
                 members_count,
                 monetary_mass: new_monetary_mass,
             });
-
-            total_weight
+        }
+        fn do_claim_uds(who: &T::AccountId) -> DispatchResultWithPostInfo {
+            T::MembersStorage::try_mutate_exists(who, |maybe_first_eligible_ud| {
+                if let Some(FirstEligibleUd(Some(ref mut first_ud_index))) = maybe_first_eligible_ud
+                {
+                    let current_ud_index = CurrentUdIndex::<T>::get();
+                    if first_ud_index.get() >= current_ud_index {
+                        DispatchResultWithPostInfo::Ok(().into())
+                    } else {
+                        let (uds_count, uds_total) = compute_claim_uds::compute_claim_uds(
+                            current_ud_index,
+                            first_ud_index.get(),
+                            PastReevals::<T>::get().into_iter(),
+                        );
+                        let _ = core::mem::replace(
+                            first_ud_index,
+                            core::num::NonZeroU16::new(current_ud_index)
+                                .expect("unrechable because current_ud_index is never zero."),
+                        );
+                        T::Currency::deposit_creating(who, uds_total);
+                        Self::deposit_event(Event::UdsClaimed {
+                            count: uds_count,
+                            total: uds_total,
+                            who: who.clone(),
+                        });
+                        Ok(().into())
+                    }
+                } else {
+                    Err(Error::<T>::AccountNotAllowedToClaimUds.into())
+                }
+            })
         }
         fn do_transfer_ud(
             origin: OriginFor<T>,
@@ -213,8 +312,7 @@ pub mod pallet {
         ) -> DispatchResultWithPostInfo {
             let who = ensure_signed(origin)?;
             let dest = T::Lookup::lookup(dest)?;
-            let ud_amount =
-                <CurrentUd<T>>::try_get().map_err(|_| DispatchError::Other("corrupted storage"))?;
+            let ud_amount = <CurrentUd<T>>::get();
             T::Currency::transfer(
                 &who,
                 &dest,
@@ -223,12 +321,10 @@ pub mod pallet {
             )?;
             Ok(().into())
         }
-        fn reeval_ud(members_count: BalanceOf<T>) -> Weight {
-            let total_weight: Weight = 0;
+        fn reeval_ud(members_count: BalanceOf<T>) {
+            let ud_amount = <CurrentUd<T>>::get();
 
-            let ud_amount = <CurrentUd<T>>::try_get().expect("corrupted storage");
-
-            let monetary_mass = <MonetaryMass<T>>::try_get().expect("corrupted storage");
+            let monetary_mass = <MonetaryMass<T>>::get();
 
             let new_ud_amount = Self::reeval_ud_formula(
                 ud_amount,
@@ -240,15 +336,21 @@ pub mod pallet {
                 ),
             );
 
-            <CurrentUd<T>>::put(new_ud_amount);
+            CurrentUd::<T>::put(new_ud_amount);
+            PastReevals::<T>::mutate(|past_reevals| {
+                if past_reevals.len() == T::MaxPastReeval::get() as usize {
+                    past_reevals.remove(0);
+                }
+                past_reevals
+                    .try_push((CurrentUdIndex::<T>::get(), new_ud_amount))
+                    .expect("Unreachable, because we removed an element just before.")
+            });
 
             Self::deposit_event(Event::UdReevalued {
                 new_ud_amount,
                 monetary_mass,
                 members_count,
             });
-
-            total_weight
         }
         fn reeval_ud_formula(
             ud_t: BalanceOf<T>,
@@ -265,19 +367,18 @@ pub mod pallet {
             // UD(t+1) = UD(t) + c² (M(t+1) / N(t+1)) / (dt/udFrequency)
             ud_t + (c_square * monetary_mass) / (members_count * count_uds_beetween_two_reevals)
         }
-        fn write_ud_history(n: T::BlockNumber, account_id: T::AccountId, ud_amount: BalanceOf<T>) {
-            let mut key = Vec::with_capacity(57);
-            key.extend_from_slice(OFFCHAIN_PREFIX_UD_HISTORY);
-            account_id.encode_to(&mut key);
-            n.encode_to(&mut key);
-            sp_io::offchain_index::set(key.as_ref(), ud_amount.encode().as_ref());
-        }
     }
 
     // CALLS //
 
     #[pallet::call]
     impl<T: Config> Pallet<T> {
+        /// Claim Universal Dividends
+        #[pallet::weight(T::WeightInfo::claim_uds(T::MaxPastReeval::get()))]
+        pub fn claim_uds(origin: OriginFor<T>) -> DispatchResultWithPostInfo {
+            let who = ensure_signed(origin)?;
+            Self::do_claim_uds(&who)
+        }
         /// Transfer some liquid free balance to another account, in milliUD.
         #[pallet::weight(T::WeightInfo::transfer_ud())]
         pub fn transfer_ud(
@@ -298,4 +399,31 @@ pub mod pallet {
             Self::do_transfer_ud(origin, dest, value, ExistenceRequirement::KeepAlive)
         }
     }
+
+    // PUBLIC FUNCTIONS
+
+    impl<T: Config> Pallet<T> {
+        pub fn init_first_eligible_ud() -> FirstEligibleUd {
+            CurrentUdIndex::<T>::get().into()
+        }
+        pub fn on_removed_member(first_ud_index: UdIndex, who: &T::AccountId) -> Weight {
+            let current_ud_index = CurrentUdIndex::<T>::get();
+            if first_ud_index < current_ud_index {
+                let (uds_count, uds_total) = compute_claim_uds::compute_claim_uds(
+                    current_ud_index,
+                    first_ud_index,
+                    PastReevals::<T>::get().into_iter(),
+                );
+                T::Currency::deposit_creating(who, uds_total);
+                Self::deposit_event(Event::UdsAutoPaidAtRemoval {
+                    count: uds_count,
+                    total: uds_total,
+                    who: who.clone(),
+                });
+                T::DbWeight::get().reads_writes(2, 1)
+            } else {
+                T::DbWeight::get().reads(1)
+            }
+        }
+    }
 }
diff --git a/pallets/universal-dividend/src/mock.rs b/pallets/universal-dividend/src/mock.rs
index 668069164..a53a0fe96 100644
--- a/pallets/universal-dividend/src/mock.rs
+++ b/pallets/universal-dividend/src/mock.rs
@@ -18,7 +18,7 @@ use super::*;
 use crate::{self as pallet_universal_dividend};
 use frame_support::{
     parameter_types,
-    traits::{Everything, Get, OnFinalize, OnInitialize},
+    traits::{Everything, OnFinalize, OnInitialize},
 };
 use frame_system as system;
 use sp_core::H256;
@@ -102,10 +102,38 @@ parameter_types! {
     pub const UdReevalPeriod: BlockNumber = 8;
 }
 
-pub struct FakeWot;
-impl Get<Vec<u64>> for FakeWot {
-    fn get() -> Vec<u64> {
-        vec![1, 2, 3]
+pub struct TestMembersStorage;
+impl frame_support::traits::StoredMap<u64, FirstEligibleUd> for TestMembersStorage {
+    fn get(key: &u64) -> FirstEligibleUd {
+        crate::TestMembers::<Test>::get(key)
+    }
+    fn try_mutate_exists<R, E: From<sp_runtime::DispatchError>>(
+        key: &u64,
+        f: impl FnOnce(&mut Option<FirstEligibleUd>) -> Result<R, E>,
+    ) -> Result<R, E> {
+        let mut value = Some(crate::TestMembers::<Test>::get(key));
+        let result = f(&mut value)?;
+        if let Some(value) = value {
+            crate::TestMembers::<Test>::insert(key, value)
+        }
+        Ok(result)
+    }
+}
+pub struct TestMembersStorageIter(frame_support::storage::PrefixIterator<(u64, FirstEligibleUd)>);
+impl From<Option<Vec<u8>>> for TestMembersStorageIter {
+    fn from(maybe_key: Option<Vec<u8>>) -> Self {
+        let mut iter = crate::TestMembers::<Test>::iter();
+        if let Some(key) = maybe_key {
+            iter.set_last_raw_key(key);
+        }
+        Self(iter)
+    }
+}
+impl Iterator for TestMembersStorageIter {
+    type Item = (u64, FirstEligibleUd);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.0.next()
     }
 }
 
@@ -113,8 +141,10 @@ impl pallet_universal_dividend::Config for Test {
     type BlockNumberIntoBalance = sp_runtime::traits::ConvertInto;
     type Currency = pallet_balances::Pallet<Test>;
     type Event = Event;
+    type MaxPastReeval = frame_support::traits::ConstU32<2>;
     type MembersCount = MembersCount;
-    type MembersIds = FakeWot;
+    type MembersStorage = TestMembersStorage;
+    type MembersStorageIter = TestMembersStorageIter;
     type SquareMoneyGrowthRate = SquareMoneyGrowthRate;
     type UdCreationPeriod = UdCreationPeriod;
     type UdReevalPeriod = UdReevalPeriod;
@@ -140,6 +170,7 @@ pub fn run_to_block(n: u64) {
     while System::block_number() < n {
         UniversalDividend::on_finalize(System::block_number());
         System::on_finalize(System::block_number());
+        System::reset_events();
         System::set_block_number(System::block_number() + 1);
         System::on_initialize(System::block_number());
         UniversalDividend::on_initialize(System::block_number());
diff --git a/pallets/universal-dividend/src/tests.rs b/pallets/universal-dividend/src/tests.rs
index 2b48bef4c..9cfbfe1a7 100644
--- a/pallets/universal-dividend/src/tests.rs
+++ b/pallets/universal-dividend/src/tests.rs
@@ -15,14 +15,15 @@
 // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
 
 use crate::mock::*;
-use frame_system::{EventRecord, Phase};
+use frame_support::{assert_err, assert_ok, assert_storage_noop};
 
 #[test]
-fn test_ud_creation() {
+fn test_claim_uds() {
     new_test_ext(UniversalDividendConfig {
         first_reeval: 8,
         first_ud: 1_000,
         initial_monetary_mass: 0,
+        initial_members: vec![1, 2, 3],
     })
     .execute_with(|| {
         // In the beginning there was no money
@@ -32,82 +33,194 @@ fn test_ud_creation() {
         assert_eq!(Balances::free_balance(4), 0);
         assert_eq!(UniversalDividend::total_money_created(), 0);
 
-        // The first UD must be created in block #2
+        // Alice can claim UDs, but this should be a no-op.
+        run_to_block(1);
+        assert_storage_noop!(assert_ok!(UniversalDividend::claim_uds(Origin::signed(1))));
+        assert_eq!(Balances::free_balance(1), 0);
+
+        // Dave is not a member, he can't claim UDs
+        assert_err!(
+            UniversalDividend::claim_uds(Origin::signed(4)),
+            crate::Error::<Test>::AccountNotAllowedToClaimUds
+        );
+
+        // At block #2, the first UD must be created, but nobody should receive money
         run_to_block(2);
+        assert_eq!(UniversalDividend::total_money_created(), 3_000);
+        assert_eq!(Balances::free_balance(1), 0);
+        assert_eq!(Balances::free_balance(2), 0);
+        assert_eq!(Balances::free_balance(3), 0);
+        assert_eq!(Balances::free_balance(4), 0);
+
+        // Alice can claim UDs, and this time she must receive exactly one UD
+        assert_ok!(UniversalDividend::claim_uds(Origin::signed(1)));
+        System::assert_has_event(Event::UniversalDividend(crate::Event::UdsClaimed {
+            count: 1,
+            total: 1_000,
+            who: 1,
+        }));
         assert_eq!(Balances::free_balance(1), 1_000);
-        assert_eq!(Balances::free_balance(2), 1_000);
-        assert_eq!(Balances::free_balance(3), 1_000);
+        // Others members should not receive any UDs with Alice claim
+        assert_eq!(Balances::free_balance(2), 0);
+        assert_eq!(Balances::free_balance(3), 0);
         assert_eq!(Balances::free_balance(4), 0);
-        assert_eq!(UniversalDividend::total_money_created(), 3_000);
 
-        // Block #2 must generate 7 events, 2 events per new account fed, plus 1 event for the creation of the UD.
-        let events = System::events();
-        println!("events: {:#?}", events);
-        assert_eq!(events.len(), 10);
-        assert_eq!(
-            events[9],
-            EventRecord {
-                phase: Phase::Initialization,
-                event: Event::UniversalDividend(crate::Event::NewUdCreated {
-                    amount: 1_000,
-                    monetary_mass: 3_000,
-                    members_count: 3,
-                }),
-                topics: vec![],
-            }
+        // At block #4, the second UD must be created, but nobody should receive money
+        run_to_block(4);
+        assert_eq!(UniversalDividend::total_money_created(), 6_000);
+        assert_eq!(Balances::free_balance(1), 1_000);
+        assert_eq!(Balances::free_balance(2), 0);
+        assert_eq!(Balances::free_balance(3), 0);
+        assert_eq!(Balances::free_balance(4), 0);
+
+        // Alice can claim UDs, And she must receive exactly one UD (the second one)
+        assert_ok!(UniversalDividend::claim_uds(Origin::signed(1)));
+        System::assert_has_event(Event::UniversalDividend(crate::Event::UdsClaimed {
+            count: 1,
+            total: 1_000,
+            who: 1,
+        }));
+        assert_eq!(Balances::free_balance(1), 2_000);
+        // Others members should not receive any UDs with Alice claim
+        assert_eq!(Balances::free_balance(2), 0);
+        assert_eq!(Balances::free_balance(3), 0);
+        assert_eq!(Balances::free_balance(4), 0);
+
+        // Bob can claim UDs, he must receive exactly two UDs
+        assert_ok!(UniversalDividend::claim_uds(Origin::signed(2)));
+        System::assert_has_event(Event::UniversalDividend(crate::Event::UdsClaimed {
+            count: 2,
+            total: 2_000,
+            who: 2,
+        }));
+        assert_eq!(Balances::free_balance(2), 2_000);
+        // Others members should not receive any UDs with Alice and Bob claims
+        assert_eq!(Balances::free_balance(3), 0);
+        assert_eq!(Balances::free_balance(4), 0);
+
+        // Dave is still not a member, he still can't claim UDs.
+        assert_err!(
+            UniversalDividend::claim_uds(Origin::signed(4)),
+            crate::Error::<Test>::AccountNotAllowedToClaimUds
         );
 
+        // At block #8, the first reevaluated UD should be created
+        run_to_block(8);
+        assert_eq!(UniversalDividend::total_money_created(), 12_225);
+
+        // Charlie can claim all his UDs at once, he must receive exactly four UDs
+        assert_ok!(UniversalDividend::claim_uds(Origin::signed(3)));
+        System::assert_has_event(Event::UniversalDividend(crate::Event::UdsClaimed {
+            count: 4,
+            total: 4_075,
+            who: 3,
+        }));
+        assert_eq!(Balances::free_balance(3), 4_075);
+    });
+}
+
+#[test]
+fn test_ud_creation() {
+    new_test_ext(UniversalDividendConfig {
+        first_reeval: 8,
+        first_ud: 1_000,
+        initial_monetary_mass: 0,
+        initial_members: vec![1, 2, 3],
+    })
+    .execute_with(|| {
+        // In the beginning there was no money
+        assert_eq!(Balances::free_balance(1), 0);
+        assert_eq!(Balances::free_balance(2), 0);
+        assert_eq!(Balances::free_balance(3), 0);
+        assert_eq!(Balances::free_balance(4), 0);
+        assert_eq!(UniversalDividend::total_money_created(), 0);
+
+        // The first UD must be created in block #2
+        run_to_block(2);
+        System::assert_has_event(Event::UniversalDividend(crate::Event::NewUdCreated {
+            amount: 1_000,
+            index: 1,
+            monetary_mass: 3_000,
+            members_count: 3,
+        }));
+        assert_eq!(UniversalDividend::total_money_created(), 3_000);
+        /*assert_eq!(Balances::free_balance(1), 1_000);
+        assert_eq!(Balances::free_balance(2), 1_000);
+        assert_eq!(Balances::free_balance(3), 1_000);
+        assert_eq!(Balances::free_balance(4), 0);*/
+
         // The second UD must be created in block #4
         run_to_block(4);
-        assert_eq!(Balances::free_balance(1), 2_000);
+        System::assert_has_event(Event::UniversalDividend(crate::Event::NewUdCreated {
+            amount: 1_000,
+            index: 2,
+            monetary_mass: 6_000,
+            members_count: 3,
+        }));
+        assert_eq!(UniversalDividend::total_money_created(), 6_000);
+        /*assert_eq!(Balances::free_balance(1), 2_000);
         assert_eq!(Balances::free_balance(2), 2_000);
         assert_eq!(Balances::free_balance(3), 2_000);
-        assert_eq!(Balances::free_balance(4), 0);
-        assert_eq!(UniversalDividend::total_money_created(), 6_000);
-
-        /*// Block #4 must generate 4 events, 1 event per account fed, plus 1 event for the creation of the UD.
-        let events = System::events();
-        println!("{:?}", events);
-        assert_eq!(events.len(), 4);
-        assert_eq!(
-            events[3],
-            EventRecord {
-                phase: Phase::Initialization,
-                event: Event::UniversalDividend(crate::Event::NewUdCreated(1000, 3)),
-                topics: vec![],
-            }
-        );*/
+        assert_eq!(Balances::free_balance(4), 0);*/
 
         // The third UD must be created in block #6
         run_to_block(6);
-        assert_eq!(Balances::free_balance(1), 3_000);
+        System::assert_has_event(Event::UniversalDividend(crate::Event::NewUdCreated {
+            amount: 1_000,
+            index: 3,
+            monetary_mass: 9_000,
+            members_count: 3,
+        }));
+        assert_eq!(UniversalDividend::total_money_created(), 9_000);
+        /*assert_eq!(Balances::free_balance(1), 3_000);
         assert_eq!(Balances::free_balance(2), 3_000);
         assert_eq!(Balances::free_balance(3), 3_000);
-        assert_eq!(Balances::free_balance(4), 0);
-        assert_eq!(UniversalDividend::total_money_created(), 9_000);
+        assert_eq!(Balances::free_balance(4), 0);*/
 
         // Block #8 should cause a re-evaluation of UD
         run_to_block(8);
-        assert_eq!(Balances::free_balance(1), 4_075);
+        System::assert_has_event(Event::UniversalDividend(crate::Event::UdReevalued {
+            new_ud_amount: 1_075,
+            monetary_mass: 9_000,
+            members_count: 3,
+        }));
+        // Then, the first reevalued UD should be created
+        System::assert_has_event(Event::UniversalDividend(crate::Event::NewUdCreated {
+            amount: 1_075,
+            index: 4,
+            monetary_mass: 12_225,
+            members_count: 3,
+        }));
+        assert_eq!(UniversalDividend::total_money_created(), 12_225);
+        /*assert_eq!(Balances::free_balance(1), 4_075);
         assert_eq!(Balances::free_balance(2), 4_075);
         assert_eq!(Balances::free_balance(3), 4_075);
-        assert_eq!(Balances::free_balance(4), 0);
-        assert_eq!(UniversalDividend::total_money_created(), 12_225);
+        assert_eq!(Balances::free_balance(4), 0);*/
 
         // Block #10 #12 and #14should creates the reevalued UD
         run_to_block(14);
-        assert_eq!(Balances::free_balance(1), 7_300);
-        assert_eq!(Balances::free_balance(2), 7_300);
-        assert_eq!(Balances::free_balance(3), 7_300);
-        assert_eq!(Balances::free_balance(4), 0);
+        System::assert_has_event(Event::UniversalDividend(crate::Event::NewUdCreated {
+            amount: 1_075,
+            index: 7,
+            monetary_mass: 21_900,
+            members_count: 3,
+        }));
         assert_eq!(UniversalDividend::total_money_created(), 21_900);
 
         // Block #16 should cause a second re-evaluation of UD
         run_to_block(16);
-        assert_eq!(Balances::free_balance(1), 8_557);
-        assert_eq!(Balances::free_balance(2), 8_557);
-        assert_eq!(Balances::free_balance(3), 8_557);
-        assert_eq!(Balances::free_balance(4), 0);
+        System::assert_has_event(Event::UniversalDividend(crate::Event::UdReevalued {
+            new_ud_amount: 1_257,
+            monetary_mass: 21_900,
+            members_count: 3,
+        }));
+        // Then, the reevalued UD should be created
+        System::assert_has_event(Event::UniversalDividend(crate::Event::NewUdCreated {
+            amount: 1_257,
+            index: 8,
+            monetary_mass: 25_671,
+            members_count: 3,
+        }));
         assert_eq!(UniversalDividend::total_money_created(), 25_671);
     });
 }
diff --git a/pallets/universal-dividend/src/types.rs b/pallets/universal-dividend/src/types.rs
new file mode 100644
index 000000000..837efa9de
--- /dev/null
+++ b/pallets/universal-dividend/src/types.rs
@@ -0,0 +1,97 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Substrate-Libre-Currency.
+//
+// Substrate-Libre-Currency 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, version 3 of the License.
+//
+// Substrate-Libre-Currency 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 Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
+
+use codec::{Decode, Encode, Error, Input, MaxEncodedLen, Output};
+use core::num::NonZeroU16;
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+use sp_std::vec::Vec;
+
+pub type UdIndex = u16;
+
+#[cfg_attr(feature = "std", derive(Deserialize, Serialize))]
+#[derive(Clone, Copy, Default, Eq, PartialEq)]
+pub struct FirstEligibleUd(pub Option<NonZeroU16>);
+
+#[cfg(feature = "std")]
+impl FirstEligibleUd {
+    pub fn min() -> Self {
+        Self(Some(NonZeroU16::new(1).expect("unreachable")))
+    }
+}
+
+impl From<UdIndex> for FirstEligibleUd {
+    fn from(ud_index: UdIndex) -> Self {
+        FirstEligibleUd(NonZeroU16::new(ud_index))
+    }
+}
+
+impl From<FirstEligibleUd> for Option<UdIndex> {
+    fn from(first_eligible_ud: FirstEligibleUd) -> Self {
+        first_eligible_ud.0.map(|ud_index| ud_index.get())
+    }
+}
+
+impl Encode for FirstEligibleUd {
+    fn size_hint(&self) -> usize {
+        self.as_u16().size_hint()
+    }
+
+    fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
+        self.as_u16().encode_to(dest)
+    }
+
+    fn encode(&self) -> Vec<u8> {
+        self.as_u16().encode()
+    }
+
+    fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
+        self.as_u16().using_encoded(f)
+    }
+}
+
+impl codec::EncodeLike for FirstEligibleUd {}
+
+impl Decode for FirstEligibleUd {
+    fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
+        Ok(match NonZeroU16::new(Decode::decode(input)?) {
+            Some(non_zero_u16) => Self(Some(non_zero_u16)),
+            None => Self(None),
+        })
+    }
+}
+
+impl MaxEncodedLen for FirstEligibleUd {
+    fn max_encoded_len() -> usize {
+        u16::max_encoded_len()
+    }
+}
+
+impl scale_info::TypeInfo for FirstEligibleUd {
+    type Identity = UdIndex;
+
+    fn type_info() -> scale_info::Type {
+        Self::Identity::type_info()
+    }
+}
+
+impl FirstEligibleUd {
+    // private
+    #[inline(always)]
+    fn as_u16(&self) -> UdIndex {
+        self.0.map(|ud_index| ud_index.get()).unwrap_or_default()
+    }
+}
diff --git a/pallets/universal-dividend/src/weights.rs b/pallets/universal-dividend/src/weights.rs
index 6dfab2bbd..e3b860880 100644
--- a/pallets/universal-dividend/src/weights.rs
+++ b/pallets/universal-dividend/src/weights.rs
@@ -21,6 +21,9 @@ use frame_support::weights::{constants::RocksDbWeight, Weight};
 /// Weight functions needed for pallet_universal_dividend.
 pub trait WeightInfo {
     fn on_initialize() -> Weight;
+    fn on_initialize_ud_created() -> Weight;
+    fn on_initialize_ud_reevalued() -> Weight;
+    fn claim_uds(n: u32) -> Weight;
     fn transfer_ud() -> Weight;
     fn transfer_ud_keep_alive() -> Weight;
 }
@@ -31,6 +34,38 @@ impl WeightInfo for () {
     fn on_initialize() -> Weight {
         2_260_000 as Weight
     }
+    // Storage: Membership CounterForMembership (r:1 w:0)
+    // Storage: UniversalDividend NextReeval (r:1 w:0)
+    // Storage: UniversalDividend CurrentUd (r:1 w:0)
+    // Storage: UniversalDividend MonetaryMass (r:1 w:1)
+    // Storage: UniversalDividend CurrentUdIndex (r:1 w:1)
+    fn on_initialize_ud_created() -> Weight {
+        (20_160_000 as Weight)
+            .saturating_add(RocksDbWeight::get().reads(5 as Weight))
+            .saturating_add(RocksDbWeight::get().writes(2 as Weight))
+    }
+    // Storage: Membership CounterForMembership (r:1 w:0)
+    // Storage: UniversalDividend NextReeval (r:1 w:1)
+    // Storage: UniversalDividend CurrentUd (r:1 w:1)
+    // Storage: UniversalDividend MonetaryMass (r:1 w:1)
+    // Storage: UniversalDividend PastReevals (r:1 w:1)
+    // Storage: UniversalDividend CurrentUdIndex (r:1 w:1)
+    fn on_initialize_ud_reevalued() -> Weight {
+        (32_770_000 as Weight)
+            .saturating_add(RocksDbWeight::get().reads(6 as Weight))
+            .saturating_add(RocksDbWeight::get().writes(5 as Weight))
+    }
+    // Storage: Identity IdentityIndexOf (r:1 w:0)
+    // Storage: Identity Identities (r:1 w:1)
+    // Storage: UniversalDividend CurrentUdIndex (r:1 w:0)
+    // Storage: UniversalDividend PastReevals (r:1 w:0)
+    fn claim_uds(n: u32) -> Weight {
+        (32_514_000 as Weight)
+            // Standard Error: 32_000
+            .saturating_add((8_000 as Weight).saturating_mul(n as Weight))
+            .saturating_add(RocksDbWeight::get().reads(4 as Weight))
+            .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+    }
     // Storage: UniversalDividend CurrentUd (r:1 w:0)
     // Storage: System Account (r:1 w:1)
     // Storage: Account PendingNewAccounts (r:0 w:1)
diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml
index 9c2f22584..18152fd8c 100644
--- a/runtime/common/Cargo.toml
+++ b/runtime/common/Cargo.toml
@@ -21,7 +21,6 @@ runtime-benchmarks = [
     'pallet-multisig/runtime-benchmarks',
     'pallet-proxy/runtime-benchmarks',
     'pallet-treasury/runtime-benchmarks',
-    'pallet-ud-accounts-storage/runtime-benchmarks',
     'sp-runtime/runtime-benchmarks',
 ]
 std = [
@@ -46,7 +45,6 @@ std = [
     'pallet-timestamp/std',
     'pallet-treasury/std',
 	'pallet-universal-dividend/std',
-    'pallet-ud-accounts-storage/std',
 	"serde/std",
 	"serde_derive",
     'sp-arithmetic/std',
@@ -71,7 +69,6 @@ pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = fa
 pallet-identity = { path = '../../pallets/identity', default-features = false }
 pallet-membership = { path = '../../pallets/membership', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
-pallet-ud-accounts-storage = { path = '../../pallets/ud-accounts-storage', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
 sp-membership = { path = '../../primitives/membership', default-features = false }
diff --git a/runtime/common/src/constants.rs b/runtime/common/src/constants.rs
index b3c5e22fc..5eeb8b20c 100644
--- a/runtime/common/src/constants.rs
+++ b/runtime/common/src/constants.rs
@@ -15,6 +15,8 @@
 // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
 
 use crate::{Balance, BlockNumber};
+use frame_support::weights::constants::WEIGHT_PER_MICROS;
+use frame_support::weights::Weight;
 use sp_runtime::Perbill;
 
 /// This determines the average expected block time that we are targeting.
@@ -56,26 +58,32 @@ pub const fn deposit(items: u32, bytes: u32) -> Balance {
     items as Balance * DEPOSIT_PER_ITEM + (bytes as Balance * DEPOSIT_PER_BYTE)
 }
 
-// WEIGHTS COMSTANTS //
+// Maximal weight proportion of normal extrinsics per block
+pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
+
+// WEIGHTS CONSTANTS //
+
+// Read DB weights
+pub const READ_WEIGHTS: Weight = 250 * WEIGHT_PER_MICROS; // ~250 µs
+
+// Write DB weights
+pub const WRITE_WEIGHTS: Weight = 1_000 * WEIGHT_PER_MICROS; // ~1000 µs
 
 // Execution cost of everything outside of the call itself:
 // signature verification, pre_dispatch and post_dispatch
-pub const EXTRINSIC_BASE_WEIGHTS: frame_support::weights::Weight = 1_000_000_000;
-
-// Maximal weight proportion of normal extrinsics per block
-pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
+pub const EXTRINSIC_BASE_WEIGHTS: Weight = READ_WEIGHTS + WRITE_WEIGHTS;
 
 // DB weights
 frame_support::parameter_types! {
     pub const DbWeight: frame_support::weights::RuntimeDbWeight = frame_support::weights::RuntimeDbWeight {
-        read: 250 * frame_support::weights::constants::WEIGHT_PER_MICROS,   // ~25 µs
-        write: 1_000 * frame_support::weights::constants::WEIGHT_PER_MICROS, // ~100 µs
+        read: READ_WEIGHTS,
+        write: WRITE_WEIGHTS,
     };
 }
 
 // Block weights limits
 pub fn block_weights(
-    expected_block_weight: frame_support::weights::Weight,
+    expected_block_weight: Weight,
     normal_ratio: sp_arithmetic::Perbill,
 ) -> frame_system::limits::BlockWeights {
     let normal_weight = normal_ratio * expected_block_weight;
diff --git a/runtime/common/src/handlers.rs b/runtime/common/src/handlers.rs
index 7e5587f55..b5f407057 100644
--- a/runtime/common/src/handlers.rs
+++ b/runtime/common/src/handlers.rs
@@ -15,12 +15,13 @@
 // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
 
 use super::entities::*;
-use super::{AccountId, IdtyIndex};
+use super::{AccountId, IdtyData, IdtyIndex};
 use frame_support::dispatch::UnfilteredDispatchable;
 use frame_support::instances::{Instance1, Instance2};
 use frame_support::pallet_prelude::Weight;
+use frame_support::traits::Get;
 use frame_support::Parameter;
-use sp_runtime::traits::{Convert, IsMember};
+use sp_runtime::traits::IsMember;
 
 pub struct OnNewSessionHandler<Runtime>(core::marker::PhantomData<Runtime>);
 impl<Runtime> pallet_authority_members::traits::OnNewSession for OnNewSessionHandler<Runtime>
@@ -40,27 +41,35 @@ type MembershipMetaData = pallet_duniter_wot::MembershipMetaData<AccountId>;
 impl<
         Inner: sp_membership::traits::OnEvent<IdtyIndex, MembershipMetaData>,
         Runtime: frame_system::Config<AccountId = AccountId>
-            + pallet_identity::Config<IdtyIndex = IdtyIndex>
+            + pallet_identity::Config<IdtyData = IdtyData, IdtyIndex = IdtyIndex>
             + pallet_membership::Config<Instance1, MetaData = MembershipMetaData>
-            + pallet_ud_accounts_storage::Config,
+            + pallet_universal_dividend::Config,
     > sp_membership::traits::OnEvent<IdtyIndex, MembershipMetaData>
     for OnMembershipEventHandler<Inner, Runtime>
 {
     fn on_event(membership_event: &sp_membership::Event<IdtyIndex, MembershipMetaData>) -> Weight {
         (match membership_event {
-            sp_membership::Event::MembershipAcquired(_idty_index, owner_key) => {
-                pallet_ud_accounts_storage::Pallet::<Runtime>::replace_account(
-                    None,
-                    Some(owner_key.0.clone()),
-                )
+            sp_membership::Event::MembershipAcquired(idty_index, _owner_key) => {
+                pallet_identity::Identities::<Runtime>::mutate_exists(idty_index, |idty_val_opt| {
+                    if let Some(ref mut idty_val) = idty_val_opt {
+                        idty_val.data =
+                            pallet_universal_dividend::Pallet::<Runtime>::init_first_eligible_ud();
+                    }
+                });
+                Runtime::DbWeight::get().reads_writes(1, 1)
             }
             sp_membership::Event::MembershipRevoked(idty_index) => {
-                if let Some(account_id) =
-                    crate::providers::IdentityAccountIdProvider::<Runtime>::convert(*idty_index)
-                {
-                    pallet_ud_accounts_storage::Pallet::<Runtime>::remove_account(account_id)
+                if let Some(idty_value) = pallet_identity::Identities::<Runtime>::get(idty_index) {
+                    if let Some(first_ud_index) = idty_value.data.into() {
+                        pallet_universal_dividend::Pallet::<Runtime>::on_removed_member(
+                            first_ud_index,
+                            &idty_value.owner_key,
+                        )
+                    } else {
+                        Runtime::DbWeight::get().reads(1)
+                    }
                 } else {
-                    0
+                    Runtime::DbWeight::get().reads(1)
                 }
             }
             _ => 0,
diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs
index 2763a8c2a..96daf2186 100644
--- a/runtime/common/src/lib.rs
+++ b/runtime/common/src/lib.rs
@@ -54,15 +54,18 @@ pub type Hash = sp_core::H256;
 /// Block header type
 pub type Header = sp_runtime::generic::Header<BlockNumber, sp_runtime::traits::BlakeTwo256>;
 
-/// Index of an identity
-pub type IdtyIndex = u32;
-
 /// Index of a transaction in the chain.
 pub type Index = u32;
 
 /// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
 pub type Signature = sp_runtime::MultiSignature;
 
+/// Index of an identity
+pub type IdtyIndex = u32;
+
+/// Identity data
+pub type IdtyData = pallet_universal_dividend::FirstEligibleUd;
+
 pub struct FullIdentificationOfImpl;
 impl sp_runtime::traits::Convert<AccountId, Option<entities::ValidatorFullIdentification>>
     for FullIdentificationOfImpl
diff --git a/runtime/common/src/pallets_config.rs b/runtime/common/src/pallets_config.rs
index e7b880f1f..eb599c40e 100644
--- a/runtime/common/src/pallets_config.rs
+++ b/runtime/common/src/pallets_config.rs
@@ -386,12 +386,21 @@ macro_rules! pallets_config {
 
         // UNIVERSAL DIVIDEND //
 
+		pub struct MembersCount;
+		impl frame_support::pallet_prelude::Get<Balance> for MembersCount {
+			fn get() -> Balance {
+				<Membership as sp_membership::traits::MembersCount>::members_count() as Balance
+			}
+		}
+
         impl pallet_universal_dividend::Config for Runtime {
             type BlockNumberIntoBalance = sp_runtime::traits::ConvertInto;
             type Currency = pallet_balances::Pallet<Runtime>;
             type Event = Event;
-            type MembersCount = common_runtime::providers::UdAccountsProvider<Runtime>;
-            type MembersIds = common_runtime::providers::UdAccountsProvider<Runtime>;
+			type MaxPastReeval = frame_support::traits::ConstU32<4>;
+            type MembersCount = MembersCount;
+            type MembersStorage = Identity;
+			type MembersStorageIter = common_runtime::providers::IdtyDataIter<Runtime>;
             type SquareMoneyGrowthRate = SquareMoneyGrowthRate;
             type UdCreationPeriod = UdCreationPeriod;
             type UdReevalPeriod = UdReevalPeriod;
@@ -399,8 +408,6 @@ macro_rules! pallets_config {
 			type WeightInfo = common_runtime::weights::pallet_universal_dividend::WeightInfo<Runtime>;
         }
 
-        impl pallet_ud_accounts_storage::Config for Runtime {}
-
         // WEB OF TRUST //
 
         use frame_support::instances::Instance1;
@@ -416,6 +423,7 @@ macro_rules! pallets_config {
             type Event = Event;
             type EnsureIdtyCallAllowed = Wot;
             type IdtyCreationPeriod = IdtyCreationPeriod;
+			type IdtyData = IdtyData;
             type IdtyIndex = IdtyIndex;
             type IdtyNameValidator = IdtyNameValidatorImpl;
             type IdtyValidationOrigin = EnsureRoot<Self::AccountId>;
diff --git a/runtime/common/src/providers.rs b/runtime/common/src/providers.rs
index 5c87d9773..193a9bd98 100644
--- a/runtime/common/src/providers.rs
+++ b/runtime/common/src/providers.rs
@@ -15,10 +15,11 @@
 // along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
 
 use crate::{AccountId, IdtyIndex};
-use frame_support::traits::Get;
+use core::marker::PhantomData;
+use sp_std::boxed::Box;
 use sp_std::vec::Vec;
 
-pub struct IdentityAccountIdProvider<Runtime>(core::marker::PhantomData<Runtime>);
+pub struct IdentityAccountIdProvider<Runtime>(PhantomData<Runtime>);
 
 impl<
         Runtime: frame_system::Config<AccountId = AccountId>
@@ -31,16 +32,33 @@ impl<
     }
 }
 
-pub struct UdAccountsProvider<Runtime>(core::marker::PhantomData<Runtime>);
-impl<Runtime: pallet_ud_accounts_storage::Config> Get<u64> for UdAccountsProvider<Runtime> {
-    fn get() -> u64 {
-        <pallet_ud_accounts_storage::Pallet<Runtime>>::accounts_len() as u64
+#[allow(clippy::type_complexity)]
+pub struct IdtyDataIter<T: pallet_identity::Config>(
+    Box<dyn Iterator<Item = pallet_identity::IdtyValue<T::BlockNumber, T::AccountId, T::IdtyData>>>,
+    PhantomData<T>,
+);
+
+impl<T: pallet_identity::Config> From<Option<Vec<u8>>> for IdtyDataIter<T> {
+    fn from(maybe_key: Option<Vec<u8>>) -> Self {
+        let mut iter = pallet_identity::Identities::<T>::iter_values();
+        if let Some(key) = maybe_key {
+            iter.set_last_raw_key(key);
+        }
+        Self(Box::new(iter), PhantomData)
     }
 }
-impl<Runtime: frame_system::Config<AccountId = AccountId> + pallet_ud_accounts_storage::Config>
-    Get<Vec<AccountId>> for UdAccountsProvider<Runtime>
-{
-    fn get() -> Vec<AccountId> {
-        <pallet_ud_accounts_storage::Pallet<Runtime>>::accounts_list()
+
+impl<T: pallet_identity::Config> Iterator for IdtyDataIter<T> {
+    type Item = (T::AccountId, T::IdtyData);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(pallet_identity::IdtyValue {
+            owner_key, data, ..
+        }) = self.0.next()
+        {
+            Some((owner_key, data))
+        } else {
+            None
+        }
     }
 }
diff --git a/runtime/common/src/weights/pallet_universal_dividend.rs b/runtime/common/src/weights/pallet_universal_dividend.rs
index 1aab206a0..9a1b5c980 100644
--- a/runtime/common/src/weights/pallet_universal_dividend.rs
+++ b/runtime/common/src/weights/pallet_universal_dividend.rs
@@ -17,7 +17,7 @@
 //! Autogenerated weights for `pallet_universal_dividend`
 //!
 //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2022-06-12, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2022-07-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
 //! EXECUTION: Some(Wasm), WASM-EXECUTION: Interpreted, CHAIN: Some("dev"), DB CACHE: 1024
 
 // Executed Command:
@@ -45,10 +45,40 @@ use sp_std::marker::PhantomData;
 /// Weight functions for `pallet_universal_dividend`.
 pub struct WeightInfo<T>(PhantomData<T>);
 impl<T: frame_system::Config> pallet_universal_dividend::WeightInfo for WeightInfo<T> {
-	// Storage: Parameters ParametersStorage (r:1 w:0)
 	fn on_initialize() -> Weight {
-		(104_055_000 as Weight)
-			.saturating_add(T::DbWeight::get().reads(1 as Weight))
+		111_220_000 as Weight
+	}
+	// Storage: Membership CounterForMembership (r:1 w:0)
+	// Storage: UniversalDividend NextReeval (r:1 w:0)
+	// Storage: UniversalDividend CurrentUd (r:1 w:0)
+	// Storage: UniversalDividend MonetaryMass (r:1 w:1)
+	// Storage: UniversalDividend CurrentUdIndex (r:1 w:1)
+	fn on_initialize_ud_created() -> Weight {
+		(525_382_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(5 as Weight))
+			.saturating_add(T::DbWeight::get().writes(2 as Weight))
+	}
+	// Storage: Membership CounterForMembership (r:1 w:0)
+	// Storage: UniversalDividend NextReeval (r:1 w:1)
+	// Storage: UniversalDividend CurrentUd (r:1 w:1)
+	// Storage: UniversalDividend MonetaryMass (r:1 w:1)
+	// Storage: UniversalDividend PastReevals (r:1 w:1)
+	// Storage: UniversalDividend CurrentUdIndex (r:1 w:1)
+	fn on_initialize_ud_reevalued() -> Weight {
+		(1_161_595_000 as Weight)
+			.saturating_add(T::DbWeight::get().reads(6 as Weight))
+			.saturating_add(T::DbWeight::get().writes(5 as Weight))
+	}
+	// Storage: Identity IdentityIndexOf (r:1 w:0)
+	// Storage: Identity Identities (r:1 w:1)
+	// Storage: UniversalDividend CurrentUdIndex (r:1 w:0)
+	// Storage: UniversalDividend PastReevals (r:1 w:0)
+	fn claim_uds(n: u32) -> Weight {
+		(1_221_776_000 as Weight)
+			// Standard Error: 958_000
+			.saturating_add((7_709_000 as Weight).saturating_mul(n as Weight))
+			.saturating_add(T::DbWeight::get().reads(4 as Weight))
+			.saturating_add(T::DbWeight::get().writes(1 as Weight))
 	}
 	// Storage: UniversalDividend CurrentUd (r:1 w:0)
 	// Storage: System Account (r:1 w:1)
diff --git a/runtime/g1/Cargo.toml b/runtime/g1/Cargo.toml
index 7fd344106..a4479f456 100644
--- a/runtime/g1/Cargo.toml
+++ b/runtime/g1/Cargo.toml
@@ -118,7 +118,6 @@ pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = fa
 pallet-identity = { path = '../../pallets/identity', default-features = false }
 pallet-membership = { path = '../../pallets/membership', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
-pallet-ud-accounts-storage = { path = '../../pallets/ud-accounts-storage', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
 sp-membership = { path = '../../primitives/membership', default-features = false }
diff --git a/runtime/g1/src/lib.rs b/runtime/g1/src/lib.rs
index c29189666..75f12540d 100644
--- a/runtime/g1/src/lib.rs
+++ b/runtime/g1/src/lib.rs
@@ -27,7 +27,8 @@ pub mod parameters;
 pub use self::parameters::*;
 pub use common_runtime::{
     constants::*, entities::*, handlers::*, AccountId, Address, Balance, BlockNumber,
-    FullIdentificationOfImpl, GetCurrentEpochIndex, Hash, Header, IdtyIndex, Index, Signature,
+    FullIdentificationOfImpl, GetCurrentEpochIndex, Hash, Header, IdtyData, IdtyIndex, Index,
+    Signature,
 };
 pub use pallet_balances::Call as BalancesCall;
 pub use pallet_identity::{IdtyStatus, IdtyValue};
@@ -227,8 +228,7 @@ construct_runtime!(
         Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>} = 22,
 
         // Universal dividend
-        UdAccountsStorage: pallet_ud_accounts_storage::{Pallet, Config<T>, Storage} = 30,
-        UniversalDividend: pallet_universal_dividend::{Pallet, Call, Config<T>, Storage, Event<T>} = 31,
+        UniversalDividend: pallet_universal_dividend::{Pallet, Call, Config<T>, Storage, Event<T>} = 30,
 
         // Web Of Trust
         Wot: pallet_duniter_wot::<Instance1>::{Pallet} = 40,
diff --git a/runtime/gdev/Cargo.toml b/runtime/gdev/Cargo.toml
index d400bdf3d..236aadec2 100644
--- a/runtime/gdev/Cargo.toml
+++ b/runtime/gdev/Cargo.toml
@@ -140,7 +140,6 @@ pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = fa
 pallet-identity = { path = '../../pallets/identity', default-features = false }
 pallet-membership = { path = '../../pallets/membership', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
-pallet-ud-accounts-storage = { path = '../../pallets/ud-accounts-storage', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
 sp-membership = { path = '../../primitives/membership', default-features = false }
diff --git a/runtime/gdev/src/lib.rs b/runtime/gdev/src/lib.rs
index 594091a2a..6e574292b 100644
--- a/runtime/gdev/src/lib.rs
+++ b/runtime/gdev/src/lib.rs
@@ -31,7 +31,8 @@ pub mod parameters;
 pub use self::parameters::*;
 pub use common_runtime::{
     constants::*, entities::*, handlers::*, AccountId, Address, Balance, BlockNumber,
-    FullIdentificationOfImpl, GetCurrentEpochIndex, Hash, Header, IdtyIndex, Index, Signature,
+    FullIdentificationOfImpl, GetCurrentEpochIndex, Hash, Header, IdtyData, IdtyIndex, Index,
+    Signature,
 };
 pub use pallet_balances::Call as BalancesCall;
 pub use pallet_duniter_test_parameters::Parameters as GenesisParameters;
@@ -228,7 +229,10 @@ common_runtime::pallets_config! {
     pub type MembershipPeriod = pallet_duniter_test_parameters::MembershipPeriod<Runtime>;
     pub type RenewablePeriod = pallet_duniter_test_parameters::MembershipRenewablePeriod<Runtime>;
     pub type PendingMembershipPeriod = pallet_duniter_test_parameters::PendingMembershipPeriod<Runtime>;
+    #[cfg(not(test))]
     pub type UdCreationPeriod = pallet_duniter_test_parameters::UdCreationPeriod<Runtime>;
+    #[cfg(test)]
+    pub type UdCreationPeriod = frame_support::traits::ConstU32<10>;
     pub type UdReevalPeriod = pallet_duniter_test_parameters::UdReevalPeriod<Runtime>;
     pub type WotFirstCertIssuableOn = pallet_duniter_test_parameters::WotFirstCertIssuableOn<Runtime>;
     pub type WotMinCertForMembership = pallet_duniter_test_parameters::WotMinCertForMembership<Runtime>;
@@ -301,8 +305,7 @@ construct_runtime!(
         Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>} = 22,
 
         // Universal dividend
-        UdAccountsStorage: pallet_ud_accounts_storage::{Pallet, Config<T>, Storage} = 30,
-        UniversalDividend: pallet_universal_dividend::{Pallet, Call, Config<T>, Storage, Event<T>} = 31,
+        UniversalDividend: pallet_universal_dividend::{Pallet, Call, Config<T>, Storage, Event<T>} = 30,
 
         // Web Of Trust
         Wot: pallet_duniter_wot::<Instance1>::{Pallet} = 40,
diff --git a/runtime/gdev/tests/common/mod.rs b/runtime/gdev/tests/common/mod.rs
index f17c23b4c..762e921fb 100644
--- a/runtime/gdev/tests/common/mod.rs
+++ b/runtime/gdev/tests/common/mod.rs
@@ -210,6 +210,7 @@ impl ExtBuilder {
                         owner_key: owner_key.clone(),
                         removable_on: 0,
                         status: IdtyStatus::Validated,
+                        data: IdtyData::min(),
                     },
                 })
                 .collect(),
@@ -266,12 +267,6 @@ impl ExtBuilder {
         .assimilate_storage(&mut t)
         .unwrap();
 
-        pallet_ud_accounts_storage::GenesisConfig::<Runtime> {
-            ud_accounts: initial_identities.values().cloned().collect(),
-        }
-        .assimilate_storage(&mut t)
-        .unwrap();
-
         pallet_universal_dividend::GenesisConfig::<Runtime> {
             first_reeval: 100,
             first_ud: 1_000,
diff --git a/runtime/gdev/tests/integration_tests.rs b/runtime/gdev/tests/integration_tests.rs
index 5505560b4..3cad420ec 100644
--- a/runtime/gdev/tests/integration_tests.rs
+++ b/runtime/gdev/tests/integration_tests.rs
@@ -17,7 +17,7 @@
 mod common;
 
 use common::*;
-use frame_support::traits::{PalletInfo, StorageInfo, StorageInfoTrait};
+use frame_support::traits::{Get, PalletInfo, StorageInfo, StorageInfoTrait};
 use frame_support::{assert_noop, assert_ok};
 use frame_support::{StorageHasher, Twox128};
 use gdev_runtime::*;
@@ -107,16 +107,60 @@ fn test_remove_identity() {
             System::events()[2].event,
             Event::Identity(pallet_identity::Event::IdtyRemoved { idty_index: 4 })
         );
+    });
+}
+#[test]
+fn test_remove_identity_after_one_ud() {
+    ExtBuilder::new(1, 3, 4).build().execute_with(|| {
+        //println!("UdCreationPeriod={}", <Runtime as pallet_universal_dividend::Config>::UdCreationPeriod::get());
+        run_to_block(<Runtime as pallet_universal_dividend::Config>::UdCreationPeriod::get() + 1);
+
+        assert_ok!(Identity::remove_identity(
+            frame_system::RawOrigin::Root.into(),
+            4,
+            None
+        ));
+
+        // Verify events
+        let events = System::events();
+        //println!("{:?}", events);
+        assert_eq!(events.len(), 5);
+        assert_eq!(
+            System::events()[0].event,
+            Event::Membership(pallet_membership::Event::MembershipRevoked(4))
+        );
+        assert_eq!(
+            System::events()[1].event,
+            Event::Balances(pallet_balances::Event::Deposit {
+                who: AccountKeyring::Dave.to_account_id(),
+                amount: 1_000
+            })
+        );
+        assert_eq!(
+            System::events()[2].event,
+            Event::Balances(pallet_balances::Event::Endowed {
+                account: AccountKeyring::Dave.to_account_id(),
+                free_balance: 1_000
+            })
+        );
+        assert_eq!(
+            System::events()[3].event,
+            Event::UniversalDividend(pallet_universal_dividend::Event::UdsAutoPaidAtRemoval {
+                count: 1,
+                total: 1_000,
+                who: AccountKeyring::Dave.to_account_id(),
+            })
+        );
+        assert_eq!(
+            System::events()[4].event,
+            Event::Identity(pallet_identity::Event::IdtyRemoved { idty_index: 4 })
+        );
 
-        // The identity should be removed from UdAccountsStorage
-        assert_eq!(UdAccountsStorage::accounts_len(), 3);
+        // Verify state
+        assert!(Identity::identity(4).is_none());
         assert_eq!(
-            UdAccountsStorage::accounts_list(),
-            vec![
-                AccountKeyring::Bob.to_account_id(),
-                AccountKeyring::Charlie.to_account_id(),
-                AccountKeyring::Alice.to_account_id(),
-            ]
+            Balances::free_balance(AccountKeyring::Dave.to_account_id()),
+            1_000
         );
     });
 }
diff --git a/runtime/gtest/Cargo.toml b/runtime/gtest/Cargo.toml
index 68915d05b..cefd2396d 100644
--- a/runtime/gtest/Cargo.toml
+++ b/runtime/gtest/Cargo.toml
@@ -118,7 +118,6 @@ pallet-duniter-wot = { path = '../../pallets/duniter-wot', default-features = fa
 pallet-identity = { path = '../../pallets/identity', default-features = false }
 pallet-membership = { path = '../../pallets/membership', default-features = false }
 pallet-provide-randomness = { path = '../../pallets/provide-randomness', default-features = false }
-pallet-ud-accounts-storage = { path = '../../pallets/ud-accounts-storage', default-features = false }
 pallet-universal-dividend = { path = '../../pallets/universal-dividend', default-features = false }
 pallet-upgrade-origin = { path = '../../pallets/upgrade-origin', default-features = false }
 sp-membership = { path = '../../primitives/membership', default-features = false }
diff --git a/runtime/gtest/src/lib.rs b/runtime/gtest/src/lib.rs
index e134dc461..43c84ab9e 100644
--- a/runtime/gtest/src/lib.rs
+++ b/runtime/gtest/src/lib.rs
@@ -27,7 +27,8 @@ pub mod parameters;
 pub use self::parameters::*;
 pub use common_runtime::{
     constants::*, entities::*, handlers::*, AccountId, Address, Balance, BlockNumber,
-    FullIdentificationOfImpl, GetCurrentEpochIndex, Hash, Header, IdtyIndex, Index, Signature,
+    FullIdentificationOfImpl, GetCurrentEpochIndex, Hash, Header, IdtyData, IdtyIndex, Index,
+    Signature,
 };
 pub use pallet_balances::Call as BalancesCall;
 pub use pallet_identity::{IdtyStatus, IdtyValue};
@@ -228,8 +229,7 @@ construct_runtime!(
         Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>} = 22,
 
         // Universal dividend
-        UdAccountsStorage: pallet_ud_accounts_storage::{Pallet, Config<T>, Storage} = 30,
-        UniversalDividend: pallet_universal_dividend::{Pallet, Call, Config<T>, Storage, Event<T>} = 31,
+        UniversalDividend: pallet_universal_dividend::{Pallet, Call, Config<T>, Storage, Event<T>} = 30,
 
         // Web Of Trust
         Wot: pallet_duniter_wot::<Instance1>::{Pallet} = 40,
-- 
GitLab