/*
 * Binance Wallet REST API
 *
 * OpenAPI Specification for the Binance Wallet REST API
 *
 * The version of the OpenAPI document: 1.0.0
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

#![allow(unused_imports)]
use async_trait::async_trait;
use derive_builder::Builder;
use reqwest;
use rust_decimal::prelude::*;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use std::collections::BTreeMap;

use crate::common::{
    config::ConfigurationRestApi,
    models::{ParamBuildError, RestApiResponse},
    utils::send_request,
};
use crate::wallet::rest_api::models;

const HAS_TIME_UNIT: bool = false;

#[async_trait]
pub trait AssetApi: Send + Sync {
    async fn asset_detail(
        &self,
        params: AssetDetailParams,
    ) -> anyhow::Result<RestApiResponse<models::AssetDetailResponse>>;
    async fn asset_dividend_record(
        &self,
        params: AssetDividendRecordParams,
    ) -> anyhow::Result<RestApiResponse<models::AssetDividendRecordResponse>>;
    async fn dust_convert(
        &self,
        params: DustConvertParams,
    ) -> anyhow::Result<RestApiResponse<models::DustConvertResponse>>;
    async fn dust_convertible_assets(
        &self,
        params: DustConvertibleAssetsParams,
    ) -> anyhow::Result<RestApiResponse<models::DustConvertibleAssetsResponse>>;
    async fn dust_transfer(
        &self,
        params: DustTransferParams,
    ) -> anyhow::Result<RestApiResponse<models::DustTransferResponse>>;
    async fn dustlog(
        &self,
        params: DustlogParams,
    ) -> anyhow::Result<RestApiResponse<models::DustlogResponse>>;
    async fn funding_wallet(
        &self,
        params: FundingWalletParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::FundingWalletResponseInner>>>;
    async fn get_assets_that_can_be_converted_into_bnb(
        &self,
        params: GetAssetsThatCanBeConvertedIntoBnbParams,
    ) -> anyhow::Result<RestApiResponse<models::GetAssetsThatCanBeConvertedIntoBnbResponse>>;
    async fn get_cloud_mining_payment_and_refund_history(
        &self,
        params: GetCloudMiningPaymentAndRefundHistoryParams,
    ) -> anyhow::Result<RestApiResponse<models::GetCloudMiningPaymentAndRefundHistoryResponse>>;
    async fn get_open_symbol_list(
        &self,
    ) -> anyhow::Result<RestApiResponse<Vec<models::GetOpenSymbolListResponseInner>>>;
    async fn query_user_delegation_history(
        &self,
        params: QueryUserDelegationHistoryParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryUserDelegationHistoryResponse>>;
    async fn query_user_universal_transfer_history(
        &self,
        params: QueryUserUniversalTransferHistoryParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryUserUniversalTransferHistoryResponse>>;
    async fn query_user_wallet_balance(
        &self,
        params: QueryUserWalletBalanceParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::QueryUserWalletBalanceResponseInner>>>;
    async fn toggle_bnb_burn_on_spot_trade_and_margin_interest(
        &self,
        params: ToggleBnbBurnOnSpotTradeAndMarginInterestParams,
    ) -> anyhow::Result<RestApiResponse<models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse>>;
    async fn trade_fee(
        &self,
        params: TradeFeeParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::TradeFeeResponseInner>>>;
    async fn user_asset(
        &self,
        params: UserAssetParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::UserAssetResponseInner>>>;
    async fn user_universal_transfer(
        &self,
        params: UserUniversalTransferParams,
    ) -> anyhow::Result<RestApiResponse<models::UserUniversalTransferResponse>>;
}

#[derive(Debug, Clone)]
pub struct AssetApiClient {
    configuration: ConfigurationRestApi,
}

impl AssetApiClient {
    pub fn new(configuration: ConfigurationRestApi) -> Self {
        Self { configuration }
    }
}

/// Request parameters for the [`asset_detail`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`asset_detail`](#method.asset_detail).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AssetDetailParams {
    /// If asset is blank, then query all positive assets user have.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub asset: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AssetDetailParams {
    /// Create a builder for [`asset_detail`].
    ///
    #[must_use]
    pub fn builder() -> AssetDetailParamsBuilder {
        AssetDetailParamsBuilder::default()
    }
}
/// Request parameters for the [`asset_dividend_record`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`asset_dividend_record`](#method.asset_dividend_record).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AssetDividendRecordParams {
    /// If asset is blank, then query all positive assets user have.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub asset: Option<String>,
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// min 7, max 30, default 7
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AssetDividendRecordParams {
    /// Create a builder for [`asset_dividend_record`].
    ///
    #[must_use]
    pub fn builder() -> AssetDividendRecordParamsBuilder {
        AssetDividendRecordParamsBuilder::default()
    }
}
/// Request parameters for the [`dust_convert`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`dust_convert`](#method.dust_convert).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct DustConvertParams {
    ///
    /// The `asset` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub asset: String,
    /// A unique id for the request
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub client_id: Option<String>,
    ///
    /// The `target_asset` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub target_asset: Option<String>,
    ///
    /// The `third_party_client_id` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub third_party_client_id: Option<String>,
    ///
    /// The `dust_quota_asset_to_target_asset_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub dust_quota_asset_to_target_asset_price: Option<rust_decimal::Decimal>,
}

impl DustConvertParams {
    /// Create a builder for [`dust_convert`].
    ///
    /// Required parameters:
    ///
    /// * `asset` — String
    ///
    #[must_use]
    pub fn builder(asset: String) -> DustConvertParamsBuilder {
        DustConvertParamsBuilder::default().asset(asset)
    }
}
/// Request parameters for the [`dust_convertible_assets`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`dust_convertible_assets`](#method.dust_convertible_assets).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct DustConvertibleAssetsParams {
    ///
    /// The `target_asset` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub target_asset: String,
    ///
    /// The `dust_quota_asset_to_target_asset_price` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub dust_quota_asset_to_target_asset_price: Option<rust_decimal::Decimal>,
}

impl DustConvertibleAssetsParams {
    /// Create a builder for [`dust_convertible_assets`].
    ///
    /// Required parameters:
    ///
    /// * `target_asset` — String
    ///
    #[must_use]
    pub fn builder(target_asset: String) -> DustConvertibleAssetsParamsBuilder {
        DustConvertibleAssetsParamsBuilder::default().target_asset(target_asset)
    }
}
/// Request parameters for the [`dust_transfer`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`dust_transfer`](#method.dust_transfer).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct DustTransferParams {
    ///
    /// The `asset` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub asset: String,
    /// `SPOT` or `MARGIN`,default `SPOT`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub account_type: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl DustTransferParams {
    /// Create a builder for [`dust_transfer`].
    ///
    /// Required parameters:
    ///
    /// * `asset` — String
    ///
    #[must_use]
    pub fn builder(asset: String) -> DustTransferParamsBuilder {
        DustTransferParamsBuilder::default().asset(asset)
    }
}
/// Request parameters for the [`dustlog`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`dustlog`](#method.dustlog).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct DustlogParams {
    /// `SPOT` or `MARGIN`,default `SPOT`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub account_type: Option<String>,
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl DustlogParams {
    /// Create a builder for [`dustlog`].
    ///
    #[must_use]
    pub fn builder() -> DustlogParamsBuilder {
        DustlogParamsBuilder::default()
    }
}
/// Request parameters for the [`funding_wallet`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`funding_wallet`](#method.funding_wallet).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct FundingWalletParams {
    /// If asset is blank, then query all positive assets user have.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub asset: Option<String>,
    /// true or false
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub need_btc_valuation: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl FundingWalletParams {
    /// Create a builder for [`funding_wallet`].
    ///
    #[must_use]
    pub fn builder() -> FundingWalletParamsBuilder {
        FundingWalletParamsBuilder::default()
    }
}
/// Request parameters for the [`get_assets_that_can_be_converted_into_bnb`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`get_assets_that_can_be_converted_into_bnb`](#method.get_assets_that_can_be_converted_into_bnb).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct GetAssetsThatCanBeConvertedIntoBnbParams {
    /// `SPOT` or `MARGIN`,default `SPOT`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub account_type: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl GetAssetsThatCanBeConvertedIntoBnbParams {
    /// Create a builder for [`get_assets_that_can_be_converted_into_bnb`].
    ///
    #[must_use]
    pub fn builder() -> GetAssetsThatCanBeConvertedIntoBnbParamsBuilder {
        GetAssetsThatCanBeConvertedIntoBnbParamsBuilder::default()
    }
}
/// Request parameters for the [`get_cloud_mining_payment_and_refund_history`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`get_cloud_mining_payment_and_refund_history`](#method.get_cloud_mining_payment_and_refund_history).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct GetCloudMiningPaymentAndRefundHistoryParams {
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub start_time: i64,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub end_time: i64,
    /// The transaction id
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub tran_id: Option<i64>,
    /// The unique flag
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub client_tran_id: Option<String>,
    /// If asset is blank, then query all positive assets user have.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub asset: Option<String>,
    /// current page, default 1, the min value is 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub current: Option<i64>,
    /// page size, default 10, the max value is 100
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub size: Option<i64>,
}

impl GetCloudMiningPaymentAndRefundHistoryParams {
    /// Create a builder for [`get_cloud_mining_payment_and_refund_history`].
    ///
    /// Required parameters:
    ///
    /// * `start_time` — i64
    /// * `end_time` — i64
    ///
    #[must_use]
    pub fn builder(
        start_time: i64,
        end_time: i64,
    ) -> GetCloudMiningPaymentAndRefundHistoryParamsBuilder {
        GetCloudMiningPaymentAndRefundHistoryParamsBuilder::default()
            .start_time(start_time)
            .end_time(end_time)
    }
}
/// Request parameters for the [`query_user_delegation_history`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`query_user_delegation_history`](#method.query_user_delegation_history).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryUserDelegationHistoryParams {
    ///
    /// The `email` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub email: String,
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub start_time: i64,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub end_time: i64,
    /// Delegate/Undelegate
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub r#type: Option<String>,
    /// If asset is blank, then query all positive assets user have.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub asset: Option<String>,
    /// current page, default 1, the min value is 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub current: Option<i64>,
    /// page size, default 10, the max value is 100
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub size: Option<i64>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl QueryUserDelegationHistoryParams {
    /// Create a builder for [`query_user_delegation_history`].
    ///
    /// Required parameters:
    ///
    /// * `email` — String
    /// * `start_time` — i64
    /// * `end_time` — i64
    ///
    #[must_use]
    pub fn builder(
        email: String,
        start_time: i64,
        end_time: i64,
    ) -> QueryUserDelegationHistoryParamsBuilder {
        QueryUserDelegationHistoryParamsBuilder::default()
            .email(email)
            .start_time(start_time)
            .end_time(end_time)
    }
}
/// Request parameters for the [`query_user_universal_transfer_history`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`query_user_universal_transfer_history`](#method.query_user_universal_transfer_history).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryUserUniversalTransferHistoryParams {
    ///
    /// The `r#type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub r#type: String,
    ///
    /// The `start_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    ///
    /// The `end_time` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// current page, default 1, the min value is 1
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub current: Option<i64>,
    /// page size, default 10, the max value is 100
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub size: Option<i64>,
    ///
    /// The `from_symbol` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub from_symbol: Option<String>,
    ///
    /// The `to_symbol` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub to_symbol: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl QueryUserUniversalTransferHistoryParams {
    /// Create a builder for [`query_user_universal_transfer_history`].
    ///
    /// Required parameters:
    ///
    /// * `r#type` — String
    ///
    #[must_use]
    pub fn builder(r#type: String) -> QueryUserUniversalTransferHistoryParamsBuilder {
        QueryUserUniversalTransferHistoryParamsBuilder::default().r#type(r#type)
    }
}
/// Request parameters for the [`query_user_wallet_balance`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`query_user_wallet_balance`](#method.query_user_wallet_balance).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryUserWalletBalanceParams {
    /// `USDT`, `ETH`, `USDC`, `BNB`, etc. default `BTC`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub quote_asset: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl QueryUserWalletBalanceParams {
    /// Create a builder for [`query_user_wallet_balance`].
    ///
    #[must_use]
    pub fn builder() -> QueryUserWalletBalanceParamsBuilder {
        QueryUserWalletBalanceParamsBuilder::default()
    }
}
/// Request parameters for the [`toggle_bnb_burn_on_spot_trade_and_margin_interest`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`toggle_bnb_burn_on_spot_trade_and_margin_interest`](#method.toggle_bnb_burn_on_spot_trade_and_margin_interest).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct ToggleBnbBurnOnSpotTradeAndMarginInterestParams {
    /// "true" or "false"; Determines whether to use BNB to pay for trading fees on SPOT
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub spot_bnb_burn: Option<String>,
    /// "true" or "false"; Determines whether to use BNB to pay for margin loan's interest
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub interest_bnb_burn: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl ToggleBnbBurnOnSpotTradeAndMarginInterestParams {
    /// Create a builder for [`toggle_bnb_burn_on_spot_trade_and_margin_interest`].
    ///
    #[must_use]
    pub fn builder() -> ToggleBnbBurnOnSpotTradeAndMarginInterestParamsBuilder {
        ToggleBnbBurnOnSpotTradeAndMarginInterestParamsBuilder::default()
    }
}
/// Request parameters for the [`trade_fee`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`trade_fee`](#method.trade_fee).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct TradeFeeParams {
    ///
    /// The `symbol` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub symbol: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl TradeFeeParams {
    /// Create a builder for [`trade_fee`].
    ///
    #[must_use]
    pub fn builder() -> TradeFeeParamsBuilder {
        TradeFeeParamsBuilder::default()
    }
}
/// Request parameters for the [`user_asset`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`user_asset`](#method.user_asset).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct UserAssetParams {
    /// If asset is blank, then query all positive assets user have.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub asset: Option<String>,
    /// Whether need btc valuation or not.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub need_btc_valuation: Option<bool>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl UserAssetParams {
    /// Create a builder for [`user_asset`].
    ///
    #[must_use]
    pub fn builder() -> UserAssetParamsBuilder {
        UserAssetParamsBuilder::default()
    }
}
/// Request parameters for the [`user_universal_transfer`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`user_universal_transfer`](#method.user_universal_transfer).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct UserUniversalTransferParams {
    ///
    /// The `r#type` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub r#type: String,
    ///
    /// The `asset` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub asset: String,
    ///
    /// The `amount` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub amount: rust_decimal::Decimal,
    ///
    /// The `from_symbol` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub from_symbol: Option<String>,
    ///
    /// The `to_symbol` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub to_symbol: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl UserUniversalTransferParams {
    /// Create a builder for [`user_universal_transfer`].
    ///
    /// Required parameters:
    ///
    /// * `r#type` — String
    /// * `asset` — String
    /// * `amount` — `rust_decimal::Decimal`
    ///
    #[must_use]
    pub fn builder(
        r#type: String,
        asset: String,
        amount: rust_decimal::Decimal,
    ) -> UserUniversalTransferParamsBuilder {
        UserUniversalTransferParamsBuilder::default()
            .r#type(r#type)
            .asset(asset)
            .amount(amount)
    }
}

#[async_trait]
impl AssetApi for AssetApiClient {
    async fn asset_detail(
        &self,
        params: AssetDetailParams,
    ) -> anyhow::Result<RestApiResponse<models::AssetDetailResponse>> {
        let AssetDetailParams { asset, recv_window } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = asset {
            query_params.insert("asset".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::AssetDetailResponse>(
            &self.configuration,
            "/sapi/v1/asset/assetDetail",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn asset_dividend_record(
        &self,
        params: AssetDividendRecordParams,
    ) -> anyhow::Result<RestApiResponse<models::AssetDividendRecordResponse>> {
        let AssetDividendRecordParams {
            asset,
            start_time,
            end_time,
            limit,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = asset {
            query_params.insert("asset".to_string(), json!(rw));
        }

        if let Some(rw) = start_time {
            query_params.insert("startTime".to_string(), json!(rw));
        }

        if let Some(rw) = end_time {
            query_params.insert("endTime".to_string(), json!(rw));
        }

        if let Some(rw) = limit {
            query_params.insert("limit".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::AssetDividendRecordResponse>(
            &self.configuration,
            "/sapi/v1/asset/assetDividend",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn dust_convert(
        &self,
        params: DustConvertParams,
    ) -> anyhow::Result<RestApiResponse<models::DustConvertResponse>> {
        let DustConvertParams {
            asset,
            client_id,
            target_asset,
            third_party_client_id,
            dust_quota_asset_to_target_asset_price,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("asset".to_string(), json!(asset));

        if let Some(rw) = client_id {
            query_params.insert("clientId".to_string(), json!(rw));
        }

        if let Some(rw) = target_asset {
            query_params.insert("targetAsset".to_string(), json!(rw));
        }

        if let Some(rw) = third_party_client_id {
            query_params.insert("thirdPartyClientId".to_string(), json!(rw));
        }

        if let Some(rw) = dust_quota_asset_to_target_asset_price {
            query_params.insert("dustQuotaAssetToTargetAssetPrice".to_string(), json!(rw));
        }

        send_request::<models::DustConvertResponse>(
            &self.configuration,
            "/sapi/v1/asset/dust-convert/convert",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn dust_convertible_assets(
        &self,
        params: DustConvertibleAssetsParams,
    ) -> anyhow::Result<RestApiResponse<models::DustConvertibleAssetsResponse>> {
        let DustConvertibleAssetsParams {
            target_asset,
            dust_quota_asset_to_target_asset_price,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("targetAsset".to_string(), json!(target_asset));

        if let Some(rw) = dust_quota_asset_to_target_asset_price {
            query_params.insert("dustQuotaAssetToTargetAssetPrice".to_string(), json!(rw));
        }

        send_request::<models::DustConvertibleAssetsResponse>(
            &self.configuration,
            "/sapi/v1/asset/dust-convert/query-convertible-assets",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn dust_transfer(
        &self,
        params: DustTransferParams,
    ) -> anyhow::Result<RestApiResponse<models::DustTransferResponse>> {
        let DustTransferParams {
            asset,
            account_type,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("asset".to_string(), json!(asset));

        if let Some(rw) = account_type {
            query_params.insert("accountType".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::DustTransferResponse>(
            &self.configuration,
            "/sapi/v1/asset/dust",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn dustlog(
        &self,
        params: DustlogParams,
    ) -> anyhow::Result<RestApiResponse<models::DustlogResponse>> {
        let DustlogParams {
            account_type,
            start_time,
            end_time,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = account_type {
            query_params.insert("accountType".to_string(), json!(rw));
        }

        if let Some(rw) = start_time {
            query_params.insert("startTime".to_string(), json!(rw));
        }

        if let Some(rw) = end_time {
            query_params.insert("endTime".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::DustlogResponse>(
            &self.configuration,
            "/sapi/v1/asset/dribblet",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn funding_wallet(
        &self,
        params: FundingWalletParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::FundingWalletResponseInner>>> {
        let FundingWalletParams {
            asset,
            need_btc_valuation,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = asset {
            query_params.insert("asset".to_string(), json!(rw));
        }

        if let Some(rw) = need_btc_valuation {
            query_params.insert("needBtcValuation".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<Vec<models::FundingWalletResponseInner>>(
            &self.configuration,
            "/sapi/v1/asset/get-funding-asset",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn get_assets_that_can_be_converted_into_bnb(
        &self,
        params: GetAssetsThatCanBeConvertedIntoBnbParams,
    ) -> anyhow::Result<RestApiResponse<models::GetAssetsThatCanBeConvertedIntoBnbResponse>> {
        let GetAssetsThatCanBeConvertedIntoBnbParams {
            account_type,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = account_type {
            query_params.insert("accountType".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::GetAssetsThatCanBeConvertedIntoBnbResponse>(
            &self.configuration,
            "/sapi/v1/asset/dust-btc",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn get_cloud_mining_payment_and_refund_history(
        &self,
        params: GetCloudMiningPaymentAndRefundHistoryParams,
    ) -> anyhow::Result<RestApiResponse<models::GetCloudMiningPaymentAndRefundHistoryResponse>>
    {
        let GetCloudMiningPaymentAndRefundHistoryParams {
            start_time,
            end_time,
            tran_id,
            client_tran_id,
            asset,
            current,
            size,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = tran_id {
            query_params.insert("tranId".to_string(), json!(rw));
        }

        if let Some(rw) = client_tran_id {
            query_params.insert("clientTranId".to_string(), json!(rw));
        }

        if let Some(rw) = asset {
            query_params.insert("asset".to_string(), json!(rw));
        }

        query_params.insert("startTime".to_string(), json!(start_time));

        query_params.insert("endTime".to_string(), json!(end_time));

        if let Some(rw) = current {
            query_params.insert("current".to_string(), json!(rw));
        }

        if let Some(rw) = size {
            query_params.insert("size".to_string(), json!(rw));
        }

        send_request::<models::GetCloudMiningPaymentAndRefundHistoryResponse>(
            &self.configuration,
            "/sapi/v1/asset/ledger-transfer/cloud-mining/queryByPage",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn get_open_symbol_list(
        &self,
    ) -> anyhow::Result<RestApiResponse<Vec<models::GetOpenSymbolListResponseInner>>> {
        let query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        send_request::<Vec<models::GetOpenSymbolListResponseInner>>(
            &self.configuration,
            "/sapi/v1/spot/open-symbol-list",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            false,
        )
        .await
    }

    async fn query_user_delegation_history(
        &self,
        params: QueryUserDelegationHistoryParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryUserDelegationHistoryResponse>> {
        let QueryUserDelegationHistoryParams {
            email,
            start_time,
            end_time,
            r#type,
            asset,
            current,
            size,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("email".to_string(), json!(email));

        query_params.insert("startTime".to_string(), json!(start_time));

        query_params.insert("endTime".to_string(), json!(end_time));

        if let Some(rw) = r#type {
            query_params.insert("type".to_string(), json!(rw));
        }

        if let Some(rw) = asset {
            query_params.insert("asset".to_string(), json!(rw));
        }

        if let Some(rw) = current {
            query_params.insert("current".to_string(), json!(rw));
        }

        if let Some(rw) = size {
            query_params.insert("size".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::QueryUserDelegationHistoryResponse>(
            &self.configuration,
            "/sapi/v1/asset/custody/transfer-history",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn query_user_universal_transfer_history(
        &self,
        params: QueryUserUniversalTransferHistoryParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryUserUniversalTransferHistoryResponse>> {
        let QueryUserUniversalTransferHistoryParams {
            r#type,
            start_time,
            end_time,
            current,
            size,
            from_symbol,
            to_symbol,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("type".to_string(), json!(r#type));

        if let Some(rw) = start_time {
            query_params.insert("startTime".to_string(), json!(rw));
        }

        if let Some(rw) = end_time {
            query_params.insert("endTime".to_string(), json!(rw));
        }

        if let Some(rw) = current {
            query_params.insert("current".to_string(), json!(rw));
        }

        if let Some(rw) = size {
            query_params.insert("size".to_string(), json!(rw));
        }

        if let Some(rw) = from_symbol {
            query_params.insert("fromSymbol".to_string(), json!(rw));
        }

        if let Some(rw) = to_symbol {
            query_params.insert("toSymbol".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::QueryUserUniversalTransferHistoryResponse>(
            &self.configuration,
            "/sapi/v1/asset/transfer",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn query_user_wallet_balance(
        &self,
        params: QueryUserWalletBalanceParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::QueryUserWalletBalanceResponseInner>>> {
        let QueryUserWalletBalanceParams {
            quote_asset,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = quote_asset {
            query_params.insert("quoteAsset".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<Vec<models::QueryUserWalletBalanceResponseInner>>(
            &self.configuration,
            "/sapi/v1/asset/wallet/balance",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn toggle_bnb_burn_on_spot_trade_and_margin_interest(
        &self,
        params: ToggleBnbBurnOnSpotTradeAndMarginInterestParams,
    ) -> anyhow::Result<RestApiResponse<models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse>>
    {
        let ToggleBnbBurnOnSpotTradeAndMarginInterestParams {
            spot_bnb_burn,
            interest_bnb_burn,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = spot_bnb_burn {
            query_params.insert("spotBNBBurn".to_string(), json!(rw));
        }

        if let Some(rw) = interest_bnb_burn {
            query_params.insert("interestBNBBurn".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse>(
            &self.configuration,
            "/sapi/v1/bnbBurn",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn trade_fee(
        &self,
        params: TradeFeeParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::TradeFeeResponseInner>>> {
        let TradeFeeParams {
            symbol,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = symbol {
            query_params.insert("symbol".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<Vec<models::TradeFeeResponseInner>>(
            &self.configuration,
            "/sapi/v1/asset/tradeFee",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn user_asset(
        &self,
        params: UserAssetParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::UserAssetResponseInner>>> {
        let UserAssetParams {
            asset,
            need_btc_valuation,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        if let Some(rw) = asset {
            query_params.insert("asset".to_string(), json!(rw));
        }

        if let Some(rw) = need_btc_valuation {
            query_params.insert("needBtcValuation".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<Vec<models::UserAssetResponseInner>>(
            &self.configuration,
            "/sapi/v3/asset/getUserAsset",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn user_universal_transfer(
        &self,
        params: UserUniversalTransferParams,
    ) -> anyhow::Result<RestApiResponse<models::UserUniversalTransferResponse>> {
        let UserUniversalTransferParams {
            r#type,
            asset,
            amount,
            from_symbol,
            to_symbol,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("type".to_string(), json!(r#type));

        query_params.insert("asset".to_string(), json!(asset));

        query_params.insert("amount".to_string(), json!(amount));

        if let Some(rw) = from_symbol {
            query_params.insert("fromSymbol".to_string(), json!(rw));
        }

        if let Some(rw) = to_symbol {
            query_params.insert("toSymbol".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::UserUniversalTransferResponse>(
            &self.configuration,
            "/sapi/v1/asset/transfer",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }
}

#[cfg(all(test, feature = "wallet"))]
mod tests {
    use super::*;
    use crate::TOKIO_SHARED_RT;
    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
    use async_trait::async_trait;
    use std::collections::HashMap;

    struct DummyRestApiResponse<T> {
        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
        status: u16,
        headers: HashMap<String, String>,
        rate_limits: Option<Vec<RestApiRateLimit>>,
    }

    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
        fn from(dummy: DummyRestApiResponse<T>) -> Self {
            Self {
                data_fn: dummy.inner,
                status: dummy.status,
                headers: dummy.headers,
                rate_limits: dummy.rate_limits,
            }
        }
    }

    struct MockAssetApiClient {
        force_error: bool,
    }

    #[async_trait]
    impl AssetApi for MockAssetApiClient {
        async fn asset_detail(
            &self,
            _params: AssetDetailParams,
        ) -> anyhow::Result<RestApiResponse<models::AssetDetailResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"CTR":{"minWithdrawAmount":"70.00000000","depositStatus":false,"withdrawFee":35,"withdrawStatus":true,"depositTip":"Delisted, Deposit Suspended"},"SKY":{"minWithdrawAmount":"0.02000000","depositStatus":true,"withdrawFee":0.01,"withdrawStatus":true}}"#).unwrap();
            let dummy_response: models::AssetDetailResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::AssetDetailResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn asset_dividend_record(
            &self,
            _params: AssetDividendRecordParams,
        ) -> anyhow::Result<RestApiResponse<models::AssetDividendRecordResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"id":1637366104,"amount":"10.00000000","asset":"BHFT","divTime":1563189166000,"enInfo":"BHFT distribution","tranId":2968885920,"direction":1},{"id":1631750237,"amount":"10.00000000","asset":"BHFT","divTime":1563189165000,"enInfo":"BHFT distribution","tranId":2968885920,"direction":1}],"total":2}"#).unwrap();
            let dummy_response: models::AssetDividendRecordResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::AssetDividendRecordResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn dust_convert(
            &self,
            _params: DustConvertParams,
        ) -> anyhow::Result<RestApiResponse<models::DustConvertResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"totalTransfered":"3.5971223","totalServiceCharge":"0.0794964","transferResult":[{"tranId":2987331510,"fromAsset":"USDT","amount":"1","transferedAmount":"3.5971223","serviceChargeAmount":"0.0794964","operateTime":1765212029749}]}"#).unwrap();
            let dummy_response: models::DustConvertResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::DustConvertResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn dust_convertible_assets(
            &self,
            _params: DustConvertibleAssetsParams,
        ) -> anyhow::Result<RestApiResponse<models::DustConvertibleAssetsResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"dribbletPercentage":"0.02","totalTransferQuotaAssetAmount":"0.7899968","totalTransferTargetAssetAmount":"0.7899968","dribbletBase":"10","details":[{"asset":"AR","assetFullName":"AR","amountFree":"0.00856","exchange":"0.00073616","toQuotaAssetAmount":"0.036808","toTargetAssetAmount":"0.036808","toTargetAssetOffExchange":"0.03607184"},{"asset":"BNB","assetFullName":"BNB","amountFree":"0.00082768","exchange":"0.01506378","toQuotaAssetAmount":"0.7531888","toTargetAssetAmount":"0.7531888","toTargetAssetOffExchange":"0.73812502"}]}"#).unwrap();
            let dummy_response: models::DustConvertibleAssetsResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::DustConvertibleAssetsResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn dust_transfer(
            &self,
            _params: DustTransferParams,
        ) -> anyhow::Result<RestApiResponse<models::DustTransferResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"totalServiceCharge":"0.02102542","totalTransfered":"1.05127099","transferResult":[{"amount":"0.03000000","fromAsset":"ETH","operateTime":1563368549307,"serviceChargeAmount":"0.00500000","tranId":2970932918,"transferedAmount":"0.25000000"},{"amount":"0.09000000","fromAsset":"LTC","operateTime":1563368549404,"serviceChargeAmount":"0.01548000","tranId":2970932918,"transferedAmount":"0.77400000"},{"amount":"248.61878453","fromAsset":"TRX","operateTime":1563368549489,"serviceChargeAmount":"0.00054542","tranId":2970932918,"transferedAmount":"0.02727099"}]}"#).unwrap();
            let dummy_response: models::DustTransferResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::DustTransferResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn dustlog(
            &self,
            _params: DustlogParams,
        ) -> anyhow::Result<RestApiResponse<models::DustlogResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"total":8,"userAssetDribblets":[{"operateTime":1615985535000,"totalTransferedAmount":"0.00132256","totalServiceChargeAmount":"0.00002699","transId":45178372831,"userAssetDribbletDetails":[{"transId":4359321,"serviceChargeAmount":"0.000009","amount":"0.0009","operateTime":1615985535000,"transferedAmount":"0.000441","fromAsset":"USDT"},{"transId":4359321,"serviceChargeAmount":"0.00001799","amount":"0.0009","operateTime":1615985535000,"transferedAmount":"0.00088156","fromAsset":"ETH"}]},{"operateTime":1616203180000,"totalTransferedAmount":"0.00058795","totalServiceChargeAmount":"0.000012","transId":4357015,"userAssetDribbletDetails":[{"transId":4357015,"serviceChargeAmount":"0.00001","amount":"0.001","operateTime":1616203180000,"transferedAmount":"0.00049","fromAsset":"USDT"},{"transId":4357015,"serviceChargeAmount":"0.000002","amount":"0.0001","operateTime":1616203180000,"transferedAmount":"0.00009795","fromAsset":"ETH"}]}]}"#).unwrap();
            let dummy_response: models::DustlogResponse = serde_json::from_value(resp_json.clone())
                .expect("should parse into models::DustlogResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn funding_wallet(
            &self,
            _params: FundingWalletParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::FundingWalletResponseInner>>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"asset":"USDT","free":"1","locked":"0","freeze":"0","withdrawing":"0","btcValuation":"0.00000091"}]"#).unwrap();
            let dummy_response: Vec<models::FundingWalletResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::FundingWalletResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn get_assets_that_can_be_converted_into_bnb(
            &self,
            _params: GetAssetsThatCanBeConvertedIntoBnbParams,
        ) -> anyhow::Result<RestApiResponse<models::GetAssetsThatCanBeConvertedIntoBnbResponse>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"details":[{"asset":"ADA","assetFullName":"ADA","amountFree":"6.21","toBTC":"0.00016848","toBNB":"0.01777302","toBNBOffExchange":"0.01741756","exchange":"0.00035546"}],"totalTransferBtc":"0.00016848","totalTransferBNB":"0.01777302","dribbletPercentage":"0.02"}"#).unwrap();
            let dummy_response: models::GetAssetsThatCanBeConvertedIntoBnbResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::GetAssetsThatCanBeConvertedIntoBnbResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn get_cloud_mining_payment_and_refund_history(
            &self,
            _params: GetCloudMiningPaymentAndRefundHistoryParams,
        ) -> anyhow::Result<RestApiResponse<models::GetCloudMiningPaymentAndRefundHistoryResponse>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"total":5,"rows":[{"createTime":1667880112000,"tranId":121230610120,"type":248,"asset":"USDT","amount":"25.0068","status":"S"},{"createTime":1666776366000,"tranId":119991507468,"type":249,"asset":"USDT","amount":"0.027","status":"S"},{"createTime":1666764505000,"tranId":119977966327,"type":248,"asset":"USDT","amount":"0.027","status":"S"},{"createTime":1666758189000,"tranId":119973601721,"type":248,"asset":"USDT","amount":"0.018","status":"S"},{"createTime":1666757278000,"tranId":119973028551,"type":248,"asset":"USDT","amount":"0.018","status":"S"}]}"#).unwrap();
            let dummy_response: models::GetCloudMiningPaymentAndRefundHistoryResponse =
                serde_json::from_value(resp_json.clone()).expect(
                    "should parse into models::GetCloudMiningPaymentAndRefundHistoryResponse",
                );

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn get_open_symbol_list(
            &self,
        ) -> anyhow::Result<RestApiResponse<Vec<models::GetOpenSymbolListResponseInner>>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"openTime":1686161202000,"symbols":["BNBBTC","BNBETH"]},{"openTime":1686222232000,"symbols":["BTCUSDT"]}]"#).unwrap();
            let dummy_response: Vec<models::GetOpenSymbolListResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::GetOpenSymbolListResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn query_user_delegation_history(
            &self,
            _params: QueryUserDelegationHistoryParams,
        ) -> anyhow::Result<RestApiResponse<models::QueryUserDelegationHistoryResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"total":3316,"rows":[{"clientTranId":"293915932290879488","transferType":"Undelegate","asset":"ETH","amount":"1","time":1695205406000},{"clientTranId":"293915892281413632","transferType":"Delegate","asset":"ETH","amount":"1","time":1695205396000}]}"#).unwrap();
            let dummy_response: models::QueryUserDelegationHistoryResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::QueryUserDelegationHistoryResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn query_user_universal_transfer_history(
            &self,
            _params: QueryUserUniversalTransferHistoryParams,
        ) -> anyhow::Result<RestApiResponse<models::QueryUserUniversalTransferHistoryResponse>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"total":2,"rows":[{"asset":"USDT","amount":"1","type":"MAIN_UMFUTURE","status":"CONFIRMED","tranId":11415955596,"timestamp":1544433328000},{"asset":"USDT","amount":"2","type":"MAIN_UMFUTURE","status":"CONFIRMED","tranId":11366865406,"timestamp":1544433328000}]}"#).unwrap();
            let dummy_response: models::QueryUserUniversalTransferHistoryResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::QueryUserUniversalTransferHistoryResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn query_user_wallet_balance(
            &self,
            _params: QueryUserWalletBalanceParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::QueryUserWalletBalanceResponseInner>>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"activate":true,"balance":"0","walletName":"Spot"},{"activate":true,"balance":"0","walletName":"Funding"},{"activate":true,"balance":"0","walletName":"Cross Margin"},{"activate":true,"balance":"0","walletName":"Isolated Margin"},{"activate":true,"balance":"0.71842752","walletName":"USDⓈ-M Futures"},{"activate":true,"balance":"0","walletName":"COIN-M Futures"},{"activate":true,"balance":"0","walletName":"Earn"},{"activate":false,"balance":"0","walletName":"Options"},{"activate":true,"balance":"0","walletName":"Trading Bots"},{"activate":true,"balance":"0","walletName":"Copy Trading"}]"#).unwrap();
            let dummy_response: Vec<models::QueryUserWalletBalanceResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::QueryUserWalletBalanceResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn toggle_bnb_burn_on_spot_trade_and_margin_interest(
            &self,
            _params: ToggleBnbBurnOnSpotTradeAndMarginInterestParams,
        ) -> anyhow::Result<
            RestApiResponse<models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse>,
        > {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value =
                serde_json::from_str(r#"{"spotBNBBurn":true,"interestBNBBurn":false}"#).unwrap();
            let dummy_response: models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse =
                serde_json::from_value(resp_json.clone()).expect(
                    "should parse into models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse",
                );

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn trade_fee(
            &self,
            _params: TradeFeeParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::TradeFeeResponseInner>>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"ADABNB","makerCommission":"0.001","takerCommission":"0.001"},{"symbol":"BNBBTC","makerCommission":"0.001","takerCommission":"0.001"}]"#).unwrap();
            let dummy_response: Vec<models::TradeFeeResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::TradeFeeResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn user_asset(
            &self,
            _params: UserAssetParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::UserAssetResponseInner>>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"asset":"AVAX","free":"1","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"BCH","free":"0.9","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"BNB","free":"887.47061626","locked":"0","freeze":"10.52","withdrawing":"0.1","ipoable":"0","btcValuation":"0"},{"asset":"BUSD","free":"9999.7","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"SHIB","free":"532.32","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"USDT","free":"50300000001.44911105","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"WRZ","free":"1","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"}]"#).unwrap();
            let dummy_response: Vec<models::UserAssetResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::UserAssetResponseInner>");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn user_universal_transfer(
            &self,
            _params: UserUniversalTransferParams,
        ) -> anyhow::Result<RestApiResponse<models::UserUniversalTransferResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"tranId":13526853623}"#).unwrap();
            let dummy_response: models::UserUniversalTransferResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::UserUniversalTransferResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }
    }

    #[test]
    fn asset_detail_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = AssetDetailParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"CTR":{"minWithdrawAmount":"70.00000000","depositStatus":false,"withdrawFee":35,"withdrawStatus":true,"depositTip":"Delisted, Deposit Suspended"},"SKY":{"minWithdrawAmount":"0.02000000","depositStatus":true,"withdrawFee":0.01,"withdrawStatus":true}}"#).unwrap();
            let expected_response : models::AssetDetailResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AssetDetailResponse");

            let resp = client.asset_detail(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn asset_detail_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = AssetDetailParams::builder().asset("asset_example".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"CTR":{"minWithdrawAmount":"70.00000000","depositStatus":false,"withdrawFee":35,"withdrawStatus":true,"depositTip":"Delisted, Deposit Suspended"},"SKY":{"minWithdrawAmount":"0.02000000","depositStatus":true,"withdrawFee":0.01,"withdrawStatus":true}}"#).unwrap();
            let expected_response : models::AssetDetailResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AssetDetailResponse");

            let resp = client.asset_detail(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn asset_detail_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = AssetDetailParams::builder().build().unwrap();

            match client.asset_detail(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn asset_dividend_record_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = AssetDividendRecordParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"id":1637366104,"amount":"10.00000000","asset":"BHFT","divTime":1563189166000,"enInfo":"BHFT distribution","tranId":2968885920,"direction":1},{"id":1631750237,"amount":"10.00000000","asset":"BHFT","divTime":1563189165000,"enInfo":"BHFT distribution","tranId":2968885920,"direction":1}],"total":2}"#).unwrap();
            let expected_response : models::AssetDividendRecordResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AssetDividendRecordResponse");

            let resp = client.asset_dividend_record(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn asset_dividend_record_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = AssetDividendRecordParams::builder().asset("asset_example".to_string()).start_time(1623319461670).end_time(1641782889000).limit(7).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"id":1637366104,"amount":"10.00000000","asset":"BHFT","divTime":1563189166000,"enInfo":"BHFT distribution","tranId":2968885920,"direction":1},{"id":1631750237,"amount":"10.00000000","asset":"BHFT","divTime":1563189165000,"enInfo":"BHFT distribution","tranId":2968885920,"direction":1}],"total":2}"#).unwrap();
            let expected_response : models::AssetDividendRecordResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AssetDividendRecordResponse");

            let resp = client.asset_dividend_record(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn asset_dividend_record_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = AssetDividendRecordParams::builder().build().unwrap();

            match client.asset_dividend_record(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn dust_convert_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = DustConvertParams::builder("asset_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"totalTransfered":"3.5971223","totalServiceCharge":"0.0794964","transferResult":[{"tranId":2987331510,"fromAsset":"USDT","amount":"1","transferedAmount":"3.5971223","serviceChargeAmount":"0.0794964","operateTime":1765212029749}]}"#).unwrap();
            let expected_response : models::DustConvertResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DustConvertResponse");

            let resp = client.dust_convert(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn dust_convert_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = DustConvertParams::builder("asset_example".to_string(),).client_id("1".to_string()).target_asset("target_asset_example".to_string()).third_party_client_id("1".to_string()).dust_quota_asset_to_target_asset_price(dec!(1.0)).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"totalTransfered":"3.5971223","totalServiceCharge":"0.0794964","transferResult":[{"tranId":2987331510,"fromAsset":"USDT","amount":"1","transferedAmount":"3.5971223","serviceChargeAmount":"0.0794964","operateTime":1765212029749}]}"#).unwrap();
            let expected_response : models::DustConvertResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DustConvertResponse");

            let resp = client.dust_convert(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn dust_convert_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = DustConvertParams::builder("asset_example".to_string())
                .build()
                .unwrap();

            match client.dust_convert(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn dust_convertible_assets_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = DustConvertibleAssetsParams::builder("target_asset_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"dribbletPercentage":"0.02","totalTransferQuotaAssetAmount":"0.7899968","totalTransferTargetAssetAmount":"0.7899968","dribbletBase":"10","details":[{"asset":"AR","assetFullName":"AR","amountFree":"0.00856","exchange":"0.00073616","toQuotaAssetAmount":"0.036808","toTargetAssetAmount":"0.036808","toTargetAssetOffExchange":"0.03607184"},{"asset":"BNB","assetFullName":"BNB","amountFree":"0.00082768","exchange":"0.01506378","toQuotaAssetAmount":"0.7531888","toTargetAssetAmount":"0.7531888","toTargetAssetOffExchange":"0.73812502"}]}"#).unwrap();
            let expected_response : models::DustConvertibleAssetsResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DustConvertibleAssetsResponse");

            let resp = client.dust_convertible_assets(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn dust_convertible_assets_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = DustConvertibleAssetsParams::builder("target_asset_example".to_string(),).dust_quota_asset_to_target_asset_price(dec!(1.0)).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"dribbletPercentage":"0.02","totalTransferQuotaAssetAmount":"0.7899968","totalTransferTargetAssetAmount":"0.7899968","dribbletBase":"10","details":[{"asset":"AR","assetFullName":"AR","amountFree":"0.00856","exchange":"0.00073616","toQuotaAssetAmount":"0.036808","toTargetAssetAmount":"0.036808","toTargetAssetOffExchange":"0.03607184"},{"asset":"BNB","assetFullName":"BNB","amountFree":"0.00082768","exchange":"0.01506378","toQuotaAssetAmount":"0.7531888","toTargetAssetAmount":"0.7531888","toTargetAssetOffExchange":"0.73812502"}]}"#).unwrap();
            let expected_response : models::DustConvertibleAssetsResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DustConvertibleAssetsResponse");

            let resp = client.dust_convertible_assets(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn dust_convertible_assets_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = DustConvertibleAssetsParams::builder("target_asset_example".to_string())
                .build()
                .unwrap();

            match client.dust_convertible_assets(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn dust_transfer_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = DustTransferParams::builder("asset_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"totalServiceCharge":"0.02102542","totalTransfered":"1.05127099","transferResult":[{"amount":"0.03000000","fromAsset":"ETH","operateTime":1563368549307,"serviceChargeAmount":"0.00500000","tranId":2970932918,"transferedAmount":"0.25000000"},{"amount":"0.09000000","fromAsset":"LTC","operateTime":1563368549404,"serviceChargeAmount":"0.01548000","tranId":2970932918,"transferedAmount":"0.77400000"},{"amount":"248.61878453","fromAsset":"TRX","operateTime":1563368549489,"serviceChargeAmount":"0.00054542","tranId":2970932918,"transferedAmount":"0.02727099"}]}"#).unwrap();
            let expected_response : models::DustTransferResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DustTransferResponse");

            let resp = client.dust_transfer(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn dust_transfer_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = DustTransferParams::builder("asset_example".to_string(),).account_type("SPOT".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"totalServiceCharge":"0.02102542","totalTransfered":"1.05127099","transferResult":[{"amount":"0.03000000","fromAsset":"ETH","operateTime":1563368549307,"serviceChargeAmount":"0.00500000","tranId":2970932918,"transferedAmount":"0.25000000"},{"amount":"0.09000000","fromAsset":"LTC","operateTime":1563368549404,"serviceChargeAmount":"0.01548000","tranId":2970932918,"transferedAmount":"0.77400000"},{"amount":"248.61878453","fromAsset":"TRX","operateTime":1563368549489,"serviceChargeAmount":"0.00054542","tranId":2970932918,"transferedAmount":"0.02727099"}]}"#).unwrap();
            let expected_response : models::DustTransferResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DustTransferResponse");

            let resp = client.dust_transfer(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn dust_transfer_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = DustTransferParams::builder("asset_example".to_string())
                .build()
                .unwrap();

            match client.dust_transfer(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn dustlog_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = DustlogParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":8,"userAssetDribblets":[{"operateTime":1615985535000,"totalTransferedAmount":"0.00132256","totalServiceChargeAmount":"0.00002699","transId":45178372831,"userAssetDribbletDetails":[{"transId":4359321,"serviceChargeAmount":"0.000009","amount":"0.0009","operateTime":1615985535000,"transferedAmount":"0.000441","fromAsset":"USDT"},{"transId":4359321,"serviceChargeAmount":"0.00001799","amount":"0.0009","operateTime":1615985535000,"transferedAmount":"0.00088156","fromAsset":"ETH"}]},{"operateTime":1616203180000,"totalTransferedAmount":"0.00058795","totalServiceChargeAmount":"0.000012","transId":4357015,"userAssetDribbletDetails":[{"transId":4357015,"serviceChargeAmount":"0.00001","amount":"0.001","operateTime":1616203180000,"transferedAmount":"0.00049","fromAsset":"USDT"},{"transId":4357015,"serviceChargeAmount":"0.000002","amount":"0.0001","operateTime":1616203180000,"transferedAmount":"0.00009795","fromAsset":"ETH"}]}]}"#).unwrap();
            let expected_response : models::DustlogResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DustlogResponse");

            let resp = client.dustlog(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn dustlog_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = DustlogParams::builder().account_type("SPOT".to_string()).start_time(1623319461670).end_time(1641782889000).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":8,"userAssetDribblets":[{"operateTime":1615985535000,"totalTransferedAmount":"0.00132256","totalServiceChargeAmount":"0.00002699","transId":45178372831,"userAssetDribbletDetails":[{"transId":4359321,"serviceChargeAmount":"0.000009","amount":"0.0009","operateTime":1615985535000,"transferedAmount":"0.000441","fromAsset":"USDT"},{"transId":4359321,"serviceChargeAmount":"0.00001799","amount":"0.0009","operateTime":1615985535000,"transferedAmount":"0.00088156","fromAsset":"ETH"}]},{"operateTime":1616203180000,"totalTransferedAmount":"0.00058795","totalServiceChargeAmount":"0.000012","transId":4357015,"userAssetDribbletDetails":[{"transId":4357015,"serviceChargeAmount":"0.00001","amount":"0.001","operateTime":1616203180000,"transferedAmount":"0.00049","fromAsset":"USDT"},{"transId":4357015,"serviceChargeAmount":"0.000002","amount":"0.0001","operateTime":1616203180000,"transferedAmount":"0.00009795","fromAsset":"ETH"}]}]}"#).unwrap();
            let expected_response : models::DustlogResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::DustlogResponse");

            let resp = client.dustlog(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn dustlog_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = DustlogParams::builder().build().unwrap();

            match client.dustlog(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn funding_wallet_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = FundingWalletParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"asset":"USDT","free":"1","locked":"0","freeze":"0","withdrawing":"0","btcValuation":"0.00000091"}]"#).unwrap();
            let expected_response : Vec<models::FundingWalletResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::FundingWalletResponseInner>");

            let resp = client.funding_wallet(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn funding_wallet_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = FundingWalletParams::builder().asset("asset_example".to_string()).need_btc_valuation("need_btc_valuation_example".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"asset":"USDT","free":"1","locked":"0","freeze":"0","withdrawing":"0","btcValuation":"0.00000091"}]"#).unwrap();
            let expected_response : Vec<models::FundingWalletResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::FundingWalletResponseInner>");

            let resp = client.funding_wallet(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn funding_wallet_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = FundingWalletParams::builder().build().unwrap();

            match client.funding_wallet(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn get_assets_that_can_be_converted_into_bnb_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = GetAssetsThatCanBeConvertedIntoBnbParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"details":[{"asset":"ADA","assetFullName":"ADA","amountFree":"6.21","toBTC":"0.00016848","toBNB":"0.01777302","toBNBOffExchange":"0.01741756","exchange":"0.00035546"}],"totalTransferBtc":"0.00016848","totalTransferBNB":"0.01777302","dribbletPercentage":"0.02"}"#).unwrap();
            let expected_response : models::GetAssetsThatCanBeConvertedIntoBnbResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetAssetsThatCanBeConvertedIntoBnbResponse");

            let resp = client.get_assets_that_can_be_converted_into_bnb(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn get_assets_that_can_be_converted_into_bnb_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = GetAssetsThatCanBeConvertedIntoBnbParams::builder().account_type("SPOT".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"details":[{"asset":"ADA","assetFullName":"ADA","amountFree":"6.21","toBTC":"0.00016848","toBNB":"0.01777302","toBNBOffExchange":"0.01741756","exchange":"0.00035546"}],"totalTransferBtc":"0.00016848","totalTransferBNB":"0.01777302","dribbletPercentage":"0.02"}"#).unwrap();
            let expected_response : models::GetAssetsThatCanBeConvertedIntoBnbResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetAssetsThatCanBeConvertedIntoBnbResponse");

            let resp = client.get_assets_that_can_be_converted_into_bnb(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn get_assets_that_can_be_converted_into_bnb_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = GetAssetsThatCanBeConvertedIntoBnbParams::builder()
                .build()
                .unwrap();

            match client
                .get_assets_that_can_be_converted_into_bnb(params)
                .await
            {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn get_cloud_mining_payment_and_refund_history_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = GetCloudMiningPaymentAndRefundHistoryParams::builder(1623319461670,1641782889000,).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":5,"rows":[{"createTime":1667880112000,"tranId":121230610120,"type":248,"asset":"USDT","amount":"25.0068","status":"S"},{"createTime":1666776366000,"tranId":119991507468,"type":249,"asset":"USDT","amount":"0.027","status":"S"},{"createTime":1666764505000,"tranId":119977966327,"type":248,"asset":"USDT","amount":"0.027","status":"S"},{"createTime":1666758189000,"tranId":119973601721,"type":248,"asset":"USDT","amount":"0.018","status":"S"},{"createTime":1666757278000,"tranId":119973028551,"type":248,"asset":"USDT","amount":"0.018","status":"S"}]}"#).unwrap();
            let expected_response : models::GetCloudMiningPaymentAndRefundHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetCloudMiningPaymentAndRefundHistoryResponse");

            let resp = client.get_cloud_mining_payment_and_refund_history(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn get_cloud_mining_payment_and_refund_history_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = GetCloudMiningPaymentAndRefundHistoryParams::builder(1623319461670,1641782889000,).tran_id(1).client_tran_id("1".to_string()).asset("asset_example".to_string()).current(1).size(10).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":5,"rows":[{"createTime":1667880112000,"tranId":121230610120,"type":248,"asset":"USDT","amount":"25.0068","status":"S"},{"createTime":1666776366000,"tranId":119991507468,"type":249,"asset":"USDT","amount":"0.027","status":"S"},{"createTime":1666764505000,"tranId":119977966327,"type":248,"asset":"USDT","amount":"0.027","status":"S"},{"createTime":1666758189000,"tranId":119973601721,"type":248,"asset":"USDT","amount":"0.018","status":"S"},{"createTime":1666757278000,"tranId":119973028551,"type":248,"asset":"USDT","amount":"0.018","status":"S"}]}"#).unwrap();
            let expected_response : models::GetCloudMiningPaymentAndRefundHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetCloudMiningPaymentAndRefundHistoryResponse");

            let resp = client.get_cloud_mining_payment_and_refund_history(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn get_cloud_mining_payment_and_refund_history_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params =
                GetCloudMiningPaymentAndRefundHistoryParams::builder(1623319461670, 1641782889000)
                    .build()
                    .unwrap();

            match client
                .get_cloud_mining_payment_and_refund_history(params)
                .await
            {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn get_open_symbol_list_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };


            let resp_json: Value = serde_json::from_str(r#"[{"openTime":1686161202000,"symbols":["BNBBTC","BNBETH"]},{"openTime":1686222232000,"symbols":["BTCUSDT"]}]"#).unwrap();
            let expected_response : Vec<models::GetOpenSymbolListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::GetOpenSymbolListResponseInner>");

            let resp = client.get_open_symbol_list().await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn get_open_symbol_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };


            let resp_json: Value = serde_json::from_str(r#"[{"openTime":1686161202000,"symbols":["BNBBTC","BNBETH"]},{"openTime":1686222232000,"symbols":["BTCUSDT"]}]"#).unwrap();
            let expected_response : Vec<models::GetOpenSymbolListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::GetOpenSymbolListResponseInner>");

            let resp = client.get_open_symbol_list().await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn get_open_symbol_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            match client.get_open_symbol_list().await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn query_user_delegation_history_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = QueryUserDelegationHistoryParams::builder("email_example".to_string(),1623319461670,1641782889000,).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":3316,"rows":[{"clientTranId":"293915932290879488","transferType":"Undelegate","asset":"ETH","amount":"1","time":1695205406000},{"clientTranId":"293915892281413632","transferType":"Delegate","asset":"ETH","amount":"1","time":1695205396000}]}"#).unwrap();
            let expected_response : models::QueryUserDelegationHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryUserDelegationHistoryResponse");

            let resp = client.query_user_delegation_history(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_user_delegation_history_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = QueryUserDelegationHistoryParams::builder("email_example".to_string(),1623319461670,1641782889000,).r#type("r#type_example".to_string()).asset("asset_example".to_string()).current(1).size(10).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":3316,"rows":[{"clientTranId":"293915932290879488","transferType":"Undelegate","asset":"ETH","amount":"1","time":1695205406000},{"clientTranId":"293915892281413632","transferType":"Delegate","asset":"ETH","amount":"1","time":1695205396000}]}"#).unwrap();
            let expected_response : models::QueryUserDelegationHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryUserDelegationHistoryResponse");

            let resp = client.query_user_delegation_history(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_user_delegation_history_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = QueryUserDelegationHistoryParams::builder(
                "email_example".to_string(),
                1623319461670,
                1641782889000,
            )
            .build()
            .unwrap();

            match client.query_user_delegation_history(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn query_user_universal_transfer_history_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = QueryUserUniversalTransferHistoryParams::builder("r#type_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":2,"rows":[{"asset":"USDT","amount":"1","type":"MAIN_UMFUTURE","status":"CONFIRMED","tranId":11415955596,"timestamp":1544433328000},{"asset":"USDT","amount":"2","type":"MAIN_UMFUTURE","status":"CONFIRMED","tranId":11366865406,"timestamp":1544433328000}]}"#).unwrap();
            let expected_response : models::QueryUserUniversalTransferHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryUserUniversalTransferHistoryResponse");

            let resp = client.query_user_universal_transfer_history(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_user_universal_transfer_history_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = QueryUserUniversalTransferHistoryParams::builder("r#type_example".to_string(),).start_time(1623319461670).end_time(1641782889000).current(1).size(10).from_symbol("from_symbol_example".to_string()).to_symbol("to_symbol_example".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":2,"rows":[{"asset":"USDT","amount":"1","type":"MAIN_UMFUTURE","status":"CONFIRMED","tranId":11415955596,"timestamp":1544433328000},{"asset":"USDT","amount":"2","type":"MAIN_UMFUTURE","status":"CONFIRMED","tranId":11366865406,"timestamp":1544433328000}]}"#).unwrap();
            let expected_response : models::QueryUserUniversalTransferHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryUserUniversalTransferHistoryResponse");

            let resp = client.query_user_universal_transfer_history(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_user_universal_transfer_history_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params =
                QueryUserUniversalTransferHistoryParams::builder("r#type_example".to_string())
                    .build()
                    .unwrap();

            match client.query_user_universal_transfer_history(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn query_user_wallet_balance_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = QueryUserWalletBalanceParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"activate":true,"balance":"0","walletName":"Spot"},{"activate":true,"balance":"0","walletName":"Funding"},{"activate":true,"balance":"0","walletName":"Cross Margin"},{"activate":true,"balance":"0","walletName":"Isolated Margin"},{"activate":true,"balance":"0.71842752","walletName":"USDⓈ-M Futures"},{"activate":true,"balance":"0","walletName":"COIN-M Futures"},{"activate":true,"balance":"0","walletName":"Earn"},{"activate":false,"balance":"0","walletName":"Options"},{"activate":true,"balance":"0","walletName":"Trading Bots"},{"activate":true,"balance":"0","walletName":"Copy Trading"}]"#).unwrap();
            let expected_response : Vec<models::QueryUserWalletBalanceResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryUserWalletBalanceResponseInner>");

            let resp = client.query_user_wallet_balance(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_user_wallet_balance_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = QueryUserWalletBalanceParams::builder().quote_asset("BTC".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"activate":true,"balance":"0","walletName":"Spot"},{"activate":true,"balance":"0","walletName":"Funding"},{"activate":true,"balance":"0","walletName":"Cross Margin"},{"activate":true,"balance":"0","walletName":"Isolated Margin"},{"activate":true,"balance":"0.71842752","walletName":"USDⓈ-M Futures"},{"activate":true,"balance":"0","walletName":"COIN-M Futures"},{"activate":true,"balance":"0","walletName":"Earn"},{"activate":false,"balance":"0","walletName":"Options"},{"activate":true,"balance":"0","walletName":"Trading Bots"},{"activate":true,"balance":"0","walletName":"Copy Trading"}]"#).unwrap();
            let expected_response : Vec<models::QueryUserWalletBalanceResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryUserWalletBalanceResponseInner>");

            let resp = client.query_user_wallet_balance(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_user_wallet_balance_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = QueryUserWalletBalanceParams::builder().build().unwrap();

            match client.query_user_wallet_balance(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn toggle_bnb_burn_on_spot_trade_and_margin_interest_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = ToggleBnbBurnOnSpotTradeAndMarginInterestParams::builder()
                .build()
                .unwrap();

            let resp_json: Value =
                serde_json::from_str(r#"{"spotBNBBurn":true,"interestBNBBurn":false}"#).unwrap();
            let expected_response: models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse =
                serde_json::from_value(resp_json.clone()).expect(
                    "should parse into models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse",
                );

            let resp = client
                .toggle_bnb_burn_on_spot_trade_and_margin_interest(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn toggle_bnb_burn_on_spot_trade_and_margin_interest_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = ToggleBnbBurnOnSpotTradeAndMarginInterestParams::builder()
                .spot_bnb_burn("spot_bnb_burn_example".to_string())
                .interest_bnb_burn("interest_bnb_burn_example".to_string())
                .recv_window(5000)
                .build()
                .unwrap();

            let resp_json: Value =
                serde_json::from_str(r#"{"spotBNBBurn":true,"interestBNBBurn":false}"#).unwrap();
            let expected_response: models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse =
                serde_json::from_value(resp_json.clone()).expect(
                    "should parse into models::ToggleBnbBurnOnSpotTradeAndMarginInterestResponse",
                );

            let resp = client
                .toggle_bnb_burn_on_spot_trade_and_margin_interest(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn toggle_bnb_burn_on_spot_trade_and_margin_interest_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = ToggleBnbBurnOnSpotTradeAndMarginInterestParams::builder()
                .build()
                .unwrap();

            match client
                .toggle_bnb_burn_on_spot_trade_and_margin_interest(params)
                .await
            {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn trade_fee_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = TradeFeeParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"ADABNB","makerCommission":"0.001","takerCommission":"0.001"},{"symbol":"BNBBTC","makerCommission":"0.001","takerCommission":"0.001"}]"#).unwrap();
            let expected_response : Vec<models::TradeFeeResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::TradeFeeResponseInner>");

            let resp = client.trade_fee(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn trade_fee_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = TradeFeeParams::builder().symbol("symbol_example".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"symbol":"ADABNB","makerCommission":"0.001","takerCommission":"0.001"},{"symbol":"BNBBTC","makerCommission":"0.001","takerCommission":"0.001"}]"#).unwrap();
            let expected_response : Vec<models::TradeFeeResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::TradeFeeResponseInner>");

            let resp = client.trade_fee(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn trade_fee_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = TradeFeeParams::builder().build().unwrap();

            match client.trade_fee(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn user_asset_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = UserAssetParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"asset":"AVAX","free":"1","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"BCH","free":"0.9","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"BNB","free":"887.47061626","locked":"0","freeze":"10.52","withdrawing":"0.1","ipoable":"0","btcValuation":"0"},{"asset":"BUSD","free":"9999.7","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"SHIB","free":"532.32","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"USDT","free":"50300000001.44911105","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"WRZ","free":"1","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"}]"#).unwrap();
            let expected_response : Vec<models::UserAssetResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::UserAssetResponseInner>");

            let resp = client.user_asset(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn user_asset_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = UserAssetParams::builder().asset("asset_example".to_string()).need_btc_valuation(true).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"asset":"AVAX","free":"1","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"BCH","free":"0.9","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"BNB","free":"887.47061626","locked":"0","freeze":"10.52","withdrawing":"0.1","ipoable":"0","btcValuation":"0"},{"asset":"BUSD","free":"9999.7","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"SHIB","free":"532.32","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"USDT","free":"50300000001.44911105","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"},{"asset":"WRZ","free":"1","locked":"0","freeze":"0","withdrawing":"0","ipoable":"0","btcValuation":"0"}]"#).unwrap();
            let expected_response : Vec<models::UserAssetResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::UserAssetResponseInner>");

            let resp = client.user_asset(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn user_asset_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = UserAssetParams::builder().build().unwrap();

            match client.user_asset(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn user_universal_transfer_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = UserUniversalTransferParams::builder(
                "r#type_example".to_string(),
                "asset_example".to_string(),
                dec!(1.0),
            )
            .build()
            .unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"tranId":13526853623}"#).unwrap();
            let expected_response: models::UserUniversalTransferResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::UserUniversalTransferResponse");

            let resp = client
                .user_universal_transfer(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn user_universal_transfer_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: false };

            let params = UserUniversalTransferParams::builder(
                "r#type_example".to_string(),
                "asset_example".to_string(),
                dec!(1.0),
            )
            .from_symbol("from_symbol_example".to_string())
            .to_symbol("to_symbol_example".to_string())
            .recv_window(5000)
            .build()
            .unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"tranId":13526853623}"#).unwrap();
            let expected_response: models::UserUniversalTransferResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::UserUniversalTransferResponse");

            let resp = client
                .user_universal_transfer(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn user_universal_transfer_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockAssetApiClient { force_error: true };

            let params = UserUniversalTransferParams::builder(
                "r#type_example".to_string(),
                "asset_example".to_string(),
                dec!(1.0),
            )
            .build()
            .unwrap();

            match client.user_universal_transfer(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }
}
