wallet_adapter/
errors.rs

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