diff --git a/Cargo.lock b/Cargo.lock index ec7389f255..b20151bda6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8707,6 +8707,7 @@ dependencies = [ "mm2_err_handle", "mm2_net", "mm2_number", + "paste", "serde", "serde_derive", "serde_json", diff --git a/mm2src/mm2_main/src/ext_api.rs b/mm2src/mm2_main/src/ext_api.rs index fbeed01e06..f1b92c145f 100644 --- a/mm2src/mm2_main/src/ext_api.rs +++ b/mm2src/mm2_main/src/ext_api.rs @@ -1,4 +1,3 @@ -/// RPCs to integrate with external third party trading API +//! RPCs for integration with external third party trading APIs. -/// RPCs to access 1inch eth-like swap API pub mod one_inch; diff --git a/mm2src/mm2_main/src/ext_api/one_inch.rs b/mm2src/mm2_main/src/ext_api/one_inch.rs index 21ca1216e4..3d47853294 100644 --- a/mm2src/mm2_main/src/ext_api/one_inch.rs +++ b/mm2src/mm2_main/src/ext_api/one_inch.rs @@ -1,4 +1,5 @@ +//! RPC implementation for integration with 1inch swap API provider. + pub mod errors; pub mod rpcs; -/// RPC imlementation to integrate 1inch api pub mod types; diff --git a/mm2src/mm2_main/src/ext_api/one_inch/errors.rs b/mm2src/mm2_main/src/ext_api/one_inch/errors.rs index 23b4fa552a..1e1eebc890 100644 --- a/mm2src/mm2_main/src/ext_api/one_inch/errors.rs +++ b/mm2src/mm2_main/src/ext_api/one_inch/errors.rs @@ -10,7 +10,7 @@ use trading_api::one_inch_api::errors::ApiClientError; #[serde(tag = "error_type", content = "error_data")] pub enum ApiIntegrationRpcError { #[from_stringify("coins::CoinFindError")] - CoinFindError(String), + NoSuchCoin(String), #[display(fmt = "EVM token needed")] CoinTypeError, #[display(fmt = "NFT not supported")] @@ -33,8 +33,8 @@ pub enum ApiIntegrationRpcError { impl HttpStatusCode for ApiIntegrationRpcError { fn status_code(&self) -> StatusCode { match self { - ApiIntegrationRpcError::CoinFindError(_) - | ApiIntegrationRpcError::CoinTypeError + ApiIntegrationRpcError::NoSuchCoin { .. } => StatusCode::NOT_FOUND, + ApiIntegrationRpcError::CoinTypeError | ApiIntegrationRpcError::NftNotSupported | ApiIntegrationRpcError::ChainNotSupported | ApiIntegrationRpcError::MyAddressError(_) diff --git a/mm2src/mm2_main/src/ext_api/one_inch/rpcs.rs b/mm2src/mm2_main/src/ext_api/one_inch/rpcs.rs index c2147b814f..bc7059a9da 100644 --- a/mm2src/mm2_main/src/ext_api/one_inch/rpcs.rs +++ b/mm2src/mm2_main/src/ext_api/one_inch/rpcs.rs @@ -29,7 +29,7 @@ pub async fn one_inch_v6_0_classic_swap_quote_rpc( let (base, base_contract) = get_coin_for_one_inch(&ctx, &req.base).await?; api_supports_coin(&base)?; let (rel, rel_contract) = get_coin_for_one_inch(&ctx, &req.rel).await?; - let sell_amount = wei_from_big_decimal(&req.amount, base.decimals()) + let sell_amount = wei_from_big_decimal(&req.amount.to_decimal(), base.decimals()) .mm_err(|err| ApiIntegrationRpcError::InvalidParam(err.to_string()))?; let query_params = ClassicSwapQuoteParams::new(base_contract, rel_contract, sell_amount.to_string()) .with_fee(req.fee) @@ -39,21 +39,9 @@ pub async fn one_inch_v6_0_classic_swap_quote_rpc( .with_parts(req.parts) .with_main_route_parts(req.main_route_parts) .with_gas_limit(req.gas_limit) - .with_include_tokens_info(if let Some(include_tokens_info) = req.include_tokens_info { - Some(include_tokens_info) - } else { - Some(INCLUDE_TOKENS_INFO) - }) - .with_include_protocols(if let Some(include_protocols) = req.include_protocols { - Some(include_protocols) - } else { - Some(INCLUDE_PROTOCOLS) - }) - .with_include_gas(if let Some(include_gas) = req.include_gas { - Some(include_gas) - } else { - Some(INCLUDE_GAS) - }) + .with_include_tokens_info(Some(req.include_tokens_info.unwrap_or(INCLUDE_TOKENS_INFO))) + .with_include_protocols(Some(req.include_protocols.unwrap_or(INCLUDE_PROTOCOLS))) + .with_include_gas(Some(req.include_gas.unwrap_or(INCLUDE_GAS))) .with_connector_tokens(req.connector_tokens) .build_query_params() .mm_err(|api_err| ApiIntegrationRpcError::from_api_error(api_err, base.decimals()))?; @@ -66,7 +54,9 @@ pub async fn one_inch_v6_0_classic_swap_quote_rpc( .mm_err(|err| ApiIntegrationRpcError::ApiDataError(err.to_string())) } -/// "1inch_classic_swap_create" rpc impl +/// "1inch_classic_swap_create" rpc implementation +/// This rpc actually returns a transaction to call the 1inch swap aggregation contract. GUI should sign it and send to the chain. +/// We don't verify the transaction in any way and trust the 1inch api. pub async fn one_inch_v6_0_classic_swap_create_rpc( ctx: MmArc, req: ClassicSwapCreateRequest, @@ -75,7 +65,7 @@ pub async fn one_inch_v6_0_classic_swap_create_rpc( api_supports_coin(&base)?; let (_, rel_contract) = get_coin_for_one_inch(&ctx, &req.rel).await?; - let sell_amount = wei_from_big_decimal(&req.amount, base.decimals()) + let sell_amount = wei_from_big_decimal(&req.amount.to_decimal(), base.decimals()) .mm_err(|err| ApiIntegrationRpcError::InvalidParam(err.to_string()))?; let single_address = base.derivation_method().single_addr_or_err().await?; @@ -93,21 +83,9 @@ pub async fn one_inch_v6_0_classic_swap_create_rpc( .with_parts(req.parts) .with_main_route_parts(req.main_route_parts) .with_gas_limit(req.gas_limit) - .with_include_tokens_info(if let Some(include_tokens_info) = req.include_tokens_info { - Some(include_tokens_info) - } else { - Some(INCLUDE_TOKENS_INFO) - }) - .with_include_protocols(if let Some(include_protocols) = req.include_protocols { - Some(include_protocols) - } else { - Some(INCLUDE_PROTOCOLS) - }) - .with_include_gas(if let Some(include_gas) = req.include_gas { - Some(include_gas) - } else { - Some(INCLUDE_GAS) - }) + .with_include_tokens_info(Some(req.include_tokens_info.unwrap_or(INCLUDE_TOKENS_INFO))) + .with_include_protocols(Some(req.include_protocols.unwrap_or(INCLUDE_PROTOCOLS))) + .with_include_gas(Some(req.include_gas.unwrap_or(INCLUDE_GAS))) .with_connector_tokens(req.connector_tokens) .with_permit(req.permit) .with_receiver(req.receiver) @@ -133,7 +111,7 @@ async fn get_coin_for_one_inch(ctx: &MmArc, ticker: &str) -> MmResult<(EthCoin, let contract = match coin.coin_type { EthCoinType::Eth => ApiClient::eth_special_contract().to_owned(), EthCoinType::Erc20 { token_addr, .. } => display_eth_address(&token_addr), - EthCoinType::Nft { .. } => return Err(MmError::new(ApiIntegrationRpcError::CoinTypeError)), + EthCoinType::Nft { .. } => return Err(MmError::new(ApiIntegrationRpcError::NftNotSupported)), }; Ok((coin, contract)) } diff --git a/mm2src/mm2_main/src/ext_api/one_inch/types.rs b/mm2src/mm2_main/src/ext_api/one_inch/types.rs index 7ccb107965..198fbbdd34 100644 --- a/mm2src/mm2_main/src/ext_api/one_inch/types.rs +++ b/mm2src/mm2_main/src/ext_api/one_inch/types.rs @@ -2,10 +2,11 @@ use crate::ext_api::one_inch::errors::FromApiValueError; use coins::eth::{u256_to_big_decimal, wei_to_gwei_decimal}; use ethereum_types::{Address, U256}; use mm2_err_handle::prelude::*; -use mm2_number::BigDecimal; +use mm2_number::{BigDecimal, MmNumber}; use rpc::v1::types::Bytes as BytesJson; use serde::{Deserialize, Serialize}; -use trading_api::one_inch_api; +use trading_api::one_inch_api::{self, + types::{ProtocolInfo, TokenInfo}}; #[derive(Clone, Debug, Deserialize)] pub struct AggregationContractRequest {} @@ -14,8 +15,7 @@ pub struct AggregationContractRequest {} pub struct ClassicSwapQuoteRequest { pub base: String, pub rel: String, - pub amount: BigDecimal, - + pub amount: MmNumber, // Optional fields pub fee: Option, pub protocols: Option, @@ -24,7 +24,6 @@ pub struct ClassicSwapQuoteRequest { pub parts: Option, pub main_route_parts: Option, pub gas_limit: Option, - pub include_tokens_info: Option, pub include_protocols: Option, pub include_gas: Option, @@ -35,9 +34,8 @@ pub struct ClassicSwapQuoteRequest { pub struct ClassicSwapCreateRequest { pub base: String, pub rel: String, - pub amount: BigDecimal, + pub amount: MmNumber, pub slippage: f32, - // Optional fields pub fee: Option, pub protocols: Option, @@ -53,7 +51,6 @@ pub struct ClassicSwapCreateRequest { pub permit: Option, pub receiver: Option, pub referrer: Option, - /// Disable gas estimation pub disable_estimate: Option, /// Allow the swap to be partially filled @@ -62,7 +59,7 @@ pub struct ClassicSwapCreateRequest { #[derive(Serialize, Debug)] pub struct ClassicSwapResponse { - pub dst_amount: BigDecimal, + pub dst_amount: MmNumber, #[serde(skip_serializing_if = "Option::is_none")] pub src_token: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -80,10 +77,10 @@ impl ClassicSwapResponse { decimals: u8, ) -> MmResult { Ok(Self { - dst_amount: u256_to_big_decimal(U256::from_dec_str(&data.dst_amount)?, decimals)?, - src_token: TokenInfo::from_api_value(data.src_token), - dst_token: TokenInfo::from_api_value(data.dst_token), - protocols: ProtocolInfo::from_api_value(data.protocols), + dst_amount: MmNumber::from(u256_to_big_decimal(U256::from_dec_str(&data.dst_amount)?, decimals)?), + src_token: data.src_token, + dst_token: data.dst_token, + protocols: data.protocols, tx: data.tx.map(|tx| TxFields::from_api_value(tx, decimals)).transpose()?, gas: data.gas, }) @@ -103,7 +100,7 @@ pub struct TxFields { impl TxFields { pub(crate) fn from_api_value( - tx_fields: trading_api::one_inch_api::types::TxFields, + tx_fields: one_inch_api::types::TxFields, decimals: u8, ) -> MmResult { Ok(Self { @@ -116,63 +113,3 @@ impl TxFields { }) } } - -#[derive(Serialize, Debug)] -pub struct TokenInfo { - pub address: Address, - pub symbol: String, - pub name: String, - pub decimals: u32, - pub eip2612: bool, - pub is_fot: bool, - pub logo_uri: String, - pub tags: Vec, -} - -impl TokenInfo { - pub(crate) fn from_api_value(opt_info: Option) -> Option { - opt_info.map(|info| Self { - address: info.address, - symbol: info.symbol, - name: info.name, - decimals: info.decimals, - eip2612: info.eip2612, - is_fot: info.is_fot, - logo_uri: info.logo_uri, - tags: info.tags, - }) - } -} - -#[derive(Debug, Serialize)] -pub struct ProtocolInfo { - pub name: String, - pub part: f64, - pub from_token_address: Address, - pub to_token_address: Address, -} - -impl ProtocolInfo { - pub(crate) fn from_api_value( - opt_info: Option>>>, - ) -> Option>>> { - opt_info.map(|v0| { - v0.into_iter() - .map(|v1| { - v1.into_iter() - .map(|v2| { - v2.into_iter() - .map(|info| Self { - name: info.name, - part: info.part, - from_token_address: info.from_token_address, - to_token_address: info.to_token_address, - }) - .collect::>() - }) - .collect::>() - }) - .collect::>() - }) - } -} diff --git a/mm2src/trading_api/Cargo.toml b/mm2src/trading_api/Cargo.toml index 79601880b5..3da6026d9a 100644 --- a/mm2src/trading_api/Cargo.toml +++ b/mm2src/trading_api/Cargo.toml @@ -19,6 +19,7 @@ mm2_number = { path = "../mm2_number" } # hw_common = { path = "../hw_common" } ethereum-types = { version = "0.13", default-features = false, features = ["std", "serialize"] } lazy_static = "1.4" +paste = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = { version = "1", features = ["preserve_order", "raw_value"] } diff --git a/mm2src/trading_api/src/lib.rs b/mm2src/trading_api/src/lib.rs index 6bcebf6beb..183e6d9bcd 100644 --- a/mm2src/trading_api/src/lib.rs +++ b/mm2src/trading_api/src/lib.rs @@ -1 +1,3 @@ +//! This module is for indirect connection to third-party trading APIs, processing their results and errors + pub mod one_inch_api; diff --git a/mm2src/trading_api/src/one_inch_api.rs b/mm2src/trading_api/src/one_inch_api.rs index 43c417ca8a..9b0af1625e 100644 --- a/mm2src/trading_api/src/one_inch_api.rs +++ b/mm2src/trading_api/src/one_inch_api.rs @@ -1,4 +1,5 @@ -// Wrapper for 1inch API +//! Wrapper for 1inch API. + pub mod client; pub mod errors; pub mod types; diff --git a/mm2src/trading_api/src/one_inch_api/client.rs b/mm2src/trading_api/src/one_inch_api/client.rs index 25f3fb2e41..a220c30339 100644 --- a/mm2src/trading_api/src/one_inch_api/client.rs +++ b/mm2src/trading_api/src/one_inch_api/client.rs @@ -11,7 +11,7 @@ use mm2_net::transport::slurp_url_with_headers; use crate::one_inch_api::errors::NativeError; -use super::errors::ApiClientError; +use super::{errors::ApiClientError, types::ClassicSwapData}; const ONE_INCH_API_ENDPOINT_V6_0: &str = "swap/v6.0/"; const SWAP_METHOD: &str = "swap"; @@ -115,9 +115,9 @@ impl ApiClient { }) } - pub fn eth_special_contract() -> &'static str { ONE_INCH_ETH_SPECIAL_CONTRACT } + pub const fn eth_special_contract() -> &'static str { ONE_INCH_ETH_SPECIAL_CONTRACT } - pub fn classic_swap_contract() -> &'static str { ONE_INCH_AGGREGATION_ROUTER_CONTRACT_V6_0 } + pub const fn classic_swap_contract() -> &'static str { ONE_INCH_AGGREGATION_ROUTER_CONTRACT_V6_0 } pub fn is_chain_supported(chain_id: u64) -> bool { ONE_INCH_V6_0_SUPPORTED_CHAINS.iter().any(|(_name, id)| *id == chain_id) @@ -132,9 +132,9 @@ impl ApiClient { fn get_swap_endpoint() -> &'static str { ONE_INCH_API_ENDPOINT_V6_0 } - pub(crate) fn get_swap_method() -> &'static str { SWAP_METHOD } + pub(crate) const fn get_swap_method() -> &'static str { SWAP_METHOD } - pub(crate) fn get_quote_method() -> &'static str { QUOTE_METHOD } + pub(crate) const fn get_quote_method() -> &'static str { QUOTE_METHOD } pub(crate) async fn call_api(api_url: Url) -> MmResult { let (status_code, _, body) = slurp_url_with_headers(api_url.as_str(), Self::get_headers()) @@ -147,4 +147,29 @@ impl ApiClient { } Ok(body) } + + pub async fn get_classic_swap_quote( + &self, + chain_id: u64, + params: QueryParams<'_>, + ) -> MmResult { + let api_url = UrlBuilder::new(self, chain_id, ApiClient::get_quote_method().to_owned()) + .with_query_params(params) + .build()?; + let value = Self::call_api(api_url).await?; + serde_json::from_value(value).map_err(|err| ApiClientError::ParseBodyError(err.to_string()).into()) + } + + pub async fn get_classic_swap_tx( + &self, + chain_id: u64, + params: QueryParams<'_>, + ) -> MmResult { + let api_url = UrlBuilder::new(self, chain_id, ApiClient::get_swap_method().to_owned()) + .with_query_params(params) + .build()?; + + let value = Self::call_api(api_url).await?; + serde_json::from_value(value).map_err(|err| ApiClientError::ParseBodyError(err.to_string()).into()) + } } diff --git a/mm2src/trading_api/src/one_inch_api/types.rs b/mm2src/trading_api/src/one_inch_api/types.rs index fff70bc1b5..a62b39cb94 100644 --- a/mm2src/trading_api/src/one_inch_api/types.rs +++ b/mm2src/trading_api/src/one_inch_api/types.rs @@ -1,10 +1,11 @@ #![allow(clippy::result_large_err)] -use super::client::{ApiClient, QueryParams, UrlBuilder}; +use super::client::QueryParams; use super::errors::ApiClientError; use ethereum_types::Address; use mm2_err_handle::mm_error::MmResult; -use serde::Deserialize; +use paste::paste; +use serde::{Deserialize, Serialize}; const ONE_INCH_MAX_SLIPPAGE: f32 = 50.0; const ONE_INCH_MAX_FEE_SHARE: f32 = 3.0; @@ -21,13 +22,23 @@ macro_rules! push_if_some { }; } +macro_rules! def_with_opt_param { + ($var: ident, $var_type: ty) => { + paste! { + pub fn [](mut self, $var: Option<$var_type>) -> Self { + self.$var = $var; + self + } + } + }; +} + /// API params builder for swap quote #[derive(Default)] pub struct ClassicSwapQuoteParams { src: String, dst: String, amount: String, - // Optional fields fee: Option, protocols: Option, @@ -36,7 +47,6 @@ pub struct ClassicSwapQuoteParams { parts: Option, main_route_parts: Option, gas_limit: Option, - include_tokens_info: Option, include_protocols: Option, include_gas: Option, @@ -53,50 +63,17 @@ impl ClassicSwapQuoteParams { } } - pub fn with_fee(mut self, fee: Option) -> Self { - self.fee = fee; - self - } - pub fn with_protocols(mut self, protocols: Option) -> Self { - self.protocols = protocols; - self - } - pub fn with_gas_price(mut self, gas_price: Option) -> Self { - self.gas_price = gas_price; - self - } - pub fn with_complexity_level(mut self, complexity_level: Option) -> Self { - self.complexity_level = complexity_level; - self - } - pub fn with_parts(mut self, parts: Option) -> Self { - self.parts = parts; - self - } - pub fn with_main_route_parts(mut self, main_route_parts: Option) -> Self { - self.main_route_parts = main_route_parts; - self - } - pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { - self.gas_limit = gas_limit; - self - } - pub fn with_include_tokens_info(mut self, include_tokens_info: Option) -> Self { - self.include_tokens_info = include_tokens_info; - self - } - pub fn with_include_protocols(mut self, include_protocols: Option) -> Self { - self.include_protocols = include_protocols; - self - } - pub fn with_include_gas(mut self, include_gas: Option) -> Self { - self.include_gas = include_gas; - self - } - pub fn with_connector_tokens(mut self, connector_tokens: Option) -> Self { - self.connector_tokens = connector_tokens; - self - } + def_with_opt_param!(fee, f32); + def_with_opt_param!(protocols, String); + def_with_opt_param!(gas_price, String); + def_with_opt_param!(complexity_level, u32); + def_with_opt_param!(parts, u32); + def_with_opt_param!(main_route_parts, u32); + def_with_opt_param!(gas_limit, u128); + def_with_opt_param!(include_tokens_info, bool); + def_with_opt_param!(include_protocols, bool); + def_with_opt_param!(include_gas, bool); + def_with_opt_param!(connector_tokens, String); pub fn build_query_params(&self) -> MmResult, ApiClientError> { self.validate_params()?; @@ -140,7 +117,6 @@ pub struct ClassicSwapCreateParams { amount: String, from: String, slippage: f32, - // Optional fields fee: Option, protocols: Option, @@ -154,6 +130,7 @@ pub struct ClassicSwapCreateParams { include_gas: Option, connector_tokens: Option, permit: Option, + /// Funds receiver receiver: Option, referrer: Option, /// Disable gas estimation @@ -174,70 +151,22 @@ impl ClassicSwapCreateParams { } } - pub fn with_fee(mut self, fee: Option) -> Self { - self.fee = fee; - self - } - pub fn with_protocols(mut self, protocols: Option) -> Self { - self.protocols = protocols; - self - } - pub fn with_gas_price(mut self, gas_price: Option) -> Self { - self.gas_price = gas_price; - self - } - pub fn with_complexity_level(mut self, complexity_level: Option) -> Self { - self.complexity_level = complexity_level; - self - } - pub fn with_parts(mut self, parts: Option) -> Self { - self.parts = parts; - self - } - pub fn with_main_route_parts(mut self, main_route_parts: Option) -> Self { - self.main_route_parts = main_route_parts; - self - } - pub fn with_gas_limit(mut self, gas_limit: Option) -> Self { - self.gas_limit = gas_limit; - self - } - pub fn with_include_tokens_info(mut self, include_tokens_info: Option) -> Self { - self.include_tokens_info = include_tokens_info; - self - } - pub fn with_include_protocols(mut self, include_protocols: Option) -> Self { - self.include_protocols = include_protocols; - self - } - pub fn with_include_gas(mut self, include_gas: Option) -> Self { - self.include_gas = include_gas; - self - } - pub fn with_connector_tokens(mut self, connector_tokens: Option) -> Self { - self.connector_tokens = connector_tokens; - self - } - pub fn with_permit(mut self, permit: Option) -> Self { - self.permit = permit; - self - } - pub fn with_receiver(mut self, receiver: Option) -> Self { - self.receiver = receiver; - self - } - pub fn with_referrer(mut self, referrer: Option) -> Self { - self.referrer = referrer; - self - } - pub fn with_disable_estimate(mut self, disable_estimate: Option) -> Self { - self.disable_estimate = disable_estimate; - self - } - pub fn with_allow_partial_fill(mut self, allow_partial_fill: Option) -> Self { - self.allow_partial_fill = allow_partial_fill; - self - } + def_with_opt_param!(fee, f32); + def_with_opt_param!(protocols, String); + def_with_opt_param!(gas_price, String); + def_with_opt_param!(complexity_level, u32); + def_with_opt_param!(parts, u32); + def_with_opt_param!(main_route_parts, u32); + def_with_opt_param!(gas_limit, u128); + def_with_opt_param!(include_tokens_info, bool); + def_with_opt_param!(include_protocols, bool); + def_with_opt_param!(include_gas, bool); + def_with_opt_param!(connector_tokens, String); + def_with_opt_param!(permit, String); + def_with_opt_param!(receiver, String); + def_with_opt_param!(referrer, String); + def_with_opt_param!(disable_estimate, bool); + def_with_opt_param!(allow_partial_fill, bool); pub fn build_query_params(&self) -> MmResult, ApiClientError> { self.validate_params()?; @@ -282,7 +211,7 @@ impl ClassicSwapCreateParams { } } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Serialize)] pub struct TokenInfo { pub address: Address, pub symbol: String, @@ -296,14 +225,12 @@ pub struct TokenInfo { pub tags: Vec, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] pub struct ProtocolInfo { pub name: String, pub part: f64, - #[serde(rename = "fromTokenAddress")] pub from_token_address: Address, - #[serde(rename = "toTokenAddress")] pub to_token_address: Address, } @@ -336,33 +263,6 @@ pub struct TxFields { pub gas: u128, } -impl ApiClient { - pub async fn get_classic_swap_quote( - &self, - chain_id: u64, - params: QueryParams<'_>, - ) -> MmResult { - let api_url = UrlBuilder::new(self, chain_id, ApiClient::get_quote_method().to_owned()) - .with_query_params(params) - .build()?; - let value = Self::call_api(api_url).await?; - serde_json::from_value(value).map_err(|err| ApiClientError::ParseBodyError(err.to_string()).into()) - } - - pub async fn get_classic_swap_tx( - &self, - chain_id: u64, - params: QueryParams<'_>, - ) -> MmResult { - let api_url = UrlBuilder::new(self, chain_id, ApiClient::get_swap_method().to_owned()) - .with_query_params(params) - .build()?; - - let value = Self::call_api(api_url).await?; - serde_json::from_value(value).map_err(|err| ApiClientError::ParseBodyError(err.to_string()).into()) - } -} - fn validate_slippage(slippage: f32) -> MmResult<(), ApiClientError> { if !(0.0..=ONE_INCH_MAX_SLIPPAGE).contains(&slippage) { return Err(ApiClientError::InvalidParam("invalid slippage param".to_owned()).into());