subxt_core/constants/mod.rs
1// Copyright 2019-2024 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5//! Access constants from metadata.
6//!
7//! Use [`get`] to retrieve a constant from some metadata, or [`validate`] to check that a static
8//! constant address lines up with the value seen in the metadata.
9//!
10//! # Example
11//!
12//! ```rust
13//! use subxt_macro::subxt;
14//! use subxt_core::constants;
15//! use subxt_core::metadata;
16//!
17//! // If we generate types without `subxt`, we need to point to `::subxt_core`:
18//! #[subxt(
19//! crate = "::subxt_core",
20//! runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale",
21//! )]
22//! pub mod polkadot {}
23//!
24//! // Some metadata we'd like to access constants in:
25//! let metadata_bytes = include_bytes!("../../../artifacts/polkadot_metadata_small.scale");
26//! let metadata = metadata::decode_from(&metadata_bytes[..]).unwrap();
27//!
28//! // We can use a static address to obtain some constant:
29//! let address = polkadot::constants().balances().existential_deposit();
30//!
31//! // This validates that the address given is in line with the metadata
32//! // we're trying to access the constant in:
33//! constants::validate(&address, &metadata).expect("is valid");
34//!
35//! // This acquires the constant (and internally also validates it):
36//! let ed = constants::get(&address, &metadata).expect("can decode constant");
37//!
38//! assert_eq!(ed, 33_333_333);
39//! ```
40
41pub mod address;
42
43use address::Address;
44use alloc::borrow::ToOwned;
45
46use crate::{Error, Metadata, error::MetadataError, metadata::DecodeWithMetadata};
47
48/// When the provided `address` is statically generated via the `#[subxt]` macro, this validates
49/// that the shape of the constant value is the same as the shape expected by the static address.
50///
51/// When the provided `address` is dynamic (and thus does not come with any expectation of the
52/// shape of the constant value), this just returns `Ok(())`
53pub fn validate<Addr: Address>(address: &Addr, metadata: &Metadata) -> Result<(), Error> {
54 if let Some(actual_hash) = address.validation_hash() {
55 let expected_hash = metadata
56 .pallet_by_name_err(address.pallet_name())?
57 .constant_hash(address.constant_name())
58 .ok_or_else(|| {
59 MetadataError::ConstantNameNotFound(address.constant_name().to_owned())
60 })?;
61 if actual_hash != expected_hash {
62 return Err(MetadataError::IncompatibleCodegen.into());
63 }
64 }
65 Ok(())
66}
67
68/// Fetch a constant out of the metadata given a constant address. If the `address` has been
69/// statically generated, this will validate that the constant shape is as expected, too.
70pub fn get<Addr: Address>(address: &Addr, metadata: &Metadata) -> Result<Addr::Target, Error> {
71 // 1. Validate constant shape if hash given:
72 validate(address, metadata)?;
73
74 // 2. Attempt to decode the constant into the type given:
75 let constant = metadata
76 .pallet_by_name_err(address.pallet_name())?
77 .constant_by_name(address.constant_name())
78 .ok_or_else(|| MetadataError::ConstantNameNotFound(address.constant_name().to_owned()))?;
79 let value = <Addr::Target as DecodeWithMetadata>::decode_with_metadata(
80 &mut constant.value(),
81 constant.ty(),
82 metadata,
83 )?;
84 Ok(value)
85}