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}