wallet_adapter/
errors.rs

1use thiserror::Error;
2use wallet_adapter_common::WalletUtilsError;
3use web_sys::js_sys::{wasm_bindgen::JsValue, Reflect};
4
5use crate::WalletEvent;
6
7/// A Result<T, WalletError>
8pub type WalletResult<T> = Result<T, WalletError>;
9
10impl From<async_channel::SendError<WalletEvent>> for WalletError {
11    fn from(value: async_channel::SendError<WalletEvent>) -> Self {
12        match value {
13            async_channel::SendError(_) => Self::ChannelError,
14        }
15    }
16}
17
18/// Error handling enum
19#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Error)]
20pub enum WalletError {
21    /// Unable to send the a [WalletEvent] via the [crate::WalletEventSender]
22    #[error("Unable to send the a `WalletEvent` variant via the WalletEventSender channel")]
23    ChannelError,
24    /// An JavaScript Error corresponding to a [JsValue] .
25    /// It contains the error type represented by `name`,
26    /// the error message `message`
27    /// and the `stack` message which offers a trace of which functions were called.
28    /// Learn about this error type from [Error - Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
29    #[error("{message}")]
30    JsError {
31        /// The name of the error from a Javascript error message
32        name: String,
33        /// The message contained in the error
34        message: String,
35        /// The stack from the JavaScript error message
36        stack: String,
37    },
38    /// An internal error that occurs when casting a JavaScript types or DOM types to a Rust type using [web_sys::wasm_bindgen::JsCast] trait
39    /// and `foo.dyn_ref::<T>()` or `foo.dyn_into::<T>()` where `foo`
40    /// is a variable of type [JsValue]. These error can also occur when trying to parse a browser error from JavaScript value
41    /// into a `WalletError::JsError`. Open an issue describing this error at -
42    /// [https://github.com/JamiiDao/SolanaWalletAdapter/issues](https://github.com/JamiiDao/SolanaWalletAdapter/issues)
43    #[error("Internal Error `{0}` occurred, this is a bug in the library please open an issue at https://github.com/JamiiDao/SolanaWalletAdapter/issues")]
44    InternalError(String),
45    /// A value of `undefined` or `null` was encountered
46    #[error("A value of `undefined` or `null` was encountered")]
47    ValueNotFound,
48    /// A value was expected but it does not exist in the `JsValue`
49    #[error("A value of `{0}` was expected but it does not exist in the `JsValue`")]
50    ExpectedValueNotFound(String),
51    /// Unable to access browser window
52    #[error("Unable to access browser window")]
53    MissingAccessToBrowserWindow,
54    /// Unable to access browser document
55    #[error("Unable to access browser document")]
56    MissingAccessToBrowserDocument,
57    /// Only `processed`, `confirmed` and `finalized` commitments are supported by Solana clusters
58    #[error("Unsupported Commitment level `{0}`. Only `processed`, `confirmed` and `finalized` commitments are supported by Solana clusters")]
59    UnsupportedCommitment(String),
60    /// The wallet version is invalid, expected SemVer version
61    #[error("The wallet version `{0}` is invalid, expected SemVer version")]
62    InvalidWalletVersion(String),
63    /// Unexpected SemVer number to parse to a `u8`
64    #[error("Unexpected SemVer number `{0}` to parse to a `u8`")]
65    InvalidSemVerNumber(String),
66    /// The byte length should be equal to 32 bytes in length
67    #[error("The byte length should be equal to 32 bytes in length")]
68    Expected32ByteLength,
69    /// The byte length should be equal to 64 bytes in length
70    #[error("The byte length should be equal to 64 bytes in length")]
71    Expected64ByteLength,
72    /// The version was not found
73    #[error("The version was not found")]
74    VersionNotFound,
75    /// This feature is not supported as a standard  or solana namespace feature
76    #[error("The feature `{0}` is not supported as a standard  or solana namespace feature")]
77    UnsupportedWalletFeature(String),
78    /// Encountered an unsupported transaction version.
79    /// Only `legacy` and `version zero` transactions are supported.
80    #[error("Encountered an unsupported transaction version. Only `legacy` and `version zero` transactions are supported.")]
81    UnsupportedTransactionVersion,
82    /// Legacy transaction versions need to be supported yet the encountered wallet does not do this.
83    #[error("Legacy transaction versions need to be supported yet the encountered wallet does not do this.")]
84    LegacyTransactionSupportRequired,
85    /// The blockchain encountered is not supported.
86    #[error("The blockchain `{0}` is not supported")]
87    UnsupportedChain(String),
88    /// The `connect` function of the `standard:connect` namespace was not found while parsing a wallet
89    #[error("The `connect` function of the `standard:connect` namespace was not found while parsing a wallet")]
90    MissingConnectFunction,
91    /// Attempted to connect to a wallet that does not exist or is yet to be registered
92    #[error("Attempted to connect to a wallet that does not exist or is yet to be registered")]
93    WalletNotFound,
94    /// Attempted to connect to an account that does not exist or might have been disconnected
95    #[error(
96        "Attempted to connect to an account that does not exist or might have been disconnected"
97    )]
98    AccountNotFound,
99    /// Unable to connect to a wallet. The user may have rejected the request
100    #[error("Unable to connect to a wallet. Error `{0}` request")]
101    WalletConnectError(String),
102    /// The connect method did not return any accounts
103    #[error("The connect method did not return any accounts")]
104    ConnectHasNoAccounts,
105    /// The wallet `standard:disconnect` feature is missing
106    #[error("The wallet `standard:disconnect` feature is missing")]
107    MissingDisconnectFunction,
108    /// The `accounts` method to get the accounts connected to a wallet is missing from wallet
109    #[error("The `accounts` method to get the accounts connected to a wallet is missing from wallet `{0}`")]
110    MissingGetAccountsFunction(String),
111    /// Unable to disconnect wallet.
112    #[error("Wallet Disconnect error - `{0}`")]
113    WalletDisconnectError(String),
114    /// Encountered an error while calling `standard:events` function
115    #[error("Encountered `standard:events` error `{0}`")]
116    StandardEventsError(String),
117    /// Called The Function for `standard:events` yet the wallet does not provide it
118    #[error("Called The Function for `standard:events` yet the wallet does not provide it")]
119    MissingStandardEventsFunction,
120    /// The wallet did not register a signIn function for `solana:signIn` namespece
121    #[error("The wallet did not register a signIn function for `solana:signIn` namespece")]
122    MissingSignInFunction,
123    /// This token expires earlier than it was issued. Make sure to set the expiry time to be a later date than the issued time
124    #[error("This token expires earlier than it was issued. Make sure to set the expiry time to be a later date than the issued time")]
125    ExpiryTimeEarlierThanIssuedTime,
126    /// This token becomes valid earlier than it was issued. Make sure to set the not_before time to be equal to or a later date than the issued time
127    #[error("This token becomes valid earlier than it was issued. Make sure to set the not_before time to be equal to or a later date than the issued time")]
128    NotBeforeTimeEarlierThanIssuedTime,
129    /// This token becomes valid after it has already expired. Make sure to set the not_before time to be equal to or a date before expiry time
130    #[error("This token becomes valid after it has already expired. Make sure to set the not_before time to be equal to or a date before expiry time")]
131    NotBeforeTimeLaterThanExpirationTime,
132    /// The expiration time is set to expire in the past
133    #[error("The expiration time is set to expire in the past")]
134    ExpirationTimeIsInThePast,
135    /// NotBefore time is set in the past
136    #[error("NotBefore time is set in the past")]
137    NotBeforeTimeIsInThePast,
138    /// Invalid Base58 Address
139    #[error("Invalid Base58 Address")]
140    InvalidBase58Address,
141    /// The nonce is required to be at least 8 characters long
142    #[error("The nonce is required to be at least 8 characters long")]
143    NonceMustBeAtLeast8Characters,
144    ///Expected a timestamp in the format specified by ISO8601
145    #[error("Invalid ISO 8601 timestamp `{0}. Only timestamps in the format specified by ISO8601 are supported.")]
146    InvalidISO8601Timestamp(String),
147    /// The message signed by the wallet is not the same as the message sent to the wallet for signing
148    #[error("The message signed by the wallet is not the same as the message sent to the wallet for signing")]
149    MessageResponseMismatch,
150    /// The Ed25519 Signature is invalid for the signed message and public key")]
151    #[error("The Ed25519 Signature is invalid for the signed message and public key")]
152    InvalidSignature,
153    /// The bytes provided for the Ed25519 Signature are invalid
154    #[error("The bytes provided for the Ed25519 Signature are invalid")]
155    InvalidEd25519SignatureBytes,
156    /// The bytes provided for the Ed25519 Public Key are invalid
157    #[error("The bytes provided for the Ed25519 Public Key are invalid")]
158    InvalidEd25519PublicKeyBytes,
159    /// The function call to Sign A Message Is Missing
160    #[error("The function call to Sign A Message Is Missing")]
161    MissingSignMessageFunction,
162    /// The message sent to the wallet to be signed is different from the message the wallet responded with
163    #[error("The message sent to the wallet to be signed is different from the message the wallet responded with")]
164    SignedMessageMismatch,
165    /// The Wallet returned an empty array of  signed messages
166    #[error("The Wallet returned an empty array of  signed messages")]
167    ReceivedAnEmptySignedMessagesArray,
168    /// The `solana:signTransaction` function is missing in the provided wallet
169    #[error("The `solana:signTransaction` function is missing in the provided wallet")]
170    MissingSignTransactionFunction,
171    /// The `sendAndSignTransaction` method did not return any signature
172    #[error("The `sendAndSignTransaction` method did not return any signature")]
173    SendAndSignTransactionSignatureEmpty,
174    /// Overflow during SystemTime::checked_add(expiration_time_milliseconds) overflow
175    #[error("SystemTime::checked_add(expiration_time_milliseconds) overflow")]
176    SystemTimeCheckedAddOverflow,
177    /// An operation resulted in an error. This is a convenience error that you can use to return any error
178    /// that was not caused by the wallet adapter, example, parsing a recipient address or the result of parsing
179    /// the body of a HTTP response using serde resulted in an error. Remember, this error is not from the [crate::WalletAdapter]
180    /// but instead an external error.
181    #[error("An operation resulted in an error `{0}`.")]
182    Op(String),
183}
184
185impl From<JsValue> for WalletError {
186    fn from(value: JsValue) -> Self {
187        let reflect = |key: &str| -> Result<String, Self> {
188            Reflect::get(&value, &key.into())
189                .map_err(|error: JsValue| WalletError::InternalError(format!("{:?}", &error)))?
190                .as_string()
191                .ok_or(WalletError::InternalError(format!(
192                    "Reflecting `{key}` in `{value:?}` did not yield a JsString"
193                )))
194        };
195
196        let name = match reflect("name") {
197            Ok(inner) => inner,
198            Err(error) => return error,
199        };
200
201        let stack = match reflect("stack") {
202            Ok(inner) => inner,
203            Err(error) => return error,
204        };
205        let message = match reflect("message") {
206            Ok(inner) => inner,
207            Err(error) => return error,
208        };
209
210        Self::JsError {
211            message,
212            name,
213            stack,
214        }
215    }
216}
217
218impl From<WalletUtilsError> for WalletError {
219    fn from(value: WalletUtilsError) -> Self {
220        match value {
221            WalletUtilsError::SystemTimeCheckedAddOverflow => Self::SystemTimeCheckedAddOverflow,
222            WalletUtilsError::ExpiryTimeEarlierThanIssuedTime => {
223                Self::ExpiryTimeEarlierThanIssuedTime
224            }
225            WalletUtilsError::ExpirationTimeIsInThePast => Self::ExpirationTimeIsInThePast,
226            WalletUtilsError::NotBeforeTimeEarlierThanIssuedTime => {
227                Self::NotBeforeTimeEarlierThanIssuedTime
228            }
229            WalletUtilsError::NotBeforeTimeIsInThePast => todo!(),
230            WalletUtilsError::NotBeforeTimeLaterThanExpirationTime => {
231                Self::NotBeforeTimeLaterThanExpirationTime
232            }
233            WalletUtilsError::InvalidISO8601Timestamp(value) => {
234                Self::InvalidISO8601Timestamp(value)
235            }
236            WalletUtilsError::InvalidBase58Address => Self::InvalidBase58Address,
237            WalletUtilsError::InvalidEd25519PublicKeyBytes => Self::InvalidEd25519PublicKeyBytes,
238            WalletUtilsError::InvalidSignature => Self::InvalidSignature,
239            WalletUtilsError::Expected64ByteLength => Self::Expected64ByteLength,
240            WalletUtilsError::Expected32ByteLength => Self::Expected32ByteLength,
241            WalletUtilsError::NonceMustBeAtLeast8Characters => Self::NonceMustBeAtLeast8Characters,
242            WalletUtilsError::MessageResponseMismatch => Self::MessageResponseMismatch,
243        }
244    }
245}