diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index b8db8c7c12..ea00074814 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -5891,11 +5891,24 @@ impl MmCoin for EthCoin { dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { - let dex_fee_amount = wei_from_big_decimal(&dex_fee_amount.fee_amount().into(), self.decimals)?; + let fee_coin = match &self.coin_type { + EthCoinType::Eth => self.ticker.to_owned(), + EthCoinType::Erc20 { platform, .. } => platform.to_owned(), + EthCoinType::Nft { .. } => return MmError::err(TradePreimageError::NftProtocolNotSupported), + }; + if dex_fee_amount.no_fee() { + return Ok(TradeFee { + coin: fee_coin, + amount: MmNumber::default(), + paid_from_trading_vol: false, + }); + } + let dex_fee_amount = wei_from_big_decimal(&dex_fee_amount.fee_amount().into(), self.decimals)?; // pass the dummy params let to_addr = addr_from_raw_pubkey(&DEX_FEE_ADDR_RAW_PUBKEY) .expect("addr_from_raw_pubkey should never fail with DEX_FEE_ADDR_RAW_PUBKEY"); + let my_address = self.derivation_method.single_addr_or_err().await?; let (eth_value, data, call_addr, fee_coin) = match &self.coin_type { EthCoinType::Eth => (dex_fee_amount, Vec::new(), &to_addr, &self.ticker), EthCoinType::Erc20 { platform, token_addr } => { @@ -5905,8 +5918,6 @@ impl MmCoin for EthCoin { }, EthCoinType::Nft { .. } => return MmError::err(TradePreimageError::NftProtocolNotSupported), }; - - let my_address = self.derivation_method.single_addr_or_err().await?; let fee_policy_for_estimate = get_swap_fee_policy_for_estimate(self.get_swap_transaction_fee_policy()); let pay_for_gas_option = self.get_swap_pay_for_gas_option(fee_policy_for_estimate).await?; let pay_for_gas_option = increase_gas_price_by_stage(pay_for_gas_option, &stage); diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 16ade17a6b..2b3fd9e621 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -3624,6 +3624,8 @@ pub enum DexFee { fee_amount: MmNumber, burn_amount: MmNumber, }, + /// Zero dex fee, exclusive to KMD only. + Zero, } impl DexFee { @@ -3640,13 +3642,14 @@ impl DexFee { match self { DexFee::Standard(t) => t.clone(), DexFee::WithBurn { fee_amount, .. } => fee_amount.clone(), + DexFee::Zero => MmNumber::default(), } } /// Gets the burn amount associated with the dex fee, if applicable. pub fn burn_amount(&self) -> Option { match self { - DexFee::Standard(_) => None, + DexFee::Standard(_) | DexFee::Zero => None, DexFee::WithBurn { burn_amount, .. } => Some(burn_amount.clone()), } } @@ -3659,6 +3662,7 @@ impl DexFee { fee_amount, burn_amount, } => fee_amount + burn_amount, + DexFee::Zero => MmNumber::default(), } } @@ -3676,6 +3680,9 @@ impl DexFee { Ok(None) } } + + /// Check and return true if DexFee is not required to trade otherwise return false. + pub fn no_fee(&self) -> bool { matches!(self, Self::Zero) } } pub struct CoinsContext { diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index df14fea09a..e25574741f 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -1418,6 +1418,14 @@ impl MmCoin for Qrc20Coin { dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { + if dex_fee_amount.no_fee() { + return Ok(TradeFee { + coin: self.platform.clone(), + amount: MmNumber::default(), + paid_from_trading_vol: false, + }); + } + let amount = wei_from_big_decimal(&dex_fee_amount.fee_amount().into(), self.utxo.decimals)?; // pass the dummy params diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 9573e6de4b..469aae4ed1 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -2478,6 +2478,14 @@ impl MmCoin for TendermintCoin { dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { + if dex_fee_amount.no_fee() { + return Ok(TradeFee { + coin: self.ticker.clone(), + amount: MmNumber::default(), + paid_from_trading_vol: false, + }); + } + self.get_fee_to_send_taker_fee_for_denom(self.ticker.clone(), self.denom.clone(), self.decimals, dex_fee_amount) .await } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index cbc7780a34..60d55a1c2c 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1844,6 +1844,14 @@ impl MmCoin for SlpToken { dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { + if dex_fee_amount.no_fee() { + return Ok(TradeFee { + coin: self.platform_coin.ticker().into(), + amount: MmNumber::default(), + paid_from_trading_vol: false, + }); + } + let slp_amount = sat_from_big_decimal(&dex_fee_amount.fee_amount().into(), self.decimals())?; // can use dummy P2PKH script_pubkey here let script_pubkey = ScriptBuilder::build_p2pkh(&H160::default().into()).into(); @@ -1861,6 +1869,7 @@ impl MmCoin for SlpToken { &stage, ) .await?; + Ok(TradeFee { coin: self.platform_coin.ticker().into(), amount: fee.into(), diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 70c8522b58..f7c4b1783f 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -1243,39 +1243,50 @@ async fn gen_taker_payment_spend_preimage( args: &GenTakerPaymentSpendArgs<'_, T>, n_time: NTimeSetting, ) -> GenPreimageResInner { - let dex_fee_address = address_from_raw_pubkey( - args.dex_fee_pub, - coin.as_ref().conf.address_prefixes.clone(), - coin.as_ref().conf.checksum_type, - coin.as_ref().conf.bech32_hrp.clone(), - coin.addr_format().clone(), - ) - .map_to_mm(|e| TxGenError::AddressDerivation(format!("Failed to derive dex_fee_address: {}", e)))?; - - let mut outputs = generate_taker_fee_tx_outputs(coin.as_ref().decimals, dex_fee_address.hash(), args.dex_fee)?; - if let DexFee::WithBurn { .. } = args.dex_fee { - let script = output_script(args.maker_address).map_to_mm(|e| { - TxGenError::Other(format!( - "Couldn't generate output script for maker address {}, error {}", - args.maker_address, e - )) - })?; - let tx_fee = coin - .get_htlc_spend_fee(DEFAULT_SWAP_TX_SPEND_SIZE, &FeeApproxStage::WithoutApprox) - .await?; - let maker_value = args - .taker_tx - .first_output() - .map_to_mm(|e| TxGenError::PrevTxIsNotValid(e.to_string()))? - .value - - outputs[0].value - - outputs[1].value - - tx_fee; - outputs.push(TransactionOutput { - value: maker_value, - script_pubkey: script.to_bytes(), - }) - } + let outputs = match args.dex_fee { + DexFee::Zero => vec![], + dex_fee => { + let dex_fee_address = address_from_raw_pubkey( + args.dex_fee_pub, + coin.as_ref().conf.address_prefixes.clone(), + coin.as_ref().conf.checksum_type, + coin.as_ref().conf.bech32_hrp.clone(), + coin.addr_format().clone(), + ) + .map_to_mm(|e| TxGenError::AddressDerivation(format!("Failed to derive dex_fee_address: {}", e)))?; + + let mut fee_outputs = + generate_taker_fee_tx_outputs(coin.as_ref().decimals, dex_fee_address.hash(), dex_fee)?; + + if let DexFee::WithBurn { .. } = dex_fee { + let script = output_script(args.maker_address).map_to_mm(|e| { + TxGenError::Other(format!( + "Couldn't generate output script for maker address {}, error {}", + args.maker_address, e + )) + })?; + + let tx_fee = coin + .get_htlc_spend_fee(DEFAULT_SWAP_TX_SPEND_SIZE, &FeeApproxStage::WithoutApprox) + .await?; + + let total_fee_value: u64 = fee_outputs.iter().map(|out| out.value).sum(); + let first_output_value = args + .taker_tx + .first_output() + .map_to_mm(|e| TxGenError::PrevTxIsNotValid(e.to_string()))? + .value; + + let maker_value = first_output_value - total_fee_value - tx_fee; + fee_outputs.push(TransactionOutput { + value: maker_value, + script_pubkey: script.to_bytes(), + }); + } + + fee_outputs + }, + }; p2sh_spending_tx_preimage( coin, @@ -1306,7 +1317,7 @@ pub async fn gen_and_sign_taker_payment_spend_preimage( let sig_hash_type = match args.dex_fee { DexFee::Standard(_) => SIGHASH_SINGLE, - DexFee::WithBurn { .. } => SIGHASH_ALL, + DexFee::WithBurn { .. } | DexFee::Zero => SIGHASH_ALL, }; let signature = calc_and_sign_sighash( @@ -1349,7 +1360,7 @@ pub async fn validate_taker_payment_spend_preimage( let sig_hash_type = match gen_args.dex_fee { DexFee::Standard(_) => SIGHASH_SINGLE, - DexFee::WithBurn { .. } => SIGHASH_ALL, + DexFee::WithBurn { .. } | DexFee::Zero => SIGHASH_ALL, }; let sig_hash = signature_hash_to_sign( @@ -1433,7 +1444,7 @@ pub async fn sign_and_broadcast_taker_payment_spend( let mut taker_signature_with_sighash = preimage.signature.to_vec(); let taker_sig_hash = match gen_args.dex_fee { DexFee::Standard(_) => (SIGHASH_SINGLE | coin.as_ref().conf.fork_id) as u8, - DexFee::WithBurn { .. } => (SIGHASH_ALL | coin.as_ref().conf.fork_id) as u8, + DexFee::WithBurn { .. } | DexFee::Zero => (SIGHASH_ALL | coin.as_ref().conf.fork_id) as u8, }; taker_signature_with_sighash.push(taker_sig_hash); @@ -1486,6 +1497,11 @@ fn generate_taker_fee_tx_outputs( address_hash: &AddressHashEnum, dex_fee: &DexFee, ) -> Result, MmError> { + // Don't add outputs for Zero DexFee. + if dex_fee.no_fee() { + return Ok(vec![]); + } + let fee_amount = dex_fee.fee_uamount(decimals)?; let mut outputs = vec![TransactionOutput { @@ -3973,6 +3989,14 @@ pub async fn get_fee_to_send_taker_fee( where T: MarketCoinOps + UtxoCommonOps, { + if dex_fee.no_fee() { + return Ok(TradeFee { + coin: coin.ticker().to_owned(), + amount: MmNumber::default(), + paid_from_trading_vol: false, + }); + } + let decimals = coin.as_ref().decimals; let outputs = generate_taker_fee_tx_outputs(decimals, &AddressHashEnum::default_address_hash(), &dex_fee)?; diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index b8c7c8c944..39c6ace6e2 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -1750,9 +1750,17 @@ impl MmCoin for ZCoin { async fn get_fee_to_send_taker_fee( &self, - _dex_fee_amount: DexFee, + dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { + if dex_fee_amount.no_fee() { + return Ok(TradeFee { + coin: self.ticker().to_owned(), + amount: MmNumber::default(), + paid_from_trading_vol: false, + }); + } + Ok(TradeFee { coin: self.ticker().to_owned(), amount: self.get_one_kbyte_tx_fee().await?.into(), diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index fc8fcfc277..1bdff48d5e 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -800,6 +800,7 @@ fn dex_fee_rate(base: &str, rel: &str) -> MmNumber { } else { &["KMD"] }; + if fee_discount_tickers.contains(&base) || fee_discount_tickers.contains(&rel) { // 1/777 - 10% BigRational::new(9.into(), 7770.into()).into() @@ -809,38 +810,17 @@ fn dex_fee_rate(base: &str, rel: &str) -> MmNumber { } pub fn dex_fee_amount(base: &str, rel: &str, trade_amount: &MmNumber, min_tx_amount: &MmNumber) -> DexFee { + // if base or rel coin is KMD, dex fee should be set zero + if base == "KMD" || rel == "KMD" { + return DexFee::Zero; + } + let rate = dex_fee_rate(base, rel); let fee = trade_amount * &rate; - if &fee <= min_tx_amount { return DexFee::Standard(min_tx_amount.clone()); } - if base == "KMD" { - // Drop the fee by 25%, which will be burned during the taker fee payment. - // - // This cut will be dropped before return if the final amount is less than - // the minimum transaction amount. - - // Fee with 25% cut - let new_fee = &fee * &MmNumber::from("0.75"); - - let (fee, burn) = if &new_fee >= min_tx_amount { - // Use the max burn value, which is 25%. - let burn_amount = &fee - &new_fee; - - (new_fee, burn_amount) - } else { - // Burn only the exceed amount because fee after 25% cut is less - // than `min_tx_amount`. - let burn_amount = &fee - min_tx_amount; - - (min_tx_amount.clone(), burn_amount) - }; - - return DexFee::with_burn(fee, burn); - } - DexFee::Standard(fee) } @@ -989,7 +969,7 @@ impl SwapTxDataMsg { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] pub struct TransactionIdentifier { /// Raw bytes of signed transaction in hexadecimal string, this should be sent as is to send_raw_transaction RPC to broadcast the transaction. /// Some payments like lightning payments don't have a tx_hex, for such payments tx_hex will be equal to tx_hash. @@ -1864,9 +1844,7 @@ mod lp_swap_tests { let rel = "ETH"; let amount = 1.into(); let actual_fee = dex_fee_amount(base, rel, &amount, &min_tx_amount); - let expected_fee = amount.clone() * (9, 7770).into() * MmNumber::from("0.75"); - let expected_burn_amount = amount * (9, 7770).into() * MmNumber::from("0.25"); - assert_eq!(DexFee::with_burn(expected_fee, expected_burn_amount), actual_fee); + assert_eq!(DexFee::Zero, actual_fee); // check the case when KMD taker fee is close to dust let base = "KMD"; @@ -1874,26 +1852,19 @@ mod lp_swap_tests { let amount = (1001 * 777, 90000000).into(); let min_tx_amount = "0.00001".into(); let actual_fee = dex_fee_amount(base, rel, &amount, &min_tx_amount); - assert_eq!( - DexFee::WithBurn { - fee_amount: "0.00001".into(), - burn_amount: "0.00000001".into() - }, - actual_fee - ); + assert_eq!(DexFee::Zero, actual_fee); let base = "BTC"; let rel = "KMD"; let amount = 1.into(); let actual_fee = dex_fee_amount(base, rel, &amount, &min_tx_amount); - let expected_fee = DexFee::Standard(amount * (9, 7770).into()); - assert_eq!(expected_fee, actual_fee); + assert_eq!(DexFee::Zero, actual_fee); let base = "BTC"; let rel = "KMD"; let amount: MmNumber = "0.001".parse::().unwrap().into(); let actual_fee = dex_fee_amount(base, rel, &amount, &min_tx_amount); - assert_eq!(DexFee::Standard(min_tx_amount), actual_fee); + assert_eq!(DexFee::Zero, actual_fee); } #[test] @@ -2413,27 +2384,25 @@ mod lp_swap_tests { std::env::set_var("MYCOIN_FEE_DISCOUNT", ""); let kmd = coins::TestCoin::new("KMD"); - let (kmd_taker_fee, kmd_burn_amount) = match dex_fee_amount_from_taker_coin(&kmd, "", &MmNumber::from(6150)) { - DexFee::Standard(_) => panic!("Wrong variant returned for KMD from `dex_fee_amount_from_taker_coin`."), - DexFee::WithBurn { - fee_amount, - burn_amount, - } => (fee_amount, burn_amount), + let kmd_burn_amount = match dex_fee_amount_from_taker_coin(&kmd, "MYCOIN", &MmNumber::from(6150)) { + DexFee::Standard(_) | DexFee::WithBurn { .. } => { + panic!("Wrong variant returned for KMD from `dex_fee_amount_from_taker_coin`.") + }, + DexFee::Zero => MmNumber::default(), }; let mycoin = coins::TestCoin::new("MYCOIN"); - let mycoin_taker_fee = match dex_fee_amount_from_taker_coin(&mycoin, "", &MmNumber::from(6150)) { - DexFee::Standard(t) => t, - DexFee::WithBurn { .. } => { + let mycoin_taker_fee = match dex_fee_amount_from_taker_coin(&mycoin, "KMD", &MmNumber::from(6150)) { + DexFee::Zero => MmNumber::default(), + DexFee::WithBurn { .. } | DexFee::Standard(_) => { panic!("Wrong variant returned for MYCOIN from `dex_fee_amount_from_taker_coin`.") }, }; - let expected_mycoin_taker_fee = &kmd_taker_fee / &MmNumber::from("0.75"); - let expected_kmd_burn_amount = &mycoin_taker_fee - &kmd_taker_fee; + let zero_amount = MmNumber::default(); - assert_eq!(expected_mycoin_taker_fee, mycoin_taker_fee); - assert_eq!(expected_kmd_burn_amount, kmd_burn_amount); + assert_eq!(zero_amount, mycoin_taker_fee); + assert_eq!(zero_amount, kmd_burn_amount); } #[test] @@ -2441,9 +2410,9 @@ mod lp_swap_tests { std::env::set_var("MYCOIN_FEE_DISCOUNT", ""); let mycoin = coins::TestCoin::new("MYCOIN"); - let mycoin_taker_fee = match dex_fee_amount_from_taker_coin(&mycoin, "", &MmNumber::from(6150)) { + let mycoin_taker_fee = match dex_fee_amount_from_taker_coin(&mycoin, "MYCOIN", &MmNumber::from(6150)) { DexFee::Standard(t) => t, - DexFee::WithBurn { .. } => { + DexFee::WithBurn { .. } | DexFee::Zero => { panic!("Wrong variant returned for MYCOIN from `dex_fee_amount_from_taker_coin`.") }, }; @@ -2451,7 +2420,7 @@ mod lp_swap_tests { let testcoin = coins::TestCoin::default(); let testcoin_taker_fee = match dex_fee_amount_from_taker_coin(&testcoin, "", &MmNumber::from(6150)) { DexFee::Standard(t) => t, - DexFee::WithBurn { .. } => { + DexFee::WithBurn { .. } | DexFee::Zero => { panic!("Wrong variant returned for TEST coin from `dex_fee_amount_from_taker_coin`.") }, }; diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index e5e8c23c7a..bb51003089 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -739,6 +739,15 @@ impl MakerSwap { }; swap_events.push(MakerSwapEvent::MakerPaymentInstructionsReceived(instructions)); + let taker_amount = MmNumber::from(self.taker_amount.clone()); + let dex_fee = dex_fee_amount_from_taker_coin(self.taker_coin.deref(), &self.r().data.maker_coin, &taker_amount); + + if dex_fee.no_fee() { + info!("zero-dex-fee: skipping taker fee validation"); + swap_events.push(MakerSwapEvent::TakerFeeValidated(TransactionIdentifier::default())); + return Ok((Some(MakerSwapCommand::SendPayment), swap_events)); + } + let taker_fee = match self.taker_coin.tx_enum_from_bytes(payload.data()) { Ok(tx) => tx, Err(e) => { @@ -751,8 +760,6 @@ impl MakerSwap { let hash = taker_fee.tx_hash_as_bytes(); info!("Taker fee tx {:02x}", hash); - let taker_amount = MmNumber::from(self.taker_amount.clone()); - let dex_fee = dex_fee_amount_from_taker_coin(self.taker_coin.deref(), &self.r().data.maker_coin, &taker_amount); let other_taker_coin_htlc_pub = self.r().other_taker_coin_htlc_pub; let taker_coin_start_block = self.r().data.taker_coin_start_block; diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs index d0e667a752..61aba78799 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs @@ -616,7 +616,10 @@ impl MmNumber::default() { + let is_kmd = "KMD" == recreate_ctx.maker_coin.ticker() || "KMD" == recreate_ctx.taker_coin.ticker(); + let dex_fee = if is_kmd { + DexFee::Zero + } else if repr.dex_fee_burn > MmNumber::default() { DexFee::with_burn(repr.dex_fee_amount, repr.dex_fee_burn) } else { DexFee::Standard(repr.dex_fee_amount) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 90c2f3655d..4c2821b92f 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -1304,6 +1304,13 @@ impl TakerSwap { let fee_amount = dex_fee_amount_from_taker_coin(self.taker_coin.deref(), &self.r().data.maker_coin, &self.taker_amount); + if fee_amount.no_fee() { + debug!("zero-dex-fee: skipping send taker fee"); + return Ok((Some(TakerSwapCommand::WaitForMakerPayment), vec![ + TakerSwapEvent::TakerFeeSent(TransactionIdentifier::default()), + ])); + } + let fee_tx = self .taker_coin .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount, self.uuid.as_bytes(), expire_at) @@ -2735,12 +2742,16 @@ pub fn max_taker_vol_from_available( rel: &str, min_tx_amount: &MmNumber, ) -> Result> { - let dex_fee_rate = dex_fee_rate(base, rel); - let threshold_coef = &(&MmNumber::from(1) + &dex_fee_rate) / &dex_fee_rate; - let max_vol = if available > min_tx_amount * &threshold_coef { - available / (MmNumber::from(1) + dex_fee_rate) - } else { + let max_vol = if base == "KMD" || rel == "KMD" { &available - min_tx_amount + } else { + let dex_fee_rate = dex_fee_rate(base, rel); + let threshold_coef = &(&MmNumber::from(1) + &dex_fee_rate) / &dex_fee_rate; + if available > min_tx_amount * &threshold_coef { + available / (MmNumber::from(1) + dex_fee_rate) + } else { + &available - min_tx_amount + } }; if &max_vol <= min_tx_amount { @@ -3162,6 +3173,7 @@ mod taker_swap_tests { let min_tx_amount = MmNumber::from("0.00001"); // For these `availables` the dex_fee must be greater than min_tx_amount + // For these `availables` the dex_fee must be lesser than min_tx_amount let source = vec![ ("0.00779", false), ("0.01", false), @@ -3186,7 +3198,8 @@ mod taker_swap_tests { assert_eq!(max_taker_vol + dex_fee, available); } - // for these `availables` the dex_fee must be the same as min_tx_amount + // KMD pairs: for these `availables` the dex_fee must be 0 + // Non KMD pairs: for these `availables` the dex_fee must be the same as min_tx_amount let source = vec![ ("0.00863333333333333333333333333333333333333333333333332", true), ("0.00863333333333333333333333333333333333333333333333331", true), @@ -3207,9 +3220,14 @@ mod taker_swap_tests { max_taker_vol.to_decimal(), dex_fee.to_decimal() ); - assert_eq!(min_tx_amount, dex_fee); assert!(min_tx_amount <= max_taker_vol); - assert_eq!(max_taker_vol + dex_fee, available); + if is_kmd { + assert_eq!(MmNumber::default(), dex_fee); + assert_eq!(&max_taker_vol + &min_tx_amount, available); + } else { + assert!(min_tx_amount <= max_taker_vol); + assert_eq!(max_taker_vol + dex_fee, available); + } } // these `availables` must return an error diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs index 29f3d07277..f75a39c891 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs @@ -732,7 +732,10 @@ impl MmNumber::default() { + let is_kmd = "KMD" == recreate_ctx.maker_coin.ticker() || "KMD" == recreate_ctx.taker_coin.ticker(); + let dex_fee = if is_kmd { + DexFee::Zero + } else if repr.dex_fee_burn > MmNumber::default() { DexFee::with_burn(repr.dex_fee_amount, repr.dex_fee_burn) } else { DexFee::Standard(repr.dex_fee_amount) diff --git a/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs b/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs index b4f074857f..1bc9255da7 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs @@ -2167,9 +2167,9 @@ fn test_get_max_taker_vol_with_kmd() { .unwrap(); assert!(rc.0.is_success(), "!max_taker_vol: {}", rc.1); let json: Json = serde_json::from_str(&rc.1).unwrap(); - // the result of equation x + x * 9 / 7770 + 0.0002 = 1 - assert_eq!(json["result"]["numer"], Json::from("1294741")); - assert_eq!(json["result"]["denom"], Json::from("1296500")); + // the result of equation x + 0.0002 = 1 (no dex fee included) + assert_eq!(json["result"]["numer"], Json::from("99989")); + assert_eq!(json["result"]["denom"], Json::from("100000")); let rc = block_on(mm_alice.rpc(&json!({ "userpass": mm_alice.userpass,