use serde::{Deserialize, Serialize};
use crate::client::OkxClient;
use crate::error::Error;
use crate::model::NumberString;
use crate::transport::Transport;
const NON_TRADABLE_ASSETS: &str = "/api/v5/asset/non-tradable-assets";
const DEPOSIT_ADDRESS: &str = "/api/v5/asset/deposit-address";
const BALANCES: &str = "/api/v5/asset/balances";
const TRANSFER: &str = "/api/v5/asset/transfer";
const TRANSFER_STATE: &str = "/api/v5/asset/transfer-state";
const WITHDRAWAL: &str = "/api/v5/asset/withdrawal";
const DEPOSIT_HISTORY: &str = "/api/v5/asset/deposit-history";
const CURRENCIES: &str = "/api/v5/asset/currencies";
const PURCHASE_REDEMPT: &str = "/api/v5/asset/purchase_redempt";
const BILLS: &str = "/api/v5/asset/bills";
const DEPOSIT_LIGHTNING: &str = "/api/v5/asset/deposit-lightning";
const WITHDRAWAL_LIGHTNING: &str = "/api/v5/asset/withdrawal-lightning";
const CANCEL_WITHDRAWAL: &str = "/api/v5/asset/cancel-withdrawal";
const CONVERT_DUST_ASSETS: &str = "/api/v5/asset/convert-dust-assets";
const ASSET_VALUATION: &str = "/api/v5/asset/asset-valuation";
const WITHDRAWAL_HISTORY: &str = "/api/v5/asset/withdrawal-history";
const DEPOSIT_WITHDRAW_STATUS: &str = "/api/v5/asset/deposit-withdraw-status";
pub struct Funding<'a, T> {
client: &'a OkxClient<T>,
}
impl<'a, T: Transport> Funding<'a, T> {
pub(crate) fn new(client: &'a OkxClient<T>) -> Self {
Self { client }
}
pub async fn get_currencies(&self, ccy: Option<&str>) -> Result<Vec<Currency>, Error> {
let query = CcyQuery { ccy };
self.client.get(CURRENCIES, &query, true).await
}
pub async fn get_balances(&self, ccy: Option<&str>) -> Result<Vec<FundingBalance>, Error> {
let query = CcyQuery { ccy };
self.client.get(BALANCES, &query, true).await
}
pub async fn get_non_tradable_assets(
&self,
ccy: Option<&str>,
) -> Result<Vec<NonTradableAsset>, Error> {
let query = CcyQuery { ccy };
self.client.get(NON_TRADABLE_ASSETS, &query, true).await
}
pub async fn get_deposit_address(&self, ccy: &str) -> Result<Vec<DepositAddress>, Error> {
let query = RequiredCcyQuery { ccy };
self.client.get(DEPOSIT_ADDRESS, &query, true).await
}
pub async fn funds_transfer(
&self,
request: &FundsTransferRequest,
) -> Result<Vec<TransferResult>, Error> {
self.client.post(TRANSFER, request, true).await
}
pub async fn transfer_state(
&self,
trans_id: &str,
transfer_type: Option<&str>,
) -> Result<Vec<TransferState>, Error> {
let query = TransferStateQuery {
trans_id,
transfer_type,
};
self.client.get(TRANSFER_STATE, &query, true).await
}
pub async fn withdrawal(
&self,
request: &WithdrawalRequest,
) -> Result<Vec<WithdrawalResult>, Error> {
self.client.post(WITHDRAWAL, request, true).await
}
pub async fn get_deposit_history(
&self,
request: &DepositHistoryRequest,
) -> Result<Vec<DepositRecord>, Error> {
self.client.get(DEPOSIT_HISTORY, request, true).await
}
pub async fn purchase_redempt(
&self,
ccy: &str,
amt: &str,
side: &str,
rate: &str,
) -> Result<Vec<PurchaseRedemptResult>, Error> {
let body = PurchaseRedemptBody {
ccy,
amt,
side,
rate,
};
self.client.post(PURCHASE_REDEMPT, &body, true).await
}
pub async fn get_bills(
&self,
request: &FundingBillsRequest,
) -> Result<Vec<FundingBill>, Error> {
self.client.get(BILLS, request, true).await
}
pub async fn get_deposit_lightning(
&self,
request: &DepositLightningRequest,
) -> Result<Vec<DepositLightning>, Error> {
self.client.get(DEPOSIT_LIGHTNING, request, true).await
}
pub async fn withdrawal_lightning(
&self,
request: &WithdrawalLightningRequest,
) -> Result<Vec<WithdrawalLightning>, Error> {
self.client.post(WITHDRAWAL_LIGHTNING, request, true).await
}
pub async fn cancel_withdrawal(&self, wd_id: &str) -> Result<Vec<WithdrawalResult>, Error> {
let body = WithdrawalIdBody { wd_id };
self.client.post(CANCEL_WITHDRAWAL, &body, true).await
}
pub async fn convert_dust_assets(
&self,
ccy: &[&str],
) -> Result<Vec<ConvertDustAssetsResult>, Error> {
let body = ConvertDustAssetsBody { ccy };
self.client.post(CONVERT_DUST_ASSETS, &body, true).await
}
pub async fn get_asset_valuation(
&self,
ccy: Option<&str>,
) -> Result<Vec<AssetValuation>, Error> {
let query = CcyQuery { ccy };
self.client.get(ASSET_VALUATION, &query, true).await
}
pub async fn get_deposit_withdraw_status(
&self,
request: &DepositWithdrawStatusRequest,
) -> Result<Vec<DepositWithdrawStatus>, Error> {
self.client
.get(DEPOSIT_WITHDRAW_STATUS, request, true)
.await
}
pub async fn get_withdrawal_history(
&self,
request: &WithdrawalHistoryRequest,
) -> Result<Vec<WithdrawalRecord>, Error> {
self.client.get(WITHDRAWAL_HISTORY, request, true).await
}
}
#[derive(Debug, Serialize)]
struct CcyQuery<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
ccy: Option<&'a str>,
}
#[derive(Debug, Serialize)]
struct RequiredCcyQuery<'a> {
ccy: &'a str,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct TransferStateQuery<'a> {
#[serde(rename = "transId")]
trans_id: &'a str,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
transfer_type: Option<&'a str>,
}
#[derive(Debug, Serialize)]
struct PurchaseRedemptBody<'a> {
ccy: &'a str,
amt: &'a str,
side: &'a str,
rate: &'a str,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct WithdrawalIdBody<'a> {
#[serde(rename = "wdId")]
wd_id: &'a str,
}
#[derive(Debug, Serialize)]
struct ConvertDustAssetsBody<'a> {
ccy: &'a [&'a str],
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FundsTransferRequest {
ccy: String,
amt: String,
#[serde(rename = "from")]
from_account: String,
to: String,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
transfer_type: Option<String>,
#[serde(rename = "subAcct", skip_serializing_if = "Option::is_none")]
sub_account: Option<String>,
#[serde(rename = "instId", skip_serializing_if = "Option::is_none")]
inst_id: Option<String>,
#[serde(rename = "toInstId", skip_serializing_if = "Option::is_none")]
to_inst_id: Option<String>,
#[serde(rename = "loanTrans", skip_serializing_if = "Option::is_none")]
loan_transfer: Option<String>,
}
impl FundsTransferRequest {
pub fn new(
ccy: impl Into<String>,
amt: impl Into<String>,
from_account: impl Into<String>,
to: impl Into<String>,
) -> Self {
Self {
ccy: ccy.into(),
amt: amt.into(),
from_account: from_account.into(),
to: to.into(),
transfer_type: None,
sub_account: None,
inst_id: None,
to_inst_id: None,
loan_transfer: None,
}
}
pub fn transfer_type(mut self, transfer_type: impl Into<String>) -> Self {
self.transfer_type = Some(transfer_type.into());
self
}
pub fn sub_account(mut self, sub_account: impl Into<String>) -> Self {
self.sub_account = Some(sub_account.into());
self
}
pub fn inst_id(mut self, inst_id: impl Into<String>) -> Self {
self.inst_id = Some(inst_id.into());
self
}
pub fn to_inst_id(mut self, to_inst_id: impl Into<String>) -> Self {
self.to_inst_id = Some(to_inst_id.into());
self
}
pub fn loan_transfer(mut self, loan_transfer: impl Into<String>) -> Self {
self.loan_transfer = Some(loan_transfer.into());
self
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawalRequest {
ccy: String,
amt: String,
dest: String,
#[serde(rename = "toAddr")]
to_addr: String,
#[serde(skip_serializing_if = "Option::is_none")]
chain: Option<String>,
#[serde(rename = "areaCode", skip_serializing_if = "Option::is_none")]
area_code: Option<String>,
#[serde(rename = "clientId", skip_serializing_if = "Option::is_none")]
client_id: Option<String>,
#[serde(rename = "toAddrType", skip_serializing_if = "Option::is_none")]
to_addr_type: Option<String>,
}
impl WithdrawalRequest {
pub fn new(
ccy: impl Into<String>,
amt: impl Into<String>,
dest: impl Into<String>,
to_addr: impl Into<String>,
) -> Self {
Self {
ccy: ccy.into(),
amt: amt.into(),
dest: dest.into(),
to_addr: to_addr.into(),
chain: None,
area_code: None,
client_id: None,
to_addr_type: None,
}
}
pub fn chain(mut self, chain: impl Into<String>) -> Self {
self.chain = Some(chain.into());
self
}
pub fn area_code(mut self, area_code: impl Into<String>) -> Self {
self.area_code = Some(area_code.into());
self
}
pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
self.client_id = Some(client_id.into());
self
}
pub fn to_addr_type(mut self, to_addr_type: impl Into<String>) -> Self {
self.to_addr_type = Some(to_addr_type.into());
self
}
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DepositHistoryRequest {
#[serde(skip_serializing_if = "Option::is_none")]
ccy: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
deposit_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
after: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
before: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
limit: Option<u32>,
#[serde(rename = "txId", skip_serializing_if = "Option::is_none")]
tx_id: Option<String>,
#[serde(rename = "depId", skip_serializing_if = "Option::is_none")]
dep_id: Option<String>,
#[serde(rename = "fromWdId", skip_serializing_if = "Option::is_none")]
from_wd_id: Option<String>,
}
impl DepositHistoryRequest {
pub fn new() -> Self {
Self::default()
}
pub fn currency(mut self, ccy: impl Into<String>) -> Self {
self.ccy = Some(ccy.into());
self
}
pub fn deposit_type(mut self, deposit_type: impl Into<String>) -> Self {
self.deposit_type = Some(deposit_type.into());
self
}
pub fn state(mut self, state: impl Into<String>) -> Self {
self.state = Some(state.into());
self
}
pub fn after(mut self, after: impl Into<String>) -> Self {
self.after = Some(after.into());
self
}
pub fn before(mut self, before: impl Into<String>) -> Self {
self.before = Some(before.into());
self
}
pub fn limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
pub fn tx_id(mut self, tx_id: impl Into<String>) -> Self {
self.tx_id = Some(tx_id.into());
self
}
pub fn dep_id(mut self, dep_id: impl Into<String>) -> Self {
self.dep_id = Some(dep_id.into());
self
}
pub fn from_wd_id(mut self, from_wd_id: impl Into<String>) -> Self {
self.from_wd_id = Some(from_wd_id.into());
self
}
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct WithdrawalHistoryRequest {
#[serde(skip_serializing_if = "Option::is_none")]
ccy: Option<String>,
#[serde(rename = "wdId", skip_serializing_if = "Option::is_none")]
wd_id: Option<String>,
#[serde(rename = "clientId", skip_serializing_if = "Option::is_none")]
client_id: Option<String>,
#[serde(rename = "txId", skip_serializing_if = "Option::is_none")]
tx_id: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
withdrawal_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
after: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
before: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
limit: Option<u32>,
#[serde(rename = "toAddrType", skip_serializing_if = "Option::is_none")]
to_addr_type: Option<String>,
}
impl WithdrawalHistoryRequest {
pub fn new() -> Self {
Self::default()
}
pub fn currency(mut self, ccy: impl Into<String>) -> Self {
self.ccy = Some(ccy.into());
self
}
pub fn withdrawal_id(mut self, wd_id: impl Into<String>) -> Self {
self.wd_id = Some(wd_id.into());
self
}
pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
self.client_id = Some(client_id.into());
self
}
pub fn tx_id(mut self, tx_id: impl Into<String>) -> Self {
self.tx_id = Some(tx_id.into());
self
}
pub fn withdrawal_type(mut self, withdrawal_type: impl Into<String>) -> Self {
self.withdrawal_type = Some(withdrawal_type.into());
self
}
pub fn state(mut self, state: impl Into<String>) -> Self {
self.state = Some(state.into());
self
}
pub fn after(mut self, after: impl Into<String>) -> Self {
self.after = Some(after.into());
self
}
pub fn before(mut self, before: impl Into<String>) -> Self {
self.before = Some(before.into());
self
}
pub fn limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
pub fn to_addr_type(mut self, to_addr_type: impl Into<String>) -> Self {
self.to_addr_type = Some(to_addr_type.into());
self
}
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct FundingBillsRequest {
#[serde(skip_serializing_if = "Option::is_none")]
ccy: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
bill_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
after: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
before: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
limit: Option<u32>,
}
impl FundingBillsRequest {
pub fn new() -> Self {
Self::default()
}
pub fn currency(mut self, ccy: impl Into<String>) -> Self {
self.ccy = Some(ccy.into());
self
}
pub fn bill_type(mut self, bill_type: impl Into<String>) -> Self {
self.bill_type = Some(bill_type.into());
self
}
pub fn after(mut self, after: impl Into<String>) -> Self {
self.after = Some(after.into());
self
}
pub fn before(mut self, before: impl Into<String>) -> Self {
self.before = Some(before.into());
self
}
pub fn limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
}
#[derive(Debug, Clone, Serialize)]
pub struct DepositLightningRequest {
ccy: String,
amt: String,
#[serde(skip_serializing_if = "Option::is_none")]
to: Option<String>,
}
impl DepositLightningRequest {
pub fn new(ccy: impl Into<String>, amt: impl Into<String>) -> Self {
Self {
ccy: ccy.into(),
amt: amt.into(),
to: None,
}
}
pub fn to(mut self, to: impl Into<String>) -> Self {
self.to = Some(to.into());
self
}
}
#[derive(Debug, Clone, Serialize)]
pub struct WithdrawalLightningRequest {
ccy: String,
invoice: String,
#[serde(skip_serializing_if = "Option::is_none")]
memo: Option<String>,
}
impl WithdrawalLightningRequest {
pub fn new(ccy: impl Into<String>, invoice: impl Into<String>) -> Self {
Self {
ccy: ccy.into(),
invoice: invoice.into(),
memo: None,
}
}
pub fn memo(mut self, memo: impl Into<String>) -> Self {
self.memo = Some(memo.into());
self
}
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DepositWithdrawStatusRequest {
#[serde(rename = "wdId", skip_serializing_if = "Option::is_none")]
wd_id: Option<String>,
#[serde(rename = "txId", skip_serializing_if = "Option::is_none")]
tx_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
ccy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
chain: Option<String>,
}
impl DepositWithdrawStatusRequest {
pub fn new() -> Self {
Self::default()
}
pub fn withdrawal_id(mut self, wd_id: impl Into<String>) -> Self {
self.wd_id = Some(wd_id.into());
self
}
pub fn tx_id(mut self, tx_id: impl Into<String>) -> Self {
self.tx_id = Some(tx_id.into());
self
}
pub fn currency(mut self, ccy: impl Into<String>) -> Self {
self.ccy = Some(ccy.into());
self
}
pub fn to(mut self, to: impl Into<String>) -> Self {
self.to = Some(to.into());
self
}
pub fn chain(mut self, chain: impl Into<String>) -> Self {
self.chain = Some(chain.into());
self
}
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Currency {
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub chain: String,
#[serde(default, rename = "minWd")]
pub min_wd: NumberString,
#[serde(default, rename = "minDep")]
pub min_dep: NumberString,
#[serde(default, rename = "minFee")]
pub min_fee: NumberString,
#[serde(default, rename = "canDep")]
pub can_dep: bool,
#[serde(default, rename = "canWd")]
pub can_wd: bool,
#[serde(default, rename = "canInternal")]
pub can_internal: bool,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct FundingBalance {
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub bal: NumberString,
#[serde(default)]
pub frozen_bal: NumberString,
#[serde(default)]
pub avail_bal: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct NonTradableAsset {
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub amt: NumberString,
#[serde(default, rename = "type")]
pub asset_type: String,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DepositAddress {
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub chain: String,
#[serde(default)]
pub addr: String,
#[serde(default)]
pub tag: String,
#[serde(default)]
pub selected: bool,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct TransferResult {
#[serde(default, rename = "transId")]
pub trans_id: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub amt: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct TransferState {
#[serde(default, rename = "transId")]
pub trans_id: String,
#[serde(default)]
pub state: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub amt: NumberString,
#[serde(default, rename = "from")]
pub from_account: String,
#[serde(default)]
pub to: String,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct WithdrawalResult {
#[serde(default, rename = "wdId")]
pub wd_id: String,
#[serde(default, rename = "clientId")]
pub client_id: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub amt: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DepositRecord {
#[serde(default, rename = "depId")]
pub dep_id: String,
#[serde(default, rename = "txId")]
pub tx_id: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub chain: String,
#[serde(default)]
pub amt: NumberString,
#[serde(default)]
pub state: String,
#[serde(default)]
pub ts: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct WithdrawalRecord {
#[serde(default, rename = "wdId")]
pub wd_id: String,
#[serde(default, rename = "clientId")]
pub client_id: String,
#[serde(default, rename = "txId")]
pub tx_id: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub chain: String,
#[serde(default)]
pub amt: NumberString,
#[serde(default)]
pub fee: NumberString,
#[serde(default)]
pub state: String,
#[serde(default)]
pub ts: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct FundingBill {
#[serde(default, rename = "billId")]
pub bill_id: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub bal_chg: NumberString,
#[serde(default)]
pub bal: NumberString,
#[serde(default, rename = "type")]
pub bill_type: String,
#[serde(default)]
pub ts: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DepositLightning {
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub amt: NumberString,
#[serde(default)]
pub invoice: String,
#[serde(default)]
pub to: String,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct WithdrawalLightning {
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub amt: NumberString,
#[serde(default, rename = "wdId")]
pub wd_id: String,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AssetValuation {
#[serde(default)]
pub details: AssetValuationDetails,
#[serde(default, rename = "totalBal")]
pub total_bal: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct AssetValuationDetails {
#[serde(default)]
pub funding: NumberString,
#[serde(default)]
pub trading: NumberString,
#[serde(default)]
pub earn: NumberString,
#[serde(default)]
pub classic: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DepositWithdrawStatus {
#[serde(default, rename = "wdId")]
pub wd_id: String,
#[serde(default, rename = "txId")]
pub tx_id: String,
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub chain: String,
#[serde(default)]
pub to: String,
#[serde(default)]
pub state: String,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ConvertDustAssetsResult {
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub amt: NumberString,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct PurchaseRedemptResult {
#[serde(default)]
pub ccy: String,
#[serde(default)]
pub amt: NumberString,
#[serde(default)]
pub side: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn funds_transfer_request_omits_unset_optional_fields() {
let value =
serde_json::to_value(FundsTransferRequest::new("USDT", "1", "6", "18")).unwrap();
assert_eq!(value["ccy"], "USDT");
assert_eq!(value["amt"], "1");
assert_eq!(value["from"], "6");
assert_eq!(value["to"], "18");
assert!(value.get("subAcct").is_none());
assert!(value.get("instId").is_none());
assert!(value.get("loanTrans").is_none());
}
#[test]
fn withdrawal_request_omits_unset_optional_fields() {
let value =
serde_json::to_value(WithdrawalRequest::new("USDT", "1", "3", "example")).unwrap();
assert_eq!(value["ccy"], "USDT");
assert_eq!(value["amt"], "1");
assert_eq!(value["dest"], "3");
assert_eq!(value["toAddr"], "example");
assert!(value.get("chain").is_none());
assert!(value.get("areaCode").is_none());
assert!(value.get("toAddrType").is_none());
}
#[test]
fn history_requests_omit_unset_optional_fields() {
let deposit = serde_urlencoded::to_string(DepositHistoryRequest::new().limit(5)).unwrap();
assert_eq!(deposit, "limit=5");
let withdrawal =
serde_urlencoded::to_string(WithdrawalHistoryRequest::new().to_addr_type("1")).unwrap();
assert_eq!(withdrawal, "toAddrType=1");
}
#[test]
fn deposit_withdraw_status_request_omits_unset_optional_fields() {
let query = serde_urlencoded::to_string(
DepositWithdrawStatusRequest::new()
.currency("USDT")
.chain("USDT-TRC20"),
)
.unwrap();
assert_eq!(query, "ccy=USDT&chain=USDT-TRC20");
}
}