subxt 0.50.0

Interact with Substrate based chains on the Polkadot Network
Documentation
// Copyright 2019-2026 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

//! Construct addresses to access storage entries with.

use crate::utils::{Maybe, YesMaybe};
use frame_decode::storage::{IntoDecodableValues, IntoEncodableValues};
use scale_decode::DecodeAsType;
use std::borrow::Cow;

/// A storage address. This allows access to a given storage entry, which can then
/// be iterated over or fetched from by providing the relevant set of keys, or
/// otherwise inspected.
pub trait Address {
    /// All of the keys required to get to an individual value at this address.
    /// Keys must always impl [`IntoEncodableValues`], and for iteration must
    /// also impl [`IntoDecodableValues`].
    type KeyParts: IntoEncodableValues + IntoDecodableValues;
    /// Type of the storage value at this location.
    type Value: DecodeAsType;
    /// Does the address point to a plain value (as opposed to a map)?
    /// Set to [`crate::utils::Yes`] to enable APIs which require a map,
    /// or [`crate::utils::Maybe`] to enable APIs which allow a map.
    type IsPlain: YesMaybe;

    /// The pallet containing this storage entry.
    fn pallet_name(&self) -> &str;

    /// The name of the storage entry.
    fn entry_name(&self) -> &str;

    /// Return a unique hash for this address which can be used to validate it against metadata.
    fn validation_hash(&self) -> Option<[u8; 32]>;
}

// Any reference to an address is a valid address.
impl<A: Address + ?Sized> Address for &'_ A {
    type KeyParts = A::KeyParts;
    type Value = A::Value;
    type IsPlain = A::IsPlain;

    fn pallet_name(&self) -> &str {
        A::pallet_name(*self)
    }

    fn entry_name(&self) -> &str {
        A::entry_name(*self)
    }

    fn validation_hash(&self) -> Option<[u8; 32]> {
        A::validation_hash(*self)
    }
}

/// An address which is generated by the static APIs.
pub struct StaticAddress<KeyParts, Value, IsPlain> {
    pallet_name: Cow<'static, str>,
    entry_name: Cow<'static, str>,
    validation_hash: Option<[u8; 32]>,
    marker: core::marker::PhantomData<(KeyParts, Value, IsPlain)>,
}

impl<KeyParts, Value, IsPlain> Clone for StaticAddress<KeyParts, Value, IsPlain> {
    fn clone(&self) -> Self {
        Self {
            pallet_name: self.pallet_name.clone(),
            entry_name: self.entry_name.clone(),
            validation_hash: self.validation_hash,
            marker: self.marker,
        }
    }
}

impl<KeyParts, Value, IsPlain> core::fmt::Debug for StaticAddress<KeyParts, Value, IsPlain> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("StaticAddress")
            .field("pallet_name", &self.pallet_name)
            .field("entry_name", &self.entry_name)
            .field("validation_hash", &self.validation_hash)
            .finish()
    }
}

impl<KeyParts, Value, IsPlain> StaticAddress<KeyParts, Value, IsPlain> {
    /// Create a new [`StaticAddress`] using static strings for the pallet and call name.
    /// This is only expected to be used from codegen.
    #[doc(hidden)]
    pub fn new_static(pallet_name: &'static str, entry_name: &'static str, hash: [u8; 32]) -> Self {
        Self {
            pallet_name: Cow::Borrowed(pallet_name),
            entry_name: Cow::Borrowed(entry_name),
            validation_hash: Some(hash),
            marker: core::marker::PhantomData,
        }
    }

    /// Create a new address.
    pub fn new(pallet_name: impl Into<String>, entry_name: impl Into<String>) -> Self {
        Self {
            pallet_name: pallet_name.into().into(),
            entry_name: entry_name.into().into(),
            validation_hash: None,
            marker: core::marker::PhantomData,
        }
    }

    /// Do not validate this storage entry prior to accessing it.
    pub fn unvalidated(mut self) -> Self {
        self.validation_hash = None;
        self
    }
}

impl<KeyParts, Value, IsPlain> Address for StaticAddress<KeyParts, Value, IsPlain>
where
    KeyParts: IntoEncodableValues + IntoDecodableValues,
    Value: DecodeAsType,
    IsPlain: YesMaybe,
{
    type KeyParts = KeyParts;
    type Value = Value;
    type IsPlain = IsPlain;

    fn pallet_name(&self) -> &str {
        &self.pallet_name
    }

    fn entry_name(&self) -> &str {
        &self.entry_name
    }

    fn validation_hash(&self) -> Option<[u8; 32]> {
        self.validation_hash
    }
}

impl<A: AsRef<str>, B: AsRef<str>> Address for (A, B) {
    type KeyParts = Vec<scale_value::Value>;
    type Value = scale_value::Value;
    type IsPlain = Maybe;

    fn pallet_name(&self) -> &str {
        self.0.as_ref()
    }

    fn entry_name(&self) -> &str {
        self.1.as_ref()
    }

    fn validation_hash(&self) -> Option<[u8; 32]> {
        None
    }
}

/// A dynamic address is simply a [`StaticAddress`] which asserts that the
/// entry *might* be a map and *might* have a default value.
pub type DynamicAddress<KeyParts = Vec<scale_value::Value>, Value = scale_value::Value> =
    StaticAddress<KeyParts, Value, Maybe>;

/// Construct a new dynamic storage address. You can define the type of the
/// storage keys and value yourself here, but have no guarantee that they will
/// be correct.
pub fn dynamic<KeyParts: IntoEncodableValues, Value: DecodeAsType>(
    pallet_name: impl Into<String>,
    entry_name: impl Into<String>,
) -> DynamicAddress<KeyParts, Value> {
    DynamicAddress::<KeyParts, Value>::new(pallet_name.into(), entry_name.into())
}