frame_decode/methods/
constant_decoder.rs

1// Copyright (C) 2022-2025 Parity Technologies (UK) Ltd. (admin@parity.io)
2// This file is a part of the frame-decode crate.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//         http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::methods::constant_type_info::{ConstantInfo, ConstantInfoError, ConstantTypeInfo};
17use crate::utils::{DecodeErrorTrace, decode_with_error_tracing};
18use alloc::vec::Vec;
19use scale_type_resolver::TypeResolver;
20
21/// An error returned trying to decode a constant.
22#[non_exhaustive]
23#[allow(missing_docs)]
24#[derive(Clone, Debug, thiserror::Error)]
25pub enum ConstantDecodeError<TypeId> {
26    #[error("Cannot get constant info: {0}")]
27    CannotGetInfo(ConstantInfoError<'static>),
28    #[error("Cannot decode constant: {reason}")]
29    CannotDecodeValue {
30        ty: TypeId,
31        reason: DecodeErrorTrace,
32    },
33    #[error("There were leftover bytes attempting to decode the constant")]
34    LeftoverBytes { bytes: Vec<u8> },
35}
36
37/// Decode a constant from the provided information.
38///
39/// # Examples
40///
41/// Decode a constant into a [`scale_value::Value`]`:
42///
43/// ```rust
44/// use frame_decode::constants::decode_constant;
45/// use frame_metadata::RuntimeMetadata;
46/// use scale_value::scale::ValueVisitor;
47/// use parity_scale_codec::Decode;
48///
49/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
50/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
51///
52/// let ed = decode_constant(
53///     "Balances",
54///     "ExistentialDeposit",
55///     &metadata,
56///     &metadata.types,
57///     ValueVisitor::new()
58/// ).unwrap();
59///
60/// println!("{ed}");
61/// ```
62///
63/// Or we can just take the constant info and decode it "manually" (in this case, remember to
64/// also check whether the bytes are fully consumed; leftover bytes indicate a failure to
65/// properly decode them):
66///
67/// ```rust
68/// use frame_decode::constants::{ConstantTypeInfo, decode_constant_with_info};
69/// use frame_metadata::RuntimeMetadata;
70/// use scale_decode::DecodeAsType;
71/// use scale_value::Value;
72/// use parity_scale_codec::Decode;
73///
74/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
75/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
76///
77/// let ed_info = metadata.constant_info("Balances", "ExistentialDeposit").unwrap();
78///
79/// let ed_bytes = ed_info.bytes;
80/// let ed_type = ed_info.type_id;
81///
82/// let ed = Value::decode_as_type(&mut &*ed_bytes, ed_type, &metadata.types).unwrap();
83/// println!("{ed}");
84/// ```
85pub fn decode_constant<'info, 'resolver, Info, Resolver, V>(
86    pallet_name: &str,
87    constant_name: &str,
88    info: &'info Info,
89    type_resolver: &'resolver V::TypeResolver,
90    visitor: V,
91) -> Result<V::Value<'info, 'resolver>, ConstantDecodeError<Info::TypeId>>
92where
93    Info: ConstantTypeInfo,
94    Info::TypeId: Clone + core::fmt::Debug,
95    Resolver: TypeResolver<TypeId = Info::TypeId>,
96    V: scale_decode::Visitor<TypeResolver = Resolver>,
97    V::Error: core::fmt::Debug,
98{
99    let info = info
100        .constant_info(pallet_name, constant_name)
101        .map_err(|e| ConstantDecodeError::CannotGetInfo(e.into_owned()))?;
102
103    decode_constant_with_info(&info, type_resolver, visitor)
104}
105
106/// Decode a constant given the [`ConstantInfo`] and a resolver to resolve the constant type.
107///
108/// # Example
109///
110/// ```rust
111/// use frame_decode::constants::{ConstantTypeInfo, decode_constant_with_info};
112/// use frame_metadata::RuntimeMetadata;
113/// use scale_value::scale::ValueVisitor;
114/// use parity_scale_codec::Decode;
115///
116/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
117/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
118///
119/// let ed_info = metadata.constant_info("Balances", "ExistentialDeposit").unwrap();
120///
121/// let ed = decode_constant_with_info(
122///     &ed_info,
123///     &metadata.types,
124///     ValueVisitor::new()
125/// ).unwrap();
126///
127/// println!("{ed}");
128/// ```
129pub fn decode_constant_with_info<'info, 'resolver, V>(
130    info: &ConstantInfo<'info, <V::TypeResolver as TypeResolver>::TypeId>,
131    type_resolver: &'resolver V::TypeResolver,
132    visitor: V,
133) -> Result<
134    V::Value<'info, 'resolver>,
135    ConstantDecodeError<<V::TypeResolver as TypeResolver>::TypeId>,
136>
137where
138    V: scale_decode::Visitor,
139    V::Error: core::fmt::Debug,
140{
141    let type_id = info.type_id.clone();
142    let cursor = &mut &*info.bytes;
143
144    let value = decode_with_error_tracing(cursor, type_id.clone(), type_resolver, visitor)
145        .map_err(|e| ConstantDecodeError::CannotDecodeValue {
146            ty: type_id,
147            reason: e,
148        })?;
149
150    if !cursor.is_empty() {
151        Err(ConstantDecodeError::LeftoverBytes {
152            bytes: cursor.to_vec(),
153        })
154    } else {
155        Ok(value)
156    }
157}