Skip to main content
4 of 4
edited title
rob
  • 2.1k
  • 6
  • 13

How does Substrate clean up accounts whose balance is below the existential deposit?

I know the concept of existential deposit (ED) means that if the account balance is below this value, it will be wiped out. I tried to figure out how it works. The AccountData is defined in the runtime while stored in the frame-system pallet. try_mutate_exists in the frame_system pallet used to mutate AccountData storage, but how and where does the substrate system wipe the account whose balance below ED? I find OnKilledAccount type in the frame_system::Config, but it's more like a callback after the account has been killed.

impl<T: Config> StoredMap<T::AccountId, T::AccountData> for Pallet<T> {
    fn get(k: &T::AccountId) -> T::AccountData {
        Account::<T>::get(k).data
    }

    fn try_mutate_exists<R, E: From<DispatchError>>(
        k: &T::AccountId,
        f: impl FnOnce(&mut Option<T::AccountData>) -> Result<R, E>,
    ) -> Result<R, E> {
        let account = Account::<T>::get(k);
        let was_providing = is_providing(&account.data);
        let mut some_data = if was_providing {
            Some(account.data)
        } else {
            None
        };
        let result = f(&mut some_data)?;
        let is_providing = some_data.is_some();
        if !was_providing && is_providing {
            Self::inc_providers(k);
        } else if was_providing && !is_providing {
            match Self::dec_providers(k)? { // This may trigger account wipe
                DecRefStatus::Reaped => return Ok(result),
                DecRefStatus::Exists => {
                    // Update value as normal...
                }
            }
        } else if !was_providing && !is_providing {
            return Ok(result);
        }
        Account::<T>::mutate(k, |a| a.data = some_data.unwrap_or_default());
        Ok(result)
    }
}