use crate::error::{BybitError, Result};
use serde::{Deserialize, Serialize};
use tracing::warn;
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CoinInfoResponse {
pub rows: Vec<CoinInfo>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CoinInfo {
pub coin: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub remain_amount: String,
#[serde(default)]
pub chains: Vec<ChainInfo>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChainInfo {
pub chain: String,
#[serde(default)]
pub chain_type: String,
#[serde(default)]
pub confirmation: String,
#[serde(default)]
pub withdraw_fee: String,
#[serde(default)]
pub deposit_min: String,
#[serde(default)]
pub withdraw_min: String,
#[serde(default)]
pub chain_deposit: String,
#[serde(default)]
pub chain_withdraw: String,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InternalTransferParams {
pub transfer_id: String,
pub coin: String,
pub amount: String,
pub from_account_type: String,
pub to_account_type: String,
}
impl InternalTransferParams {
pub fn validate(&self) -> Result<()> {
if self.coin.is_empty() {
return Err(BybitError::InvalidParam("coin cannot be empty".into()));
}
let amount: rust_decimal::Decimal = self
.amount
.parse()
.map_err(|_| BybitError::InvalidParam("amount must be a valid number".into()))?;
if amount <= rust_decimal::Decimal::ZERO {
return Err(BybitError::InvalidParam("amount must be positive".into()));
}
Ok(())
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransferResponse {
pub transfer_id: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransferList {
pub list: Vec<TransferRecord>,
#[serde(default)]
pub next_page_cursor: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransferRecord {
pub transfer_id: String,
pub coin: String,
pub amount: String,
pub from_account_type: String,
pub to_account_type: String,
pub timestamp: String,
pub status: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DepositAddressResponse {
pub coin: String,
pub chains: Vec<DepositChainAddress>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DepositChainAddress {
pub chain_type: String,
pub address_deposit: String,
#[serde(default)]
pub tag_deposit: String,
pub chain: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DepositRecords {
pub rows: Vec<DepositRecord>,
#[serde(default)]
pub next_page_cursor: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DepositRecord {
pub coin: String,
pub chain: String,
pub amount: String,
pub tx_i_d: String,
pub status: i32,
pub to_address: String,
#[serde(default)]
pub tag: String,
#[serde(default)]
pub deposit_fee: String,
#[serde(default)]
pub success_at: String,
#[serde(default)]
pub confirmations: String,
#[serde(default)]
pub tx_index: String,
#[serde(default)]
pub block_hash: String,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawParams {
pub coin: String,
pub chain: String,
pub address: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
pub amount: String,
pub timestamp: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub force_chain: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub account_type: Option<String>,
}
impl WithdrawParams {
pub fn new(coin: &str, chain: &str, address: &str, amount: &str) -> Self {
Self {
coin: coin.to_string(),
chain: chain.to_string(),
address: address.to_string(),
tag: None,
amount: amount.to_string(),
timestamp: crate::auth::get_timestamp(),
force_chain: None,
account_type: None,
}
}
pub fn validate(&self) -> Result<()> {
if self.coin.is_empty() {
return Err(BybitError::InvalidParam("coin cannot be empty".into()));
}
if self.chain.is_empty() {
return Err(BybitError::InvalidParam("chain cannot be empty".into()));
}
if self.address.is_empty() {
return Err(BybitError::InvalidParam("address cannot be empty".into()));
}
let amount: rust_decimal::Decimal = self
.amount
.parse()
.map_err(|_| BybitError::InvalidParam("amount must be a valid number".into()))?;
if amount <= rust_decimal::Decimal::ZERO {
return Err(BybitError::InvalidParam("amount must be positive".into()));
}
if amount > rust_decimal::Decimal::from(10000) {
warn!(
coin = %self.coin,
amount = %self.amount,
address = %self.address,
"Large withdrawal detected - please verify details"
);
}
Ok(())
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawResponse {
pub id: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawRecords {
pub rows: Vec<WithdrawRecord>,
#[serde(default)]
pub next_page_cursor: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawRecord {
#[serde(default)]
pub withdraw_id: String,
#[serde(default)]
pub tx_i_d: String,
#[serde(default)]
pub withdraw_type: i32,
pub coin: String,
pub chain: String,
pub amount: String,
#[serde(default)]
pub withdraw_fee: String,
pub status: String,
pub to_address: String,
#[serde(default)]
pub tag: String,
pub create_time: String,
pub update_time: String,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawableAmount {
#[serde(default)]
pub limit_amount_usd: String,
pub withdrawable_amount: WithdrawableAmountDetail,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawableAmountDetail {
#[serde(default)]
pub s_p_o_t: WithdrawableAmountItem,
#[serde(default)]
pub f_u_n_d: WithdrawableAmountItem,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawableAmountItem {
#[serde(default)]
pub coin: String,
#[serde(default)]
pub withdrawable_amount: String,
#[serde(default)]
pub available_balance: String,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelWithdrawParams {
pub id: String,
}