use std::{error::Error as StdError, fmt, str::FromStr, sync::Arc};
use bigdecimal::ParseBigDecimalError;
use bytes::Bytes;
use serde::{Deserialize, de::DeserializeOwned};
use serde_json::Value;
use thiserror::Error;
use tokio::sync::oneshot;
use yawc::Frame;
pub type WsStream = yawc::TcpWebSocket;
pub type ResponseSender = oneshot::Sender<Bytes>;
pub type Error = Box<dyn std::error::Error + Send + Sync>;
pub enum InternalCommand {
Send(Frame),
Close,
}
#[derive(PartialEq, Clone, Debug, Copy)]
pub enum ExternalEvent {
Connected,
Disconnected,
Exited,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RequestScope {
Public,
Private,
}
impl fmt::Display for RequestScope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RequestScope::Public => write!(f, "public"),
RequestScope::Private => write!(f, "private"),
}
}
}
#[derive(Debug, Error)]
pub enum ClientError {
#[error("RPC error: {0:?}")]
Rpc(Value),
#[error("transport error")]
Transport(#[from] Box<dyn std::error::Error + Send + Sync>),
#[error("JSON parse error")]
Parse(#[from] serde_json::Error),
#[error("oneshot receive error")]
Recv(#[from] oneshot::error::RecvError),
#[error("Anyhow error: {0}")]
Anyhow(#[from] anyhow::Error),
#[error("Environment variable error: {0}")]
EnvVar(#[from] std::env::VarError),
#[error("Rpc error {error:?}")]
RpcError { error: Value },
#[error("Conversion error: {0}")]
Conversion(#[from] crate::models::error::ConversionError),
#[error("String error: {0}")]
StringError(#[from] Box<dyn StdError>),
#[error("Approval error: {0}")]
ApprovalError(String),
#[error("Subaccount error: {0}")]
SubaccountError(String),
#[error("Balance error: {0}")]
InsufficientBalance(String),
#[error("Rounding error: {0}")]
RoundingError(#[from] ParseBigDecimalError),
#[error("Timeout error: {0}")]
TimeoutError(#[from] tokio::time::error::Elapsed),
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum Environment {
Mainnet,
#[default]
Testnet,
}
impl Environment {
pub fn get_url(&self) -> &str {
match self {
Environment::Mainnet => "wss://api.lyra.finance/ws",
Environment::Testnet => "wss://testnet.api.derive.xyz/v3/ws",
}
}
pub fn get_default_rpc(&self) -> String {
match self {
Environment::Testnet => "https://sepolia.rpc.sentio.xyz".to_string(),
Environment::Mainnet => "https://eth.drpc.org".to_string(),
}
}
}
impl FromStr for Environment {
type Err = ();
fn from_str(env: &str) -> Result<Self, Self::Err> {
match env.to_lowercase().as_str() {
"mainnet" => Ok(Environment::Mainnet),
"testnet" => Ok(Environment::Testnet),
_ => Err(()),
}
}
}
#[derive(Deserialize, Debug)]
pub struct ChannelResponse {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub current_subscriptions: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub remaining_subscriptions: Option<Vec<String>>,
#[serde(default)]
pub status: Value,
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
pub enum SubscribeResponse {
Ok { id: u64, result: ChannelResponse },
Err { id: u64, error: Value },
}
#[derive(Deserialize, Debug)]
pub struct Pagination {
#[allow(dead_code)]
count: u64,
#[allow(dead_code)]
num_pages: u64,
}
#[derive(Deserialize, Debug)]
pub struct RpcResult<T> {
pub id: u64,
pub result: T,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pagination: Option<Pagination>,
}
#[derive(Deserialize, Debug)]
pub struct RpcError {
pub id: u64,
pub error: Value,
#[serde(default)]
pub jsonrpc: Option<String>,
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
pub enum RpcResponse<T> {
Ok(RpcResult<T>),
}
pub type RpcResponseResult<T> = Result<RpcResult<T>, ClientError>;
pub type EventSender<T> = tokio::sync::broadcast::Sender<T>;
pub type EventStream<T> = tokio_stream::wrappers::BroadcastStream<T>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DispatchResult {
Delivered,
NoReceivers,
DecodeError,
}
pub struct SubscriptionRoute {
pub type_name: &'static str,
pub dispatch: Arc<dyn Fn(&Bytes) -> DispatchResult + Send + Sync>,
}
#[derive(Deserialize, Debug)]
pub struct SubscriptionNotification<T> {
pub params: SubscriptionParams<T>,
}
#[derive(Deserialize, Debug)]
pub struct SubscriptionParams<T> {
#[allow(dead_code)]
pub channel: String,
pub data: T,
}
pub fn decode_subscription_data<T>(bytes: &Bytes) -> Result<T, ClientError>
where
T: DeserializeOwned,
{
let notification: SubscriptionNotification<T> = serde_json::from_slice(bytes)?;
Ok(notification.params.data)
}
pub trait ChannelSpec {
type Output: DeserializeOwned + Send + Clone + 'static;
fn scope(&self) -> RequestScope;
fn channel(&self) -> String;
fn decode(bytes: &Bytes) -> Result<Self::Output, ClientError> {
decode_subscription_data(bytes)
}
}