diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 47722ccab2..2c64179745 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -48,8 +48,8 @@ use crate::rpc_command::init_withdraw::{InitWithdrawCoin, WithdrawTaskHandleShar use crate::rpc_command::{account_balance, get_new_address, init_account_balance, init_create_account, init_scan_for_new_addresses}; use crate::{coin_balance, scan_for_new_addresses_impl, BalanceResult, CoinWithDerivationMethod, DerivationMethod, - DexFee, Eip1559Ops, MakerNftSwapOpsV2, ParseCoinAssocTypes, ParseNftAssocTypes, PayForGasParams, - PrivKeyPolicy, RpcCommonOps, SendNftMakerPaymentArgs, SpendNftMakerPaymentArgs, ToBytes, + DexFee, MakerNftSwapOpsV2, ParseCoinAssocTypes, ParseNftAssocTypes, PayForGasParams, PrivKeyPolicy, + RpcCommonOps, SendNftMakerPaymentArgs, SpendNftMakerPaymentArgs, SwapPriorityFeeOps, ToBytes, ValidateNftMakerPaymentArgs, ValidateWatcherSpendInput, WatcherSpendType}; use async_trait::async_trait; use bitcrypto::{dhash160, keccak256, ripemd160, sha256}; @@ -7373,7 +7373,7 @@ fn extract_gas_limit_from_conf(coin_conf: &Json) -> Result SwapTxFeePolicy { self.swap_txfee_policy.lock().unwrap().clone() } fn set_swap_transaction_fee_policy(&self, swap_txfee_policy: SwapTxFeePolicy) { diff --git a/mm2src/coins/eth/wallet_connect.rs b/mm2src/coins/eth/wallet_connect.rs index 30c5747269..3db6d93e1e 100644 --- a/mm2src/coins/eth/wallet_connect.rs +++ b/mm2src/coins/eth/wallet_connect.rs @@ -3,8 +3,7 @@ use super::{ChainSpec, EthCoin, EthPrivKeyPolicy}; use crate::common::Future01CompatExt; use crate::hd_wallet::AddrToString; -use crate::Eip1559Ops; -use crate::{BytesJson, MarketCoinOps, TransactionErr}; +use crate::{BytesJson, MarketCoinOps, SwapPriorityFeeOps, TransactionErr}; use common::log::info; use common::u256_to_hex; diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 1626e9d8af..305a87c1e1 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -244,8 +244,8 @@ use rpc_command::{get_new_address::{GetNewAddressTaskManager, GetNewAddressTaskM pub mod tendermint; use tendermint::htlc::CustomTendermintMsgType; -use tendermint::{CosmosTransaction, TendermintCoin, TendermintFeeDetails, TendermintProtocolInfo, TendermintToken, - TendermintTokenProtocolInfo}; +use tendermint::{CosmosTransaction, TendermintCoin, TendermintFeeDetails, TendermintPriorityFeeOption, + TendermintProtocolInfo, TendermintToken, TendermintTokenProtocolInfo}; #[doc(hidden)] #[allow(unused_variables)] @@ -2194,6 +2194,10 @@ pub enum WithdrawFee { gas_limit: u64, gas_price: f64, }, + CosmosGasPriority { + gas_limit: u64, + gas_price_option: TendermintPriorityFeeOption, + }, } /// Rename to `GetWithdrawSenderAddresses` when withdraw supports multiple `from` addresses. @@ -2636,10 +2640,12 @@ pub enum TradePreimageValue { UpperBound(BigDecimal), } -#[derive(Clone, Debug, Serialize, Deserialize, Default)] +#[derive(Clone, Debug, Serialize, Deserialize, Default, PartialEq)] pub enum SwapTxFeePolicy { + /// Different tx fee policies not supported (some default gas price getting is used) #[default] Unsupported, + /// Tx fee policy defined internally, by default Internal, Low, Medium, @@ -2658,7 +2664,7 @@ pub struct SwapTxFeePolicyRequest { pub enum SwapTxFeePolicyError { #[from_stringify("CoinFindError")] NoSuchCoin(String), - #[display(fmt = "eip-1559 policy is not supported for coin {}", _0)] + #[display(fmt = "tx fee policy is not supported for coin {}", _0)] NotSupported(String), } @@ -5929,8 +5935,9 @@ fn coins_conf_check(ctx: &MmArc, coins_en: &Json, ticker: &str, req: Option<&Jso Ok(()) } +/// Operations for transaction priority fee policy used in swaps #[async_trait] -pub trait Eip1559Ops { +pub trait SwapPriorityFeeOps { /// Return swap transaction fee policy fn get_swap_transaction_fee_policy(&self) -> SwapTxFeePolicy; @@ -5938,12 +5945,13 @@ pub trait Eip1559Ops { fn set_swap_transaction_fee_policy(&self, swap_txfee_policy: SwapTxFeePolicy); } -/// Get eip 1559 transaction fee per gas policy (low, medium, high) set for the coin +/// Get priority transaction fee policy (low, medium, high) set for the coin pub async fn get_swap_transaction_fee_policy(ctx: MmArc, req: SwapTxFeePolicyRequest) -> SwapTxFeePolicyResult { let coin = lp_coinfind_or_err(&ctx, &req.coin).await.map_mm_err()?; match coin { MmCoinEnum::EthCoin(eth_coin) => Ok(eth_coin.get_swap_transaction_fee_policy()), MmCoinEnum::Qrc20Coin(qrc20_coin) => Ok(qrc20_coin.get_swap_transaction_fee_policy()), + MmCoinEnum::Tendermint(tendermint_coin) => Ok(tendermint_coin.get_swap_transaction_fee_policy()), _ => MmError::err(SwapTxFeePolicyError::NotSupported(req.coin)), } } @@ -5960,6 +5968,10 @@ pub async fn set_swap_transaction_fee_policy(ctx: MmArc, req: SwapTxFeePolicyReq qrc20_coin.set_swap_transaction_fee_policy(req.swap_tx_fee_policy); Ok(qrc20_coin.get_swap_transaction_fee_policy()) }, + MmCoinEnum::Tendermint(tendermint_coin) => { + tendermint_coin.set_swap_transaction_fee_policy(req.swap_tx_fee_policy); + Ok(tendermint_coin.get_swap_transaction_fee_policy()) + }, _ => MmError::err(SwapTxFeePolicyError::NotSupported(req.coin)), } } diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index 02fec1b2fa..d58919e741 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -16,16 +16,16 @@ use crate::utxo::{qtum, ActualFeeRate, AddrFromStrError, BroadcastTxErr, FeePoli HistoryUtxoTx, HistoryUtxoTxMap, MatureUnspentList, RecentlySpentOutPointsGuard, UnsupportedAddr, UtxoActivationParams, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps, UtxoFromLegacyReqErr, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom, UTXO_LOCK}; -use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, ConfirmPaymentInput, DexFee, Eip1559Ops, +use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionFut, RawTransactionRequest, RawTransactionResult, RefundPaymentArgs, SearchForSwapTxSpendInput, - SendPaymentArgs, SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, SwapOps, SwapTxFeePolicy, - TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionData, - TransactionDetails, TransactionEnum, TransactionErr, TransactionResult, TransactionType, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateOtherPubKeyErr, - ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WeakSpawner, WithdrawError, - WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; + SendPaymentArgs, SignRawTransactionRequest, SignatureResult, SpendPaymentArgs, SwapOps, + SwapPriorityFeeOps, SwapTxFeePolicy, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, + TradePreimageValue, TransactionData, TransactionDetails, TransactionEnum, TransactionErr, + TransactionResult, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, + ValidateFeeArgs, ValidateOtherPubKeyErr, ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, + WatcherOps, WeakSpawner, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use chain::TransactionOutput; @@ -1533,7 +1533,7 @@ fn transfer_event_from_log(log: &LogEntry) -> Result SwapTxFeePolicy { SwapTxFeePolicy::Unsupported } fn set_swap_transaction_fee_policy(&self, _swap_txfee_policy: SwapTxFeePolicy) {} diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index c249b02c75..6461517802 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -17,12 +17,13 @@ use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, HistorySyncState, MarketCoinOps, MmCoin, NegotiateSwapContractAddrErr, PrivKeyBuildPolicy, PrivKeyPolicy, PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, RawTransactionResult, RefundPaymentArgs, RpcCommonOps, SearchForSwapTxSpendInput, SendPaymentArgs, - SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, ToBytes, TradeFee, - TradePreimageError, TradePreimageFut, TradePreimageResult, TradePreimageValue, TransactionData, - TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, TransactionResult, TransactionType, - TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, - ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, VerificationError, VerificationResult, - WaitForHTLCTxSpendArgs, WatcherOps, WeakSpawner, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest}; + SignRawTransactionRequest, SignatureError, SignatureResult, SpendPaymentArgs, SwapOps, SwapPriorityFeeOps, + SwapTxFeePolicy, ToBytes, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, + TradePreimageValue, TransactionData, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, + TransactionResult, TransactionType, TxFeeDetails, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateFeeArgs, ValidateOtherPubKeyErr, ValidatePaymentFut, ValidatePaymentInput, + VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WeakSpawner, WithdrawError, + WithdrawFee, WithdrawFut, WithdrawRequest}; use async_std::prelude::FutureExt as AsyncStdFutureExt; use async_trait::async_trait; use bip32::DerivationPath; @@ -111,6 +112,8 @@ const DEFAULT_MIN_BALANCE_FOR_IBC_ROUTING: f32 = 2.0; pub(crate) const MIN_TX_SATOSHIS: i64 = 1; +pub(crate) const PRIORITY_FEE_LEVELS: usize = 3; + // ABCI Request Defaults const ABCI_REQUEST_HEIGHT: Option = None; const ABCI_REQUEST_PROVE: bool = false; @@ -120,6 +123,8 @@ const DEFAULT_GAS_PRICE: f64 = 0.25; pub(super) const TIMEOUT_HEIGHT_DELTA: u64 = 100; pub const GAS_LIMIT_DEFAULT: u64 = 125_000; pub const GAS_WANTED_BASE_VALUE: f64 = 50_000.; +/// Multiplier to increment simulated gas limit +pub(crate) const GAS_USED_MULTIPLIER: f64 = 1.5; pub(crate) const TX_DEFAULT_MEMO: &str = ""; // https://github.com/irisnet/irismod/blob/5016c1be6fdbcffc319943f33713f4a057622f0a/modules/htlc/types/validation.go#L19-L22 @@ -205,12 +210,50 @@ pub struct TendermintProtocolInfo { pub account_prefix: String, pub chain_id: ChainId, gas_price: Option, + priority_gas_prices: Option<[f64; PRIORITY_FEE_LEVELS]>, /// Key represents the account prefix of the target chain and /// the value is the channel ID used for sending transactions. #[serde(default)] ibc_channels: HashMap, } +#[derive(Clone, Debug, Deserialize, PartialEq)] +pub enum TendermintPriorityFeeOption { + Low = 0, + Average = 1, + High = 2, + Default = -1, +} + +impl From for TendermintPriorityFeeOption { + fn from(swap_fee_policy: SwapTxFeePolicy) -> Self { + match swap_fee_policy { + SwapTxFeePolicy::Unsupported => Self::Default, + SwapTxFeePolicy::Internal => Self::Default, + SwapTxFeePolicy::Low => Self::Low, + SwapTxFeePolicy::Medium => Self::Average, + SwapTxFeePolicy::High => Self::High, + } + } +} + +/// Tx fee params for calculate_fee fn +#[derive(Clone, Debug, Deserialize, PartialEq)] +pub(crate) enum CalculateFeeParams { + /// Fee params for withdraw + WithdrawFee(Option), + /// Priority fee policy for swaps + SwapTxFeePolicy(SwapTxFeePolicy), +} + +impl From> for CalculateFeeParams { + fn from(opt_withdraw_fee: Option) -> Self { Self::WithdrawFee(opt_withdraw_fee) } +} + +impl From for CalculateFeeParams { + fn from(swap_fee_policy: SwapTxFeePolicy) -> Self { Self::SwapTxFeePolicy(swap_fee_policy) } +} + #[derive(Clone)] pub struct ActivatedTokenInfo { pub(crate) decimals: u8, @@ -405,6 +448,8 @@ pub struct TendermintCoinImpl { /// My address pub account_id: AccountId, pub activation_policy: TendermintActivationPolicy, + priority_gas_prices: Option<[f64; PRIORITY_FEE_LEVELS]>, + swap_txfee_policy: Mutex, pub tokens_info: PaMutex>, /// This spawner is used to spawn coin's related futures that should be aborted on coin deactivation /// or on [`MmArc::stop`]. @@ -770,6 +815,8 @@ impl TendermintCoin { ticker, account_id, activation_policy, + priority_gas_prices: protocol_info.priority_gas_prices, + swap_txfee_policy: Mutex::new(SwapTxFeePolicy::Internal), avg_blocktime: conf.avg_blocktime, tokens_info: PaMutex::new(HashMap::new()), abortable_system, @@ -863,6 +910,15 @@ impl TendermintCoin { #[inline(always)] fn gas_price(&self) -> f64 { self.protocol_info.gas_price.unwrap_or(DEFAULT_GAS_PRICE) } + /// Return gas price for priority level, which was set in the coins file, + /// or default gas price + fn priority_gas_price(&self, priority_fee_option: TendermintPriorityFeeOption) -> f64 { + let default_gas_price = self.gas_price(); + self.priority_gas_prices + .map(|prices| *prices.get(priority_fee_option as usize).unwrap_or(&default_gas_price)) + .unwrap_or(default_gas_price) + } + #[allow(unused)] async fn get_latest_block(&self) -> MmResult { let request = GetLatestBlockRequest {}; @@ -1106,13 +1162,13 @@ impl TendermintCoin { msg: Any, timeout_height: u64, memo: &str, - withdraw_fee: Option, + fee_params: CalculateFeeParams, ) -> MmResult { let activated_priv_key = if let Ok(activated_priv_key) = self.activation_policy.activated_key_or_err() { activated_priv_key } else { - let (gas_price, gas_limit) = self.gas_info_for_withdraw(&withdraw_fee, GAS_LIMIT_DEFAULT); - let amount = ((GAS_WANTED_BASE_VALUE * 1.5) * gas_price).ceil(); + let (gas_price, gas_limit) = self.gas_price_and_limit(fee_params, GAS_LIMIT_DEFAULT); + let amount = ((GAS_WANTED_BASE_VALUE * GAS_USED_MULTIPLIER) * gas_price).ceil(); let fee_amount = Coin { denom: self.platform_denom().clone(), @@ -1167,10 +1223,9 @@ impl TendermintCoin { )) })?; - let (gas_price, gas_limit) = self.gas_info_for_withdraw(&withdraw_fee, GAS_LIMIT_DEFAULT); - - let amount = ((gas.gas_used as f64 * 1.5) * gas_price).ceil(); + let (gas_price, gas_limit) = self.gas_price_and_limit(fee_params, GAS_LIMIT_DEFAULT); + let amount = ((gas.gas_used as f64 * GAS_USED_MULTIPLIER) * gas_price).ceil(); let fee_amount = Coin { denom: self.platform_denom().clone(), amount: (amount as u64).into(), @@ -1187,13 +1242,13 @@ impl TendermintCoin { msg: Any, timeout_height: u64, memo: &str, - withdraw_fee: Option, + fee_params: CalculateFeeParams, ) -> MmResult { let priv_key = if let Some(priv_key) = priv_key { priv_key } else { - let (gas_price, _) = self.gas_info_for_withdraw(&withdraw_fee, 0); - return Ok(((GAS_WANTED_BASE_VALUE * 1.5) * gas_price).ceil() as u64); + let (gas_price, _) = self.gas_price_and_limit(fee_params, 0); + return Ok(((GAS_WANTED_BASE_VALUE * GAS_USED_MULTIPLIER) * gas_price).ceil() as u64); }; let mut account_info = self.account_info(account_id).await?; @@ -1241,9 +1296,8 @@ impl TendermintCoin { )) })?; - let (gas_price, _) = self.gas_info_for_withdraw(&withdraw_fee, 0); - - Ok(((gas.gas_used as f64 * 1.5) * gas_price).ceil() as u64) + let (gas_price, _) = self.gas_price_and_limit(fee_params, 0); + Ok(((gas.gas_used as f64 * GAS_USED_MULTIPLIER) * gas_price).ceil() as u64) } pub(super) async fn account_info(&self, account_id: &AccountId) -> MmResult { @@ -1758,7 +1812,7 @@ impl TendermintCoin { create_htlc_tx.msg_payload.clone(), timeout_height, TX_DEFAULT_MEMO, - None + coin.get_swap_transaction_fee_policy().into() ) .await ); @@ -1855,8 +1909,13 @@ impl TendermintCoin { let timeout_height = current_block + TIMEOUT_HEIGHT_DELTA; let fee = try_tx_s!( - coin.calculate_fee(tx_payload.clone(), timeout_height, TX_DEFAULT_MEMO, None) - .await + coin.calculate_fee( + tx_payload.clone(), + timeout_height, + TX_DEFAULT_MEMO, + coin.get_swap_transaction_fee_policy().into() + ) + .await ); let timeout = expires_at.checked_sub(now_sec()).unwrap_or_default(); @@ -2228,7 +2287,7 @@ impl TendermintCoin { create_htlc_tx.msg_payload.clone(), timeout_height, TX_DEFAULT_MEMO, - None, + self.get_swap_transaction_fee_policy().into(), ) .await .map_mm_err()?; @@ -2280,7 +2339,7 @@ impl TendermintCoin { msg_send, timeout_height, TX_DEFAULT_MEMO, - None, + self.get_swap_transaction_fee_policy().into(), ) .await .map_mm_err()?; @@ -2459,13 +2518,18 @@ impl TendermintCoin { } } - pub(crate) fn gas_info_for_withdraw( - &self, - withdraw_fee: &Option, - fallback_gas_limit: u64, - ) -> (f64, u64) { - match withdraw_fee { - Some(WithdrawFee::CosmosGas { gas_price, gas_limit }) => (*gas_price, *gas_limit), + pub(crate) fn gas_price_and_limit(&self, fee_params: CalculateFeeParams, fallback_gas_limit: u64) -> (f64, u64) { + match fee_params { + CalculateFeeParams::WithdrawFee(Some(WithdrawFee::CosmosGas { gas_price, gas_limit })) => { + (gas_price, gas_limit) + }, + CalculateFeeParams::WithdrawFee(Some(WithdrawFee::CosmosGasPriority { + gas_price_option, + gas_limit, + })) => (self.priority_gas_price(gas_price_option), gas_limit), + CalculateFeeParams::SwapTxFeePolicy(swap_fee_policy) => { + (self.priority_gas_price(swap_fee_policy.into()), fallback_gas_limit) + }, _ => (self.gas_price(), fallback_gas_limit), } } @@ -2625,7 +2689,7 @@ impl TendermintCoin { // `delegate` uses more gas than the regular transactions let gas_limit_default = (GAS_LIMIT_DEFAULT * 3) / 2; - let (_, gas_limit) = self.gas_info_for_withdraw(&req.fee, gas_limit_default); + let (_, gas_limit) = self.gas_price_and_limit(req.fee.clone().into(), gas_limit_default); let fee_amount_u64 = self .calculate_account_fee_amount_as_u64( @@ -2634,7 +2698,7 @@ impl TendermintCoin { msg_for_fee_prediction, timeout_height, &req.memo, - req.fee, + req.fee.into(), ) .await .map_mm_err()?; @@ -2762,7 +2826,7 @@ impl TendermintCoin { // This uses more gas than any other transactions let gas_limit_default = GAS_LIMIT_DEFAULT * 2; - let (_, gas_limit) = self.gas_info_for_withdraw(&req.fee, gas_limit_default); + let (_, gas_limit) = self.gas_price_and_limit(req.fee.clone().into(), gas_limit_default); let fee_amount_u64 = self .calculate_account_fee_amount_as_u64( @@ -2771,7 +2835,7 @@ impl TendermintCoin { undelegate_msg.clone(), timeout_height, &req.memo, - req.fee, + req.fee.clone().into(), ) .await .map_mm_err()?; @@ -2962,7 +3026,7 @@ impl TendermintCoin { // This uses more gas than the regular transactions let gas_limit_default = (GAS_LIMIT_DEFAULT * 3) / 2; - let (_, gas_limit) = self.gas_info_for_withdraw(&req.fee, gas_limit_default); + let (_, gas_limit) = self.gas_price_and_limit(req.fee.clone().into(), gas_limit_default); let fee_amount_u64 = self .calculate_account_fee_amount_as_u64( @@ -2971,7 +3035,7 @@ impl TendermintCoin { msg.clone(), timeout_height, &req.memo, - req.fee, + req.fee.clone().into(), ) .await .map_mm_err()?; @@ -3309,9 +3373,9 @@ impl MmCoin for TendermintCoin { let timeout_height = current_block + TIMEOUT_HEIGHT_DELTA; let (_, gas_limit) = if is_ibc_transfer { - coin.gas_info_for_withdraw(&req.fee, IBC_GAS_LIMIT_DEFAULT) + coin.gas_price_and_limit(req.fee.clone().into(), IBC_GAS_LIMIT_DEFAULT) } else { - coin.gas_info_for_withdraw(&req.fee, GAS_LIMIT_DEFAULT) + coin.gas_price_and_limit(req.fee.clone().into(), GAS_LIMIT_DEFAULT) }; let fee_amount_u64 = coin @@ -3321,7 +3385,7 @@ impl MmCoin for TendermintCoin { msg_payload.clone(), timeout_height, &memo, - req.fee, + req.fee.into(), ) .await .map_mm_err()?; @@ -3972,8 +4036,13 @@ impl SwapOps for TendermintCoin { let timeout_height = current_block + TIMEOUT_HEIGHT_DELTA; let fee = try_tx_s!( - self.calculate_fee(claim_htlc_tx.msg_payload.clone(), timeout_height, TX_DEFAULT_MEMO, None) - .await + self.calculate_fee( + claim_htlc_tx.msg_payload.clone(), + timeout_height, + TX_DEFAULT_MEMO, + self.get_swap_transaction_fee_policy().into() + ) + .await ); let (_tx_id, tx_raw) = try_tx_s!( @@ -4028,8 +4097,13 @@ impl SwapOps for TendermintCoin { let timeout_height = current_block + TIMEOUT_HEIGHT_DELTA; let fee = try_tx_s!( - self.calculate_fee(claim_htlc_tx.msg_payload.clone(), timeout_height, TX_DEFAULT_MEMO, None) - .await + self.calculate_fee( + claim_htlc_tx.msg_payload.clone(), + timeout_height, + TX_DEFAULT_MEMO, + self.get_swap_transaction_fee_policy().into() + ) + .await ); let (tx_id, tx_raw) = try_tx_s!( @@ -4229,6 +4303,14 @@ pub fn tendermint_priv_key_policy( } } +impl SwapPriorityFeeOps for TendermintCoin { + fn get_swap_transaction_fee_policy(&self) -> SwapTxFeePolicy { self.swap_txfee_policy.lock().unwrap().clone() } + + fn set_swap_transaction_fee_policy(&self, swap_txfee_policy: SwapTxFeePolicy) { + *self.swap_txfee_policy.lock().unwrap() = swap_txfee_policy + } +} + pub(crate) async fn create_withdraw_msg_as_any( sender: AccountId, receiver: AccountId, @@ -4295,6 +4377,7 @@ pub mod tendermint_falsecoin_tests { use cosmrs::proto::cosmos::tx::v1beta1::{GetTxRequest, GetTxResponse}; use crypto::privkey::key_pair_from_seed; use mocktopus::mocking::{MockResult, Mockable}; + use num_traits::FromPrimitive; use std::{mem::discriminant, num::NonZeroUsize}; pub const IRIS_TESTNET_HTLC_PAIR1_SEED: &str = "iris test seed"; @@ -4342,6 +4425,7 @@ pub mod tendermint_falsecoin_tests { account_prefix: String::from(IRIS_PREFIX), chain_id: ChainId::from_str("nyancat-9").unwrap(), gas_price: None, + priority_gas_prices: None, ibc_channels: HashMap::new(), } } @@ -4357,6 +4441,7 @@ pub mod tendermint_falsecoin_tests { account_prefix: String::from(IRIS_PREFIX), chain_id: ChainId::from_str("nyancat-9").unwrap(), gas_price: None, + priority_gas_prices: Some([0.1, 0.2, 0.3]), ibc_channels, } } @@ -4369,6 +4454,7 @@ pub mod tendermint_falsecoin_tests { account_prefix: String::from(NUCLEUS_PREFIX), chain_id: ChainId::from_str("nucleus-testnet").unwrap(), gas_price: None, + priority_gas_prices: None, ibc_channels: HashMap::new(), } } @@ -4452,7 +4538,7 @@ pub mod tendermint_falsecoin_tests { create_htlc_tx.msg_payload.clone(), timeout_height, TX_DEFAULT_MEMO, - None, + coin.get_swap_transaction_fee_policy().into(), ) .await .unwrap() @@ -4491,9 +4577,14 @@ pub mod tendermint_falsecoin_tests { let timeout_height = current_block + TIMEOUT_HEIGHT_DELTA; let fee = block_on(async { - coin.calculate_fee(claim_htlc_tx.msg_payload.clone(), timeout_height, TX_DEFAULT_MEMO, None) - .await - .unwrap() + coin.calculate_fee( + claim_htlc_tx.msg_payload.clone(), + timeout_height, + TX_DEFAULT_MEMO, + coin.get_swap_transaction_fee_policy().into(), + ) + .await + .unwrap() }); let send_tx_fut = coin.common_send_raw_tx_bytes( @@ -5437,4 +5528,44 @@ pub mod tendermint_falsecoin_tests { assert_eq!(expected_channel, actual_channel); assert_eq!(expected_channel_str, actual_channel_str); } + + /// Test getting priority fee for swaps + #[test] + fn test_swap_priority_fee_policy() { + const PRIORITY_FEE_HIGH: f64 = 0.3; + let nodes = vec![RpcNode::for_test(IRIS_TESTNET_RPC_URL)]; + + let protocol_conf = get_iris_protocol(); + + let ctx = mm2_core::mm_ctx::MmCtxBuilder::default().into_mm_arc(); + + let conf = TendermintConf { + avg_blocktime: AVG_BLOCKTIME, + derivation_path: None, + }; + + let key_pair = key_pair_from_seed(IRIS_TESTNET_HTLC_PAIR1_SEED).unwrap(); + let tendermint_pair = TendermintKeyPair::new(key_pair.private().secret, *key_pair.public()); + let activation_policy = + TendermintActivationPolicy::with_private_key_policy(TendermintPrivKeyPolicy::Iguana(tendermint_pair)); + + let coin = block_on(TendermintCoin::init( + &ctx, + "IRIS-TEST".to_string(), + conf, + protocol_conf, + nodes, + false, + activation_policy, + TendermintWalletConnectionType::Native, + )) + .unwrap(); + + coin.set_swap_transaction_fee_policy(SwapTxFeePolicy::High); + let (gas_price, _) = coin.gas_price_and_limit( + CalculateFeeParams::SwapTxFeePolicy(coin.get_swap_transaction_fee_policy()), + 0_u64, + ); + assert_eq!(BigDecimal::from_f64(gas_price), BigDecimal::from_f64(PRIORITY_FEE_HIGH)); + } } diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index c85fe08605..b3ad934b0e 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -460,9 +460,9 @@ impl MmCoin for TendermintToken { let timeout_height = current_block + TIMEOUT_HEIGHT_DELTA; let (_, gas_limit) = if is_ibc_transfer { - platform.gas_info_for_withdraw(&req.fee, IBC_GAS_LIMIT_DEFAULT) + platform.gas_price_and_limit(req.fee.clone().into(), IBC_GAS_LIMIT_DEFAULT) } else { - platform.gas_info_for_withdraw(&req.fee, GAS_LIMIT_DEFAULT) + platform.gas_price_and_limit(req.fee.clone().into(), GAS_LIMIT_DEFAULT) }; let fee_amount_u64 = platform @@ -472,7 +472,7 @@ impl MmCoin for TendermintToken { msg_payload.clone(), timeout_height, &memo, - req.fee, + req.fee.into(), ) .await .map_mm_err()?; diff --git a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs index 81fe6d4a10..59564d6d73 100644 --- a/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/eth_docker_tests.rs @@ -21,10 +21,10 @@ use coins::{lp_coinfind, CoinsContext, DexFee, FundingTxSpend, GenTakerFundingSp SpendMakerPaymentArgs, TakerCoinSwapOpsV2, TxPreimageWithSig, ValidateMakerPaymentArgs, ValidateTakerFundingArgs}; use coins::{CoinProtocol, CoinWithDerivationMethod, CommonSwapOpsV2, ConfirmPaymentInput, DerivationMethod, - Eip1559Ops, FoundSwapTxSpend, MakerNftSwapOpsV2, MarketCoinOps, NftSwapInfo, ParseCoinAssocTypes, - ParseNftAssocTypes, PrivKeyBuildPolicy, RefundNftMakerPaymentArgs, RefundPaymentArgs, - SearchForSwapTxSpendInput, SendNftMakerPaymentArgs, SendPaymentArgs, SpendNftMakerPaymentArgs, - SpendPaymentArgs, SwapOps, SwapTxFeePolicy, SwapTxTypeWithSecretHash, ToBytes, Transaction, + FoundSwapTxSpend, MakerNftSwapOpsV2, MarketCoinOps, NftSwapInfo, ParseCoinAssocTypes, ParseNftAssocTypes, + PrivKeyBuildPolicy, RefundNftMakerPaymentArgs, RefundPaymentArgs, SearchForSwapTxSpendInput, + SendNftMakerPaymentArgs, SendPaymentArgs, SpendNftMakerPaymentArgs, SpendPaymentArgs, SwapOps, + SwapPriorityFeeOps, SwapTxFeePolicy, SwapTxTypeWithSecretHash, ToBytes, Transaction, ValidateNftMakerPaymentArgs}; use common::{block_on, block_on_f01, now_sec}; use crypto::Secp256k1Secret; diff --git a/mm2src/mm2_main/tests/docker_tests/tendermint_tests.rs b/mm2src/mm2_main/tests/docker_tests/tendermint_tests.rs index 4656613683..065bd1f662 100644 --- a/mm2src/mm2_main/tests/docker_tests/tendermint_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/tendermint_tests.rs @@ -1,3 +1,4 @@ +use coins::utxo::utxo_common::big_decimal_from_sat; use common::{block_on, log}; use mm2_number::BigDecimal; use mm2_rpc::data::legacy::OrderbookResponse; @@ -14,6 +15,7 @@ use mm2_test_helpers::structs::{Bip44Chain, HDAccountAddressId, OrderbookAddress use serde_json::json; use std::collections::HashSet; use std::iter::FromIterator; +use std::str::FromStr; use std::sync::Mutex; use std::thread; use std::time::Duration; @@ -809,6 +811,45 @@ fn test_tendermint_remove_delegation() { assert_eq!(undelegation_entry["balance"], "0.15"); } +#[test] +fn test_priority_gas_on_tendermint_withdraw() { + const GAS_PRICE_HIGH: f64 = 0.3; + const GAS_USED: u64 = 55470; + const GAS_USED_MULTIPLIER: f64 = 1.5; + let coins = json!([atom_testnet_conf()]); + let coin = coins[0]["coin"].as_str().unwrap(); + + let conf = Mm2TestConf::seednode(TENDERMINT_TEST_SEED, &coins); + let mm = MarketMakerIt::start(conf.conf, conf.rpc_password, None).unwrap(); + + let activation_res = block_on(enable_tendermint(&mm, coin, &[], ATOM_TENDERMINT_RPC_URLS, false)); + log!("Activation {}", serde_json::to_string(&activation_res).unwrap()); + + let request = block_on(mm.rpc(&json!({ + "userpass": mm.userpass, + "method": "withdraw", + "coin": coin, + "to": "cosmos1w5h6wud7a8zpa539rc99ehgl9gwkad3wjsjq8v", + "amount": "0.1", + "fee": { + "type": "CosmosGasPriority", + "gas_limit": 150000, + "gas_price_option": "High" + } + }))) + .unwrap(); + assert_eq!(request.0, common::StatusCode::OK, "'withdraw' failed: {}", request.1); + let tx_details: TransactionDetails = serde_json::from_str(&request.1).unwrap(); + let expected = big_decimal_from_sat( + (GAS_PRICE_HIGH * GAS_USED as f64 * GAS_USED_MULTIPLIER).ceil() as i64, + 6, + ); + assert_eq!( + BigDecimal::from_str(tx_details.fee_details["amount"].as_str().unwrap()).unwrap(), + expected + ); +} + mod swap { use super::*; diff --git a/mm2src/mm2_test_helpers/src/for_tests.rs b/mm2src/mm2_test_helpers/src/for_tests.rs index 37a8b6bfb4..61f425a74c 100644 --- a/mm2src/mm2_test_helpers/src/for_tests.rs +++ b/mm2src/mm2_test_helpers/src/for_tests.rs @@ -645,6 +645,7 @@ pub fn atom_testnet_conf() -> Json { "denom": "uatom", "account_prefix": "cosmos", "chain_id": "cosmoshub-testnet", + "priority_gas_prices": [0.1, 0.2, 0.3] }, }, "derivation_path": "m/44'/118'",