Claude pass

This commit is contained in:
James Wilson
2025-10-03 11:45:30 +01:00
parent 40952df6cb
commit 2b7ce2f773
7 changed files with 548 additions and 98 deletions
+3 -3
View File
@@ -7,7 +7,7 @@ use crate::{
blocks::Extrinsics, blocks::Extrinsics,
client::{OfflineClientT, OnlineClientT}, client::{OfflineClientT, OnlineClientT},
config::{Config, HashFor, Header}, config::{Config, HashFor, Header},
error::{BlockError, DecodeError, Error}, error::{BlockError, Error},
events, events,
runtime_api::RuntimeApi, runtime_api::RuntimeApi,
storage::StorageClientAt, storage::StorageClientAt,
@@ -173,10 +173,10 @@ where
4 => u32::decode(cursor)?.into(), 4 => u32::decode(cursor)?.into(),
8 => u64::decode(cursor)?, 8 => u64::decode(cursor)?,
_ => { _ => {
return Err(Error::Decode(DecodeError::custom_string(format!( return Err(Error::Other(format!(
"state call AccountNonceApi_account_nonce returned an unexpected number of bytes: {} (expected 2, 4 or 8)", "state call AccountNonceApi_account_nonce returned an unexpected number of bytes: {} (expected 2, 4 or 8)",
account_nonce_bytes.len() account_nonce_bytes.len()
)))); )));
} }
}; };
Ok(account_nonce) Ok(account_nonce)
+11 -13
View File
@@ -11,7 +11,7 @@ use scale_decode::{DecodeAsType, TypeResolver, visitor::DecodeAsTypeResult};
use std::{borrow::Cow, marker::PhantomData}; use std::{borrow::Cow, marker::PhantomData};
use super::{Error, MetadataError}; use super::Error;
/// An error dispatching a transaction. /// An error dispatching a transaction.
#[derive(Debug, thiserror::Error, PartialEq, Eq)] #[derive(Debug, thiserror::Error, PartialEq, Eq)]
@@ -169,24 +169,22 @@ impl std::fmt::Display for ModuleError {
impl ModuleError { impl ModuleError {
/// Return more details about this error. /// Return more details about this error.
pub fn details(&self) -> Result<ModuleErrorDetails<'_>, MetadataError> { pub fn details(&self) -> Option<ModuleErrorDetails<'_>> {
let pallet = self.metadata.pallet_by_index_err(self.pallet_index())?; let pallet = self.metadata.pallet_by_index(self.pallet_index())?;
let variant = pallet let variant = pallet.error_variant_by_index(self.error_index())?;
.error_variant_by_index(self.error_index())
.ok_or_else(|| MetadataError::VariantIndexNotFound(self.error_index()))?;
Ok(ModuleErrorDetails { pallet, variant }) Some(ModuleErrorDetails { pallet, variant })
} }
/// Return a formatted string of the resolved error details for debugging/display purposes. /// Return a formatted string of the resolved error details for debugging/display purposes.
pub fn details_string(&self) -> String { pub fn details_string(&self) -> String {
match self.details() { match self.details() {
Ok(details) => format!( Some(details) => format!(
"{pallet_name}::{variant_name}", "{pallet_name}::{variant_name}",
pallet_name = details.pallet.name(), pallet_name = details.pallet.name(),
variant_name = details.variant.name, variant_name = details.variant.name,
), ),
Err(_) => format!( None => format!(
"Unknown pallet error '{bytes:?}' (pallet and error details cannot be retrieved)", "Unknown pallet error '{bytes:?}' (pallet and error details cannot be retrieved)",
bytes = self.bytes bytes = self.bytes
), ),
@@ -223,7 +221,7 @@ impl ModuleError {
/// Details about the module error. /// Details about the module error.
pub struct ModuleErrorDetails<'a> { pub struct ModuleErrorDetails<'a> {
/// The pallet that the error is in /// The pallet that the error is in
pub pallet: crate::metadata::types::PalletMetadata<'a>, pub pallet: crate::metadata::PalletMetadata<'a>,
/// The variant representing the error /// The variant representing the error
pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>, pub variant: &'a scale_info::Variant<scale_info::form::PortableForm>,
} }
@@ -238,7 +236,7 @@ impl DispatchError {
let bytes = bytes.into(); let bytes = bytes.into();
let dispatch_error_ty_id = metadata let dispatch_error_ty_id = metadata
.dispatch_error_ty() .dispatch_error_ty()
.ok_or(MetadataError::DispatchErrorNotFound)?; .ok_or_else(|| super::Error::Other("DispatchError type not found in metadata".to_string()))?;
// The aim is to decode our bytes into roughly this shape. This is copied from // The aim is to decode our bytes into roughly this shape. This is copied from
// `sp_runtime::DispatchError`; we need the variant names and any inner variant // `sp_runtime::DispatchError`; we need the variant names and any inner variant
@@ -290,10 +288,10 @@ impl DispatchError {
} }
// Decode into our temporary error: // Decode into our temporary error:
let decoded_dispatch_err = DecodedDispatchError::decode_with_metadata( let decoded_dispatch_err = DecodedDispatchError::decode_as_type(
&mut &*bytes, &mut &*bytes,
dispatch_error_ty_id, dispatch_error_ty_id,
&metadata, metadata.types(),
)?; )?;
// Convert into the outward-facing error, mainly by handling the Module variant. // Convert into the outward-facing error, mainly by handling the Module variant.
+512 -68
View File
@@ -6,7 +6,7 @@
mod dispatch_error; mod dispatch_error;
use subxt_core::error::{BlockError as CoreBlockError, Error as CoreError}; use subxt_core::error::Error as CoreError;
crate::macros::cfg_unstable_light_client! { crate::macros::cfg_unstable_light_client! {
pub use subxt_lightclient::LightClientError; pub use subxt_lightclient::LightClientError;
@@ -19,11 +19,20 @@ pub use dispatch_error::{
// Re-expose the errors we use from other crates here: // Re-expose the errors we use from other crates here:
pub use crate::Metadata; pub use crate::Metadata;
pub use scale_decode::Error as DecodeError;
pub use scale_encode::Error as EncodeError;
pub use subxt_core::error::{ExtrinsicError, MetadataError, StorageError};
pub use subxt_metadata::TryFromError as MetadataTryFromError; pub use subxt_metadata::TryFromError as MetadataTryFromError;
// Re-export subxt-core error types that we'll use directly:
pub use subxt_core::error::{
ConstantError as CoreConstantError,
CustomValueError as CoreCustomValueError,
EventsError as CoreEventsError,
ExtrinsicError as CoreExtrinsicError,
ExtrinsicParamsError,
RuntimeApiError as CoreRuntimeApiError,
StorageError as CoreStorageError,
ViewFunctionError as CoreViewFunctionError,
};
/// The underlying error enum, generic over the type held by the `Runtime` /// The underlying error enum, generic over the type held by the `Runtime`
/// variant. Prefer to use the [`Error<E>`] and [`Error`] aliases over /// variant. Prefer to use the [`Error<E>`] and [`Error`] aliases over
/// using this type directly. /// using this type directly.
@@ -31,71 +40,66 @@ pub use subxt_metadata::TryFromError as MetadataTryFromError;
#[non_exhaustive] #[non_exhaustive]
pub enum Error { pub enum Error {
/// Io error. /// Io error.
#[error("Io error: {0}")] #[error(transparent)]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
/// Codec error.
#[error("Scale codec error: {0}")]
Codec(#[from] codec::Error),
/// Rpc error. /// Rpc error.
#[error(transparent)] #[error(transparent)]
Rpc(#[from] RpcError), Rpc(#[from] RpcError),
/// Serde serialization error /// Serde serialization error
#[error("Serde json error: {0}")] #[error(transparent)]
Serialization(#[from] serde_json::error::Error), Serialization(#[from] serde_json::error::Error),
/// Error working with metadata.
#[error("Metadata error: {0}")]
Metadata(#[from] MetadataError),
/// Error decoding metadata. /// Error decoding metadata.
#[error("Metadata Decoding error: {0}")] #[error(transparent)]
MetadataDecoding(#[from] MetadataTryFromError), MetadataDecoding(#[from] MetadataTryFromError),
/// Runtime error. /// Runtime error.
#[error("Runtime error: {0}")] #[error(transparent)]
Runtime(#[from] DispatchError), Runtime(#[from] DispatchError),
/// Error decoding to a [`crate::dynamic::Value`].
#[error("Error decoding into dynamic value: {0}")]
Decode(#[from] DecodeError),
/// Error encoding from a [`crate::dynamic::Value`].
#[error("Error encoding from dynamic value: {0}")]
Encode(#[from] EncodeError),
/// Transaction progress error. /// Transaction progress error.
#[error("Transaction error: {0}")] #[error(transparent)]
Transaction(#[from] TransactionError), Transaction(#[from] TransactionError),
/// Error constructing the appropriate extrinsic params.
#[error("Extrinsic params error: {0}")]
Extrinsic(#[from] ExtrinsicError),
/// Block related error. /// Block related error.
#[error("Block error: {0}")] #[error(transparent)]
Block(#[from] BlockError), Block(#[from] BlockError),
/// An error encoding a storage address. /// Storage error.
#[error("Error encoding storage address: {0}")] #[error(transparent)]
StorageAddress(#[from] StorageError), Storage(#[from] StorageError),
/// Storage key error.
#[error(transparent)]
StorageKey(#[from] StorageKeyError),
/// Storage value error.
#[error(transparent)]
StorageValue(#[from] StorageValueError),
/// Constant error.
#[error(transparent)]
Constant(#[from] ConstantError),
/// Custom value error.
#[error(transparent)]
CustomValue(#[from] CustomValueError),
/// Runtime API error.
#[error(transparent)]
RuntimeApi(#[from] RuntimeApiError),
/// View function error.
#[error(transparent)]
ViewFunction(#[from] ViewFunctionError),
/// Events error.
#[error(transparent)]
Events(#[from] EventsError),
/// Extrinsic error.
#[error(transparent)]
Extrinsic(#[from] ExtrinsicError),
/// The bytes representing an error that we were unable to decode. /// The bytes representing an error that we were unable to decode.
#[error("An error occurred but it could not be decoded: {0:?}")] #[error("An error occurred but it could not be decoded: {0:?}")]
Unknown(Vec<u8>), Unknown(Vec<u8>),
/// Light client error. /// Light client error.
#[cfg(feature = "unstable-light-client")] #[cfg(feature = "unstable-light-client")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-light-client")))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable-light-client")))]
#[error("An error occurred but it could not be decoded: {0}")] #[error(transparent)]
LightClient(#[from] LightClientError), LightClient(#[from] LightClientError),
/// Other error. /// Other error.
#[error("Other error: {0}")] #[error("Other error: {0}")]
Other(String), Other(String),
} }
impl From<CoreError> for Error {
fn from(value: CoreError) -> Self {
match value {
CoreError::Codec(e) => Error::Codec(e),
CoreError::Metadata(e) => Error::Metadata(e),
CoreError::StorageError(e) => Error::StorageAddress(e),
CoreError::Decode(e) => Error::Decode(e),
CoreError::Encode(e) => Error::Encode(e),
CoreError::Extrinsic(e) => Error::Extrinsic(e),
CoreError::Block(e) => Error::Block(e.into()),
}
}
}
impl<'a> From<&'a str> for Error { impl<'a> From<&'a str> for Error {
fn from(error: &'a str) -> Self { fn from(error: &'a str) -> Self {
Error::Other(error.into()) Error::Other(error.into())
@@ -114,9 +118,17 @@ impl From<std::convert::Infallible> for Error {
} }
} }
impl From<scale_decode::visitor::DecodeError> for Error { impl From<codec::Error> for Error {
fn from(value: scale_decode::visitor::DecodeError) -> Self { fn from(value: codec::Error) -> Self {
Error::Decode(value.into()) // Codec errors typically happen during event/extrinsic decoding, so we map to Other
Error::Other(format!("Codec error: {}", value))
}
}
impl From<scale_decode::Error> for Error {
fn from(value: scale_decode::Error) -> Self {
// Scale decode errors typically happen during decoding, so we map to Other
Error::Other(format!("Decode error: {}", value))
} }
} }
@@ -126,6 +138,62 @@ impl From<subxt_rpcs::Error> for Error {
} }
} }
// Add From implementations for core error types through their module-specific wrappers
impl From<CoreStorageError> for Error {
fn from(e: CoreStorageError) -> Self {
Error::Storage(StorageError::from(e))
}
}
impl From<CoreExtrinsicError> for Error {
fn from(e: CoreExtrinsicError) -> Self {
Error::Extrinsic(ExtrinsicError::from(e))
}
}
impl From<CoreConstantError> for Error {
fn from(e: CoreConstantError) -> Self {
Error::Constant(ConstantError::from(e))
}
}
impl From<CoreCustomValueError> for Error {
fn from(e: CoreCustomValueError) -> Self {
Error::CustomValue(CustomValueError::from(e))
}
}
impl From<CoreRuntimeApiError> for Error {
fn from(e: CoreRuntimeApiError) -> Self {
Error::RuntimeApi(RuntimeApiError::from(e))
}
}
impl From<CoreViewFunctionError> for Error {
fn from(e: CoreViewFunctionError) -> Self {
Error::ViewFunction(ViewFunctionError::from(e))
}
}
impl From<CoreEventsError> for Error {
fn from(e: CoreEventsError) -> Self {
Error::Events(EventsError::from(e))
}
}
// Add From implementations for frame_decode error types that go through StorageError
impl From<frame_decode::storage::StorageInfoError<'_>> for Error {
fn from(e: frame_decode::storage::StorageInfoError<'_>) -> Self {
Error::Storage(StorageError::from(e))
}
}
impl From<frame_decode::storage::StorageKeyEncodeError> for Error {
fn from(e: frame_decode::storage::StorageKeyEncodeError) -> Self {
Error::Storage(StorageError::from(e))
}
}
impl Error { impl Error {
/// Checks whether the error was caused by a RPC re-connection. /// Checks whether the error was caused by a RPC re-connection.
pub fn is_disconnected_will_reconnect(&self) -> bool { pub fn is_disconnected_will_reconnect(&self) -> bool {
@@ -185,31 +253,10 @@ pub enum BlockError {
/// Index of the extrinsic that failed to decode. /// Index of the extrinsic that failed to decode.
extrinsic_index: usize, extrinsic_index: usize,
/// The decode error. /// The decode error.
error: subxt_core::error::ExtrinsicDecodeError, error: frame_decode::extrinsics::ExtrinsicDecodeError,
}, },
} }
impl From<CoreBlockError> for BlockError {
fn from(value: CoreBlockError) -> Self {
match value {
CoreBlockError::LeftoverBytes {
extrinsic_index,
num_leftover_bytes,
} => BlockError::LeftoverBytes {
extrinsic_index,
num_leftover_bytes,
},
CoreBlockError::ExtrinsicDecodeError {
extrinsic_index,
error,
} => BlockError::ExtrinsicDecodeError {
extrinsic_index,
error,
},
}
}
}
impl BlockError { impl BlockError {
/// Produce an error that a block with the given hash cannot be found. /// Produce an error that a block with the given hash cannot be found.
pub fn not_found(hash: impl AsRef<[u8]>) -> BlockError { pub fn not_found(hash: impl AsRef<[u8]>) -> BlockError {
@@ -238,3 +285,400 @@ pub enum TransactionError {
#[error("The transaction was dropped: {0}")] #[error("The transaction was dropped: {0}")]
Dropped(String), Dropped(String),
} }
// Module-specific error types following the subxt-core pattern:
/// Errors that can occur when working with storage.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum StorageError {
#[error("Storage: The static storage address used is not compatible with the live chain")]
IncompatibleCodegen,
#[error("Storage: Can't find storage value - pallet with name '{0}' not found")]
PalletNameNotFound(String),
#[error("Storage: Entry '{entry_name}' not found in pallet '{pallet_name}'")]
StorageEntryNotFound {
pallet_name: String,
entry_name: String,
},
#[error("Storage: Cannot obtain storage information from metadata: {0}")]
StorageInfoError(String),
#[error("Storage: Cannot decode storage value: {0}")]
StorageValueDecodeError(String),
#[error("Storage: Cannot encode storage key: {0}")]
StorageKeyEncodeError(#[from] frame_decode::storage::StorageKeyEncodeError),
#[error("Storage: RPC error - {0}")]
Rpc(#[from] RpcError),
#[error("Storage: Could not fetch next entry from storage subscription - {reason}")]
StorageEventError {
reason: String,
},
#[error(
"Storage: Wrong number of keys provided (expected {num_keys_expected}, got {num_keys_provided})"
)]
WrongNumberOfKeysProvidedForFetch {
num_keys_provided: usize,
num_keys_expected: usize,
},
#[error(
"Storage: Too many keys provided for iteration (expected at most {max_keys_expected}, got {num_keys_provided})"
)]
TooManyKeysProvidedForIter {
num_keys_provided: usize,
max_keys_expected: usize,
},
}
impl From<CoreStorageError> for StorageError {
fn from(e: CoreStorageError) -> Self {
match e {
CoreStorageError::IncompatibleCodegen => StorageError::IncompatibleCodegen,
CoreStorageError::PalletNameNotFound(name) => StorageError::PalletNameNotFound(name),
CoreStorageError::StorageEntryNotFound { pallet_name, entry_name } => {
StorageError::StorageEntryNotFound { pallet_name, entry_name }
}
CoreStorageError::StorageInfoError(e) => StorageError::StorageInfoError(e.to_string()),
CoreStorageError::StorageValueDecodeError(e) => {
StorageError::StorageValueDecodeError(e.to_string())
}
CoreStorageError::StorageKeyEncodeError(e) => StorageError::StorageKeyEncodeError(e),
_ => StorageError::StorageInfoError(e.to_string()),
}
}
}
impl From<frame_decode::storage::StorageInfoError<'_>> for StorageError {
fn from(e: frame_decode::storage::StorageInfoError<'_>) -> Self {
StorageError::StorageInfoError(e.to_string())
}
}
/// Errors that can occur when working with storage keys.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum StorageKeyError {
#[error("Storage: Could not decode storage key - {reason}")]
DecodeError {
reason: frame_decode::storage::StorageKeyDecodeError<String>,
},
#[error("Storage: Could not decode storage key - leftover bytes after decoding")]
LeftoverBytes {
leftover_bytes: Vec<u8>,
},
#[error("Storage: Could not decode part of storage key at index {index} - {reason}")]
DecodePartError {
index: usize,
reason: scale_decode::Error,
},
#[error("Storage: Could not decode values from storage key - {reason}")]
DecodeKeyValueError {
reason: frame_decode::storage::StorageKeyValueDecodeError,
},
}
/// Errors that can occur when working with storage values.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum StorageValueError {
#[error("Storage: Could not decode storage value - {reason}")]
DecodeError {
reason: scale_decode::Error,
},
#[error("Storage: Could not decode storage value - leftover bytes after decoding")]
LeftoverBytes {
leftover_bytes: Vec<u8>,
},
}
/// Errors that can occur when working with constants.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum ConstantError {
#[error("Constant: The static constant address used is not compatible with the live chain")]
IncompatibleCodegen,
#[error("Constant: Can't find constant - pallet with name '{0}' not found")]
PalletNameNotFound(String),
#[error("Constant: '{constant_name}' not found in pallet '{pallet_name}'")]
ConstantNameNotFound {
pallet_name: String,
constant_name: String,
},
#[error("Constant: Failed to decode constant - {0}")]
CouldNotDecodeConstant(String),
}
impl From<CoreConstantError> for ConstantError {
fn from(e: CoreConstantError) -> Self {
match e {
CoreConstantError::IncompatibleCodegen => ConstantError::IncompatibleCodegen,
CoreConstantError::PalletNameNotFound(name) => ConstantError::PalletNameNotFound(name),
CoreConstantError::ConstantNameNotFound { pallet_name, constant_name } => {
ConstantError::ConstantNameNotFound { pallet_name, constant_name }
}
CoreConstantError::CouldNotDecodeConstant(e) => {
ConstantError::CouldNotDecodeConstant(e.to_string())
}
_ => ConstantError::CouldNotDecodeConstant(e.to_string()),
}
}
}
/// Errors that can occur when working with custom values.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum CustomValueError {
#[error("Custom Value: The static custom value address used is not compatible with the live chain")]
IncompatibleCodegen,
#[error("Custom Value: '{0}' was not found")]
NotFound(String),
#[error("Custom Value: Failed to decode custom value - {0}")]
CouldNotDecodeCustomValue(String),
}
impl From<CoreCustomValueError> for CustomValueError {
fn from(e: CoreCustomValueError) -> Self {
match e {
CoreCustomValueError::IncompatibleCodegen => CustomValueError::IncompatibleCodegen,
CoreCustomValueError::NotFound(name) => CustomValueError::NotFound(name),
CoreCustomValueError::CouldNotDecodeCustomValue(e) => {
CustomValueError::CouldNotDecodeCustomValue(e.to_string())
}
_ => CustomValueError::CouldNotDecodeCustomValue(e.to_string()),
}
}
}
/// Errors that can occur when working with runtime APIs.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum RuntimeApiError {
#[error("Runtime API: The static Runtime API address used is not compatible with the live chain")]
IncompatibleCodegen,
#[error("Runtime API: Trait '{0}' not found")]
TraitNotFound(String),
#[error("Runtime API: Method '{method_name}' not found in trait '{trait_name}'")]
MethodNotFound {
trait_name: String,
method_name: String,
},
#[error("Runtime API: Failed to encode inputs - {0}")]
CouldNotEncodeInputs(String),
#[error("Runtime API: Failed to decode response - {0}")]
CouldNotDecodeResponse(String),
#[error("Runtime API: RPC error - {0}")]
Rpc(#[from] RpcError),
}
impl From<CoreRuntimeApiError> for RuntimeApiError {
fn from(e: CoreRuntimeApiError) -> Self {
match e {
CoreRuntimeApiError::IncompatibleCodegen => RuntimeApiError::IncompatibleCodegen,
CoreRuntimeApiError::TraitNotFound(name) => RuntimeApiError::TraitNotFound(name),
CoreRuntimeApiError::MethodNotFound { trait_name, method_name } => {
RuntimeApiError::MethodNotFound { trait_name, method_name }
}
CoreRuntimeApiError::CouldNotEncodeInputs(e) => {
RuntimeApiError::CouldNotEncodeInputs(e.to_string())
}
CoreRuntimeApiError::CouldNotDecodeResponse(e) => {
RuntimeApiError::CouldNotDecodeResponse(e.to_string())
}
_ => RuntimeApiError::CouldNotDecodeResponse(e.to_string()),
}
}
}
/// Errors that can occur when working with view functions.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum ViewFunctionError {
#[error("View Function: The static View Function address used is not compatible with the live chain")]
IncompatibleCodegen,
#[error("View Function: Pallet '{0}' not found")]
PalletNotFound(String),
#[error("View Function: '{function_name}' not found in pallet '{pallet_name}'")]
ViewFunctionNotFound {
pallet_name: String,
function_name: String,
},
#[error("View Function: Failed to encode inputs - {0}")]
CouldNotEncodeInputs(String),
#[error("View Function: Failed to decode response - {0}")]
CouldNotDecodeResponse(String),
#[error("View Function: RPC error - {0}")]
Rpc(#[from] RpcError),
}
impl From<CoreViewFunctionError> for ViewFunctionError {
fn from(e: CoreViewFunctionError) -> Self {
match e {
CoreViewFunctionError::IncompatibleCodegen => ViewFunctionError::IncompatibleCodegen,
CoreViewFunctionError::PalletNotFound(name) => ViewFunctionError::PalletNotFound(name),
CoreViewFunctionError::ViewFunctionNotFound { pallet_name, function_name } => {
ViewFunctionError::ViewFunctionNotFound { pallet_name, function_name }
}
CoreViewFunctionError::CouldNotEncodeInputs(e) => {
ViewFunctionError::CouldNotEncodeInputs(e.to_string())
}
CoreViewFunctionError::CouldNotDecodeResponse(e) => {
ViewFunctionError::CouldNotDecodeResponse(e.to_string())
}
_ => ViewFunctionError::CouldNotDecodeResponse(e.to_string()),
}
}
}
/// Errors that can occur when working with events.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum EventsError {
#[error("Events: Can't decode event - can't decode phase: {0}")]
CannotDecodePhase(codec::Error),
#[error("Events: Can't decode event - can't decode pallet index: {0}")]
CannotDecodePalletIndex(codec::Error),
#[error("Events: Can't decode event - can't decode variant index: {0}")]
CannotDecodeVariantIndex(codec::Error),
#[error("Events: Can't decode event - can't find pallet with index {0}")]
CannotFindPalletWithIndex(u8),
#[error("Events: Can't decode event - can't find variant with index {variant_index} in pallet {pallet_name}")]
CannotFindVariantWithIndex {
pallet_name: String,
variant_index: u8,
},
#[error("Events: Can't decode field {field_name:?} in event {pallet_name}.{event_name} - {reason}")]
CannotDecodeFieldInEvent {
pallet_name: String,
event_name: String,
field_name: String,
reason: scale_decode::visitor::DecodeError,
},
#[error("Events: Can't decode event topics: {0}")]
CannotDecodeEventTopics(codec::Error),
#[error("Events: Can't decode fields of event {pallet_name}.{event_name} - {reason}")]
CannotDecodeEventFields {
pallet_name: String,
event_name: String,
reason: scale_decode::Error,
},
#[error("Events: Can't decode event {pallet_name}.{event_name} to Event enum - {reason}")]
CannotDecodeEventEnum {
pallet_name: String,
event_name: String,
reason: scale_decode::Error,
},
#[error("Events: RPC error - {0}")]
Rpc(#[from] RpcError),
}
impl From<CoreEventsError> for EventsError {
fn from(e: CoreEventsError) -> Self {
match e {
CoreEventsError::CannotDecodePhase(err) => EventsError::CannotDecodePhase(err),
CoreEventsError::CannotDecodePalletIndex(err) => EventsError::CannotDecodePalletIndex(err),
CoreEventsError::CannotDecodeVariantIndex(err) => EventsError::CannotDecodeVariantIndex(err),
CoreEventsError::CannotFindPalletWithIndex(idx) => EventsError::CannotFindPalletWithIndex(idx),
CoreEventsError::CannotFindVariantWithIndex { pallet_name, variant_index } => {
EventsError::CannotFindVariantWithIndex { pallet_name, variant_index }
}
CoreEventsError::CannotDecodeFieldInEvent { pallet_name, event_name, field_name, reason } => {
EventsError::CannotDecodeFieldInEvent { pallet_name, event_name, field_name, reason }
}
CoreEventsError::CannotDecodeEventTopics(err) => EventsError::CannotDecodeEventTopics(err),
CoreEventsError::CannotDecodeEventFields { pallet_name, event_name, reason } => {
EventsError::CannotDecodeEventFields { pallet_name, event_name, reason }
}
CoreEventsError::CannotDecodeEventEnum { pallet_name, event_name, reason } => {
EventsError::CannotDecodeEventEnum { pallet_name, event_name, reason }
}
_ => EventsError::CannotDecodeEventTopics(codec::Error::from("Unknown events error")),
}
}
}
/// Errors that can occur when working with extrinsics/transactions.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum ExtrinsicError {
#[error("Extrinsic: The extrinsic payload is not compatible with the live chain")]
IncompatibleCodegen,
#[error("Extrinsic: Can't find extrinsic - pallet with name '{0}' not found")]
PalletNameNotFound(String),
#[error("Extrinsic: Call '{call_name}' doesn't exist in pallet '{pallet_name}'")]
CallNameNotFound {
pallet_name: String,
call_name: String,
},
#[error("Extrinsic: Can't encode call data - {0}")]
CannotEncodeCallData(scale_encode::Error),
#[error("Extrinsic: Unsupported extrinsic version")]
UnsupportedVersion,
#[error("Extrinsic: Cannot construct transaction extensions - {0}")]
Params(#[from] ExtrinsicParamsError),
#[error("Extrinsic: Cannot decode transaction extension '{name}' - {error}")]
CouldNotDecodeTransactionExtension {
name: String,
error: scale_decode::Error,
},
#[error("Extrinsic: Leftover bytes after decoding extrinsic at index {extrinsic_index} ({num_leftover_bytes} bytes remaining)")]
LeftoverBytes {
extrinsic_index: usize,
num_leftover_bytes: usize,
},
#[error("Extrinsic: Failed to decode extrinsic at index {extrinsic_index} - {error}")]
ExtrinsicDecodeError {
extrinsic_index: usize,
error: frame_decode::extrinsics::ExtrinsicDecodeError,
},
#[error("Extrinsic: Failed to decode fields of extrinsic at index {extrinsic_index} - {error}")]
CannotDecodeFields {
extrinsic_index: usize,
error: scale_decode::Error,
},
#[error("Extrinsic: Failed to decode extrinsic at index {extrinsic_index} to root enum - {error}")]
CannotDecodeIntoRootExtrinsic {
extrinsic_index: usize,
error: scale_decode::Error,
},
#[error("Extrinsic: RPC error - {0}")]
Rpc(#[from] RpcError),
}
impl From<CoreExtrinsicError> for ExtrinsicError {
fn from(e: CoreExtrinsicError) -> Self {
match e {
CoreExtrinsicError::IncompatibleCodegen => ExtrinsicError::IncompatibleCodegen,
CoreExtrinsicError::PalletNameNotFound(name) => ExtrinsicError::PalletNameNotFound(name),
CoreExtrinsicError::CallNameNotFound { pallet_name, call_name } => {
ExtrinsicError::CallNameNotFound { pallet_name, call_name }
}
CoreExtrinsicError::CannotEncodeCallData(err) => ExtrinsicError::CannotEncodeCallData(err),
CoreExtrinsicError::UnsupportedVersion => ExtrinsicError::UnsupportedVersion,
CoreExtrinsicError::Params(err) => ExtrinsicError::Params(err),
CoreExtrinsicError::CouldNotDecodeTransactionExtension { name, error } => {
ExtrinsicError::CouldNotDecodeTransactionExtension { name, error }
}
CoreExtrinsicError::LeftoverBytes { extrinsic_index, num_leftover_bytes } => {
ExtrinsicError::LeftoverBytes { extrinsic_index, num_leftover_bytes }
}
CoreExtrinsicError::ExtrinsicDecodeError { extrinsic_index, error } => {
ExtrinsicError::ExtrinsicDecodeError { extrinsic_index, error }
}
CoreExtrinsicError::CannotDecodeFields { extrinsic_index, error } => {
ExtrinsicError::CannotDecodeFields { extrinsic_index, error }
}
CoreExtrinsicError::CannotDecodeIntoRootExtrinsic { extrinsic_index, error } => {
ExtrinsicError::CannotDecodeIntoRootExtrinsic { extrinsic_index, error }
}
_ => ExtrinsicError::CannotEncodeCallData(scale_encode::Error::custom_string(e.to_string())),
}
}
}
+8 -7
View File
@@ -6,7 +6,7 @@ use crate::{
backend::{BackendExt, BlockRef}, backend::{BackendExt, BlockRef},
client::{OfflineClientT, OnlineClientT}, client::{OfflineClientT, OnlineClientT},
config::{Config, HashFor}, config::{Config, HashFor},
error::{Error, MetadataError, StorageError}, error::Error,
storage::storage_value::StorageValue, storage::storage_value::StorageValue,
}; };
use codec::Decode; use codec::Decode;
@@ -60,23 +60,24 @@ where
T: Config, T: Config,
Client: OfflineClientT<T>, Client: OfflineClientT<T>,
{ {
/// Access a specific storage entry. This returns a [`StorageEntryClient`] which can be used to
/// interact with the storage entry at this specific block.
pub fn entry<Addr: Address>(&'_ self, address: Addr) -> Result<StorageEntryClient<'_, T, Client, Addr, Addr::IsPlain>, Error> { pub fn entry<Addr: Address>(&'_ self, address: Addr) -> Result<StorageEntryClient<'_, T, Client, Addr, Addr::IsPlain>, Error> {
subxt_core::storage::validate(&address, &self.client.metadata())?; subxt_core::storage::validate(&address, &self.metadata)?;
use frame_decode::storage::StorageTypeInfo; use frame_decode::storage::StorageTypeInfo;
let types = self.metadata.types(); let types = self.metadata.types();
let info = self let info = self
.client .metadata
.metadata()
.storage_info(address.pallet_name(), address.entry_name())?; .storage_info(address.pallet_name(), address.entry_name())?;
Ok(StorageEntryClient { Ok(StorageEntryClient {
client: self.client.clone(), client: self.client.clone(),
block_ref: self.block_ref.clone(), block_ref: self.block_ref.clone(),
address, address,
info, info,
types, types,
_marker: core::marker::PhantomData _marker: core::marker::PhantomData
}) })
} }
} }
+6 -2
View File
@@ -4,8 +4,9 @@
use super::storage_value::StorageValue; use super::storage_value::StorageValue;
use super::storage_key::StorageKey; use super::storage_key::StorageKey;
use crate::error::StorageKeyError;
use subxt_core::storage::address::Address; use subxt_core::storage::address::Address;
use frame_decode::storage::StorageInfo; use frame_decode::storage::{StorageInfo, IntoDecodableValues};
use scale_info::PortableRegistry; use scale_info::PortableRegistry;
use std::borrow::Cow; use std::borrow::Cow;
@@ -42,7 +43,10 @@ impl<'entry, 'atblock, Addr: Address> StorageEntry<'entry, 'atblock, Addr> {
/// Decode the key for this storage entry. This gives back a type from which we can /// Decode the key for this storage entry. This gives back a type from which we can
/// decode specific parts of the key hash (where applicable). /// decode specific parts of the key hash (where applicable).
pub fn key(&'_ self) -> Result<StorageKey<'_, 'atblock, Addr::KeyParts>, StorageKeyError> { pub fn key(&'_ self) -> Result<StorageKey<'_, 'atblock, Addr::KeyParts>, StorageKeyError>
where
Addr::KeyParts: IntoDecodableValues,
{
StorageKey::new(self.value.info, self.value.types, &self.key) StorageKey::new(self.value.info, self.value.types, &self.key)
} }
+1
View File
@@ -2,6 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0. // This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details. // see LICENSE for license details.
use crate::error::StorageKeyError;
use frame_decode::storage::{StorageInfo, StorageKey as StorageKeyPartInfo, IntoDecodableValues}; use frame_decode::storage::{StorageInfo, StorageKey as StorageKeyPartInfo, IntoDecodableValues};
use scale_info::PortableRegistry; use scale_info::PortableRegistry;
use core::marker::PhantomData; use core::marker::PhantomData;
+7 -5
View File
@@ -8,7 +8,7 @@ use scale_info::PortableRegistry;
use core::marker::PhantomData; use core::marker::PhantomData;
use std::borrow::Cow; use std::borrow::Cow;
use crate::Error; use crate::error::StorageValueError;
/// This represents a storage value. /// This represents a storage value.
pub struct StorageValue<'entry, 'atblock, Value> { pub struct StorageValue<'entry, 'atblock, Value> {
@@ -39,22 +39,24 @@ impl<'entry, 'atblock, Value: DecodeAsType> StorageValue<'entry, 'atblock, Value
} }
/// Decode this storage value into the provided response type. /// Decode this storage value into the provided response type.
pub fn decode(&self) -> Result<Value, Error> { pub fn decode(&self) -> Result<Value, StorageValueError> {
self.decode_as::<Value>() self.decode_as::<Value>()
} }
/// Decode this storage value into an arbitrary type. /// Decode this storage value into an arbitrary type.
pub fn decode_as<T: DecodeAsType>(&self) -> Result<T, Error> { pub fn decode_as<T: DecodeAsType>(&self) -> Result<T, StorageValueError> {
let cursor = &mut &*self.bytes; let cursor = &mut &*self.bytes;
let value = T::decode_as_type( let value = T::decode_as_type(
cursor, cursor,
self.info.value_id, self.info.value_id,
self.types, self.types,
).map_err(|e| todo!("Define proper errors"))?; ).map_err(|reason| StorageValueError::DecodeError { reason })?;
if !cursor.is_empty() { if !cursor.is_empty() {
return Err(todo!("Define proper errors")); return Err(StorageValueError::LeftoverBytes {
leftover_bytes: cursor.to_vec(),
});
} }
Ok(value) Ok(value)