Skip to content

Commit 5c8a4b1

Browse files
authored
fix(eth): Correctly implement ETH max withdrawal logic (#2531)
EVM max withdrawals were broken due to the max flag always being set to false. This commit passes the correct `req.max` value and also addresses two low-balance edge cases: it avoids panic from arithmetic underflow when calculating max amount to transfer, and gracefully handles cryptic RPC errors by converting known “insufficient funds” messages into a clear `WithdrawError::AmountTooLow`.
1 parent eab8081 commit 5c8a4b1

3 files changed

Lines changed: 32 additions & 5 deletions

File tree

mm2src/coins/eth.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6837,6 +6837,12 @@ fn get_valid_nft_addr_to_withdraw(
68376837
pub enum EthGasDetailsErr {
68386838
#[display(fmt = "Invalid fee policy: {}", _0)]
68396839
InvalidFeePolicy(String),
6840+
#[display(
6841+
fmt = "Amount {} is too low. Required minimum is {} to cover fees.",
6842+
amount,
6843+
threshold
6844+
)]
6845+
AmountTooLow { amount: BigDecimal, threshold: BigDecimal },
68406846
#[from_stringify("NumConversError")]
68416847
#[display(fmt = "Internal error: {}", _0)]
68426848
Internal(String),
@@ -6919,7 +6925,11 @@ async fn get_eth_gas_details_from_withdraw_fee(
69196925

69206926
// covering edge case by deducting the standard transfer fee when we want to max withdraw ETH
69216927
let eth_value_for_estimate = if fungible_max && eth_coin.coin_type == EthCoinType::Eth {
6922-
eth_value - calc_total_fee(U256::from(eth_coin.gas_limit.eth_send_coins), &pay_for_gas_option).map_mm_err()?
6928+
let estimated_fee =
6929+
calc_total_fee(U256::from(eth_coin.gas_limit.eth_send_coins), &pay_for_gas_option).map_mm_err()?;
6930+
// Defaulting to zero is safe; if the balance is indeed too low, the `estimate_gas` call below
6931+
// will fail, and we will catch and handle that error gracefully.
6932+
eth_value.checked_sub(estimated_fee).unwrap_or_default()
69236933
} else {
69246934
eth_value
69256935
};
@@ -6939,9 +6949,25 @@ async fn get_eth_gas_details_from_withdraw_fee(
69396949
max_fee_per_gas,
69406950
..CallRequest::default()
69416951
};
6942-
// TODO Note if the wallet's balance is insufficient to withdraw, then `estimate_gas` may fail with the `Exception` error.
6943-
// TODO Ideally we should determine the case when we have the insufficient balance and return `WithdrawError::NotSufficientBalance`.
6944-
let gas_limit = eth_coin.estimate_gas_wrapper(estimate_gas_req).compat().await?;
6952+
let gas_limit = match eth_coin.estimate_gas_wrapper(estimate_gas_req).compat().await {
6953+
Ok(gas_limit) => gas_limit,
6954+
Err(e) => {
6955+
let error_str = e.to_string().to_lowercase();
6956+
if error_str.contains("insufficient funds") || error_str.contains("exceeds allowance") {
6957+
let standard_tx_fee =
6958+
calc_total_fee(U256::from(eth_coin.gas_limit.eth_send_coins), &pay_for_gas_option).map_mm_err()?;
6959+
let threshold = u256_to_big_decimal(standard_tx_fee, eth_coin.decimals).map_mm_err()?;
6960+
let amount = u256_to_big_decimal(eth_value, eth_coin.decimals).map_mm_err()?;
6961+
6962+
return MmError::err(EthGasDetailsErr::AmountTooLow { amount, threshold });
6963+
}
6964+
// This can be a transport error or a non-standard insufficient funds error.
6965+
// In the latter case,
6966+
// we can add to the above error handling of insufficient funds on a case-by-case basis.
6967+
return MmError::err(EthGasDetailsErr::Transport(e.to_string()));
6968+
},
6969+
};
6970+
69456971
Ok((gas_limit, pay_for_gas_option))
69466972
}
69476973

mm2src/coins/eth/eth_withdraw.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ where
259259
data.clone().into(),
260260
my_address,
261261
call_addr,
262-
false,
262+
req.max,
263263
)
264264
.await
265265
.map_mm_err()?;

mm2src/coins/lp_coins.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3295,6 +3295,7 @@ impl From<EthGasDetailsErr> for WithdrawError {
32953295
fn from(e: EthGasDetailsErr) -> Self {
32963296
match e {
32973297
EthGasDetailsErr::InvalidFeePolicy(e) => WithdrawError::InvalidFeePolicy(e),
3298+
EthGasDetailsErr::AmountTooLow { amount, threshold } => WithdrawError::AmountTooLow { amount, threshold },
32983299
EthGasDetailsErr::Internal(e) => WithdrawError::InternalError(e),
32993300
EthGasDetailsErr::Transport(e) => WithdrawError::Transport(e),
33003301
EthGasDetailsErr::NftProtocolNotSupported => WithdrawError::NftProtocolNotSupported,

0 commit comments

Comments
 (0)