Skip to content

Commit

Permalink
feat(LRAPI): add 1inch classic swap rpc (#2222)
Browse files Browse the repository at this point in the history
This commit adds initial code to connect to 1inch Liquidity Routing API (LRAPI) provider and two new RPCs to use 1inch classic swap API. It also adds 'approve' and 'allowance' RPCs (for ERC20 tokens).
  • Loading branch information
dimxy authored Dec 3, 2024
1 parent af89011 commit 8f83308
Show file tree
Hide file tree
Showing 27 changed files with 1,820 additions and 72 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ members = [
"mm2src/proxy_signature",
"mm2src/rpc_task",
"mm2src/trezor",
"mm2src/trading_api",
]

exclude = [
Expand Down
41 changes: 18 additions & 23 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,20 +524,6 @@ pub type Web3RpcFut<T> = Box<dyn Future<Item = T, Error = MmError<Web3RpcError>>
pub type Web3RpcResult<T> = Result<T, MmError<Web3RpcError>>;
type EthPrivKeyPolicy = PrivKeyPolicy<KeyPair>;

#[macro_export]
macro_rules! wei_from_gwei_decimal {
($big_decimal: expr) => {
$crate::eth::wei_from_big_decimal($big_decimal, $crate::eth::ETH_GWEI_DECIMALS)
};
}

#[macro_export]
macro_rules! wei_to_gwei_decimal {
($gwei: expr) => {
$crate::eth::u256_to_big_decimal($gwei, $crate::eth::ETH_GWEI_DECIMALS)
};
}

#[derive(Clone, Debug)]
pub(crate) struct LegacyGasPrice {
pub(crate) gas_price: U256,
Expand Down Expand Up @@ -582,11 +568,11 @@ impl TryFrom<PayForGasParams> for PayForGasOption {
fn try_from(param: PayForGasParams) -> Result<Self, Self::Error> {
match param {
PayForGasParams::Legacy(legacy) => Ok(Self::Legacy(LegacyGasPrice {
gas_price: wei_from_gwei_decimal!(&legacy.gas_price)?,
gas_price: wei_from_gwei_decimal(&legacy.gas_price)?,
})),
PayForGasParams::Eip1559(eip1559) => Ok(Self::Eip1559(Eip1559FeePerGas {
max_fee_per_gas: wei_from_gwei_decimal!(&eip1559.max_fee_per_gas)?,
max_priority_fee_per_gas: wei_from_gwei_decimal!(&eip1559.max_priority_fee_per_gas)?,
max_fee_per_gas: wei_from_gwei_decimal(&eip1559.max_fee_per_gas)?,
max_priority_fee_per_gas: wei_from_gwei_decimal(&eip1559.max_priority_fee_per_gas)?,
})),
}
}
Expand Down Expand Up @@ -1082,6 +1068,9 @@ impl EthCoinImpl {
let guard = self.erc20_tokens_infos.lock().unwrap();
(*guard).clone()
}

#[inline(always)]
pub fn chain_id(&self) -> u64 { self.chain_id }
}

async fn get_raw_transaction_impl(coin: EthCoin, req: RawTransactionRequest) -> RawTransactionResult {
Expand Down Expand Up @@ -1204,8 +1193,8 @@ pub async fn withdraw_erc1155(ctx: MmArc, withdraw_type: WithdrawErc1155) -> Wit
let fee_details = EthTxFeeDetails::new(gas, pay_for_gas_option, fee_coin)?;

Ok(TransactionNftDetails {
tx_hex: BytesJson::from(signed_bytes.to_vec()),
tx_hash: format!("{:02x}", signed.tx_hash_as_bytes()),
tx_hex: BytesJson::from(signed_bytes.to_vec()), // TODO: should we return tx_hex 0x-prefixed (everywhere)?
tx_hash: format!("{:02x}", signed.tx_hash_as_bytes()), // TODO: add 0x hash (use unified hash format for eth wherever it is returned)
from: vec![eth_coin.my_address()?],
to: vec![withdraw_type.to],
contract_type: ContractType::Erc1155,
Expand Down Expand Up @@ -1296,7 +1285,7 @@ pub async fn withdraw_erc721(ctx: MmArc, withdraw_type: WithdrawErc721) -> Withd

Ok(TransactionNftDetails {
tx_hex: BytesJson::from(signed_bytes.to_vec()),
tx_hash: format!("{:02x}", signed.tx_hash_as_bytes()),
tx_hash: format!("{:02x}", signed.tx_hash_as_bytes()), // TODO: add 0x hash (use unified hash format for eth wherever it is returned)
from: vec![eth_coin.my_address()?],
to: vec![withdraw_type.to],
contract_type: ContractType::Erc721,
Expand Down Expand Up @@ -2456,7 +2445,7 @@ impl MarketCoinOps for EthCoin {
let fut = async move {
coin.send_raw_transaction(bytes.into())
.await
.map(|res| format!("{:02x}", res))
.map(|res| format!("{:02x}", res)) // TODO: add 0x hash (use unified hash format for eth wherever it is returned)
.map_err(|e| ERRL!("{}", e))
};

Expand Down Expand Up @@ -4762,7 +4751,7 @@ impl EthCoin {
self.call(request, Some(BlockId::Number(BlockNumber::Latest))).await
}

fn allowance(&self, spender: Address) -> Web3RpcFut<U256> {
pub fn allowance(&self, spender: Address) -> Web3RpcFut<U256> {
let coin = self.clone();
let fut = async move {
match coin.coin_type {
Expand Down Expand Up @@ -4827,7 +4816,7 @@ impl EthCoin {
Box::new(fut.boxed().compat())
}

fn approve(&self, spender: Address, amount: U256) -> EthTxFut {
pub fn approve(&self, spender: Address, amount: U256) -> EthTxFut {
let coin = self.clone();
let fut = async move {
let token_addr = match coin.coin_type {
Expand Down Expand Up @@ -6194,6 +6183,12 @@ pub fn wei_from_big_decimal(amount: &BigDecimal, decimals: u8) -> NumConversResu
U256::from_dec_str(&amount).map_to_mm(|e| NumConversError::new(format!("{:?}", e)))
}

pub fn wei_from_gwei_decimal(bigdec: &BigDecimal) -> NumConversResult<U256> {
wei_from_big_decimal(bigdec, ETH_GWEI_DECIMALS)
}

pub fn wei_to_gwei_decimal(wei: U256) -> NumConversResult<BigDecimal> { u256_to_big_decimal(wei, ETH_GWEI_DECIMALS) }

impl Transaction for SignedEthTx {
fn tx_hex(&self) -> Vec<u8> { rlp::encode(self).to_vec() }

Expand Down
52 changes: 26 additions & 26 deletions mm2src/coins/eth/eip1559_gas_fee.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Provides estimations of base and priority fee per gas or fetch estimations from a gas api provider
use super::web3_transport::FeeHistoryResult;
use super::{Web3RpcError, Web3RpcResult};
use crate::{wei_from_gwei_decimal, wei_to_gwei_decimal, EthCoin, NumConversError};
use super::{wei_from_gwei_decimal, wei_to_gwei_decimal, Web3RpcError, Web3RpcResult};
use crate::{EthCoin, NumConversError};
use ethereum_types::U256;
use mm2_err_handle::mm_error::MmError;
use mm2_err_handle::or_mm_error::OrMmError;
Expand Down Expand Up @@ -104,24 +104,24 @@ impl TryFrom<InfuraFeePerGas> for FeePerGasEstimated {

fn try_from(infura_fees: InfuraFeePerGas) -> Result<Self, Self::Error> {
Ok(Self {
base_fee: wei_from_gwei_decimal!(&infura_fees.estimated_base_fee)?,
base_fee: wei_from_gwei_decimal(&infura_fees.estimated_base_fee)?,
low: FeePerGasLevel {
max_fee_per_gas: wei_from_gwei_decimal!(&infura_fees.low.suggested_max_fee_per_gas)?,
max_priority_fee_per_gas: wei_from_gwei_decimal!(&infura_fees.low.suggested_max_priority_fee_per_gas)?,
max_fee_per_gas: wei_from_gwei_decimal(&infura_fees.low.suggested_max_fee_per_gas)?,
max_priority_fee_per_gas: wei_from_gwei_decimal(&infura_fees.low.suggested_max_priority_fee_per_gas)?,
min_wait_time: Some(infura_fees.low.min_wait_time_estimate),
max_wait_time: Some(infura_fees.low.max_wait_time_estimate),
},
medium: FeePerGasLevel {
max_fee_per_gas: wei_from_gwei_decimal!(&infura_fees.medium.suggested_max_fee_per_gas)?,
max_priority_fee_per_gas: wei_from_gwei_decimal!(
&infura_fees.medium.suggested_max_priority_fee_per_gas
max_fee_per_gas: wei_from_gwei_decimal(&infura_fees.medium.suggested_max_fee_per_gas)?,
max_priority_fee_per_gas: wei_from_gwei_decimal(
&infura_fees.medium.suggested_max_priority_fee_per_gas,
)?,
min_wait_time: Some(infura_fees.medium.min_wait_time_estimate),
max_wait_time: Some(infura_fees.medium.max_wait_time_estimate),
},
high: FeePerGasLevel {
max_fee_per_gas: wei_from_gwei_decimal!(&infura_fees.high.suggested_max_fee_per_gas)?,
max_priority_fee_per_gas: wei_from_gwei_decimal!(&infura_fees.high.suggested_max_priority_fee_per_gas)?,
max_fee_per_gas: wei_from_gwei_decimal(&infura_fees.high.suggested_max_fee_per_gas)?,
max_priority_fee_per_gas: wei_from_gwei_decimal(&infura_fees.high.suggested_max_priority_fee_per_gas)?,
min_wait_time: Some(infura_fees.high.min_wait_time_estimate),
max_wait_time: Some(infura_fees.high.max_wait_time_estimate),
},
Expand All @@ -143,33 +143,33 @@ impl TryFrom<BlocknativeBlockPricesResponse> for FeePerGasEstimated {
return Ok(FeePerGasEstimated::default());
}
Ok(Self {
base_fee: wei_from_gwei_decimal!(&block_prices.block_prices[0].base_fee_per_gas)?,
base_fee: wei_from_gwei_decimal(&block_prices.block_prices[0].base_fee_per_gas)?,
low: FeePerGasLevel {
max_fee_per_gas: wei_from_gwei_decimal!(
&block_prices.block_prices[0].estimated_prices[2].max_fee_per_gas
max_fee_per_gas: wei_from_gwei_decimal(
&block_prices.block_prices[0].estimated_prices[2].max_fee_per_gas,
)?,
max_priority_fee_per_gas: wei_from_gwei_decimal!(
&block_prices.block_prices[0].estimated_prices[2].max_priority_fee_per_gas
max_priority_fee_per_gas: wei_from_gwei_decimal(
&block_prices.block_prices[0].estimated_prices[2].max_priority_fee_per_gas,
)?,
min_wait_time: None,
max_wait_time: None,
},
medium: FeePerGasLevel {
max_fee_per_gas: wei_from_gwei_decimal!(
&block_prices.block_prices[0].estimated_prices[1].max_fee_per_gas
max_fee_per_gas: wei_from_gwei_decimal(
&block_prices.block_prices[0].estimated_prices[1].max_fee_per_gas,
)?,
max_priority_fee_per_gas: wei_from_gwei_decimal!(
&block_prices.block_prices[0].estimated_prices[1].max_priority_fee_per_gas
max_priority_fee_per_gas: wei_from_gwei_decimal(
&block_prices.block_prices[0].estimated_prices[1].max_priority_fee_per_gas,
)?,
min_wait_time: None,
max_wait_time: None,
},
high: FeePerGasLevel {
max_fee_per_gas: wei_from_gwei_decimal!(
&block_prices.block_prices[0].estimated_prices[0].max_fee_per_gas
max_fee_per_gas: wei_from_gwei_decimal(
&block_prices.block_prices[0].estimated_prices[0].max_fee_per_gas,
)?,
max_priority_fee_per_gas: wei_from_gwei_decimal!(
&block_prices.block_prices[0].estimated_prices[0].max_priority_fee_per_gas
max_priority_fee_per_gas: wei_from_gwei_decimal(
&block_prices.block_prices[0].estimated_prices[0].max_priority_fee_per_gas,
)?,
min_wait_time: None,
max_wait_time: None,
Expand Down Expand Up @@ -260,7 +260,7 @@ impl FeePerGasSimpleEstimator {
let max_priority_fee_per_gas = Self::percentile_of(&level_rewards, Self::PRIORITY_FEE_PERCENTILES[level_index]);
// Convert the priority fee to BigDecimal gwei, falling back to 0 on error.
let max_priority_fee_per_gas_gwei =
wei_to_gwei_decimal!(max_priority_fee_per_gas).unwrap_or_else(|_| BigDecimal::from(0));
wei_to_gwei_decimal(max_priority_fee_per_gas).unwrap_or_else(|_| BigDecimal::from(0));

// Calculate the max fee per gas by adjusting the base fee and adding the priority fee.
let adjust_max_fee =
Expand All @@ -273,7 +273,7 @@ impl FeePerGasSimpleEstimator {

Ok(FeePerGasLevel {
max_priority_fee_per_gas,
max_fee_per_gas: wei_from_gwei_decimal!(&max_fee_per_gas_dec)?,
max_fee_per_gas: wei_from_gwei_decimal(&max_fee_per_gas_dec)?,
// TODO: Consider adding default wait times if applicable (and mark them as uncertain).
min_wait_time: None,
max_wait_time: None,
Expand All @@ -290,7 +290,7 @@ impl FeePerGasSimpleEstimator {
.first()
.cloned()
.unwrap_or_else(|| U256::from(0));
let latest_base_fee_dec = wei_to_gwei_decimal!(latest_base_fee).unwrap_or_else(|_| BigDecimal::from(0));
let latest_base_fee_dec = wei_to_gwei_decimal(latest_base_fee).unwrap_or_else(|_| BigDecimal::from(0));

// The predicted base fee is not used for calculating eip1559 values here and is provided for other purposes
// (f.e if the caller would like to do own estimates of max fee and max priority fee)
Expand Down
4 changes: 2 additions & 2 deletions mm2src/coins/eth/eth_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,8 @@ impl EthCoin {
.and_then(|t| serde_json::from_value(t).map_err(Into::into))
}

/// Get chain id
pub(crate) async fn chain_id(&self) -> Result<U256, web3::Error> {
/// Get chain id from network
pub(crate) async fn network_chain_id(&self) -> Result<U256, web3::Error> {
self.try_rpc_send("eth_chainId", vec![])
.await
.and_then(|t| serde_json::from_value(t).map_err(Into::into))
Expand Down
7 changes: 7 additions & 0 deletions mm2src/coins/eth/eth_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1040,3 +1040,10 @@ fn test_gas_limit_conf() {
&& eth_coin.gas_limit.eth_max_trade_gas == 150_000
);
}

#[test]
fn test_h256_to_str() {
let h = H256::from_str("5136701f11060010841c9708c3eb26f6606a070b8ae43f4b98b6d7b10a545258").unwrap();
let b: BytesJson = h.0.to_vec().into();
println!("H256={}", format!("0x{:02x}", b));
}
7 changes: 5 additions & 2 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,8 @@ pub mod coins_tests;
pub mod eth;
use eth::erc20::get_erc20_ticker_by_contract_address;
use eth::eth_swap_v2::{PaymentStatusErr, PrepareTxDataError, ValidatePaymentV2Err};
use eth::GetValidEthWithdrawAddError;
use eth::{eth_coin_from_conf_and_request, get_eth_address, EthCoin, EthGasDetailsErr, EthTxFeeDetails,
GetEthAddressError, SignedEthTx};
GetEthAddressError, GetValidEthWithdrawAddError, SignedEthTx};

pub mod hd_wallet;
use hd_wallet::{AccountUpdatingError, AddressDerivingError, HDAccountOps, HDAddressId, HDAddressOps, HDCoinAddress,
Expand Down Expand Up @@ -661,6 +660,10 @@ impl TransactionErr {
}
}

impl std::fmt::Display for TransactionErr {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.get_plain_text_format()) }
}

#[derive(Debug, PartialEq)]
pub enum FoundSwapTxSpend {
Spent(TransactionEnum),
Expand Down
18 changes: 9 additions & 9 deletions mm2src/coins/rpc_command/get_estimated_fees.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! RPCs to start/stop gas fee estimator and get estimated base and priority fee per gas
use crate::eth::{EthCoin, EthCoinType, FeeEstimatorContext, FeeEstimatorState, FeePerGasEstimated};
use crate::{lp_coinfind_or_err, wei_to_gwei_decimal, AsyncMutex, CoinFindError, MmCoinEnum, NumConversError};
use crate::eth::{wei_to_gwei_decimal, EthCoin, EthCoinType, FeeEstimatorContext, FeeEstimatorState, FeePerGasEstimated};
use crate::{lp_coinfind_or_err, AsyncMutex, CoinFindError, MmCoinEnum, NumConversError};
use common::executor::{spawn_abortable, Timer};
use common::log::debug;
use common::{HttpStatusCode, StatusCode};
Expand Down Expand Up @@ -66,22 +66,22 @@ impl TryFrom<FeePerGasEstimated> for FeePerGasEstimatedExt {

fn try_from(fees: FeePerGasEstimated) -> Result<Self, Self::Error> {
Ok(Self {
base_fee: wei_to_gwei_decimal!(fees.base_fee)?,
base_fee: wei_to_gwei_decimal(fees.base_fee)?,
low: FeePerGasLevel {
max_fee_per_gas: wei_to_gwei_decimal!(fees.low.max_fee_per_gas)?,
max_priority_fee_per_gas: wei_to_gwei_decimal!(fees.low.max_priority_fee_per_gas)?,
max_fee_per_gas: wei_to_gwei_decimal(fees.low.max_fee_per_gas)?,
max_priority_fee_per_gas: wei_to_gwei_decimal(fees.low.max_priority_fee_per_gas)?,
min_wait_time: fees.low.min_wait_time,
max_wait_time: fees.low.max_wait_time,
},
medium: FeePerGasLevel {
max_fee_per_gas: wei_to_gwei_decimal!(fees.medium.max_fee_per_gas)?,
max_priority_fee_per_gas: wei_to_gwei_decimal!(fees.medium.max_priority_fee_per_gas)?,
max_fee_per_gas: wei_to_gwei_decimal(fees.medium.max_fee_per_gas)?,
max_priority_fee_per_gas: wei_to_gwei_decimal(fees.medium.max_priority_fee_per_gas)?,
min_wait_time: fees.medium.min_wait_time,
max_wait_time: fees.medium.max_wait_time,
},
high: FeePerGasLevel {
max_fee_per_gas: wei_to_gwei_decimal!(fees.high.max_fee_per_gas)?,
max_priority_fee_per_gas: wei_to_gwei_decimal!(fees.high.max_priority_fee_per_gas)?,
max_fee_per_gas: wei_to_gwei_decimal(fees.high.max_fee_per_gas)?,
max_priority_fee_per_gas: wei_to_gwei_decimal(fees.high.max_priority_fee_per_gas)?,
min_wait_time: fees.high.min_wait_time,
max_wait_time: fees.high.max_wait_time,
},
Expand Down
1 change: 1 addition & 0 deletions mm2src/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ lazy_static = "1.4"
log = "0.4.17"
parking_lot = { version = "0.12.0", features = ["nightly"] }
parking_lot_core = { version = "0.6", features = ["nightly"] }
paste = "1.0"
primitive-types = "0.11.1"
rand = { version = "0.7", features = ["std", "small_rng"] }
rustc-hash = "1.1.0"
Expand Down
Loading

0 comments on commit 8f83308

Please sign in to comment.