Skip to content

Commit

Permalink
review (shamardy): eliminate dup fn, reuse get_erc20_ticker_by_contra…
Browse files Browse the repository at this point in the history
…ct_address fn, add new get_platform_ticker fn
  • Loading branch information
dimxy committed Feb 26, 2025
1 parent bbb115e commit c850505
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 68 deletions.
33 changes: 3 additions & 30 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ use erc20::get_token_decimals;
pub(crate) mod eth_swap_v2;
use eth_swap_v2::{extract_id_from_tx_data, EthPaymentType, PaymentMethod, SpendTxSearchParams};

pub const ETH_PROTOCOL_TYPE: &str = "ETH";
pub const ERC20_PROTOCOL_TYPE: &str = "ERC20";

/// https://github.com/artemii235/etomic-swap/blob/master/contracts/EtomicSwap.sol
/// Dev chain (195.201.137.5:8565) contract address: 0x83965C539899cC0F918552e5A26915de40ee8852
/// Ropsten: https://ropsten.etherscan.io/address/0x7bc1bbdd6a0a722fc9bffc49c921b685ecb84b94
Expand Down Expand Up @@ -7370,33 +7373,3 @@ impl MakerCoinSwapOpsV2 for EthCoin {
self.spend_maker_payment_v2_impl(args).await
}
}

/// Find a EVM token name in the coins file by a given contract address.
/// If contract_addr is empty the function returns the platform coin name
pub async fn find_token_by_address(ctx: &MmArc, chain_id: u64, contract_addr: Option<EthAddress>) -> Option<String> {
let coin_ctx = CoinsContext::from_ctx(ctx).unwrap();
let coins = coin_ctx.coins.lock().await;
coins
.iter()
.find(|(_ticker, coin_struct)| {
if let MmCoinEnum::EthCoin(eth_coin) = &coin_struct.inner {
match eth_coin.coin_type {
EthCoinType::Erc20 { token_addr, .. } => {
if let Some(contract_addr) = contract_addr {
if token_addr == contract_addr && eth_coin.chain_id() == chain_id {
return true;
}
}
},
EthCoinType::Eth => {
if contract_addr.is_none() && eth_coin.chain_id() == chain_id {
return true;
}
},
EthCoinType::Nft { .. } => {},
}
}
false
})
.map(|(ticker, _)| ticker.clone())
}
33 changes: 28 additions & 5 deletions mm2src/coins/eth/erc20.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::{ERC20_PROTOCOL_TYPE, ETH_PROTOCOL_TYPE};
use crate::eth::web3_transport::Web3Transport;
use crate::eth::{EthCoin, ERC20_CONTRACT};
use crate::{CoinsContext, MmCoinEnum};
Expand All @@ -6,6 +7,7 @@ use ethereum_types::Address;
use futures_util::TryFutureExt;
use mm2_core::mm_ctx::MmArc;
use mm2_err_handle::mm_error::MmResult;
use std::str::FromStr;
use web3::types::{BlockId, BlockNumber, CallRequest};
use web3::{Transport, Web3};

Expand Down Expand Up @@ -71,18 +73,39 @@ pub async fn get_erc20_token_info(coin: &EthCoin, token_addr: Address) -> Result
}

/// Finds if an ERC20 token is in coins config by its contract address and returns its ticker.
pub fn get_erc20_ticker_by_contract_address(ctx: &MmArc, platform: &str, contract_address: &str) -> Option<String> {
pub fn get_platform_ticker(ctx: &MmArc, chain_id: u64) -> Option<String> {
ctx.conf["coins"].as_array()?.iter().find_map(|coin| {
let protocol = coin.get("protocol")?;
let protocol_type = protocol.get("type")?.as_str()?;
if protocol_type != "ERC20" {
if protocol_type != ETH_PROTOCOL_TYPE {
return None;
}
let coin_chain_id = coin.get("chain_id")?.as_u64()?;
if coin_chain_id == chain_id {
coin.get("coin")?.as_str().map(|s| s.to_string())
} else {
None
}
})
}

/// Finds if an ERC20 token is in coins config by its contract address and returns its ticker.
pub fn get_erc20_ticker_by_contract_address(
ctx: &MmArc,
platform_ticker: &str,
contract_address: &Address,
) -> Option<String> {
ctx.conf["coins"].as_array()?.iter().find_map(|coin| {
let protocol = coin.get("protocol")?;
let protocol_type = protocol.get("type")?.as_str()?;
if protocol_type != ERC20_PROTOCOL_TYPE {
return None;
}
let protocol_data = protocol.get("protocol_data")?;
let coin_platform = protocol_data.get("platform")?.as_str()?;
let coin_contract_address = protocol_data.get("contract_address")?.as_str()?;
let coin_platform_ticker = protocol_data.get("platform")?.as_str()?;
let coin_contract_address = Address::from_str(protocol_data.get("contract_address")?.as_str()?).ok()?;

if coin_platform == platform && coin_contract_address == contract_address {
if coin_platform_ticker == platform_ticker && &coin_contract_address == contract_address {
coin.get("coin")?.as_str().map(|s| s.to_string())
} else {
None
Expand Down
23 changes: 17 additions & 6 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ use crypto::{derive_secp256k1_secret, Bip32Error, Bip44Chain, CryptoCtx, CryptoC
Secp256k1ExtendedPublicKey, Secp256k1Secret, WithHwRpcError};
use derive_more::Display;
use enum_derives::{EnumFromStringify, EnumFromTrait};
use ethereum_types::{H256, U256};
use ethereum_types::{Address as EthAddress, H256, U256};
use futures::compat::Future01CompatExt;
use futures::lock::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
use futures::{FutureExt, TryFutureExt};
Expand Down Expand Up @@ -221,7 +221,6 @@ use eth::erc20::get_erc20_ticker_by_contract_address;
use eth::eth_swap_v2::{PaymentStatusErr, PrepareTxDataError, ValidatePaymentV2Err};
use eth::{eth_coin_from_conf_and_request, get_eth_address, EthCoin, EthGasDetailsErr, EthTxFeeDetails,
GetEthAddressError, GetValidEthWithdrawAddError, SignedEthTx};
use ethereum_types::Address as EthAddress;

pub mod hd_wallet;
use hd_wallet::{AccountUpdatingError, AddressDerivingError, HDAccountOps, HDAddressId, HDAddressOps, HDCoinAddress,
Expand Down Expand Up @@ -4350,18 +4349,26 @@ pub enum CustomTokenError {
fmt = "Token with the same ticker already exists in coins configs, ticker in config: {}",
ticker_in_config
)]
DuplicateTickerInConfig { ticker_in_config: String },
DuplicateTickerInConfig {
ticker_in_config: String,
},
#[display(
fmt = "Token with the same contract address already exists in coins configs, ticker in config: {}",
ticker_in_config
)]
DuplicateContractInConfig { ticker_in_config: String },
DuplicateContractInConfig {
ticker_in_config: String,
},
#[display(
fmt = "Token is already activated, ticker: {}, contract address: {}",
ticker,
contract_address
)]
TokenWithSameContractAlreadyActivated { ticker: String, contract_address: String },
TokenWithSameContractAlreadyActivated {
ticker: String,
contract_address: String,
},
InvalidErc20Address,
}

impl CoinProtocol {
Expand Down Expand Up @@ -4425,7 +4432,11 @@ impl CoinProtocol {
// if it is duplicated in config, we will have two orderbooks one using the ticker and one using the contract address.
// Todo: We should use the contract address for orderbook topics instead of the ticker once we make custom tokens non-wallet only.
// If a coin is added to the config later, users who added it as a custom token and did not update will not see the orderbook.
if let Some(existing_ticker) = get_erc20_ticker_by_contract_address(ctx, platform, contract_address) {
if let Some(existing_ticker) = get_erc20_ticker_by_contract_address(
ctx,
platform,
&EthAddress::from_str(contract_address).map_err(|_| MmError::new(CustomTokenError::InvalidErc20Address))?,
) {
return Err(MmError::new(CustomTokenError::DuplicateContractInConfig {
ticker_in_config: existing_ticker,
}));
Expand Down
6 changes: 3 additions & 3 deletions mm2src/mm2_main/src/rpc/dispatcher/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,9 @@ async fn dispatcher_v2(request: MmRpcRequest, ctx: MmArc) -> DispatcherResult<Re
handle_mmrpc(ctx, request, one_inch_v6_0_classic_swap_liquidity_sources_rpc).await
},
"1inch_v6_0_classic_swap_tokens" => handle_mmrpc(ctx, request, one_inch_v6_0_classic_swap_tokens_rpc).await,
"lr_best_quote" => handle_mmrpc(ctx, request, lr_best_quote_rpc).await,
"lr_quotes_for_tokens" => handle_mmrpc(ctx, request, lr_quotes_for_tokens_rpc).await,
"lr_fill_order" => handle_mmrpc(ctx, request, lr_fill_order_rpc).await,
"preview::lr_best_quote" => handle_mmrpc(ctx, request, lr_best_quote_rpc).await,
"preview::lr_quotes_for_tokens" => handle_mmrpc(ctx, request, lr_quotes_for_tokens_rpc).await,
"preview::lr_fill_order" => handle_mmrpc(ctx, request, lr_fill_order_rpc).await,
_ => MmError::err(DispatcherError::NoSuchMethod),
}
}
Expand Down
2 changes: 1 addition & 1 deletion mm2src/mm2_main/src/rpc/lp_commands/lr_swap.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//? RPC implementations for swaps with liquidity routing (LR) of EVM tokens
//! RPC implementations for swaps with liquidity routing (LR) of EVM tokens
use super::one_inch::types::ClassicSwapDetails;
use crate::rpc::lp_commands::one_inch::errors::ApiIntegrationRpcError;
Expand Down
2 changes: 1 addition & 1 deletion mm2src/mm2_main/src/rpc/lp_commands/lr_swap/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//? Types for LR swaps rpc
//! Types for LR swaps rpc
use crate::lp_ordermatch::RpcOrderbookEntryV2;
use crate::rpc::lp_commands::one_inch::types::ClassicSwapDetails;
Expand Down
41 changes: 20 additions & 21 deletions mm2src/mm2_main/src/rpc/lp_commands/one_inch/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::rpc::lp_commands::one_inch::errors::FromApiValueError;
use coins::eth::find_token_by_address;
use coins::eth::erc20::{get_erc20_ticker_by_contract_address, get_platform_ticker};
use coins::eth::{u256_to_big_decimal, wei_to_gwei_decimal};
use coins::Ticker;
use common::true_f;
use ethereum_types::{Address, U256};
use mm2_core::mm_ctx::MmArc;
Expand All @@ -11,7 +12,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::str::FromStr;
use trading_api::one_inch_api::{self,
classic_swap_types::{ProtocolImage, ProtocolInfo, TokenInfo},
classic_swap_types::{ProtocolImage, ProtocolInfo, TokenInfo as LrTokenInfo},
client::ApiClient};

construct_detailed!(DetailedAmount, amount);
Expand All @@ -25,9 +26,9 @@ pub struct AggregationContractRequest {}
#[serde(deny_unknown_fields)]
pub struct ClassicSwapQuoteRequest {
/// Base coin ticker
pub base: String,
pub base: Ticker,
/// Rel coin ticker
pub rel: String,
pub rel: Ticker,
/// Swap amount in coins (with fraction)
pub amount: MmNumber,
/// Partner fee, percentage of src token amount will be sent to referrer address, min: 0; max: 3.
Expand Down Expand Up @@ -70,9 +71,9 @@ pub struct ClassicSwapQuoteRequest {
#[serde(deny_unknown_fields)]
pub struct ClassicSwapCreateRequest {
/// Base coin ticker
pub base: String,
pub base: Ticker,
/// Rel coin ticker
pub rel: String,
pub rel: Ticker,
/// Swap amount in coins (with fraction)
pub amount: MmNumber,
/// Allowed slippage, min: 0; max: 50
Expand Down Expand Up @@ -136,17 +137,17 @@ pub struct ClassicSwapDetails {
pub dst_amount: DetailedAmount,
/// Source (base) token info
#[serde(skip_serializing_if = "Option::is_none")]
pub src_token: Option<TokenInfo>,
pub src_token: Option<LrTokenInfo>,
/// Source (base) token name as it is defined in the coins file
pub src_token_kdf: Option<String>,
pub src_token_kdf: Option<Ticker>,
/// Destination (rel) token info
#[serde(skip_serializing_if = "Option::is_none")]
pub dst_token: Option<TokenInfo>,
pub dst_token: Option<LrTokenInfo>,
/// Destination (rel) token name as it is defined in the coins file.
/// This is used to show route tokens in the GUI, like they are in the coin file.
/// However, route tokens can be missed in the coins file and therefore cannot be filled.
/// In this case GUI may use TokenInfo::Address or TokenInfo::Symbol
pub dst_token_kdf: Option<String>,
/// In this case GUI may use LrTokenInfo::Address or LrTokenInfo::Symbol
pub dst_token_kdf: Option<Ticker>,
/// Used liquidity sources
#[serde(skip_serializing_if = "Option::is_none")]
pub protocols: Option<Vec<Vec<Vec<ProtocolInfo>>>>,
Expand All @@ -162,18 +163,16 @@ pub type ClassicSwapResponse = ClassicSwapDetails;

impl ClassicSwapDetails {
/// Get token name as it is defined in the coins file by contract address
async fn token_name_kdf(ctx: &MmArc, chain_id: u64, token_info: &TokenInfo) -> Option<String> {
//let ctx = self.ctx.lock().await;
//let coin_ctx = CoinsContext::from_ctx(&ctx).unwrap();

async fn token_name_kdf(ctx: &MmArc, chain_id: u64, token_info: &LrTokenInfo) -> Option<Ticker> {
let special_contract =
Address::from_str(ApiClient::eth_special_contract()).expect("1inch special address must be valid"); // TODO: must call 1inch to get it, instead of burned consts
let token_address = if token_info.address == special_contract {
None

let platform_ticker = get_platform_ticker(ctx, chain_id)?;
if token_info.address == special_contract {
Some(platform_ticker)
} else {
Some(token_info.address)
};
find_token_by_address(ctx, chain_id, token_address).await
get_erc20_ticker_by_contract_address(ctx, &platform_ticker, &token_info.address)
}
}

pub(crate) async fn from_api_classic_swap_data(
Expand Down Expand Up @@ -244,5 +243,5 @@ pub struct ClassicSwapTokensRequest {

#[derive(Serialize)]
pub struct ClassicSwapTokensResponse {
pub tokens: HashMap<String, TokenInfo>,
pub tokens: HashMap<Ticker, LrTokenInfo>,
}
2 changes: 1 addition & 1 deletion mm2src/mm2_main/src/rpc/lp_commands/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub async fn get_token_info(ctx: MmArc, req: TokenInfoRequest) -> MmResult<Token
TokenInfoError::InvalidRequest(error)
})?;

let config_ticker = get_erc20_ticker_by_contract_address(&ctx, platform, contract_address_str);
let config_ticker = get_erc20_ticker_by_contract_address(&ctx, platform, &contract_address);
let token_info = get_erc20_token_info(&eth_coin, contract_address)
.await
.map_to_mm(TokenInfoError::RetrieveInfoError)?;
Expand Down

0 comments on commit c850505

Please sign in to comment.