Skip to main content

binance_sdk/stocks/rest_api/apis/
tokenized_api.rs

1/*
2 * Stocks Trading REST API
3 *
4 * REST APIs for Binance Stocks Trading. All endpoints under `/sapi/v1/equity/_*`.
5 *
6 * The version of the OpenAPI document: 1.0.0
7 *
8 *
9 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
10 * https://openapi-generator.tech
11 * Do not edit the class manually.
12 */
13
14#![allow(unused_imports)]
15use async_trait::async_trait;
16use derive_builder::Builder;
17use reqwest;
18use rust_decimal::prelude::*;
19use serde::{Deserialize, Serialize};
20use serde_json::{Value, json};
21use std::collections::BTreeMap;
22
23use crate::common::{
24    config::ConfigurationRestApi,
25    models::{ParamBuildError, RestApiResponse},
26    utils::send_request,
27};
28use crate::stocks::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait TokenizedApi: Send + Sync {
34    async fn tokenized_convert_history(
35        &self,
36        params: TokenizedConvertHistoryParams,
37    ) -> anyhow::Result<RestApiResponse<models::TokenizedConvertHistoryResponse>>;
38    async fn tokenized_convert_status(
39        &self,
40        params: TokenizedConvertStatusParams,
41    ) -> anyhow::Result<RestApiResponse<models::TokenizedConvertStatusResponse>>;
42    async fn tokenized_mint(
43        &self,
44        params: TokenizedMintParams,
45    ) -> anyhow::Result<RestApiResponse<models::TokenizedMintResponse>>;
46    async fn tokenized_redeem(
47        &self,
48        params: TokenizedRedeemParams,
49    ) -> anyhow::Result<RestApiResponse<models::TokenizedRedeemResponse>>;
50}
51
52#[derive(Debug, Clone)]
53pub struct TokenizedApiClient {
54    configuration: ConfigurationRestApi,
55}
56
57impl TokenizedApiClient {
58    pub fn new(configuration: ConfigurationRestApi) -> Self {
59        Self { configuration }
60    }
61}
62
63#[allow(non_camel_case_types)]
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub enum TokenizedConvertStatusConvertTypeEnum {
66    #[serde(rename = "MINT")]
67    Mint,
68    #[serde(rename = "REDEEM")]
69    Redeem,
70}
71
72impl TokenizedConvertStatusConvertTypeEnum {
73    #[must_use]
74    pub fn as_str(&self) -> &'static str {
75        match self {
76            Self::Mint => "MINT",
77            Self::Redeem => "REDEEM",
78        }
79    }
80}
81
82impl std::str::FromStr for TokenizedConvertStatusConvertTypeEnum {
83    type Err = Box<dyn std::error::Error + Send + Sync>;
84
85    fn from_str(s: &str) -> Result<Self, Self::Err> {
86        match s {
87            "MINT" => Ok(Self::Mint),
88            "REDEEM" => Ok(Self::Redeem),
89            other => {
90                Err(format!("invalid TokenizedConvertStatusConvertTypeEnum: {}", other).into())
91            }
92        }
93    }
94}
95
96/// Request parameters for the [`tokenized_convert_history`] operation.
97///
98/// This struct holds all of the inputs you can pass when calling
99/// [`tokenized_convert_history`](#method.tokenized_convert_history).
100#[derive(Clone, Debug, Builder, Deserialize, Default)]
101#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
102pub struct TokenizedConvertHistoryParams {
103    /// Start time (ms epoch).
104    ///
105    /// This field is **optional.
106    #[builder(setter(into), default)]
107    #[serde(rename = "startTime", default)]
108    pub start_time: Option<i64>,
109    /// End time (ms epoch).
110    ///
111    /// This field is **optional.
112    #[builder(setter(into), default)]
113    #[serde(rename = "endTime", default)]
114    pub end_time: Option<i64>,
115    /// Last record id from the previous page. Omit (or leave unset) to fetch the first page.
116    ///
117    /// This field is **optional.
118    #[builder(setter(into), default)]
119    #[serde(rename = "lastId", default)]
120    pub last_id: Option<i64>,
121    /// Page size. Default `20`, max `100`.
122    ///
123    /// This field is **optional.
124    #[builder(setter(into), default)]
125    #[serde(rename = "size", default)]
126    pub size: Option<i32>,
127    /// The value cannot be greater than `60000`.
128    ///
129    /// This field is **optional.
130    #[builder(setter(into), default)]
131    #[serde(rename = "recvWindow", default)]
132    pub recv_window: Option<i64>,
133}
134
135impl TokenizedConvertHistoryParams {
136    /// Create a builder for [`tokenized_convert_history`].
137    ///
138    #[must_use]
139    pub fn builder() -> TokenizedConvertHistoryParamsBuilder {
140        TokenizedConvertHistoryParamsBuilder::default()
141    }
142}
143/// Request parameters for the [`tokenized_convert_status`] operation.
144///
145/// This struct holds all of the inputs you can pass when calling
146/// [`tokenized_convert_status`](#method.tokenized_convert_status).
147#[derive(Clone, Debug, Builder, Deserialize)]
148#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
149pub struct TokenizedConvertStatusParams {
150    /// Convert request id returned by `/tokenized/mint` or `/redeem`.
151    ///
152    /// This field is **required.
153    #[builder(setter(into))]
154    #[serde(rename = "issuerRequestId")]
155    pub issuer_request_id: String,
156    /// `MINT` or `REDEEM`.
157    ///
158    /// This field is **required.
159    #[builder(setter(into))]
160    #[serde(rename = "convertType")]
161    pub convert_type: TokenizedConvertStatusConvertTypeEnum,
162    /// The value cannot be greater than `60000`.
163    ///
164    /// This field is **optional.
165    #[builder(setter(into), default)]
166    #[serde(rename = "recvWindow", default)]
167    pub recv_window: Option<i64>,
168}
169
170impl TokenizedConvertStatusParams {
171    /// Create a builder for [`tokenized_convert_status`].
172    ///
173    /// Required parameters:
174    ///
175    /// * `issuer_request_id` — Convert request id returned by `/tokenized/mint` or `/redeem`.
176    /// * `convert_type` — `MINT` or `REDEEM`.
177    ///
178    #[must_use]
179    pub fn builder(
180        issuer_request_id: String,
181        convert_type: TokenizedConvertStatusConvertTypeEnum,
182    ) -> TokenizedConvertStatusParamsBuilder {
183        TokenizedConvertStatusParamsBuilder::default()
184            .issuer_request_id(issuer_request_id)
185            .convert_type(convert_type)
186    }
187}
188/// Request parameters for the [`tokenized_mint`] operation.
189///
190/// This struct holds all of the inputs you can pass when calling
191/// [`tokenized_mint`](#method.tokenized_mint).
192#[derive(Clone, Debug, Builder, Deserialize)]
193#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
194pub struct TokenizedMintParams {
195    /// Underlying US-equity ticker, e.g. `AAPL`, `TSLA`. Resolved against the active-symbol list; unknown tickers return `-26004`. The target tokenized asset is looked up from this field via `/market/tokenized-assets`.
196    ///
197    /// This field is **required.
198    #[builder(setter(into))]
199    #[serde(rename = "underlyingAsset")]
200    pub underlying_asset: String,
201    /// Quantity of the underlying asset to mint from. Must be > 0.
202    ///
203    /// This field is **required.
204    #[builder(setter(into))]
205    #[serde(rename = "underlyingAssetAmount")]
206    pub underlying_asset_amount: String,
207    /// Client order id for idempotency. Format `^[a-zA-Z0-9-_]{32,36}$`. Auto-generated when omitted.
208    ///
209    /// This field is **optional.
210    #[builder(setter(into), default)]
211    #[serde(rename = "clientOrderId", default)]
212    pub client_order_id: Option<String>,
213    /// The value cannot be greater than `60000`.
214    ///
215    /// This field is **optional.
216    #[builder(setter(into), default)]
217    #[serde(rename = "recvWindow", default)]
218    pub recv_window: Option<i64>,
219}
220
221impl TokenizedMintParams {
222    /// Create a builder for [`tokenized_mint`].
223    ///
224    /// Required parameters:
225    ///
226    /// * `underlying_asset` — Underlying US-equity ticker, e.g. `AAPL`, `TSLA`. Resolved against the active-symbol list; unknown tickers return `-26004`. The target tokenized asset is looked up from this field via `/market/tokenized-assets`.
227    /// * `underlying_asset_amount` — Quantity of the underlying asset to mint from. Must be > 0.
228    ///
229    #[must_use]
230    pub fn builder(
231        underlying_asset: String,
232        underlying_asset_amount: String,
233    ) -> TokenizedMintParamsBuilder {
234        TokenizedMintParamsBuilder::default()
235            .underlying_asset(underlying_asset)
236            .underlying_asset_amount(underlying_asset_amount)
237    }
238}
239/// Request parameters for the [`tokenized_redeem`] operation.
240///
241/// This struct holds all of the inputs you can pass when calling
242/// [`tokenized_redeem`](#method.tokenized_redeem).
243#[derive(Clone, Debug, Builder, Deserialize)]
244#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
245pub struct TokenizedRedeemParams {
246    /// Tokenized asset to redeem, e.g. `AAPLB`. Not a US-equity ticker — this is the on-chain tokenized asset identifier. Unknown asset returns `-1102` (the message currently says the parameter was empty/malformed, but it was in fact sent — it is simply unknown). The target underlying ticker is looked up from this field via `/market/tokenized-assets`.
247    ///
248    /// This field is **required.
249    #[builder(setter(into))]
250    #[serde(rename = "tokenizedAsset")]
251    pub tokenized_asset: String,
252    /// Quantity of the tokenized asset to redeem. Must be > 0.
253    ///
254    /// This field is **required.
255    #[builder(setter(into))]
256    #[serde(rename = "tokenizedAssetAmount")]
257    pub tokenized_asset_amount: String,
258    /// Client order id for idempotency. Format `^[a-zA-Z0-9-_]{32,36}$`. Auto-generated when omitted.
259    ///
260    /// This field is **optional.
261    #[builder(setter(into), default)]
262    #[serde(rename = "clientOrderId", default)]
263    pub client_order_id: Option<String>,
264    /// The value cannot be greater than `60000`.
265    ///
266    /// This field is **optional.
267    #[builder(setter(into), default)]
268    #[serde(rename = "recvWindow", default)]
269    pub recv_window: Option<i64>,
270}
271
272impl TokenizedRedeemParams {
273    /// Create a builder for [`tokenized_redeem`].
274    ///
275    /// Required parameters:
276    ///
277    /// * `tokenized_asset` — Tokenized asset to redeem, e.g. `AAPLB`. Not a US-equity ticker — this is the on-chain tokenized asset identifier. Unknown asset returns `-1102` (the message currently says the parameter was empty/malformed, but it was in fact sent — it is simply unknown). The target underlying ticker is looked up from this field via `/market/tokenized-assets`.
278    /// * `tokenized_asset_amount` — Quantity of the tokenized asset to redeem. Must be > 0.
279    ///
280    #[must_use]
281    pub fn builder(
282        tokenized_asset: String,
283        tokenized_asset_amount: String,
284    ) -> TokenizedRedeemParamsBuilder {
285        TokenizedRedeemParamsBuilder::default()
286            .tokenized_asset(tokenized_asset)
287            .tokenized_asset_amount(tokenized_asset_amount)
288    }
289}
290
291#[async_trait]
292impl TokenizedApi for TokenizedApiClient {
293    async fn tokenized_convert_history(
294        &self,
295        params: TokenizedConvertHistoryParams,
296    ) -> anyhow::Result<RestApiResponse<models::TokenizedConvertHistoryResponse>> {
297        let TokenizedConvertHistoryParams {
298            start_time,
299            end_time,
300            last_id,
301            size,
302            recv_window,
303        } = params;
304
305        let mut query_params = BTreeMap::new();
306        let body_params = BTreeMap::new();
307
308        if let Some(rw) = start_time {
309            query_params.insert("startTime".to_string(), json!(rw));
310        }
311
312        if let Some(rw) = end_time {
313            query_params.insert("endTime".to_string(), json!(rw));
314        }
315
316        if let Some(rw) = last_id {
317            query_params.insert("lastId".to_string(), json!(rw));
318        }
319
320        if let Some(rw) = size {
321            query_params.insert("size".to_string(), json!(rw));
322        }
323
324        if let Some(rw) = recv_window {
325            query_params.insert("recvWindow".to_string(), json!(rw));
326        }
327
328        send_request::<models::TokenizedConvertHistoryResponse>(
329            &self.configuration,
330            "/sapi/v1/equity/tokenized/history",
331            reqwest::Method::GET,
332            query_params,
333            body_params,
334            if HAS_TIME_UNIT {
335                self.configuration.time_unit
336            } else {
337                None
338            },
339            true,
340        )
341        .await
342    }
343
344    async fn tokenized_convert_status(
345        &self,
346        params: TokenizedConvertStatusParams,
347    ) -> anyhow::Result<RestApiResponse<models::TokenizedConvertStatusResponse>> {
348        let TokenizedConvertStatusParams {
349            issuer_request_id,
350            convert_type,
351            recv_window,
352        } = params;
353
354        let mut query_params = BTreeMap::new();
355        let body_params = BTreeMap::new();
356
357        query_params.insert("issuerRequestId".to_string(), json!(issuer_request_id));
358
359        query_params.insert("convertType".to_string(), json!(convert_type));
360
361        if let Some(rw) = recv_window {
362            query_params.insert("recvWindow".to_string(), json!(rw));
363        }
364
365        send_request::<models::TokenizedConvertStatusResponse>(
366            &self.configuration,
367            "/sapi/v1/equity/tokenized/convert-status",
368            reqwest::Method::GET,
369            query_params,
370            body_params,
371            if HAS_TIME_UNIT {
372                self.configuration.time_unit
373            } else {
374                None
375            },
376            true,
377        )
378        .await
379    }
380
381    async fn tokenized_mint(
382        &self,
383        params: TokenizedMintParams,
384    ) -> anyhow::Result<RestApiResponse<models::TokenizedMintResponse>> {
385        let TokenizedMintParams {
386            underlying_asset,
387            underlying_asset_amount,
388            client_order_id,
389            recv_window,
390        } = params;
391
392        let mut query_params = BTreeMap::new();
393        let body_params = BTreeMap::new();
394
395        query_params.insert("underlyingAsset".to_string(), json!(underlying_asset));
396
397        query_params.insert(
398            "underlyingAssetAmount".to_string(),
399            json!(underlying_asset_amount),
400        );
401
402        if let Some(rw) = client_order_id {
403            query_params.insert("clientOrderId".to_string(), json!(rw));
404        }
405
406        if let Some(rw) = recv_window {
407            query_params.insert("recvWindow".to_string(), json!(rw));
408        }
409
410        send_request::<models::TokenizedMintResponse>(
411            &self.configuration,
412            "/sapi/v1/equity/tokenized/mint",
413            reqwest::Method::POST,
414            query_params,
415            body_params,
416            if HAS_TIME_UNIT {
417                self.configuration.time_unit
418            } else {
419                None
420            },
421            true,
422        )
423        .await
424    }
425
426    async fn tokenized_redeem(
427        &self,
428        params: TokenizedRedeemParams,
429    ) -> anyhow::Result<RestApiResponse<models::TokenizedRedeemResponse>> {
430        let TokenizedRedeemParams {
431            tokenized_asset,
432            tokenized_asset_amount,
433            client_order_id,
434            recv_window,
435        } = params;
436
437        let mut query_params = BTreeMap::new();
438        let body_params = BTreeMap::new();
439
440        query_params.insert("tokenizedAsset".to_string(), json!(tokenized_asset));
441
442        query_params.insert(
443            "tokenizedAssetAmount".to_string(),
444            json!(tokenized_asset_amount),
445        );
446
447        if let Some(rw) = client_order_id {
448            query_params.insert("clientOrderId".to_string(), json!(rw));
449        }
450
451        if let Some(rw) = recv_window {
452            query_params.insert("recvWindow".to_string(), json!(rw));
453        }
454
455        send_request::<models::TokenizedRedeemResponse>(
456            &self.configuration,
457            "/sapi/v1/equity/tokenized/redeem",
458            reqwest::Method::POST,
459            query_params,
460            body_params,
461            if HAS_TIME_UNIT {
462                self.configuration.time_unit
463            } else {
464                None
465            },
466            true,
467        )
468        .await
469    }
470}
471
472#[cfg(all(test, feature = "stocks"))]
473mod tests {
474    use super::*;
475    use crate::TOKIO_SHARED_RT;
476    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
477    use async_trait::async_trait;
478    use std::collections::HashMap;
479
480    struct DummyRestApiResponse<T> {
481        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
482        status: u16,
483        headers: HashMap<String, String>,
484        rate_limits: Option<Vec<RestApiRateLimit>>,
485    }
486
487    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
488        fn from(dummy: DummyRestApiResponse<T>) -> Self {
489            Self {
490                data_fn: dummy.inner,
491                status: dummy.status,
492                headers: dummy.headers,
493                rate_limits: dummy.rate_limits,
494            }
495        }
496    }
497
498    struct MockTokenizedApiClient {
499        force_error: bool,
500    }
501
502    #[async_trait]
503    impl TokenizedApi for MockTokenizedApiClient {
504        async fn tokenized_convert_history(
505            &self,
506            _params: TokenizedConvertHistoryParams,
507        ) -> anyhow::Result<RestApiResponse<models::TokenizedConvertHistoryResponse>> {
508            if self.force_error {
509                return Err(ConnectorError::ConnectorClientError {
510                    msg: "ResponseError".to_string(),
511                    code: None,
512                }
513                .into());
514            }
515
516            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"underlyingAsset":"AAPL","underlyingAssetAmount":"1.000000000","tokenizedAsset":"AAPLB","tokenizedAssetAmount":"1.000000000","issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","convertType":"MINT","status":"S","createdAt":1735900000000,"updatedAt":1735900060000}],"hasMore":true,"nextLastId":10020}"#).unwrap_or_else(|_| serde_json::json!({}));
517            let dummy_response: models::TokenizedConvertHistoryResponse =
518                serde_json::from_value(resp_json.clone())
519                    .expect("should parse into models::TokenizedConvertHistoryResponse");
520
521            let dummy = DummyRestApiResponse {
522                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
523                status: 200,
524                headers: HashMap::new(),
525                rate_limits: None,
526            };
527
528            Ok(dummy.into())
529        }
530
531        async fn tokenized_convert_status(
532            &self,
533            _params: TokenizedConvertStatusParams,
534        ) -> anyhow::Result<RestApiResponse<models::TokenizedConvertStatusResponse>> {
535            if self.force_error {
536                return Err(ConnectorError::ConnectorClientError {
537                    msg: "ResponseError".to_string(),
538                    code: None,
539                }
540                .into());
541            }
542
543            let resp_json: Value = serde_json::from_str(r#"{"underlyingAsset":"AAPL","underlyingAssetAmount":"1.000000000","tokenizedAsset":"AAPLB","tokenizedAssetAmount":"1.000000000","issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","convertType":"MINT","status":"S","createdAt":1735900000000,"updatedAt":1735900060000}"#).unwrap_or_else(|_| serde_json::json!({}));
544            let dummy_response: models::TokenizedConvertStatusResponse =
545                serde_json::from_value(resp_json.clone())
546                    .expect("should parse into models::TokenizedConvertStatusResponse");
547
548            let dummy = DummyRestApiResponse {
549                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
550                status: 200,
551                headers: HashMap::new(),
552                rate_limits: None,
553            };
554
555            Ok(dummy.into())
556        }
557
558        async fn tokenized_mint(
559            &self,
560            _params: TokenizedMintParams,
561        ) -> anyhow::Result<RestApiResponse<models::TokenizedMintResponse>> {
562            if self.force_error {
563                return Err(ConnectorError::ConnectorClientError {
564                    msg: "ResponseError".to_string(),
565                    code: None,
566                }
567                .into());
568            }
569
570            let resp_json: Value = serde_json::from_str(
571                r#"{"issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","status":"P"}"#,
572            )
573            .unwrap_or_else(|_| serde_json::json!({}));
574            let dummy_response: models::TokenizedMintResponse =
575                serde_json::from_value(resp_json.clone())
576                    .expect("should parse into models::TokenizedMintResponse");
577
578            let dummy = DummyRestApiResponse {
579                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
580                status: 200,
581                headers: HashMap::new(),
582                rate_limits: None,
583            };
584
585            Ok(dummy.into())
586        }
587
588        async fn tokenized_redeem(
589            &self,
590            _params: TokenizedRedeemParams,
591        ) -> anyhow::Result<RestApiResponse<models::TokenizedRedeemResponse>> {
592            if self.force_error {
593                return Err(ConnectorError::ConnectorClientError {
594                    msg: "ResponseError".to_string(),
595                    code: None,
596                }
597                .into());
598            }
599
600            let resp_json: Value = serde_json::from_str(
601                r#"{"issuerRequestId":"redeem-20260505-a1b2c3d4e5f6","status":"P"}"#,
602            )
603            .unwrap_or_else(|_| serde_json::json!({}));
604            let dummy_response: models::TokenizedRedeemResponse =
605                serde_json::from_value(resp_json.clone())
606                    .expect("should parse into models::TokenizedRedeemResponse");
607
608            let dummy = DummyRestApiResponse {
609                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
610                status: 200,
611                headers: HashMap::new(),
612                rate_limits: None,
613            };
614
615            Ok(dummy.into())
616        }
617    }
618
619    #[test]
620    fn tokenized_convert_history_required_params_success() {
621        TOKIO_SHARED_RT.block_on(async {
622            let client = MockTokenizedApiClient { force_error: false };
623
624            let params = TokenizedConvertHistoryParams::builder().build().unwrap();
625
626            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"underlyingAsset":"AAPL","underlyingAssetAmount":"1.000000000","tokenizedAsset":"AAPLB","tokenizedAssetAmount":"1.000000000","issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","convertType":"MINT","status":"S","createdAt":1735900000000,"updatedAt":1735900060000}],"hasMore":true,"nextLastId":10020}"#).unwrap_or_else(|_| serde_json::json!({}));
627            let expected_response : models::TokenizedConvertHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::TokenizedConvertHistoryResponse");
628
629            let resp = client.tokenized_convert_history(params).await.expect("Expected a response");
630            let data_future = resp.data();
631            let actual_response = data_future.await.unwrap();
632            assert_eq!(actual_response, expected_response);
633        });
634    }
635
636    #[test]
637    fn tokenized_convert_history_optional_params_success() {
638        TOKIO_SHARED_RT.block_on(async {
639            let client = MockTokenizedApiClient { force_error: false };
640
641            let params = TokenizedConvertHistoryParams::builder().start_time(1735800000000).end_time(1735900000000).last_id(10019).size(20).recv_window(5000).build().unwrap();
642
643            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"underlyingAsset":"AAPL","underlyingAssetAmount":"1.000000000","tokenizedAsset":"AAPLB","tokenizedAssetAmount":"1.000000000","issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","convertType":"MINT","status":"S","createdAt":1735900000000,"updatedAt":1735900060000}],"hasMore":true,"nextLastId":10020}"#).unwrap_or_else(|_| serde_json::json!({}));
644            let expected_response : models::TokenizedConvertHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::TokenizedConvertHistoryResponse");
645
646            let resp = client.tokenized_convert_history(params).await.expect("Expected a response");
647            let data_future = resp.data();
648            let actual_response = data_future.await.unwrap();
649            assert_eq!(actual_response, expected_response);
650        });
651    }
652
653    #[test]
654    fn tokenized_convert_history_response_error() {
655        TOKIO_SHARED_RT.block_on(async {
656            let client = MockTokenizedApiClient { force_error: true };
657
658            let params = TokenizedConvertHistoryParams::builder().build().unwrap();
659
660            match client.tokenized_convert_history(params).await {
661                Ok(_) => panic!("Expected an error"),
662                Err(err) => {
663                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
664                }
665            }
666        });
667    }
668
669    #[test]
670    fn tokenized_convert_status_required_params_success() {
671        TOKIO_SHARED_RT.block_on(async {
672            let client = MockTokenizedApiClient { force_error: false };
673
674            let params = TokenizedConvertStatusParams::builder("mint-20260505-8f3b9e1a2d3c4b5a".to_string(),TokenizedConvertStatusConvertTypeEnum::Mint,).build().unwrap();
675
676            let resp_json: Value = serde_json::from_str(r#"{"underlyingAsset":"AAPL","underlyingAssetAmount":"1.000000000","tokenizedAsset":"AAPLB","tokenizedAssetAmount":"1.000000000","issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","convertType":"MINT","status":"S","createdAt":1735900000000,"updatedAt":1735900060000}"#).unwrap_or_else(|_| serde_json::json!({}));
677            let expected_response : models::TokenizedConvertStatusResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::TokenizedConvertStatusResponse");
678
679            let resp = client.tokenized_convert_status(params).await.expect("Expected a response");
680            let data_future = resp.data();
681            let actual_response = data_future.await.unwrap();
682            assert_eq!(actual_response, expected_response);
683        });
684    }
685
686    #[test]
687    fn tokenized_convert_status_optional_params_success() {
688        TOKIO_SHARED_RT.block_on(async {
689            let client = MockTokenizedApiClient { force_error: false };
690
691            let params = TokenizedConvertStatusParams::builder("mint-20260505-8f3b9e1a2d3c4b5a".to_string(),TokenizedConvertStatusConvertTypeEnum::Mint,).recv_window(5000).build().unwrap();
692
693            let resp_json: Value = serde_json::from_str(r#"{"underlyingAsset":"AAPL","underlyingAssetAmount":"1.000000000","tokenizedAsset":"AAPLB","tokenizedAssetAmount":"1.000000000","issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","convertType":"MINT","status":"S","createdAt":1735900000000,"updatedAt":1735900060000}"#).unwrap_or_else(|_| serde_json::json!({}));
694            let expected_response : models::TokenizedConvertStatusResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::TokenizedConvertStatusResponse");
695
696            let resp = client.tokenized_convert_status(params).await.expect("Expected a response");
697            let data_future = resp.data();
698            let actual_response = data_future.await.unwrap();
699            assert_eq!(actual_response, expected_response);
700        });
701    }
702
703    #[test]
704    fn tokenized_convert_status_response_error() {
705        TOKIO_SHARED_RT.block_on(async {
706            let client = MockTokenizedApiClient { force_error: true };
707
708            let params = TokenizedConvertStatusParams::builder(
709                "mint-20260505-8f3b9e1a2d3c4b5a".to_string(),
710                TokenizedConvertStatusConvertTypeEnum::Mint,
711            )
712            .build()
713            .unwrap();
714
715            match client.tokenized_convert_status(params).await {
716                Ok(_) => panic!("Expected an error"),
717                Err(err) => {
718                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
719                }
720            }
721        });
722    }
723
724    #[test]
725    fn tokenized_mint_required_params_success() {
726        TOKIO_SHARED_RT.block_on(async {
727            let client = MockTokenizedApiClient { force_error: false };
728
729            let params = TokenizedMintParams::builder("AAPL".to_string(), "1".to_string())
730                .build()
731                .unwrap();
732
733            let resp_json: Value = serde_json::from_str(
734                r#"{"issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","status":"P"}"#,
735            )
736            .unwrap_or_else(|_| serde_json::json!({}));
737            let expected_response: models::TokenizedMintResponse =
738                serde_json::from_value(resp_json.clone())
739                    .expect("should parse into models::TokenizedMintResponse");
740
741            let resp = client
742                .tokenized_mint(params)
743                .await
744                .expect("Expected a response");
745            let data_future = resp.data();
746            let actual_response = data_future.await.unwrap();
747            assert_eq!(actual_response, expected_response);
748        });
749    }
750
751    #[test]
752    fn tokenized_mint_optional_params_success() {
753        TOKIO_SHARED_RT.block_on(async {
754            let client = MockTokenizedApiClient { force_error: false };
755
756            let params = TokenizedMintParams::builder("AAPL".to_string(), "1".to_string())
757                .client_order_id("mint-client-id-32chars-0000000001".to_string())
758                .recv_window(5000)
759                .build()
760                .unwrap();
761
762            let resp_json: Value = serde_json::from_str(
763                r#"{"issuerRequestId":"mint-20260505-8f3b9e1a2d3c4b5a","status":"P"}"#,
764            )
765            .unwrap_or_else(|_| serde_json::json!({}));
766            let expected_response: models::TokenizedMintResponse =
767                serde_json::from_value(resp_json.clone())
768                    .expect("should parse into models::TokenizedMintResponse");
769
770            let resp = client
771                .tokenized_mint(params)
772                .await
773                .expect("Expected a response");
774            let data_future = resp.data();
775            let actual_response = data_future.await.unwrap();
776            assert_eq!(actual_response, expected_response);
777        });
778    }
779
780    #[test]
781    fn tokenized_mint_response_error() {
782        TOKIO_SHARED_RT.block_on(async {
783            let client = MockTokenizedApiClient { force_error: true };
784
785            let params = TokenizedMintParams::builder("AAPL".to_string(), "1".to_string())
786                .build()
787                .unwrap();
788
789            match client.tokenized_mint(params).await {
790                Ok(_) => panic!("Expected an error"),
791                Err(err) => {
792                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
793                }
794            }
795        });
796    }
797
798    #[test]
799    fn tokenized_redeem_required_params_success() {
800        TOKIO_SHARED_RT.block_on(async {
801            let client = MockTokenizedApiClient { force_error: false };
802
803            let params = TokenizedRedeemParams::builder("AAPLB".to_string(), "1".to_string())
804                .build()
805                .unwrap();
806
807            let resp_json: Value = serde_json::from_str(
808                r#"{"issuerRequestId":"redeem-20260505-a1b2c3d4e5f6","status":"P"}"#,
809            )
810            .unwrap_or_else(|_| serde_json::json!({}));
811            let expected_response: models::TokenizedRedeemResponse =
812                serde_json::from_value(resp_json.clone())
813                    .expect("should parse into models::TokenizedRedeemResponse");
814
815            let resp = client
816                .tokenized_redeem(params)
817                .await
818                .expect("Expected a response");
819            let data_future = resp.data();
820            let actual_response = data_future.await.unwrap();
821            assert_eq!(actual_response, expected_response);
822        });
823    }
824
825    #[test]
826    fn tokenized_redeem_optional_params_success() {
827        TOKIO_SHARED_RT.block_on(async {
828            let client = MockTokenizedApiClient { force_error: false };
829
830            let params = TokenizedRedeemParams::builder("AAPLB".to_string(), "1".to_string())
831                .client_order_id("redeem-client-id-32chars-000000001".to_string())
832                .recv_window(5000)
833                .build()
834                .unwrap();
835
836            let resp_json: Value = serde_json::from_str(
837                r#"{"issuerRequestId":"redeem-20260505-a1b2c3d4e5f6","status":"P"}"#,
838            )
839            .unwrap_or_else(|_| serde_json::json!({}));
840            let expected_response: models::TokenizedRedeemResponse =
841                serde_json::from_value(resp_json.clone())
842                    .expect("should parse into models::TokenizedRedeemResponse");
843
844            let resp = client
845                .tokenized_redeem(params)
846                .await
847                .expect("Expected a response");
848            let data_future = resp.data();
849            let actual_response = data_future.await.unwrap();
850            assert_eq!(actual_response, expected_response);
851        });
852    }
853
854    #[test]
855    fn tokenized_redeem_response_error() {
856        TOKIO_SHARED_RT.block_on(async {
857            let client = MockTokenizedApiClient { force_error: true };
858
859            let params = TokenizedRedeemParams::builder("AAPLB".to_string(), "1".to_string())
860                .build()
861                .unwrap();
862
863            match client.tokenized_redeem(params).await {
864                Ok(_) => panic!("Expected an error"),
865                Err(err) => {
866                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
867                }
868            }
869        });
870    }
871}