Skip to content

Commit

Permalink
refactor on review notes
Browse files Browse the repository at this point in the history
  • Loading branch information
dimxy committed Sep 20, 2024
1 parent 5df753b commit 57192ca
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 264 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions mm2src/mm2_main/src/ext_api.rs
Original file line number Diff line number Diff line change
@@ -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;
3 changes: 2 additions & 1 deletion mm2src/mm2_main/src/ext_api/one_inch.rs
Original file line number Diff line number Diff line change
@@ -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;
6 changes: 3 additions & 3 deletions mm2src/mm2_main/src/ext_api/one_inch/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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(_)
Expand Down
46 changes: 12 additions & 34 deletions mm2src/mm2_main/src/ext_api/one_inch/rpcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()))?;
Expand All @@ -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,
Expand All @@ -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?;

Expand All @@ -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)
Expand All @@ -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))
}
Expand Down
85 changes: 11 additions & 74 deletions mm2src/mm2_main/src/ext_api/one_inch/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand All @@ -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<f32>,
pub protocols: Option<String>,
Expand All @@ -24,7 +24,6 @@ pub struct ClassicSwapQuoteRequest {
pub parts: Option<u32>,
pub main_route_parts: Option<u32>,
pub gas_limit: Option<u128>,

pub include_tokens_info: Option<bool>,
pub include_protocols: Option<bool>,
pub include_gas: Option<bool>,
Expand All @@ -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<f32>,
pub protocols: Option<String>,
Expand All @@ -53,7 +51,6 @@ pub struct ClassicSwapCreateRequest {
pub permit: Option<String>,
pub receiver: Option<String>,
pub referrer: Option<String>,

/// Disable gas estimation
pub disable_estimate: Option<bool>,
/// Allow the swap to be partially filled
Expand All @@ -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<TokenInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -80,10 +77,10 @@ impl ClassicSwapResponse {
decimals: u8,
) -> MmResult<Self, FromApiValueError> {
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,
})
Expand All @@ -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<Self, FromApiValueError> {
Ok(Self {
Expand All @@ -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<String>,
}

impl TokenInfo {
pub(crate) fn from_api_value(opt_info: Option<one_inch_api::types::TokenInfo>) -> Option<Self> {
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<Vec<Vec<Vec<one_inch_api::types::ProtocolInfo>>>>,
) -> Option<Vec<Vec<Vec<Self>>>> {
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::<Vec<_>>()
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
})
}
}
1 change: 1 addition & 0 deletions mm2src/trading_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
2 changes: 2 additions & 0 deletions mm2src/trading_api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
//! This module is for indirect connection to third-party trading APIs, processing their results and errors

pub mod one_inch_api;
3 changes: 2 additions & 1 deletion mm2src/trading_api/src/one_inch_api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Wrapper for 1inch API
//! Wrapper for 1inch API.

pub mod client;
pub mod errors;
pub mod types;
35 changes: 30 additions & 5 deletions mm2src/trading_api/src/one_inch_api/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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)
Expand All @@ -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<Value, ApiClientError> {
let (status_code, _, body) = slurp_url_with_headers(api_url.as_str(), Self::get_headers())
Expand All @@ -147,4 +147,29 @@ impl ApiClient {
}
Ok(body)
}

pub async fn get_classic_swap_quote(
&self,
chain_id: u64,
params: QueryParams<'_>,
) -> MmResult<ClassicSwapData, ApiClientError> {
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<ClassicSwapData, ApiClientError> {
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())
}
}
Loading

0 comments on commit 57192ca

Please sign in to comment.