use thiserror::Error;
use wallet_adapter_common::WalletUtilsError;
use web_sys::js_sys::{wasm_bindgen::JsValue, Reflect};
use crate::WalletEvent;
pub type WalletResult<T> = Result<T, WalletError>;
impl From<async_channel::SendError<WalletEvent>> for WalletError {
fn from(value: async_channel::SendError<WalletEvent>) -> Self {
match value {
async_channel::SendError(_) => Self::ChannelError,
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Error)]
pub enum WalletError {
#[error("Unable to send the a `WalletEvent` variant via the WalletEventSender channel")]
ChannelError,
#[error("{message}")]
JsError {
name: String,
message: String,
stack: String,
},
#[error("Internal Error `{0}` occurred, this is a bug in the library please open an issue at https://github.com/JamiiDao/SolanaWalletAdapter/issues")]
InternalError(String),
#[error("A value of `undefined` or `null` was encountered")]
ValueNotFound,
#[error("A value of `{0}` was expected but it does not exist in the `JsValue`")]
ExpectedValueNotFound(String),
#[error("Unable to access browser window")]
MissingAccessToBrowserWindow,
#[error("Unable to access browser document")]
MissingAccessToBrowserDocument,
#[error("Unsupported Commitment level `{0}`. Only `processed`, `confirmed` and `finalized` commitments are supported by Solana clusters")]
UnsupportedCommitment(String),
#[error("The wallet version `{0}` is invalid, expected SemVer version")]
InvalidWalletVersion(String),
#[error("Unexpected SemVer number `{0}` to parse to a `u8`")]
InvalidSemVerNumber(String),
#[error("The byte length should be equal to 32 bytes in length")]
Expected32ByteLength,
#[error("The byte length should be equal to 64 bytes in length")]
Expected64ByteLength,
#[error("The version was not found")]
VersionNotFound,
#[error("The feature `{0}` is not supported as a standard or solana namespace feature")]
UnsupportedWalletFeature(String),
#[error("Encountered an unsupported transaction version. Only `legacy` and `version zero` transactions are supported.")]
UnsupportedTransactionVersion,
#[error("Legacy transaction versions need to be supported yet the encountered wallet does not do this.")]
LegacyTransactionSupportRequired,
#[error("The blockchain `{0}` is not supported")]
UnsupportedChain(String),
#[error("The `connect` function of the `standard:connect` namespace was not found while parsing a wallet")]
MissingConnectFunction,
#[error("Attempted to connect to a wallet that does not exist or is yet to be registered")]
WalletNotFound,
#[error(
"Attempted to connect to an account that does not exist or might have been disconnected"
)]
AccountNotFound,
#[error("Unable to connect to a wallet. Error `{0}` request")]
WalletConnectError(String),
#[error("The connect method did not return any accounts")]
ConnectHasNoAccounts,
#[error("The wallet `standard:disconnect` feature is missing")]
MissingDisconnectFunction,
#[error("The `accounts` method to get the accounts connected to a wallet is missing from wallet `{0}`")]
MissingGetAccountsFunction(String),
#[error("Wallet Disconnect error - `{0}`")]
WalletDisconnectError(String),
#[error("Encountered `standard:events` error `{0}`")]
StandardEventsError(String),
#[error("Called The Function for `standard:events` yet the wallet does not provide it")]
MissingStandardEventsFunction,
#[error("The wallet did not register a signIn function for `solana:signIn` namespece")]
MissingSignInFunction,
#[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")]
ExpiryTimeEarlierThanIssuedTime,
#[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")]
NotBeforeTimeEarlierThanIssuedTime,
#[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")]
NotBeforeTimeLaterThanExpirationTime,
#[error("The expiration time is set to expire in the past")]
ExpirationTimeIsInThePast,
#[error("NotBefore time is set in the past")]
NotBeforeTimeIsInThePast,
#[error("Invalid Base58 Address")]
InvalidBase58Address,
#[error("The nonce is required to be at least 8 characters long")]
NonceMustBeAtLeast8Characters,
#[error("Invalid ISO 8601 timestamp `{0}. Only timestamps in the format specified by ISO8601 are supported.")]
InvalidISO8601Timestamp(String),
#[error("The message signed by the wallet is not the same as the message sent to the wallet for signing")]
MessageResponseMismatch,
#[error("The Ed25519 Signature is invalid for the signed message and public key")]
InvalidSignature,
#[error("The bytes provided for the Ed25519 Signature are invalid")]
InvalidEd25519SignatureBytes,
#[error("The bytes provided for the Ed25519 Public Key are invalid")]
InvalidEd25519PublicKeyBytes,
#[error("The function call to Sign A Message Is Missing")]
MissingSignMessageFunction,
#[error("The message sent to the wallet to be signed is different from the message the wallet responded with")]
SignedMessageMismatch,
#[error("The Wallet returned an empty array of signed messages")]
ReceivedAnEmptySignedMessagesArray,
#[error("The `solana:signTransaction` function is missing in the provided wallet")]
MissingSignTransactionFunction,
#[error("The `sendAndSignTransaction` method did not return any signature")]
SendAndSignTransactionSignatureEmpty,
#[error("SystemTime::checked_add(expiration_time_milliseconds) overflow")]
SystemTimeCheckedAddOverflow,
#[error("An operation resulted in an error `{0}`.")]
Op(String),
}
impl From<JsValue> for WalletError {
fn from(value: JsValue) -> Self {
let reflect = |key: &str| -> Result<String, Self> {
Reflect::get(&value, &key.into())
.map_err(|error: JsValue| WalletError::InternalError(format!("{:?}", &error)))?
.as_string()
.ok_or(WalletError::InternalError(format!(
"Reflecting `{key}` in `{value:?}` did not yield a JsString"
)))
};
let name = match reflect("name") {
Ok(inner) => inner,
Err(error) => return error,
};
let stack = match reflect("stack") {
Ok(inner) => inner,
Err(error) => return error,
};
let message = match reflect("message") {
Ok(inner) => inner,
Err(error) => return error,
};
Self::JsError {
message,
name,
stack,
}
}
}
impl From<WalletUtilsError> for WalletError {
fn from(value: WalletUtilsError) -> Self {
match value {
WalletUtilsError::SystemTimeCheckedAddOverflow => Self::SystemTimeCheckedAddOverflow,
WalletUtilsError::ExpiryTimeEarlierThanIssuedTime => {
Self::ExpiryTimeEarlierThanIssuedTime
}
WalletUtilsError::ExpirationTimeIsInThePast => Self::ExpirationTimeIsInThePast,
WalletUtilsError::NotBeforeTimeEarlierThanIssuedTime => {
Self::NotBeforeTimeEarlierThanIssuedTime
}
WalletUtilsError::NotBeforeTimeIsInThePast => todo!(),
WalletUtilsError::NotBeforeTimeLaterThanExpirationTime => {
Self::NotBeforeTimeLaterThanExpirationTime
}
WalletUtilsError::InvalidISO8601Timestamp(value) => {
Self::InvalidISO8601Timestamp(value)
}
WalletUtilsError::InvalidBase58Address => Self::InvalidBase58Address,
WalletUtilsError::InvalidEd25519PublicKeyBytes => Self::InvalidEd25519PublicKeyBytes,
WalletUtilsError::InvalidSignature => Self::InvalidSignature,
WalletUtilsError::Expected64ByteLength => Self::Expected64ByteLength,
WalletUtilsError::Expected32ByteLength => Self::Expected32ByteLength,
WalletUtilsError::NonceMustBeAtLeast8Characters => Self::NonceMustBeAtLeast8Characters,
WalletUtilsError::MessageResponseMismatch => Self::MessageResponseMismatch,
}
}
}