1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
// Copyright (C) 2022-2023 Parity Technologies (UK) Ltd. (admin@parity.io)
// This file is a part of the scale-value crate.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Decode extrinsics and storage values from substrate based networks which expose `frame-metadata::RuntimeMetadata`
//! like Polkadot.
//!
//! - See [`extrinsics`] for decoding Extrinsics.
//! - See [`storage`] for decoding storage keys and values.
//!
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
mod decoding;
mod utils;
pub mod extrinsics {
//! This module contains functions for decoding extrinsics.
//!
//! - See [`decode_extrinsic`] for a general function to decode modern or historic extrinsics.
//! - See [`decode_extrinsic_current`] for a helper to decode modern extrinsics.
//! - See [`decode_extrinsic_legacy`] for a helper (and example) on decoding legacy extrinsics.
//!
use crate::utils::InfoAndResolver;
use scale_type_resolver::TypeResolver;
pub use crate::decoding::extrinsic_decoder::{
decode_extrinsic, Extrinsic, ExtrinsicDecodeError, ExtrinsicExtensions, ExtrinsicOwned,
ExtrinsicSignature, ExtrinsicType,
};
pub use crate::decoding::extrinsic_type_info::{
ExtrinsicCallInfo, ExtrinsicExtensionInfo, ExtrinsicInfoArg, ExtrinsicInfoError,
ExtrinsicSignatureInfo, ExtrinsicTypeInfo,
};
/// Decode an extrinsic in a modern runtime (ie one exposing V14+ metadata).
///
/// See [`decode_extrinsic`] for a more comprehensive example.
///
/// # Example
///
/// ```rust
/// use frame_decode::extrinsics::decode_extrinsic_current;
/// use frame_metadata::RuntimeMetadata;
/// use parity_scale_codec::Decode;
///
/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { panic!() };
///
/// let extrinsics_bytes = std::fs::read("artifacts/exts_10000000_9180.json").unwrap();
/// let extrinsics_hex: Vec<String> = serde_json::from_slice(&extrinsics_bytes).unwrap();
///
/// for ext_hex in extrinsics_hex {
/// let ext_bytes = hex::decode(ext_hex.trim_start_matches("0x")).unwrap();
///
/// // Decode the extrinsic, returning information about it:
/// let ext_info = decode_extrinsic_current(&mut &*ext_bytes, &metadata).unwrap();
///
/// // Now we can use this information to inspect the extrinsic and decode the
/// // different values inside it (see the `decode_extrinsic` docs).
/// }
/// ```
pub fn decode_extrinsic_current<'info_and_resolver, T>(
cursor: &mut &[u8],
metadata: &'info_and_resolver T,
) -> Result<
Extrinsic<'info_and_resolver, <T::Info as ExtrinsicTypeInfo>::TypeId>,
ExtrinsicDecodeError,
>
where
T: InfoAndResolver,
T::Info: ExtrinsicTypeInfo,
<T::Info as ExtrinsicTypeInfo>::TypeId: core::fmt::Debug + Clone,
T::Resolver:
scale_type_resolver::TypeResolver<TypeId = <T::Info as ExtrinsicTypeInfo>::TypeId>,
{
decode_extrinsic(cursor, metadata.info(), metadata.resolver())
}
/// Decode an extrinsic in a historic runtime (ie one prior to V14 metadata). This is basically
/// just an alias for [`decode_extrinsic`].
///
/// To understand more about the historic types required to decode old blocks, see [`scale_info_legacy`].
///
/// # Example
///
/// ```rust
/// use frame_decode::extrinsics::decode_extrinsic_legacy;
/// use frame_metadata::RuntimeMetadata;
/// use parity_scale_codec::Decode;
/// use scale_info_legacy::ChainTypeRegistry;
///
/// let metadata_bytes = std::fs::read("artifacts/metadata_5000000_30.scale").unwrap();
/// let RuntimeMetadata::V12(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { panic!() };
///
/// let extrinsics_bytes = std::fs::read("artifacts/exts_5000000_30.json").unwrap();
/// let extrinsics_hex: Vec<String> = serde_json::from_slice(&extrinsics_bytes).unwrap();
///
/// // For historic types, we also need to provide type definitions, since they aren't in the
/// // metadata. We use scale-info-legacy to do this, and have already defined types for the
/// // Polkadot relay chain, so let's load those in:
/// let historic_type_bytes = std::fs::read("types/polkadot_types.yaml").unwrap();
/// let historic_types: ChainTypeRegistry = serde_yaml::from_slice(&historic_type_bytes).unwrap();
///
/// // We configure the loaded types for the spec version of the extrinsics we want to decode,
/// // because types can vary between different spec versions.
/// let mut historic_types_for_spec = historic_types.for_spec_version(30);
///
/// // We also want to embelish these types with information from the metadata itself. This avoids
/// // needing to hardcode a load of type definitions that we can already construct from the metadata.
/// let types_from_metadata = frame_decode::helpers::type_registry_from_metadata(&metadata).unwrap();
/// historic_types_for_spec.prepend(types_from_metadata);
///
/// for ext_hex in extrinsics_hex {
/// let ext_bytes = hex::decode(ext_hex.trim_start_matches("0x")).unwrap();
///
/// // Decode the extrinsic, returning information about it:
/// let ext_info = decode_extrinsic_legacy(&mut &*ext_bytes, &metadata, &historic_types_for_spec).unwrap();
///
/// // Now we can use this information to inspect the extrinsic and decode the
/// // different values inside it (see the `decode_extrinsic` docs).
/// }
/// ```
pub fn decode_extrinsic_legacy<'info, Info, Resolver>(
cursor: &mut &[u8],
info: &'info Info,
type_resolver: &Resolver,
) -> Result<Extrinsic<'info, Info::TypeId>, ExtrinsicDecodeError>
where
Info: ExtrinsicTypeInfo,
Info::TypeId: core::fmt::Debug + Clone,
Resolver: TypeResolver<TypeId = Info::TypeId>,
{
decode_extrinsic(cursor, info, type_resolver)
}
}
pub mod storage {
//! This module contains functions for decoding storage keys and values.
//!
//! - See [`decode_storage_key`] and [`decode_storage_value`] to decode storage keys or values
//! from modern or historic runtimes.
//! - See [`decode_storage_key_current`] and [`decode_storage_value_current`] to decode modern
//! storage keys and values.
//! - See [`decode_storage_key_legacy`] and [`decode_storage_value_legacy`] to decode historic
//! storage keys and values (with examples).
//!
use crate::utils::InfoAndResolver;
use scale_decode::Visitor;
use scale_type_resolver::TypeResolver;
pub use crate::decoding::storage_type_info::{
StorageHasher, StorageInfo, StorageInfoError, StorageKeyInfo, StorageTypeInfo,
};
pub use crate::decoding::storage_decoder::{
decode_storage_key, decode_storage_value, StorageKey, StorageKeyDecodeError,
StorageKeyPart, StorageKeyPartValue, StorageValueDecodeError,
};
type TypeIdOf<T> = <<T as InfoAndResolver>::Info as StorageTypeInfo>::TypeId;
/// Decode a storage key in a modern runtime, returning information about it.
///
/// This information can be used to identify and, where possible, decode the parts of the storage key.
///
/// See [`decode_storage_key`] for a more complete example.
///
/// # Example
///
/// Here, we decode some storage keys from a block.
///
/// ```rust
/// use frame_decode::storage::decode_storage_key_current;
/// use frame_decode::helpers::decode_with_visitor;
/// use frame_metadata::RuntimeMetadata;
/// use parity_scale_codec::Decode;
/// use scale_value::scale::ValueVisitor;
///
/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
///
/// let storage_keyval_bytes = std::fs::read("artifacts/storage_10000000_9180_system_account.json").unwrap();
/// let storage_keyval_hex: Vec<(String, String)> = serde_json::from_slice(&storage_keyval_bytes).unwrap();
///
/// for (key, _val) in storage_keyval_hex {
/// let key_bytes = hex::decode(key.trim_start_matches("0x")).unwrap();
///
/// // Decode the storage key, returning information about it:
/// let storage_info = decode_storage_key_current(
/// "System",
/// "Account",
/// &mut &*key_bytes,
/// &metadata,
/// ).unwrap();
///
/// // See `decode_storage_key` for more.
/// }
/// ```
pub fn decode_storage_key_current<T>(
pallet_name: &str,
storage_entry: &str,
cursor: &mut &[u8],
metadata: &T,
) -> Result<StorageKey<TypeIdOf<T>>, StorageKeyDecodeError<TypeIdOf<T>>>
where
T: InfoAndResolver,
T::Info: StorageTypeInfo,
TypeIdOf<T>: core::fmt::Debug + Clone,
T::Resolver: TypeResolver<TypeId = TypeIdOf<T>>,
{
decode_storage_key(
pallet_name,
storage_entry,
cursor,
metadata.info(),
metadata.resolver(),
)
}
/// Decode a storage key in a historic (pre-V14-metadata) runtime, returning information about it.
///
/// This information can be used to identify and, where possible, decode the parts of the storage key.
///
/// This is basically just an alias for [`decode_storage_key`]. See that for a more complete example.
///
/// # Example
///
/// Here, we decode some storage keys from a block.
///
/// ```rust
/// use frame_decode::storage::decode_storage_key_legacy;
/// use frame_metadata::RuntimeMetadata;
/// use parity_scale_codec::Decode;
/// use scale_info_legacy::ChainTypeRegistry;
///
/// let metadata_bytes = std::fs::read("artifacts/metadata_5000000_30.scale").unwrap();
/// let RuntimeMetadata::V12(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { panic!() };
///
/// let storage_keyval_bytes = std::fs::read("artifacts/storage_5000000_30_staking_validators.json").unwrap();
/// let storage_keyval_hex: Vec<(String, String)> = serde_json::from_slice(&storage_keyval_bytes).unwrap();
///
/// // For historic types, we also need to provide type definitions, since they aren't in the
/// // metadata. We use scale-info-legacy to do this, and have already defined types for the
/// // Polkadot relay chain, so let's load those in:
/// let historic_type_bytes = std::fs::read("types/polkadot_types.yaml").unwrap();
/// let historic_types: ChainTypeRegistry = serde_yaml::from_slice(&historic_type_bytes).unwrap();
///
/// // We configure the loaded types for the spec version of the extrinsics we want to decode,
/// // because types can vary between different spec versions.
/// let mut historic_types_for_spec = historic_types.for_spec_version(30);
///
/// // We also want to embelish these types with information from the metadata itself. This avoids
/// // needing to hardcode a load of type definitions that we can already construct from the metadata.
/// let types_from_metadata = frame_decode::helpers::type_registry_from_metadata(&metadata).unwrap();
/// historic_types_for_spec.prepend(types_from_metadata);
///
/// for (key, _val) in storage_keyval_hex {
/// let key_bytes = hex::decode(key.trim_start_matches("0x")).unwrap();
///
/// // Decode the storage key, returning information about it:
/// let storage_info = decode_storage_key_legacy(
/// "Staking",
/// "Validators",
/// &mut &*key_bytes,
/// &metadata,
/// &historic_types_for_spec
/// ).unwrap();
///
/// // See `decode_storage_key` for more.
/// }
/// ```
#[cfg(feature = "legacy")]
pub fn decode_storage_key_legacy<Info, Resolver>(
pallet_name: &str,
storage_entry: &str,
cursor: &mut &[u8],
info: &Info,
type_resolver: &Resolver,
) -> Result<StorageKey<Info::TypeId>, StorageKeyDecodeError<Info::TypeId>>
where
Info: StorageTypeInfo,
Info::TypeId: Clone + core::fmt::Debug,
Resolver: TypeResolver<TypeId = Info::TypeId>,
{
decode_storage_key(pallet_name, storage_entry, cursor, info, type_resolver)
}
/// Decode a storage value in a modern (V14-metadata-or-later) runtime.
///
/// # Example
///
/// Here, we decode some storage values from a block.
///
/// ```rust
/// use frame_decode::storage::decode_storage_value_current;
/// use frame_decode::helpers::decode_with_visitor;
/// use frame_metadata::RuntimeMetadata;
/// use parity_scale_codec::Decode;
/// use scale_value::scale::ValueVisitor;
///
/// let metadata_bytes = std::fs::read("artifacts/metadata_10000000_9180.scale").unwrap();
/// let RuntimeMetadata::V14(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { return };
///
/// let storage_keyval_bytes = std::fs::read("artifacts/storage_10000000_9180_system_account.json").unwrap();
/// let storage_keyval_hex: Vec<(String, String)> = serde_json::from_slice(&storage_keyval_bytes).unwrap();
///
/// for (_key, val) in storage_keyval_hex {
/// let value_bytes = hex::decode(val.trim_start_matches("0x")).unwrap();
///
/// // Decode the storage value, here into a scale_value::Value:
/// let account_value = decode_storage_value_current(
/// "System",
/// "Account",
/// &mut &*value_bytes,
/// &metadata,
/// ValueVisitor::new()
/// ).unwrap();
/// }
/// ```
pub fn decode_storage_value_current<'scale, 'resolver, T, V>(
pallet_name: &str,
storage_entry: &str,
cursor: &mut &'scale [u8],
metadata: &'resolver T,
visitor: V,
) -> Result<V::Value<'scale, 'resolver>, StorageValueDecodeError<TypeIdOf<T>>>
where
T: InfoAndResolver,
T::Info: StorageTypeInfo,
TypeIdOf<T>: core::fmt::Debug + Clone,
T::Resolver: scale_type_resolver::TypeResolver<TypeId = TypeIdOf<T>>,
V: Visitor<TypeResolver = T::Resolver>,
V::Error: core::fmt::Debug,
{
decode_storage_value(
pallet_name,
storage_entry,
cursor,
metadata.info(),
metadata.resolver(),
visitor,
)
}
/// Decode a storage value in a historic (pre-V14-metadata) runtime. This is basically
/// just an alias for [`decode_storage_value`].
///
/// # Example
///
/// Here, we decode some storage values from a block.
///
/// ```rust
/// use frame_decode::storage::decode_storage_value_legacy;
/// use frame_metadata::RuntimeMetadata;
/// use parity_scale_codec::Decode;
/// use scale_info_legacy::ChainTypeRegistry;
/// use scale_value::scale::ValueVisitor;
///
/// let metadata_bytes = std::fs::read("artifacts/metadata_5000000_30.scale").unwrap();
/// let RuntimeMetadata::V12(metadata) = RuntimeMetadata::decode(&mut &*metadata_bytes).unwrap() else { panic!() };
///
/// let storage_keyval_bytes = std::fs::read("artifacts/storage_5000000_30_staking_validators.json").unwrap();
/// let storage_keyval_hex: Vec<(String, String)> = serde_json::from_slice(&storage_keyval_bytes).unwrap();
///
/// // For historic types, we also need to provide type definitions, since they aren't in the
/// // metadata. We use scale-info-legacy to do this, and have already defined types for the
/// // Polkadot relay chain, so let's load those in:
/// let historic_type_bytes = std::fs::read("types/polkadot_types.yaml").unwrap();
/// let historic_types: ChainTypeRegistry = serde_yaml::from_slice(&historic_type_bytes).unwrap();
///
/// // We configure the loaded types for the spec version of the extrinsics we want to decode,
/// // because types can vary between different spec versions.
/// let mut historic_types_for_spec = historic_types.for_spec_version(30);
///
/// // We also want to embelish these types with information from the metadata itself. This avoids
/// // needing to hardcode a load of type definitions that we can already construct from the metadata.
/// let types_from_metadata = frame_decode::helpers::type_registry_from_metadata(&metadata).unwrap();
/// historic_types_for_spec.prepend(types_from_metadata);
///
/// for (_key, val) in storage_keyval_hex {
/// let value_bytes = hex::decode(val.trim_start_matches("0x")).unwrap();
///
/// // Decode the storage value, here into a scale_value::Value:
/// let account_value = decode_storage_value_legacy(
/// "Staking",
/// "Validators",
/// &mut &*value_bytes,
/// &metadata,
/// &historic_types_for_spec,
/// ValueVisitor::new()
/// ).unwrap();
/// }
/// ```
#[cfg(feature = "legacy")]
pub fn decode_storage_value_legacy<'scale, 'resolver, Info, Resolver, V>(
pallet_name: &str,
storage_entry: &str,
cursor: &mut &'scale [u8],
info: &Info,
type_resolver: &'resolver Resolver,
visitor: V,
) -> Result<V::Value<'scale, 'resolver>, StorageValueDecodeError<Info::TypeId>>
where
Info: StorageTypeInfo,
Info::TypeId: Clone + core::fmt::Debug,
Resolver: TypeResolver<TypeId = Info::TypeId>,
V: scale_decode::Visitor<TypeResolver = Resolver>,
V::Error: core::fmt::Debug,
{
decode_storage_value(
pallet_name,
storage_entry,
cursor,
info,
type_resolver,
visitor,
)
}
}
pub mod helpers {
//! Helper functions and types to assist with decoding.
//!
//! - [`type_registry_from_metadata`] is expected to be used when decoding things from historic
//! runtimes, adding the ability to decode some types from information in the metadata.
//! - [`decode_with_error_tracing`] is like [`decode_with_visitor`], but
//! will use a tracing visitor (if the `error-tracing` feature is enabled) to provide more
//! information in the event that decoding fails.
//! - [`list_storage_entries`] returns an iterator over all of the storage entries available in
//! some metadata.
//!
pub use crate::utils::{decode_with_error_tracing, DecodeErrorTrace};
pub use crate::utils::{list_storage_entries, list_storage_entries_any, StorageEntry};
#[cfg(feature = "legacy")]
pub use crate::utils::{type_registry_from_metadata, type_registry_from_metadata_any};
/// An alias to [`scale_decode::visitor::decode_with_visitor`]. This can be used to decode the byte ranges
/// given back from functions like [`crate::extrinsics::decode_extrinsic_current`] or
/// [`crate::storage::decode_storage_key_current`].
///
pub use scale_decode::visitor::decode_with_visitor;
/// An alias to the underlying [`scale-decode`] crate.
///
pub use scale_decode;
}
#[cfg(all(test, feature = "legacy"))]
mod test {
use crate::decoding::extrinsic_type_info::ExtrinsicTypeInfo;
use crate::decoding::storage_type_info::StorageTypeInfo;
use crate::utils::{InfoAndResolver, ToStorageEntriesList, ToTypeRegistry};
macro_rules! impls_trait {
($type:ty, $trait:path) => {
const _: () = {
const fn assert_impl<T: $trait>() {}
assert_impl::<$type>();
};
};
}
// Just a sanity check that all of the metadata versions we expect implement
// all of the key traits. Makes it harder to miss something when adding a new metadata
// version; just add it below and implement the traits until everything compiles.
#[rustfmt::skip]
const _: () = {
impls_trait!(frame_metadata::v14::RuntimeMetadataV14, InfoAndResolver);
impls_trait!(frame_metadata::v15::RuntimeMetadataV15, InfoAndResolver);
impls_trait!(frame_metadata::v16::RuntimeMetadataV16, InfoAndResolver);
impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v14::RuntimeMetadataV14, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v15::RuntimeMetadataV15, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v16::RuntimeMetadataV16, ExtrinsicTypeInfo);
impls_trait!(frame_metadata::v8::RuntimeMetadataV8, StorageTypeInfo);
impls_trait!(frame_metadata::v9::RuntimeMetadataV9, StorageTypeInfo);
impls_trait!(frame_metadata::v10::RuntimeMetadataV10, StorageTypeInfo);
impls_trait!(frame_metadata::v11::RuntimeMetadataV11, StorageTypeInfo);
impls_trait!(frame_metadata::v12::RuntimeMetadataV12, StorageTypeInfo);
impls_trait!(frame_metadata::v13::RuntimeMetadataV13, StorageTypeInfo);
impls_trait!(frame_metadata::v14::RuntimeMetadataV14, StorageTypeInfo);
impls_trait!(frame_metadata::v15::RuntimeMetadataV15, StorageTypeInfo);
impls_trait!(frame_metadata::v16::RuntimeMetadataV16, StorageTypeInfo);
impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ToStorageEntriesList);
impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ToStorageEntriesList);
impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ToStorageEntriesList);
impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ToStorageEntriesList);
impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ToStorageEntriesList);
impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ToStorageEntriesList);
impls_trait!(frame_metadata::v14::RuntimeMetadataV14, ToStorageEntriesList);
impls_trait!(frame_metadata::v15::RuntimeMetadataV15, ToStorageEntriesList);
impls_trait!(frame_metadata::v16::RuntimeMetadataV16, ToStorageEntriesList);
// This is a legacy trait and so only legacy metadata versions implement it:
impls_trait!(frame_metadata::v8::RuntimeMetadataV8, ToTypeRegistry);
impls_trait!(frame_metadata::v9::RuntimeMetadataV9, ToTypeRegistry);
impls_trait!(frame_metadata::v10::RuntimeMetadataV10, ToTypeRegistry);
impls_trait!(frame_metadata::v11::RuntimeMetadataV11, ToTypeRegistry);
impls_trait!(frame_metadata::v12::RuntimeMetadataV12, ToTypeRegistry);
impls_trait!(frame_metadata::v13::RuntimeMetadataV13, ToTypeRegistry);
};
}