use std::fmt;
use rust_decimal::Decimal;
use crate::types::{Exchange, Timestamp};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Network {
Bitcoin,
Ethereum,
Arbitrum,
BnbSmartChain,
Tron,
Solana,
Polygon,
Base,
Optimism,
AvalancheC,
XrpLedger,
Stellar,
Cosmos,
Aptos,
Sui,
Ton,
Near,
Polkadot,
Other(String),
}
impl Network {
pub fn id(&self) -> &str {
match self {
Self::Bitcoin => "bitcoin",
Self::Ethereum => "ethereum",
Self::Arbitrum => "arbitrum",
Self::BnbSmartChain => "bnb_smart_chain",
Self::Tron => "tron",
Self::Solana => "solana",
Self::Polygon => "polygon",
Self::Base => "base",
Self::Optimism => "optimism",
Self::AvalancheC => "avalanche_c",
Self::XrpLedger => "xrp_ledger",
Self::Stellar => "stellar",
Self::Cosmos => "cosmos",
Self::Aptos => "aptos",
Self::Sui => "sui",
Self::Ton => "ton",
Self::Near => "near",
Self::Polkadot => "polkadot",
Self::Other(id) => id,
}
}
pub fn same_chain(&self, other: &Self) -> bool {
match (self, other) {
(Self::Other(left), Self::Other(right)) => left == right,
(Self::Other(_), _) | (_, Self::Other(_)) => false,
_ => self.id() == other.id(),
}
}
}
impl fmt::Display for Network {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.id())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AssetNetwork {
pub exchange: Exchange,
pub asset: String,
pub network: Network,
pub provider_id: String,
pub deposit_enabled: bool,
pub withdrawal_enabled: bool,
pub withdrawal_fee: Option<WithdrawalFee>,
pub minimum_withdrawal: Option<Decimal>,
pub maximum_withdrawal: Option<Decimal>,
pub memo_required: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum WithdrawalFee {
Fixed(Decimal),
Rate {
rate: Decimal,
minimum: Option<Decimal>,
maximum: Option<Decimal>,
},
}
impl WithdrawalFee {
pub fn for_amount(&self, amount: Decimal) -> Decimal {
match self {
Self::Fixed(fee) => *fee,
Self::Rate {
rate,
minimum,
maximum,
} => {
let mut fee = amount * *rate;
if let Some(minimum) = minimum {
fee = fee.max(*minimum);
}
if let Some(maximum) = maximum {
fee = fee.min(*maximum);
}
fee
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DepositAddress {
pub exchange: Exchange,
pub asset: String,
pub network: Network,
pub address: Option<String>,
pub memo: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DepositAddressEntry {
pub exchange: Exchange,
pub asset: String,
pub network: Option<Network>,
pub provider_network: Option<String>,
pub address: Option<String>,
pub memo: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExchangeDestination {
pub exchange: Exchange,
pub asset: String,
pub network: Network,
pub address: String,
pub memo: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ChainDestination {
pub asset: String,
pub network: Network,
pub address: String,
pub memo: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExchangeTransferRequest {
pub asset: String,
pub source_network: Option<Network>,
pub destination_network: Option<Network>,
pub amount: Decimal,
}
impl ExchangeTransferRequest {
pub fn new(asset: impl Into<String>, amount: Decimal) -> Self {
Self {
asset: asset.into().to_ascii_uppercase(),
source_network: None,
destination_network: None,
amount,
}
}
pub fn source_network(mut self, network: Network) -> Self {
self.source_network = Some(network);
self
}
pub fn destination_network(mut self, network: Network) -> Self {
self.destination_network = Some(network);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ChainTransferRequest {
pub asset: String,
pub source_network: Option<Network>,
pub destination: ChainDestination,
pub amount: Decimal,
}
impl ChainTransferRequest {
pub fn new(asset: impl Into<String>, destination: ChainDestination, amount: Decimal) -> Self {
Self {
asset: asset.into().to_ascii_uppercase(),
source_network: None,
destination,
amount,
}
}
pub fn source_network(mut self, network: Network) -> Self {
self.source_network = Some(network);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum TransferDestination {
Exchange(ExchangeDestination),
Chain(ChainDestination),
}
impl TransferDestination {
pub fn asset(&self) -> &str {
match self {
Self::Exchange(value) => &value.asset,
Self::Chain(value) => &value.asset,
}
}
pub fn network(&self) -> &Network {
match self {
Self::Exchange(value) => &value.network,
Self::Chain(value) => &value.network,
}
}
pub fn address(&self) -> &str {
match self {
Self::Exchange(value) => &value.address,
Self::Chain(value) => &value.address,
}
}
pub fn memo(&self) -> Option<&str> {
match self {
Self::Exchange(value) => value.memo.as_deref(),
Self::Chain(value) => value.memo.as_deref(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum WithdrawalStatus {
Pending,
Processing,
Completed,
Cancelled,
Failed,
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DepositStatus {
Pending,
Completed,
Failed,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Withdrawal {
pub id: String,
pub asset: String,
pub network: Option<Network>,
pub provider_network: Option<String>,
pub amount: Decimal,
pub fee: Option<Decimal>,
pub destination: Option<TransferDestination>,
pub status: WithdrawalStatus,
pub provider_status: String,
pub tx_id: Option<String>,
pub created_at: Option<Timestamp>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Deposit {
pub id: String,
pub asset: String,
pub network: Option<Network>,
pub provider_network: Option<String>,
pub amount: Decimal,
pub address: Option<String>,
pub memo: Option<String>,
pub status: DepositStatus,
pub provider_status: String,
pub tx_id: Option<String>,
pub created_at: Option<Timestamp>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WithdrawalQuote {
pub fee: Option<Decimal>,
pub expected_receive: Option<Decimal>,
pub minimum_amount: Option<Decimal>,
pub maximum_amount: Option<Decimal>,
pub address_allowed: Option<bool>,
pub travel_rule: TravelRuleRequirement,
pub expires_at: Option<Timestamp>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum TravelRuleRequirement {
NotRequired,
Required {
consent_url: Option<String>,
},
}
#[cfg(test)]
mod tests {
use super::Network;
#[test]
fn unknown_networks_match_only_the_same_provider_identifier() {
assert!(
Network::Other("LIGHTNING".to_string())
.same_chain(&Network::Other("LIGHTNING".to_string()))
);
assert!(
!Network::Other("LIGHTNING".to_string())
.same_chain(&Network::Other("lightning".to_string()))
);
assert!(!Network::Other("bitcoin".to_string()).same_chain(&Network::Bitcoin));
}
}