diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index 8dce039841..074c496db6 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -5987,7 +5987,6 @@ impl MmCoin for EthCoin { &self, value: TradePreimageValue, stage: FeeApproxStage, - include_refund_fee: bool, ) -> TradePreimageResult { let pay_for_gas_option = self .get_swap_pay_for_gas_option(self.get_swap_transaction_fee_policy()) @@ -5997,7 +5996,7 @@ impl MmCoin for EthCoin { let gas_limit = match self.coin_type { EthCoinType::Eth => { // this gas_limit includes gas for `ethPayment` and optionally `senderRefund` contract calls - if include_refund_fee { + if matches!(stage, FeeApproxStage::OrderIssueMax | FeeApproxStage::TradePreimageMax) { U256::from(self.gas_limit.eth_payment) + U256::from(self.gas_limit.eth_sender_refund) } else { U256::from(self.gas_limit.eth_payment) @@ -6027,7 +6026,7 @@ impl MmCoin for EthCoin { gas += approve_gas_limit; } // add 'senderRefund' gas if requested - if include_refund_fee { + if matches!(stage, FeeApproxStage::TradePreimage | FeeApproxStage::TradePreimageMax) { gas += U256::from(self.gas_limit.erc20_sender_refund); } gas @@ -6808,10 +6807,10 @@ fn increase_gas_price_by_stage(pay_for_gas_option: PayForGasOption, level: &FeeA FeeApproxStage::StartSwap => { increase_by_percent_one_gwei(gas_price, GAS_PRICE_APPROXIMATION_PERCENT_ON_START_SWAP) }, - FeeApproxStage::OrderIssue => { + FeeApproxStage::OrderIssue | FeeApproxStage::OrderIssueMax => { increase_by_percent_one_gwei(gas_price, GAS_PRICE_APPROXIMATION_PERCENT_ON_ORDER_ISSUE) }, - FeeApproxStage::TradePreimage => { + FeeApproxStage::TradePreimage | FeeApproxStage::TradePreimageMax => { increase_by_percent_one_gwei(gas_price, GAS_PRICE_APPROXIMATION_PERCENT_ON_TRADE_PREIMAGE) }, FeeApproxStage::WatcherPreimage => { diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index 5c9c8ad679..eb71448081 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -331,34 +331,26 @@ fn get_sender_trade_preimage() { let actual = block_on(coin.get_sender_trade_fee( TradePreimageValue::UpperBound(150.into()), FeeApproxStage::WithoutApprox, - true, )) .expect("!get_sender_trade_fee"); - let expected = expected_fee(GAS_PRICE, gas_limit::ETH_PAYMENT + gas_limit::ETH_SENDER_REFUND); + let expected = expected_fee(GAS_PRICE, gas_limit::ETH_PAYMENT); assert_eq!(actual, expected); let value = u256_to_big_decimal(100.into(), 18).expect("!u256_to_big_decimal"); - let actual = - block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::OrderIssue, true)) - .expect("!get_sender_trade_fee"); - let expected = expected_fee( - GAS_PRICE_APPROXIMATION_ON_ORDER_ISSUE, - gas_limit::ETH_PAYMENT + gas_limit::ETH_SENDER_REFUND, - ); + let actual = block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::OrderIssue)) + .expect("!get_sender_trade_fee"); + let expected = expected_fee(GAS_PRICE_APPROXIMATION_ON_ORDER_ISSUE, gas_limit::ETH_PAYMENT); assert_eq!(actual, expected); let value = u256_to_big_decimal(1.into(), 18).expect("!u256_to_big_decimal"); - let actual = block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::StartSwap, true)) + let actual = block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::StartSwap)) .expect("!get_sender_trade_fee"); - let expected = expected_fee( - GAS_PRICE_APPROXIMATION_ON_START_SWAP, - gas_limit::ETH_PAYMENT + gas_limit::ETH_SENDER_REFUND, - ); + let expected = expected_fee(GAS_PRICE_APPROXIMATION_ON_START_SWAP, gas_limit::ETH_PAYMENT); assert_eq!(actual, expected); let value = u256_to_big_decimal(10000000000u64.into(), 18).expect("!u256_to_big_decimal"); let actual = - block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::TradePreimage, true)) + block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::TradePreimageMax)) .expect("!get_sender_trade_fee"); let expected = expected_fee( GAS_PRICE_APPROXIMATION_ON_TRADE_PREIMAGE, @@ -405,25 +397,18 @@ fn get_erc20_sender_trade_preimage() { // value is allowed unsafe { ALLOWANCE = 1000 }; let value = u256_to_big_decimal(1000.into(), 18).expect("u256_to_big_decimal"); - let actual = block_on(coin.get_sender_trade_fee( - TradePreimageValue::UpperBound(value), - FeeApproxStage::WithoutApprox, - true, - )) - .expect("!get_sender_trade_fee"); + let actual = + block_on(coin.get_sender_trade_fee(TradePreimageValue::UpperBound(value), FeeApproxStage::WithoutApprox)) + .expect("!get_sender_trade_fee"); log!("{:?}", actual.amount.to_decimal()); unsafe { assert!(!ESTIMATE_GAS_CALLED) } - assert_eq!( - actual, - expected_trade_fee(gas_limit::ERC20_PAYMENT + gas_limit::ERC20_SENDER_REFUND, GAS_PRICE) - ); + assert_eq!(actual, expected_trade_fee(gas_limit::ERC20_PAYMENT, GAS_PRICE)); // value is greater than allowance unsafe { ALLOWANCE = 999 }; let value = u256_to_big_decimal(1000.into(), 18).expect("u256_to_big_decimal"); - let actual = - block_on(coin.get_sender_trade_fee(TradePreimageValue::UpperBound(value), FeeApproxStage::StartSwap, true)) - .expect("!get_sender_trade_fee"); + let actual = block_on(coin.get_sender_trade_fee(TradePreimageValue::UpperBound(value), FeeApproxStage::StartSwap)) + .expect("!get_sender_trade_fee"); unsafe { assert!(ESTIMATE_GAS_CALLED); ESTIMATE_GAS_CALLED = false; @@ -431,7 +416,7 @@ fn get_erc20_sender_trade_preimage() { assert_eq!( actual, expected_trade_fee( - gas_limit::ERC20_PAYMENT + gas_limit::ERC20_SENDER_REFUND + APPROVE_GAS_LIMIT, + gas_limit::ERC20_PAYMENT + APPROVE_GAS_LIMIT, GAS_PRICE_APPROXIMATION_ON_START_SWAP ) ); @@ -439,24 +424,19 @@ fn get_erc20_sender_trade_preimage() { // value is allowed unsafe { ALLOWANCE = 1000 }; let value = u256_to_big_decimal(999.into(), 18).expect("u256_to_big_decimal"); - let actual = - block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::OrderIssue, true)) - .expect("!get_sender_trade_fee"); + let actual = block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::OrderIssue)) + .expect("!get_sender_trade_fee"); unsafe { assert!(!ESTIMATE_GAS_CALLED) } assert_eq!( actual, - expected_trade_fee( - gas_limit::ERC20_PAYMENT + gas_limit::ERC20_SENDER_REFUND, - GAS_PRICE_APPROXIMATION_ON_ORDER_ISSUE - ) + expected_trade_fee(gas_limit::ERC20_PAYMENT, GAS_PRICE_APPROXIMATION_ON_ORDER_ISSUE) ); // value is greater than allowance unsafe { ALLOWANCE = 1000 }; let value = u256_to_big_decimal(1500.into(), 18).expect("u256_to_big_decimal"); - let actual = - block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::TradePreimage, true)) - .expect("!get_sender_trade_fee"); + let actual = block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(value), FeeApproxStage::TradePreimage)) + .expect("!get_sender_trade_fee"); unsafe { assert!(ESTIMATE_GAS_CALLED); ESTIMATE_GAS_CALLED = false; diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index a55665da62..1275b2c4d6 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -1303,7 +1303,6 @@ impl MmCoin for LightningCoin { &self, _value: TradePreimageValue, _stage: FeeApproxStage, - _include_refund_fee: bool, ) -> TradePreimageResult { Ok(TradeFee { coin: self.ticker().to_owned(), diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index d5bafc7631..54fa128a14 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -2627,6 +2627,7 @@ impl TransactionDetails { } } +/// Transaction fee to pay for swap transactions (could be total for two transactions: taker fee and payment fee txns) #[derive(Clone, Debug, PartialEq, Serialize)] pub struct TradeFee { pub coin: String, @@ -2712,6 +2713,8 @@ impl AddAssign for CoinBalance { } /// The approximation is needed to cover the dynamic miner fee changing during a swap. +/// Also used to indicate refund fee is needed for eth +/// Also used to indicate utxo fee correction is needed due to a possible change output #[derive(Clone, Copy, Debug)] pub enum FeeApproxStage { /// Do not increase the trade fee. @@ -2722,8 +2725,12 @@ pub enum FeeApproxStage { WatcherPreimage, /// Increase the trade fee significantly. OrderIssue, - /// Increase the trade fee largely. + /// Increase the trade fee significantly (used to calculate max volume). + OrderIssueMax, + /// Increase the trade fee largely in the trade_preimage rpc. TradePreimage, + /// Increase the trade fee in the trade_preimage rpc (used to calculate max volume for trade preimage). + TradePreimageMax, } #[derive(Debug)] @@ -3622,7 +3629,6 @@ pub trait MmCoin: SwapOps + WatcherOps + MarketCoinOps + Send + Sync + 'static { &self, value: TradePreimageValue, stage: FeeApproxStage, - include_refund_fee: bool, ) -> TradePreimageResult; /// Get fee to be paid by receiver per whole swap and check if the wallet has sufficient balance to pay the fee. diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index b6014cb3e4..9bfa78c19b 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -539,7 +539,9 @@ impl Qrc20Coin { /// or should be sum of gas fee of all contract calls. pub async fn get_qrc20_tx_fee(&self, gas_fee: u64) -> Result { match try_s!(self.get_fee_rate().await) { - ActualFeeRate::Dynamic(amount) | ActualFeeRate::FixedPerKb(amount) => Ok(amount + gas_fee), + ActualFeeRate::Dynamic(amount) + | ActualFeeRate::FixedPerKb(amount) + | ActualFeeRate::FixedPerKbDingo(amount) => Ok(amount + gas_fee), } } @@ -1289,7 +1291,6 @@ impl MmCoin for Qrc20Coin { &self, value: TradePreimageValue, stage: FeeApproxStage, - include_refund_fee: bool, ) -> TradePreimageResult { let decimals = self.utxo.decimals; // pass the dummy params @@ -1323,7 +1324,7 @@ impl MmCoin for Qrc20Coin { }; // Optionally calculate refund fee. - let sender_refund_fee = if include_refund_fee { + let sender_refund_fee = if matches!(stage, FeeApproxStage::TradePreimage | FeeApproxStage::TradePreimageMax) { let sender_refund_output = self .sender_refund_output(&self.swap_contract_address, swap_id, value, secret_hash, receiver_addr) .map_mm_err()?; diff --git a/mm2src/coins/qrc20/qrc20_tests.rs b/mm2src/coins/qrc20/qrc20_tests.rs index 79498af368..946e2963a8 100644 --- a/mm2src/coins/qrc20/qrc20_tests.rs +++ b/mm2src/coins/qrc20/qrc20_tests.rs @@ -19,7 +19,7 @@ cfg_native!( use mocktopus::mocking::{MockResult, Mockable}; ); -const EXPECTED_TX_FEE: i64 = 1000; +const DEFAULT_TX_FEE_RATE: i64 = 1000; const CONTRACT_CALL_GAS_FEE: i64 = (QRC20_GAS_LIMIT_DEFAULT * QRC20_GAS_PRICE_DEFAULT) as i64; const SWAP_PAYMENT_GAS_FEE: i64 = (QRC20_PAYMENT_GAS_LIMIT * QRC20_GAS_PRICE_DEFAULT) as i64; const TAKER_PAYMENT_SPEND_SEARCH_INTERVAL: f64 = 1.; @@ -59,9 +59,9 @@ pub fn qrc20_coin_for_test(priv_key: [u8; 32], fallback_swap: Option<&str>) -> ( (ctx, coin) } -fn check_tx_fee(coin: &Qrc20Coin, expected_tx_fee: ActualFeeRate) { - let actual_tx_fee = block_on(coin.get_fee_rate()).unwrap(); - assert_eq!(actual_tx_fee, expected_tx_fee); +fn check_fee_rate(coin: &Qrc20Coin, expected_fee_rate: ActualFeeRate) { + let actual_fee_rate = block_on(coin.get_fee_rate()).unwrap(); + assert_eq!(actual_fee_rate, expected_fee_rate); } #[cfg(not(target_arch = "wasm32"))] @@ -144,7 +144,7 @@ fn test_withdraw_impl_fee_details() { // 1000 from satoshi, // where decimals = 8, // 1000 is fixed fee - "miner_fee": "0.00001", + "miner_fee": "0.00000299", "gas_limit": 2_500_000, "gas_price": 40, // (gas_limit * gas_price) from satoshi in Qtum @@ -715,12 +715,12 @@ fn test_get_trade_fee() { 172, 110, 180, 13, 123, 179, 10, 49, ]; let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); - // check if the coin's tx fee is expected - check_tx_fee(&coin, ActualFeeRate::FixedPerKb(EXPECTED_TX_FEE as u64)); + // check if the coin's tx fee rate is expected + check_fee_rate(&coin, ActualFeeRate::FixedPerKb(DEFAULT_TX_FEE_RATE as u64)); let actual_trade_fee = block_on_f01(coin.get_trade_fee()).unwrap(); let expected_trade_fee_amount = big_decimal_from_sat( - 2 * CONTRACT_CALL_GAS_FEE + SWAP_PAYMENT_GAS_FEE + EXPECTED_TX_FEE, + 2 * CONTRACT_CALL_GAS_FEE + SWAP_PAYMENT_GAS_FEE + DEFAULT_TX_FEE_RATE, coin.utxo.decimals, ); let expected = TradeFee { @@ -742,20 +742,22 @@ fn test_sender_trade_preimage_zero_allowance() { 231, 153, 202, 20, 238, 120, 64, ]; let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); - // check if the coin's tx fee is expected - check_tx_fee(&coin, ActualFeeRate::FixedPerKb(EXPECTED_TX_FEE as u64)); + const EXPECTED_PAYMENT_TX_FEE: i64 = 535; + const EXPECTED_REFUND_TX_FEE: i64 = 396; + // check if the coin's tx fee rate is expected + check_fee_rate(&coin, ActualFeeRate::FixedPerKb(DEFAULT_TX_FEE_RATE as u64)); let allowance = block_on(coin.allowance(coin.swap_contract_address)).expect("!allowance"); assert_eq!(allowance, 0.into()); let erc20_payment_fee_with_one_approve = big_decimal_from_sat( - CONTRACT_CALL_GAS_FEE + SWAP_PAYMENT_GAS_FEE + EXPECTED_TX_FEE, + CONTRACT_CALL_GAS_FEE + SWAP_PAYMENT_GAS_FEE + EXPECTED_PAYMENT_TX_FEE, coin.utxo.decimals, ); - let sender_refund_fee = big_decimal_from_sat(CONTRACT_CALL_GAS_FEE + EXPECTED_TX_FEE, coin.utxo.decimals); + let sender_refund_fee = big_decimal_from_sat(CONTRACT_CALL_GAS_FEE + EXPECTED_REFUND_TX_FEE, coin.utxo.decimals); let actual = - block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(1.into()), FeeApproxStage::WithoutApprox, true)) + block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(1.into()), FeeApproxStage::TradePreimageMax)) // pass TradePreimageMax to add change output txfee, to correct the max vol calc .expect("!get_sender_trade_fee"); // one `approve` contract call should be included into the expected trade fee let expected = TradeFee { @@ -778,24 +780,28 @@ fn test_sender_trade_preimage_with_allowance() { 143, 221, 19, 47, 74, 175, 100, ]; let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); - // check if the coin's tx fee is expected - check_tx_fee(&coin, ActualFeeRate::FixedPerKb(EXPECTED_TX_FEE as u64)); + const EXPECTED_PAYMENT_WITHOUT_APPROVE_TX_FEE: i64 = 576; + const EXPECTED_PAYMENT_WITH_APPROVES_TX_FEE: i64 = 790; + const EXPECTED_REFUND_TX_FEE: i64 = 544; + // check if the coin's tx fee rate is expected + check_fee_rate(&coin, ActualFeeRate::FixedPerKb(DEFAULT_TX_FEE_RATE as u64)); let allowance = block_on(coin.allowance(coin.swap_contract_address)).expect("!allowance"); assert_eq!(allowance, 300_000_000.into()); - let erc20_payment_fee_without_approve = - big_decimal_from_sat(SWAP_PAYMENT_GAS_FEE + EXPECTED_TX_FEE, coin.utxo.decimals); + let erc20_payment_fee_without_approve = big_decimal_from_sat( + SWAP_PAYMENT_GAS_FEE + EXPECTED_PAYMENT_WITHOUT_APPROVE_TX_FEE, + coin.utxo.decimals, + ); let erc20_payment_fee_with_two_approves = big_decimal_from_sat( - 2 * CONTRACT_CALL_GAS_FEE + SWAP_PAYMENT_GAS_FEE + EXPECTED_TX_FEE, + 2 * CONTRACT_CALL_GAS_FEE + SWAP_PAYMENT_GAS_FEE + EXPECTED_PAYMENT_WITH_APPROVES_TX_FEE, coin.utxo.decimals, ); - let sender_refund_fee = big_decimal_from_sat(CONTRACT_CALL_GAS_FEE + EXPECTED_TX_FEE, coin.utxo.decimals); + let sender_refund_fee = big_decimal_from_sat(CONTRACT_CALL_GAS_FEE + EXPECTED_REFUND_TX_FEE, coin.utxo.decimals); let actual = block_on(coin.get_sender_trade_fee( TradePreimageValue::Exact(BigDecimal::try_from(2.5).unwrap()), - FeeApproxStage::WithoutApprox, - true, + FeeApproxStage::TradePreimageMax, )) .expect("!get_sender_trade_fee"); // the expected fee should not include any `approve` contract call @@ -808,8 +814,7 @@ fn test_sender_trade_preimage_with_allowance() { let actual = block_on(coin.get_sender_trade_fee( TradePreimageValue::Exact(BigDecimal::try_from(3.5).unwrap()), - FeeApproxStage::WithoutApprox, - true, + FeeApproxStage::TradePreimageMax, )) .expect("!get_sender_trade_fee"); // two `approve` contract calls should be included into the expected trade fee @@ -865,11 +870,10 @@ fn test_get_sender_trade_fee_preimage_for_correct_ticker() { )) .unwrap(); - let actual = - block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(0.into()), FeeApproxStage::OrderIssue, true)) - .err() - .unwrap() - .into_inner(); + let actual = block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(0.into()), FeeApproxStage::OrderIssue)) + .err() + .unwrap() + .into_inner(); // expecting TradePreimageError::NotSufficientBalance let expected = TradePreimageError::NotSufficientBalance { coin: "tQTUM".to_string(), @@ -889,8 +893,9 @@ fn test_receiver_trade_preimage() { 143, 221, 19, 47, 74, 175, 100, ]; let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); - // check if the coin's tx fee is expected - check_tx_fee(&coin, ActualFeeRate::FixedPerKb(EXPECTED_TX_FEE as u64)); + const EXPECTED_TX_FEE: i64 = 544; + // check if the coin's tx fee rate is expected + check_fee_rate(&coin, ActualFeeRate::FixedPerKb(DEFAULT_TX_FEE_RATE as u64)); let actual = block_on_f01(coin.get_receiver_trade_fee(FeeApproxStage::WithoutApprox)).expect("!get_receiver_trade_fee"); @@ -914,8 +919,9 @@ fn test_taker_fee_tx_fee() { 143, 221, 19, 47, 74, 175, 100, ]; let (_ctx, coin) = qrc20_coin_for_test(priv_key, None); - // check if the coin's tx fee is expected - check_tx_fee(&coin, ActualFeeRate::FixedPerKb(EXPECTED_TX_FEE as u64)); + const EXPECTED_TX_FEE: i64 = 447; + // check if the coin's tx fee rate is expected + check_fee_rate(&coin, ActualFeeRate::FixedPerKb(DEFAULT_TX_FEE_RATE as u64)); let expected_balance = CoinBalance { spendable: BigDecimal::from(5u32), unspendable: BigDecimal::from(0u32), diff --git a/mm2src/coins/siacoin.rs b/mm2src/coins/siacoin.rs index 1fee959604..b95e383b8d 100644 --- a/mm2src/coins/siacoin.rs +++ b/mm2src/coins/siacoin.rs @@ -284,7 +284,6 @@ impl MmCoin for SiaCoin { &self, _value: TradePreimageValue, _stage: FeeApproxStage, - _include_refund_fee: bool, ) -> TradePreimageResult { unimplemented!() } diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index ce8035a33d..ae2868282b 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -3531,7 +3531,6 @@ impl MmCoin for TendermintCoin { &self, value: TradePreimageValue, _stage: FeeApproxStage, - _include_refund_fee: bool, ) -> TradePreimageResult { let amount = match value { TradePreimageValue::Exact(decimal) | TradePreimageValue::UpperBound(decimal) => decimal, diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index 220ddedf48..8ecd08d86b 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -610,7 +610,6 @@ impl MmCoin for TendermintToken { &self, value: TradePreimageValue, _stage: FeeApproxStage, - _include_refund_fee: bool, ) -> TradePreimageResult { let amount = match value { TradePreimageValue::Exact(decimal) | TradePreimageValue::UpperBound(decimal) => decimal, diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index 1dc64d30db..311ccf0006 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -456,7 +456,6 @@ impl MmCoin for TestCoin { &self, _value: TradePreimageValue, _stage: FeeApproxStage, - _include_refund_fee: bool, ) -> TradePreimageResult { unimplemented!() } diff --git a/mm2src/coins/utxo.rs b/mm2src/coins/utxo.rs index 9d114d3365..00810d742e 100644 --- a/mm2src/coins/utxo.rs +++ b/mm2src/coins/utxo.rs @@ -290,6 +290,8 @@ pub enum FeeRate { Dynamic(EstimateFeeMethod), /// Tell the coin that it has fixed tx fee per kb. FixedPerKb(u64), + /// Use fixed tx fee per kb for DINGO-like coins. + FixedPerKbDingo(u64), } /// The actual "runtime" tx fee rate (per kb) that is received from RPC in case of dynamic calculation @@ -298,17 +300,20 @@ pub enum FeeRate { pub enum ActualFeeRate { /// fee amount per Kbyte received from coin RPC Dynamic(u64), - /// Use specified fee amount per each 1 kb of transaction and also per each output less than the fee amount. - /// Used by DOGE, but more coins might support it too. + /// Use specified fee amount per each 1 kb of transaction. FixedPerKb(u64), + /// Use specified fee amount per each 1 kb of transaction and also per each output less than the fee amount. + /// Used in DINGO coin, but more coins might support it too. + FixedPerKbDingo(u64), } impl ActualFeeRate { fn get_tx_fee(&self, tx_size: u64) -> u64 { match self { ActualFeeRate::Dynamic(fee_rate) => (fee_rate * tx_size) / KILO_BYTE, - // return fee_rate here as swap spend transaction size is always less than 1 kb - ActualFeeRate::FixedPerKb(fee_rate) => { + ActualFeeRate::FixedPerKb(fee_rate) => (fee_rate * tx_size) / KILO_BYTE, + ActualFeeRate::FixedPerKbDingo(fee_rate) => { + // Implement rounding mechanism (earlier used in DOGE, now in DINGO coin) let tx_size_kb = if tx_size % KILO_BYTE == 0 { tx_size / KILO_BYTE } else { @@ -323,7 +328,8 @@ impl ActualFeeRate { fn get_tx_fee_for_change(&self, tx_size: u64) -> u64 { match self { ActualFeeRate::Dynamic(fee_rate) => (*fee_rate * P2PKH_OUTPUT_LEN) / KILO_BYTE, - ActualFeeRate::FixedPerKb(fee_rate) => { + ActualFeeRate::FixedPerKb(fee_rate) => (*fee_rate * P2PKH_OUTPUT_LEN) / KILO_BYTE, + ActualFeeRate::FixedPerKbDingo(fee_rate) => { // take into account the change output if tx_size_kb(tx with change) > tx_size_kb(tx without change) if tx_size % KILO_BYTE + P2PKH_OUTPUT_LEN > KILO_BYTE { *fee_rate diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index e99d00eecc..e580b0d8f2 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -1334,7 +1334,6 @@ impl MmCoin for BchCoin { &self, value: TradePreimageValue, stage: FeeApproxStage, - _include_refund_fee: bool, // refund fee is taken from swap output ) -> TradePreimageResult { utxo_common::get_sender_trade_fee(self, value, stage).await } diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index 69fef89948..7a24a58353 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -965,7 +965,6 @@ impl MmCoin for QtumCoin { &self, value: TradePreimageValue, stage: FeeApproxStage, - _include_refund_fee: bool, // refund fee is taken from swap output ) -> TradePreimageResult { utxo_common::get_sender_trade_fee(self, value, stage).await } diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index b85afa4989..0c3c93bb81 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -1764,7 +1764,6 @@ impl MmCoin for SlpToken { &self, value: TradePreimageValue, stage: FeeApproxStage, - _include_refund_fee: bool, // refund fee is taken from swap output ) -> TradePreimageResult { let slp_amount = match value { TradePreimageValue::Exact(decimal) | TradePreimageValue::UpperBound(decimal) => { diff --git a/mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs b/mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs index 6b8d52ce9a..ee671fecf4 100644 --- a/mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs +++ b/mm2src/coins/utxo/utxo_builder/utxo_coin_builder.rs @@ -7,7 +7,7 @@ use crate::utxo::rpc_clients::{ }; use crate::utxo::tx_cache::{UtxoVerboseCacheOps, UtxoVerboseCacheShared}; use crate::utxo::utxo_block_header_storage::BlockHeaderStorage; -use crate::utxo::utxo_builder::utxo_conf_builder::{UtxoConfBuilder, UtxoConfError}; +use crate::utxo::utxo_builder::utxo_conf_builder::{UtxoConfBuilder, UtxoConfError, UtxoFeeConfig}; use crate::utxo::{ output_script, ElectrumBuilderArgs, FeeRate, RecentlySpentOutPoints, UtxoCoinConf, UtxoCoinFields, UtxoHDWallet, UtxoRpcMode, UtxoSyncStatus, UtxoSyncStatusLoopHandle, UTXO_DUST_AMOUNT, @@ -504,9 +504,9 @@ pub trait UtxoCoinBuilderCommonOps { } async fn tx_fee(&self, rpc_client: &UtxoRpcClientEnum) -> UtxoCoinBuildResult { - let tx_fee = match self.conf()["txfee"].as_u64() { - None => FeeRate::FixedPerKb(1000), - Some(0) => { + let tx_fee = match UtxoFeeConfig::parse_val(self.conf()) { + UtxoFeeConfig::NotSet => FeeRate::FixedPerKb(1000), + UtxoFeeConfig::Dynamic => { let fee_method = match &rpc_client { UtxoRpcClientEnum::Electrum(_) => EstimateFeeMethod::Standard, UtxoRpcClientEnum::Native(client) => client @@ -517,7 +517,8 @@ pub trait UtxoCoinBuilderCommonOps { }; FeeRate::Dynamic(fee_method) }, - Some(fee) => FeeRate::FixedPerKb(fee), + UtxoFeeConfig::FixedPerKb(fee) => FeeRate::FixedPerKb(fee), + UtxoFeeConfig::FixedPerKbDingo(fee) => FeeRate::FixedPerKbDingo(fee), }; Ok(tx_fee) } diff --git a/mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs b/mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs index be870a181b..64e0cec321 100644 --- a/mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs +++ b/mm2src/coins/utxo/utxo_builder/utxo_conf_builder.rs @@ -340,3 +340,24 @@ impl<'a> UtxoConfBuilder<'a> { self.conf["avg_blocktime"].as_u64() } } + +/// 'txfee' coins param config values +pub(crate) enum UtxoFeeConfig { + NotSet, + Dynamic, + FixedPerKb(u64), + FixedPerKbDingo(u64), +} + +impl UtxoFeeConfig { + /// Parse the txfee-related coins param, like: + /// "txfee"=0 and/or "dingo_fee"=true + pub(crate) fn parse_val(conf: &Json) -> Self { + match (conf["txfee"].as_u64(), conf["dingo_fee"].as_bool()) { + (Some(0), _) => Self::Dynamic, + (Some(val), Some(true)) => Self::FixedPerKbDingo(val), + (Some(val), _) => Self::FixedPerKb(val), + (_, _) => Self::NotSet, + } + } +} diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index b3091ffe5b..e20cb5510b 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -116,6 +116,7 @@ pub async fn get_fee_rate(coin: &UtxoCoinFields) -> UtxoRpcResult Ok(ActualFeeRate::Dynamic(fee_rate)) }, FeeRate::FixedPerKb(satoshis) => Ok(ActualFeeRate::FixedPerKb(*satoshis)), + FeeRate::FixedPerKbDingo(satoshis) => Ok(ActualFeeRate::FixedPerKbDingo(*satoshis)), } } @@ -319,7 +320,7 @@ pub async fn get_htlc_spend_fee( // increase dynamic fee for a chance if it grows in the swap ActualFeeRate::Dynamic(increase_dynamic_fee_by_stage(coin, dynamic_fee_rate, stage)) }, - ActualFeeRate::FixedPerKb(_) => fee_rate, + ActualFeeRate::FixedPerKb(_) | ActualFeeRate::FixedPerKbDingo(_) => fee_rate, }; let min_relay_fee_rate = get_min_relay_rate(coin).await.map_mm_err()?; @@ -4149,6 +4150,7 @@ pub fn get_trade_fee(coin: T) -> Box f, ActualFeeRate::FixedPerKb(f) => f, + ActualFeeRate::FixedPerKbDingo(f) => f, }; Ok(TradeFee { coin: ticker, @@ -4218,7 +4220,9 @@ where // We need to add extra tx fee for the absent change output for e.g. to ensure max_taker_vol is calculated correctly // (If we do not do this then in a swap the change output may appear and we may not have sufficient balance to pay taker fee) - let total_fee = if tx.outputs.len() == outputs_count { + let total_fee = if tx.outputs.len() == outputs_count + && matches!(stage, FeeApproxStage::TradePreimageMax | FeeApproxStage::OrderIssueMax) + { // take into account the change output data.fee_amount + actual_fee_rate.get_tx_fee_for_change(0) } else { @@ -4227,7 +4231,7 @@ where }; Ok(big_decimal_from_sat(total_fee as i64, decimals)) }, - ActualFeeRate::FixedPerKb(_fee) => { + ActualFeeRate::FixedPerKb(_fee) | ActualFeeRate::FixedPerKbDingo(_fee) => { let outputs_count = outputs.len(); let (unspents, _recently_sent_txs) = coin.get_unspent_ordered_list(&my_address).await.map_mm_err()?; let mut tx_builder = UtxoTxBuilder::new(coin) @@ -4243,9 +4247,13 @@ where TradePreimageError::from_generate_tx_error(e, ticker.to_string(), decimals, is_amount_upper_bound) })?; - // We need to add extra tx fee for the absent change output for e.g. to ensure max_taker_vol is calculated correctly + // We need to add extra tx fee for the absent change output for e.g. to ensure max_maker_vol or max_taker_vol is calculated correctly // (If we do not do this then in a swap the change output may appear and we may not have sufficient balance to pay taker fee) - let total_fee = if tx.outputs.len() == outputs_count { + let total_fee = if tx.outputs.len() == outputs_count + && matches!(stage, FeeApproxStage::TradePreimageMax | FeeApproxStage::OrderIssueMax) + { + // Do this for TradePreimageMax stage only to ensure max vol is not too low. + // Don't do this for TradePreimage stage (or others) as an insufficient amount error may be collected let tx = UtxoTx::from(tx); let tx_bytes = serialize(&tx); // take into account the change output @@ -4975,12 +4983,12 @@ where // Take into account that the dynamic fee may increase at each of the following stages up to [`UtxoCoinFields::tx_fee_volatility_percent`]: // - until a swap is started; // - during the swap. - FeeApproxStage::OrderIssue => base_percent * 2., + FeeApproxStage::OrderIssue | FeeApproxStage::OrderIssueMax => base_percent * 2., // Take into account that the dynamic fee may increase at each of the following stages up to [`UtxoCoinFields::tx_fee_volatility_percent`]: // - until an order is issued; // - until a swap is started; // - during the swap. - FeeApproxStage::TradePreimage => base_percent * 2.5, + FeeApproxStage::TradePreimage | FeeApproxStage::TradePreimageMax => base_percent * 2.5, }; increase_by_percent(dynamic_fee, percent) } diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 56f6eb1c32..b0fccc370a 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -1030,7 +1030,6 @@ impl MmCoin for UtxoStandardCoin { &self, value: TradePreimageValue, stage: FeeApproxStage, - _include_refund_fee: bool, // refund fee is taken from swap output ) -> TradePreimageResult { utxo_common::get_sender_trade_fee(self, value, stage).await } diff --git a/mm2src/coins/utxo/utxo_tests.rs b/mm2src/coins/utxo/utxo_tests.rs index 147fe9fb31..fe6b1d08c6 100644 --- a/mm2src/coins/utxo/utxo_tests.rs +++ b/mm2src/coins/utxo/utxo_tests.rs @@ -225,7 +225,7 @@ fn test_generate_transaction() { let outputs = vec![TransactionOutput { script_pubkey: vec![].into(), - value: 98001, + value: 98781, }]; let builder = block_on(UtxoTxBuilder::new(&coin)) @@ -236,7 +236,7 @@ fn test_generate_transaction() { // so no extra outputs should appear in generated transaction assert_eq!(generated.0.outputs.len(), 1); - assert_eq!(generated.1.fee_amount, 1000 + 999); + assert_eq!(generated.1.fee_amount, 220 + 999); assert_eq!(generated.1.received_by_me, 0); assert_eq!(generated.1.spent_by_me, 100000); @@ -262,10 +262,10 @@ fn test_generate_transaction() { let generated = block_on(builder.build()).unwrap(); assert_eq!(generated.0.outputs.len(), 1); - assert_eq!(generated.1.fee_amount, 1000); - assert_eq!(generated.1.received_by_me, 99000); + assert_eq!(generated.1.fee_amount, 211); + assert_eq!(generated.1.received_by_me, 99789); assert_eq!(generated.1.spent_by_me, 100000); - assert_eq!(generated.0.outputs[0].value, 99000); + assert_eq!(generated.0.outputs[0].value, 99789); let unspents = vec![UnspentInfo { value: 100000, @@ -627,7 +627,7 @@ fn test_withdraw_impl_set_fixed_fee() { let expected = Some( UtxoFeeDetails { coin: Some(TEST_COIN_NAME.into()), - amount: "0.1".parse().unwrap(), + amount: "0.0245".parse().unwrap(), } .into(), ); @@ -925,7 +925,7 @@ fn test_withdraw_kmd_rewards_impl( }; let expected_fee = TxFeeDetails::Utxo(UtxoFeeDetails { coin: Some("KMD".into()), - amount: "0.00001".parse().unwrap(), + amount: "0.00000245".parse().unwrap(), }); let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(tx_details.fee_details, Some(expected_fee)); @@ -1003,7 +1003,7 @@ fn test_withdraw_rick_rewards_none() { }; let expected_fee = TxFeeDetails::Utxo(UtxoFeeDetails { coin: Some(TEST_COIN_NAME.into()), - amount: "0.00001".parse().unwrap(), + amount: "0.00000245".parse().unwrap(), }); let tx_details = block_on_f01(coin.withdraw(withdraw_req)).unwrap(); assert_eq!(tx_details.fee_details, Some(expected_fee)); @@ -1346,9 +1346,6 @@ fn test_generate_transaction_random_values() { let tx_size = est_tx_size(generated.0.inputs.len(), generated.0.outputs.len()); let estimated_txfee = fee_rate * tx_size / 1000; - //println!("generated.1.fee_amount={} estimated_txfee={} received_by_me={} output_vals.len={} generated.0.outputs.len={} dust={}, fee_rate={}", - // generated.1.fee_amount, estimated_txfee, generated.1.received_by_me, output_vals.len(), generated.0.outputs.len(), dust, fee_rate); - const CHANGE_OUTPUT_SIZE: u64 = 1 + 25 + 8; let max_overpay = dust + fee_rate * CHANGE_OUTPUT_SIZE / 1000; // could be slight overpay due to dust change removed from tx if generated.1.fee_amount > estimated_txfee { @@ -2798,7 +2795,6 @@ fn test_get_sender_trade_fee_dynamic_tx_fee() { let fee1 = block_on(coin.get_sender_trade_fee( TradePreimageValue::UpperBound(my_balance.clone()), FeeApproxStage::WithoutApprox, - false, )) .expect("!get_sender_trade_fee"); @@ -2807,19 +2803,15 @@ fn test_get_sender_trade_fee_dynamic_tx_fee() { let fee2 = block_on(coin.get_sender_trade_fee( TradePreimageValue::Exact(value_without_fee), FeeApproxStage::WithoutApprox, - false, )) .expect("!get_sender_trade_fee"); assert_eq!(fee1, fee2); // `2.21934443` value was obtained as a result of executing the `max_taker_vol` RPC call for this wallet let max_taker_vol = BigDecimal::from_str("2.21934443").expect("!BigDecimal::from_str"); - let fee3 = block_on(coin.get_sender_trade_fee( - TradePreimageValue::Exact(max_taker_vol), - FeeApproxStage::WithoutApprox, - false, - )) - .expect("!get_sender_trade_fee"); + let fee3 = + block_on(coin.get_sender_trade_fee(TradePreimageValue::Exact(max_taker_vol), FeeApproxStage::WithoutApprox)) + .expect("!get_sender_trade_fee"); assert_eq!(fee1, fee3); } @@ -3050,7 +3042,9 @@ fn firo_spark_tx_details() { #[test] fn test_generate_tx_doge_fee() { - // A tx below 1kb is always 0,01 doge fee per kb. + // Doge coin does not use fee rounding anymore, so this is not true now: 'a tx below 1kb is always 0,01 doge fee per kb' + // That is, this test was fixed for lesser txfee. + // See DINGO coin for the fee rounding. let config = json!({ "coin": "DOGE", "name": "dogecoin", @@ -3096,7 +3090,7 @@ fn test_generate_tx_doge_fee() { .add_available_inputs(unspents) .add_outputs(outputs); let (_, data) = block_on(builder.build()).unwrap(); - let expected_fee = 1000000; + let expected_fee = 227000; assert_eq!(expected_fee, data.fee_amount); let unspents = vec![UnspentInfo { @@ -3117,7 +3111,7 @@ fn test_generate_tx_doge_fee() { .add_available_inputs(unspents) .add_outputs(outputs); let (_, data) = block_on(builder.build()).unwrap(); - let expected_fee = 2000000; + let expected_fee = 1592000; assert_eq!(expected_fee, data.fee_amount); let unspents = vec![UnspentInfo { @@ -3138,7 +3132,7 @@ fn test_generate_tx_doge_fee() { .add_available_inputs(unspents) .add_outputs(outputs); let (_, data) = block_on(builder.build()).unwrap(); - let expected_fee = 3000000; + let expected_fee = 2292000; assert_eq!(expected_fee, data.fee_amount); } @@ -3154,6 +3148,84 @@ fn doge_mtp() { assert_eq!(mtp, 1614849084); } +#[test] +fn test_parse_fixed_utxo_txfee_config() { + let config = json!({ + "coin": "DOGE", + "name": "dogecoin", + "fname": "Dogecoin", + "rpcport": 22555, + "pubtype": 30, + "p2shtype": 22, + "wiftype": 158, + "txfee": 1000000, + "force_min_relay_fee": true, + "mm2": 1, + "required_confirmations": 2, + "avg_blocktime": 1, + "protocol": { + "type": "UTXO" + } + }); + let request = json!({ + "method": "electrum", + "coin": "DOGE", + "servers": [{"url": "electrum1.cipig.net:10060"},{"url": "electrum2.cipig.net:10060"},{"url": "electrum3.cipig.net:10060"}], + }); + let ctx = MmCtxBuilder::default().into_mm_arc(); + let params = UtxoActivationParams::from_legacy_req(&request).unwrap(); + + let priv_key = Secp256k1Secret::from([1; 32]); + let doge = block_on(utxo_standard_coin_with_priv_key( + &ctx, "DOGE", &config, ¶ms, priv_key, + )) + .unwrap(); + assert!(matches!(doge.as_ref().tx_fee, FeeRate::FixedPerKb(1000000_u64))); +} + +#[test] +fn test_parse_fixed_dingo_txfee_config() { + let config = json!({ + "coin": "DINGO", + "name": "dingocoin", + "fname": "Dingocoin", + "sign_message_prefix": "Dingocoin Signed Message:\n", + "rpcport": 34646, + "pubtype": 30, + "p2shtype": 22, + "wiftype": 158, + "txfee": 100000000, + "dingo_fee": true, + "force_min_relay_fee": true, + "dust": 100000000, + "mm2": 1, + "required_confirmations": 5, + "avg_blocktime": 60, + "protocol": { + "type": "UTXO" + }, + "derivation_path": "m/44'/3'", + "links": { + "github": "https://github.com/dingocoin/dingocoin", + "homepage": "https://dingocoin.com" + } + }); + let request = json!({ + "method": "electrum", + "coin": "DINGO", + "servers": [{"url": "elecx1.dingocoin.com:3342"},{"url": "elecx1.dingocoin.com:3339"},{"url": "elecx2.dingocoin.com:3342"},{"url": "elecx2.dingocoin.com:3339"},{"url": "delecx.twinkykms.com:3342"},{"url": "delecx.twinkykms.com:3339"}], + }); + let ctx = MmCtxBuilder::default().into_mm_arc(); + let params = UtxoActivationParams::from_legacy_req(&request).unwrap(); + + let priv_key = Secp256k1Secret::from([1; 32]); + let dingo = block_on(utxo_standard_coin_with_priv_key( + &ctx, "DINGO", &config, ¶ms, priv_key, + )) + .unwrap(); + assert!(matches!(dingo.as_ref().tx_fee, FeeRate::FixedPerKbDingo(100000000_u64))); +} + #[test] fn firo_mtp() { let electrum = electrum_client_for_test(&[ @@ -3646,7 +3718,7 @@ fn test_withdraw_p2pk_balance() { assert_eq!(output_script, expected_script); // And it should have this value (p2pk balance - amount sent - fees). - assert_eq!(transaction.outputs[1].value, 899999000); + assert_eq!(transaction.outputs[1].value, 899999755); } /// `UtxoStandardCoin` has to check UTXO maturity if `check_utxo_maturity` is `true`. @@ -3829,6 +3901,8 @@ fn test_split_qtum() { log!("Res = {:?}", res); } +/// Test to validate the fix for https://github.com/KomodoPlatform/komodo-defi-framework/issues/2313 produces valid txfee for tx with many inputs +/// (as before the fix) #[test] fn test_raven_low_tx_fee_okay() { let config = json!({ @@ -4006,11 +4080,11 @@ fn test_raven_low_tx_fee_okay() { .add_available_inputs(unspents) .add_outputs(outputs); let (_, data) = block_on(builder.build()).unwrap(); - let expected_fee = 3000000; + let expected_fee = 2065000; assert_eq!(expected_fee, data.fee_amount); } -/// Test to validate fix for https://github.com/KomodoPlatform/komodo-defi-framework/issues/2313 +/// Test to validate the fix for https://github.com/KomodoPlatform/komodo-defi-framework/issues/2313 (code before the fix created tx with too low txfee ) #[test] fn test_raven_low_tx_fee_error() { let config = json!({ @@ -4184,7 +4258,7 @@ fn test_raven_low_tx_fee_error() { .add_available_inputs(unspents) .add_outputs(outputs); let (_, data) = block_on(builder.build()).unwrap(); - let expected_fee = 3000000; + let expected_fee = 2031000; assert_eq!(expected_fee, data.fee_amount); } diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index 635f6f963d..62f0bd4f82 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -395,7 +395,7 @@ impl ZCoin { async fn get_one_kbyte_tx_fee(&self) -> UtxoRpcResult { let fee = self.get_fee_rate().await?; match fee { - ActualFeeRate::Dynamic(fee) | ActualFeeRate::FixedPerKb(fee) => { + ActualFeeRate::Dynamic(fee) | ActualFeeRate::FixedPerKb(fee) | ActualFeeRate::FixedPerKbDingo(fee) => { Ok(big_decimal_from_sat_unsigned(fee, self.decimals())) }, } @@ -1828,7 +1828,6 @@ impl MmCoin for ZCoin { &self, _value: TradePreimageValue, _stage: FeeApproxStage, - _include_refund_fee: bool, ) -> TradePreimageResult { Ok(TradeFee { coin: self.ticker().to_owned(), diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index 8e23fed368..d726209b57 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -1161,7 +1161,7 @@ impl BalanceTradeFeeUpdatedHandler for BalanceUpdateOrdermatchHandler { } // Get the max maker available volume to check if the wallet balances are sufficient for the issued maker orders. // Note although the maker orders are issued already, but they are not matched yet, so pass the `OrderIssue` stage. - let new_volume = match calc_max_maker_vol(&ctx, coin, new_balance, FeeApproxStage::OrderIssue).await { + let new_volume = match calc_max_maker_vol(&ctx, coin, new_balance, FeeApproxStage::OrderIssueMax).await { Ok(vol_info) => vol_info.volume, Err(e) if e.get_inner().not_sufficient_balance() => MmNumber::from(0), Err(e) => { @@ -3748,15 +3748,15 @@ pub async fn lp_ordermatch_loop(ctx: MmArc) { continue; }, }; - let max_vol = match calc_max_maker_vol(&ctx, &base, ¤t_balance, FeeApproxStage::OrderIssue).await - { - Ok(vol_info) => vol_info.volume, - Err(e) => { - log::info!("Error {} on balance check to kickstart order {}, cancelling", e, uuid); - to_cancel.push(uuid); - continue; - }, - }; + let max_vol = + match calc_max_maker_vol(&ctx, &base, ¤t_balance, FeeApproxStage::OrderIssueMax).await { + Ok(vol_info) => vol_info.volume, + Err(e) => { + log::info!("Error {} on balance check to kickstart order {}, cancelling", e, uuid); + to_cancel.push(uuid); + continue; + }, + }; if max_vol < order.available_amount() { order.max_base_vol = order.reserved_amount() + max_vol; } diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 010a5948cf..ecd06ab055 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -174,12 +174,6 @@ const NEGOTIATE_SEND_INTERVAL: f64 = 30.; /// If a certain P2P message is not received, swap will be aborted after this time expires. const NEGOTIATION_TIMEOUT_SEC: u64 = 90; -/// Add refund fee to calculate maximum available balance for a swap (including possible refund) -pub(crate) const INCLUDE_REFUND_FEE: bool = true; - -/// Do not add refund fee to calculate fee needed only to make a successful swap -pub(crate) const NO_REFUND_FEE: bool = false; - const MAX_STARTED_AT_DIFF: u64 = MAX_TIME_GAP_FOR_CONNECTED_PEER * 3; cfg_wasm32! { diff --git a/mm2src/mm2_main/src/lp_swap/check_balance.rs b/mm2src/mm2_main/src/lp_swap/check_balance.rs index db4fca2179..d225f7d7f2 100644 --- a/mm2src/mm2_main/src/lp_swap/check_balance.rs +++ b/mm2src/mm2_main/src/lp_swap/check_balance.rs @@ -81,7 +81,6 @@ pub async fn check_my_coin_balance_for_swap( locked_by_swaps: Some(locked.to_decimal()), }); } - Ok(balance.into()) } diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 9679b0db07..dd3ade917b 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -10,8 +10,7 @@ use super::{ tx_helper_topic, wait_for_maker_payment_conf_until, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, SavedSwap, SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapPubkeys, SwapTxDataMsg, SwapsContext, TransactionIdentifier, - INCLUDE_REFUND_FEE, NO_REFUND_FEE, TAKER_FEE_VALIDATION_ATTEMPTS, TAKER_FEE_VALIDATION_RETRY_DELAY_SECS, - WAIT_CONFIRM_INTERVAL_SEC, + TAKER_FEE_VALIDATION_ATTEMPTS, TAKER_FEE_VALIDATION_RETRY_DELAY_SECS, WAIT_CONFIRM_INTERVAL_SEC, }; use crate::lp_dispatcher::{DispatcherContext, LpEvents}; use crate::lp_network::subscribe_to_topic; @@ -502,9 +501,7 @@ impl MakerSwap { // do not use self.r().data here as it is not initialized at this step yet let preimage_value = TradePreimageValue::Exact(self.maker_amount.clone()); let stage = FeeApproxStage::StartSwap; - let get_sender_trade_fee_fut = self - .maker_coin - .get_sender_trade_fee(preimage_value, stage, NO_REFUND_FEE); + let get_sender_trade_fee_fut = self.maker_coin.get_sender_trade_fee(preimage_value, stage); let maker_payment_trade_fee = match get_sender_trade_fee_fut.await { Ok(fee) => fee, Err(e) => { @@ -2389,7 +2386,7 @@ pub async fn check_balance_for_maker_swap( None => { let preimage_value = TradePreimageValue::Exact(volume.to_decimal()); let maker_payment_trade_fee = my_coin - .get_sender_trade_fee(preimage_value, stage, INCLUDE_REFUND_FEE) + .get_sender_trade_fee(preimage_value, stage) .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, my_coin.ticker()))?; let taker_payment_spend_trade_fee = other_coin @@ -2426,7 +2423,7 @@ pub async fn maker_swap_trade_preimage( let rel_coin_ticker = rel_coin.ticker(); let volume = if req.max { let balance = base_coin.my_spendable_balance().compat().await.map_mm_err()?; - calc_max_maker_vol(ctx, &base_coin, &balance, FeeApproxStage::TradePreimage) + calc_max_maker_vol(ctx, &base_coin, &balance, FeeApproxStage::TradePreimageMax) .await .map_mm_err()? .volume @@ -2444,7 +2441,7 @@ pub async fn maker_swap_trade_preimage( let preimage_value = TradePreimageValue::Exact(volume.to_decimal()); let base_coin_fee = base_coin - .get_sender_trade_fee(preimage_value, FeeApproxStage::TradePreimage, NO_REFUND_FEE) + .get_sender_trade_fee(preimage_value, FeeApproxStage::TradePreimage) .await .mm_err(|e| TradePreimageRpcError::from_trade_preimage_error(e, base_coin_ticker))?; let rel_coin_fee = rel_coin @@ -2511,7 +2508,7 @@ pub struct CoinVolumeInfo { /// Returns [`CheckBalanceError::NotSufficientBalance`] if the balance is insufficient. pub async fn get_max_maker_vol(ctx: &MmArc, my_coin: &MmCoinEnum) -> CheckBalanceResult { let my_balance = my_coin.my_spendable_balance().compat().await.map_mm_err()?; - calc_max_maker_vol(ctx, my_coin, &my_balance, FeeApproxStage::OrderIssue).await + calc_max_maker_vol(ctx, my_coin, &my_balance, FeeApproxStage::OrderIssueMax).await } /// Calculates max Maker volume. @@ -2530,7 +2527,7 @@ pub async fn calc_max_maker_vol( let preimage_value = TradePreimageValue::UpperBound(volume.to_decimal()); let trade_fee = coin - .get_sender_trade_fee(preimage_value, stage, INCLUDE_REFUND_FEE) + .get_sender_trade_fee(preimage_value, stage) .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, ticker))?; 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 e1054eb165..18679d945c 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap_v2.rs @@ -6,11 +6,11 @@ use super::{ }; use crate::lp_swap::maker_swap::MakerSwapPreparedParams; use crate::lp_swap::swap_lock::SwapLock; +use crate::lp_swap::swap_v2_pb::*; use crate::lp_swap::{ broadcast_swap_v2_msg_every, check_balance_for_maker_swap, recv_swap_v2_msg, SwapConfirmationsSettings, TransactionIdentifier, MAKER_SWAP_V2_TYPE, MAX_STARTED_AT_DIFF, }; -use crate::lp_swap::{swap_v2_pb::*, NO_REFUND_FEE}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use coins::hd_wallet::AddrToString; @@ -920,7 +920,7 @@ impl fee, diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index 4dbfcd7d6b..e00078203d 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -10,7 +10,7 @@ use super::{ get_locked_amount, recv_swap_msg, swap_topic, wait_for_maker_payment_conf_until, AtomicSwap, LockedAmount, MySwapInfo, NegotiationDataMsg, NegotiationDataV2, NegotiationDataV3, RecoveredSwap, RecoveredSwapAction, SavedSwap, SavedSwapIo, SavedTradeFee, SwapConfirmationsSettings, SwapError, SwapMsg, SwapPubkeys, SwapTxDataMsg, - SwapsContext, TransactionIdentifier, INCLUDE_REFUND_FEE, NO_REFUND_FEE, WAIT_CONFIRM_INTERVAL_SEC, + SwapsContext, TransactionIdentifier, WAIT_CONFIRM_INTERVAL_SEC, }; use crate::lp_network::subscribe_to_topic; use crate::lp_ordermatch::TakerOrderBuilder; @@ -1099,9 +1099,7 @@ impl TakerSwap { }, } }; - let get_sender_trade_fee_fut = self - .taker_coin - .get_sender_trade_fee(preimage_value, stage, NO_REFUND_FEE); + let get_sender_trade_fee_fut = self.taker_coin.get_sender_trade_fee(preimage_value, stage); let taker_payment_trade_fee = match get_sender_trade_fee_fut.await { Ok(fee) => fee, Err(e) => { @@ -2649,7 +2647,7 @@ pub async fn check_balance_for_taker_swap( .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, my_coin.ticker()))?; let preimage_value = TradePreimageValue::Exact(volume.to_decimal()); let taker_payment_trade_fee = my_coin - .get_sender_trade_fee(preimage_value, stage, INCLUDE_REFUND_FEE) + .get_sender_trade_fee(preimage_value, stage) .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, my_coin.ticker()))?; let maker_payment_spend_trade_fee = other_coin @@ -2749,7 +2747,7 @@ pub async fn taker_swap_trade_preimage( let preimage_value = TradePreimageValue::Exact(my_coin_volume.to_decimal()); let my_coin_trade_fee = my_coin - .get_sender_trade_fee(preimage_value, stage, NO_REFUND_FEE) + .get_sender_trade_fee(preimage_value, stage) .await .mm_err(|e| TradePreimageRpcError::from_trade_preimage_error(e, my_coin_ticker))?; let other_coin_trade_fee = other_coin @@ -2822,7 +2820,7 @@ pub async fn max_taker_vol(ctx: MmArc, req: Json) -> Result>, S Err(err) => return ERR!("!lp_coinfind({}): {}", req.coin, err), }; let other_coin = req.trade_with.as_ref().unwrap_or(&req.coin); - let fut = calc_max_taker_vol(&ctx, &coin, other_coin, FeeApproxStage::TradePreimage); + let fut = calc_max_taker_vol(&ctx, &coin, other_coin, FeeApproxStage::TradePreimageMax); let max_vol = match fut.await { Ok(max_vol) => max_vol, Err(e) if e.get_inner().not_sufficient_balance() => { @@ -2909,7 +2907,7 @@ pub async fn calc_max_taker_vol( let max_possible = &balance - &locked; let preimage_value = TradePreimageValue::UpperBound(max_possible.to_decimal()); let max_trade_fee = coin - .get_sender_trade_fee(preimage_value, stage, INCLUDE_REFUND_FEE) + .get_sender_trade_fee(preimage_value, stage) .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, my_coin))?; 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 b9cd68edd7..334c71b77e 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap_v2.rs @@ -5,11 +5,11 @@ use super::{ NEGOTIATION_TIMEOUT_SEC, }; use crate::lp_swap::swap_lock::SwapLock; +use crate::lp_swap::swap_v2_pb::*; use crate::lp_swap::{ broadcast_swap_v2_msg_every, check_balance_for_taker_swap, recv_swap_v2_msg, swap_v2_topic, SwapConfirmationsSettings, TransactionIdentifier, MAX_STARTED_AT_DIFF, TAKER_SWAP_V2_TYPE, }; -use crate::lp_swap::{swap_v2_pb::*, NO_REFUND_FEE}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use coins::hd_wallet::AddrToString; @@ -1036,7 +1036,7 @@ impl fee, diff --git a/mm2src/mm2_main/src/lp_swap/trade_preimage.rs b/mm2src/mm2_main/src/lp_swap/trade_preimage.rs index 333ea85c83..61ca92cfd4 100644 --- a/mm2src/mm2_main/src/lp_swap/trade_preimage.rs +++ b/mm2src/mm2_main/src/lp_swap/trade_preimage.rs @@ -326,15 +326,21 @@ impl TradePreimageRpcError { ) -> TradePreimageRpcError { match maker_order_err { MakerOrderBuildError::BaseEqualRel => TradePreimageRpcError::BaseEqualRel, + // TODO: this error is displayed as 'The volume .. of the .. coin less than minimum transaction amount ' + // what may be thought as 'dust' but this is 'min order amount' + // better fix display as 'minimum order amount' MakerOrderBuildError::MaxBaseVolTooLow { actual, threshold } => TradePreimageRpcError::VolumeTooLow { coin: base.to_owned(), volume: actual.to_decimal(), - threshold: threshold.to_decimal(), + threshold: threshold.to_decimal(), // TODO: this is reported as 'The volume .. of the .. coin less than minimum transaction amount' what may be thought as dust ut this is 'min volume' }, MakerOrderBuildError::PriceTooLow { actual, threshold } => TradePreimageRpcError::PriceTooLow { price: actual.to_decimal(), threshold: threshold.to_decimal(), }, + // TODO: this error is displayed as 'The volume .. of the .. coin less than minimum transaction amount ' + // what may be thought as 'dust' but this is 'min order amount' + // better fix display as 'minimum order amount' MakerOrderBuildError::RelVolTooLow { actual, threshold } => TradePreimageRpcError::VolumeTooLow { coin: rel.to_owned(), volume: actual.to_decimal(), diff --git a/mm2src/mm2_main/tests/docker_tests/docker_ordermatch_tests.rs b/mm2src/mm2_main/tests/docker_tests/docker_ordermatch_tests.rs index 1c81c3cbd9..c852e2b638 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_ordermatch_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_ordermatch_tests.rs @@ -1174,6 +1174,7 @@ fn test_best_orders_filter_response() { } // https://github.com/KomodoPlatform/atomicDEX-API/issues/1148 +// here 'zombie' means 'unusable order' #[test] fn test_zombie_order_after_balance_reduce_and_mm_restart() { let coins = json! ([ @@ -1246,7 +1247,7 @@ fn test_zombie_order_after_balance_reduce_and_mm_restart() { .unwrap(); assert!(send_raw.0.is_success(), "!send_raw: {}", send_raw.1); - let new_expected_vol: BigDecimal = "499.99998".parse().unwrap(); + let new_expected_vol: BigDecimal = "499.99999481".parse().unwrap(); thread::sleep(Duration::from_secs(32)); 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 5e2adca310..11ac6cdc76 100644 --- a/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs +++ b/mm2src/mm2_main/tests/docker_tests/docker_tests_inner.rs @@ -587,7 +587,7 @@ fn order_should_be_updated_when_balance_is_decreased_alice_subscribes_after_upda "userpass": mm_bob.userpass, "method": "withdraw", "coin": "MYCOIN", - "amount": "499.99998", + "amount": "499.99999481", "to": "R9imXLs1hEcU9KbFDQq2hJEEJ1P5UoekaF", }))) .unwrap(); @@ -622,7 +622,7 @@ fn order_should_be_updated_when_balance_is_decreased_alice_subscribes_after_upda assert_eq!(asks.len(), 1, "Bob MYCOIN/MYCOIN1 orderbook must have exactly 1 ask"); let order_volume = asks[0]["maxvolume"].as_str().unwrap(); - assert_eq!("500", order_volume); + assert_eq!("500", order_volume); // 1000.0 - (499.99999481 + 0.00000274 txfee) = (500.0 + 0.00000274 txfee) log!("Get MYCOIN/MYCOIN1 orderbook on Alice side"); let rc = block_on(mm_alice.rpc(&json!({ @@ -737,7 +737,7 @@ fn order_should_be_updated_when_balance_is_decreased_alice_subscribes_before_upd "userpass": mm_bob.userpass, "method": "withdraw", "coin": "MYCOIN", - "amount": "499.99998", + "amount": "499.99999481", "to": "R9imXLs1hEcU9KbFDQq2hJEEJ1P5UoekaF", }))) .unwrap(); @@ -772,7 +772,7 @@ fn order_should_be_updated_when_balance_is_decreased_alice_subscribes_before_upd assert_eq!(asks.len(), 1, "Bob MYCOIN/MYCOIN1 orderbook must have exactly 1 ask"); let order_volume = asks[0]["maxvolume"].as_str().unwrap(); - assert_eq!("500", order_volume); + assert_eq!("500", order_volume); // 1000.0 - (499.99999481 + 0.00000245 txfee) = (500.0 + 0.00000274 txfee) log!("Get MYCOIN/MYCOIN1 orderbook on Alice side"); let rc = block_on(mm_alice.rpc(&json!({ @@ -971,7 +971,7 @@ fn test_match_and_trade_setprice_max() { log!("orderbook {:?}", bob_orderbook); let asks = bob_orderbook["asks"].as_array().unwrap(); assert_eq!(asks.len(), 1, "MYCOIN/MYCOIN1 orderbook must have exactly 1 ask"); - assert_eq!(asks[0]["maxvolume"], Json::from("999.99999")); + assert_eq!(asks[0]["maxvolume"], Json::from("999.99999726")); let rc = block_on(mm_alice.rpc(&json!({ "userpass": mm_alice.userpass, @@ -1080,7 +1080,7 @@ fn test_max_taker_vol_swap() { .unwrap(); assert!(rc.0.is_success(), "!max_taker_vol: {}", rc.1); let vol: MaxTakerVolResponse = serde_json::from_str(&rc.1).unwrap(); - let expected_vol = MmNumber::from((647499741, 12965000)); + let expected_vol = MmNumber::from((1294999865579, 25930000000)); let actual_vol = MmNumber::from(vol.result.clone()); log!("actual vol {}", actual_vol.to_decimal()); @@ -1182,10 +1182,10 @@ fn test_buy_when_coins_locked_by_other_swap() { "base": "MYCOIN", "rel": "MYCOIN1", "price": 1, - // the result of equation x + x / 777 + 0.00002 = 1 + // the result of equation x + x / 777 + 0.00000274 dexfee_txfee + 0.00000245 payment_txfee = 1 "volume": { - "numer":"77698446", - "denom":"77800000" + "numer":"77699596737", + "denom":"77800000000" }, }))) .unwrap(); @@ -1206,8 +1206,8 @@ fn test_buy_when_coins_locked_by_other_swap() { // it is slightly more than previous volume so it should fail // because the total sum of used funds will be slightly more than available 2 "volume": { - "numer":"77698447", - "denom":"77800000" + "numer":"77699599999", // increase volume +0.00000001 + "denom":"77800000000" }, }))) .unwrap(); @@ -1276,10 +1276,10 @@ fn test_sell_when_coins_locked_by_other_swap() { "base": "MYCOIN1", "rel": "MYCOIN", "price": 1, - // the result of equation x + x / 777 + 0.00002 = 1 + // the result of equation x + x / 777 + 0.00000245 + 0.00000274 = 1 "volume": { - "numer":"77698446", - "denom":"77800000" + "numer":"77699596737", + "denom":"77800000000" }, }))) .unwrap(); @@ -1289,6 +1289,7 @@ fn test_sell_when_coins_locked_by_other_swap() { block_on(mm_alice.wait_for_log(22., |log| log.contains("Entering the taker_swap_loop MYCOIN/MYCOIN1"))).unwrap(); // TODO when sell call is made immediately swap might be not put into swap ctx yet so locked // amount returns 0 + // NOTE: in this test sometimes Alice has time to send only the taker fee, sometimes can send even the payment tx too thread::sleep(Duration::from_secs(6)); let rc = block_on(mm_alice.rpc(&json!({ @@ -1300,8 +1301,8 @@ fn test_sell_when_coins_locked_by_other_swap() { // it is slightly more than previous volume so it should fail // because the total sum of used funds will be slightly more than available 2 "volume": { - "numer":"77698447", - "denom":"77800000" + "numer":"77699599999", // ensure volume > 1.00000000 + "denom":"77800000000" }, }))) .unwrap(); @@ -1340,10 +1341,11 @@ fn test_buy_max() { "base": "MYCOIN", "rel": "MYCOIN1", "price": 1, - // the result of equation x + x / 777 + 0.00002 = 1 + // the result of equation x + x / 777 + 0.00000274 dexfee_txfee + 0.00000245 payment_txfee = 1 + // (btw no need to add refund txfee - it's taken from the spend amount for utxo taker) "volume": { - "numer":"77698446", - "denom":"77800000" + "numer":"77699596737", + "denom":"77800000000" }, }))) .unwrap(); @@ -1357,8 +1359,8 @@ fn test_buy_max() { "price": 1, // it is slightly more than previous volume so it should fail "volume": { - "numer":"77698447", - "denom":"77800000" + "numer":"77699596738", + "denom":"77800000000" }, }))) .unwrap(); @@ -1414,12 +1416,12 @@ fn test_maker_trade_preimage() { }))) .unwrap(); assert!(rc.0.is_success(), "!trade_preimage: {}", rc.1); - let base_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00001", false); - let rel_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00002", true); - let volume = MmNumber::from("9.99999"); + let base_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00000274", false); // txfee from get_sender_trade_fee + let rel_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00000992", true); + let volume = MmNumber::from("9.99999726"); // 1.0 - 0.00000274 from calc_max_maker_vol - let my_coin_total = TotalTradeFeeForTest::new("MYCOIN", "0.00001", "0.00001"); - let my_coin1_total = TotalTradeFeeForTest::new("MYCOIN1", "0.00002", "0"); + let my_coin_total = TotalTradeFeeForTest::new("MYCOIN", "0.00000274", "0.00000274"); + let my_coin1_total = TotalTradeFeeForTest::new("MYCOIN1", "0.00000992", "0"); let expected = TradePreimageResult::MakerPreimage(MakerPreimage { base_coin_fee, @@ -1451,12 +1453,12 @@ fn test_maker_trade_preimage() { let mut actual: RpcSuccessResponse = serde_json::from_str(&rc.1).unwrap(); actual.result.sort_total_fees(); - let base_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00002", false); - let rel_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00001", true); - let volume = MmNumber::from("19.99998"); + let base_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00000548", false); + let rel_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00000496", true); + let volume = MmNumber::from("19.99999452"); - let my_coin_total = TotalTradeFeeForTest::new("MYCOIN", "0.00001", "0"); - let my_coin1_total = TotalTradeFeeForTest::new("MYCOIN1", "0.00002", "0.00002"); + let my_coin_total = TotalTradeFeeForTest::new("MYCOIN", "0.00000496", "0"); + let my_coin1_total = TotalTradeFeeForTest::new("MYCOIN1", "0.00000548", "0.00000548"); let expected = TradePreimageResult::MakerPreimage(MakerPreimage { base_coin_fee, rel_coin_fee, @@ -1478,7 +1480,7 @@ fn test_maker_trade_preimage() { "rel": "MYCOIN", "swap_method": "setprice", "price": 1, - "volume": "19.99998", + "volume": "19.99999109", // actually try max value (balance - txfee = 20.0 - 0.00000823) }, }))) .unwrap(); @@ -1486,11 +1488,11 @@ fn test_maker_trade_preimage() { let mut actual: RpcSuccessResponse = serde_json::from_str(&rc.1).unwrap(); actual.result.sort_total_fees(); - let base_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00002", false); - let rel_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00001", true); + let base_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00000891", false); // txfee updated for calculated max volume (not 616) + let rel_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00000496", true); - let total_my_coin = TotalTradeFeeForTest::new("MYCOIN", "0.00001", "0"); - let total_my_coin1 = TotalTradeFeeForTest::new("MYCOIN1", "0.00002", "0.00002"); + let total_my_coin = TotalTradeFeeForTest::new("MYCOIN", "0.00000496", "0"); + let total_my_coin1 = TotalTradeFeeForTest::new("MYCOIN1", "0.00000891", "0.00000891"); let expected = TradePreimageResult::MakerPreimage(MakerPreimage { base_coin_fee, @@ -1580,13 +1582,13 @@ fn test_taker_trade_preimage() { let mut actual: RpcSuccessResponse = serde_json::from_str(&rc.1).unwrap(); actual.result.sort_total_fees(); - let base_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00001", false); - let rel_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00002", true); + let base_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00000274", false); + let rel_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00000992", true); let taker_fee = TradeFeeForTest::new("MYCOIN", "0.01", false); - let fee_to_send_taker_fee = TradeFeeForTest::new("MYCOIN", "0.00001", false); + let fee_to_send_taker_fee = TradeFeeForTest::new("MYCOIN", "0.00000245", false); - let my_coin_total_fee = TotalTradeFeeForTest::new("MYCOIN", "0.01002", "0.01002"); - let my_coin1_total_fee = TotalTradeFeeForTest::new("MYCOIN1", "0.00002", "0"); + let my_coin_total_fee = TotalTradeFeeForTest::new("MYCOIN", "0.01000519", "0.01000519"); + let my_coin1_total_fee = TotalTradeFeeForTest::new("MYCOIN1", "0.00000992", "0"); let expected = TradePreimageResult::TakerPreimage(TakerPreimage { base_coin_fee, @@ -1614,13 +1616,13 @@ fn test_taker_trade_preimage() { let mut actual: RpcSuccessResponse = serde_json::from_str(&rc.1).unwrap(); actual.result.sort_total_fees(); - let base_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00001", true); - let rel_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00002", false); + let base_coin_fee = TradeFeeForTest::new("MYCOIN", "0.00000496", true); + let rel_coin_fee = TradeFeeForTest::new("MYCOIN1", "0.00000548", false); // fee to send taker payment let taker_fee = TradeFeeForTest::new("MYCOIN1", "0.02", false); - let fee_to_send_taker_fee = TradeFeeForTest::new("MYCOIN1", "0.00002", false); + let fee_to_send_taker_fee = TradeFeeForTest::new("MYCOIN1", "0.0000049", false); - let my_coin_total_fee = TotalTradeFeeForTest::new("MYCOIN", "0.00001", "0"); - let my_coin1_total_fee = TotalTradeFeeForTest::new("MYCOIN1", "0.02004", "0.02004"); + let my_coin_total_fee = TotalTradeFeeForTest::new("MYCOIN", "0.00000496", "0"); + let my_coin1_total_fee = TotalTradeFeeForTest::new("MYCOIN1", "0.02001038", "0.02001038"); // taker_fee + rel_coin_fee + fee_to_send_taker_fee let expected = TradePreimageResult::TakerPreimage(TakerPreimage { base_coin_fee, @@ -1680,8 +1682,8 @@ fn test_trade_preimage_not_sufficient_balance() { log!("{:?}", block_on(enable_native(&mm, "MYCOIN1", &[], None))); log!("{:?}", block_on(enable_native(&mm, "MYCOIN", &[], None))); - fill_balance_functor(MmNumber::from("0.000015").to_decimal()); - // Try sell the max amount with the zero balance. + fill_balance_functor(MmNumber::from("0.00001273").to_decimal()); // volume < txfee + dust = 274 + 1000 + // Try sell the max amount with the zero balance. let rc = block_on(mm.rpc(&json!({ "userpass": mm.userpass, "mmrpc": "2.0", @@ -1696,10 +1698,10 @@ fn test_trade_preimage_not_sufficient_balance() { }))) .unwrap(); assert!(!rc.0.is_success(), "trade_preimage success, but should fail: {}", rc.1); - let available = MmNumber::from("0.000015").to_decimal(); - // Required at least 0.00002 MYCOIN to pay the transaction_fee(0.00001) and to send a value not less than dust(0.00001). - let required = MmNumber::from("0.00002").to_decimal(); - expect_not_sufficient_balance(&rc.1, available, required, None); + let available = MmNumber::from("0.00001273").to_decimal(); + // Required at least 0.00001274 MYCOIN to pay the transaction_fee(0.00000274) and to send a value not less than dust(0.00001) and not less than min_trading_vol (10 * dust). + let required = MmNumber::from("0.00001274").to_decimal(); // TODO: this is not true actually: we can't create orders less that min_trading_vol = 10 * dust + expect_not_sufficient_balance(&rc.1, available, required, Some(MmNumber::from("0").to_decimal())); let rc = block_on(mm.rpc(&json!({ "userpass": mm.userpass, @@ -1716,8 +1718,8 @@ fn test_trade_preimage_not_sufficient_balance() { .unwrap(); assert!(!rc.0.is_success(), "trade_preimage success, but should fail: {}", rc.1); // Required 0.00001 MYCOIN to pay the transaction fee and the specified 0.1 volume. - let available = MmNumber::from("0.000015").to_decimal(); - let required = MmNumber::from("0.10001").to_decimal(); + let available = MmNumber::from("0.00001273").to_decimal(); + let required = MmNumber::from("0.1000024").to_decimal(); expect_not_sufficient_balance(&rc.1, available, required, None); let rc = block_on(mm.rpc(&json!({ @@ -1734,11 +1736,11 @@ fn test_trade_preimage_not_sufficient_balance() { }))) .unwrap(); assert!(!rc.0.is_success(), "trade_preimage success, but should fail: {}", rc.1); - // balance(0.000015) - let available = MmNumber::from("0.000015").to_decimal(); - // balance(0.000015) + transaction_fee(0.00001) - let required = MmNumber::from("0.00002").to_decimal(); - expect_not_sufficient_balance(&rc.1, available, required, None); + // balance(0.00001273) + let available = MmNumber::from("0.00001273").to_decimal(); + // required min_tx_amount(0.00001) + transaction_fee(0.00000274) + let required = MmNumber::from("0.00001274").to_decimal(); + expect_not_sufficient_balance(&rc.1, available, required, Some(MmNumber::from("0").to_decimal())); fill_balance_functor(MmNumber::from("7.770085").to_decimal()); let rc = block_on(mm.rpc(&json!({ @@ -1755,12 +1757,12 @@ fn test_trade_preimage_not_sufficient_balance() { }))) .unwrap(); assert!(!rc.0.is_success(), "trade_preimage success, but should fail: {}", rc.1); - let available = MmNumber::from("7.7701").to_decimal(); + let available = MmNumber::from("7.77009773").to_decimal(); // `required = volume + fee_to_send_taker_payment + dex_fee + fee_to_send_dex_fee`, - // where `volume = 7.77`, `fee_to_send_taker_payment = fee_to_send_dex_fee = 0.00001`, `dex_fee = 0.01`. + // where `volume = 7.77`, `fee_to_send_taker_payment = 0.00000393, fee_to_send_dex_fee = 0.00000422`, `dex_fee = 0.01`. // Please note `dex_fee = 7.77 / 777` with dex_fee = 0.01 - // required = 7.77 + 0.01 (dex_fee) + (0.0001 * 2) = 7.78002 - let required = MmNumber::from("7.78002"); + // required = 7.77 + 0.01 (dex_fee) + (0.00000393 + 0.00000422) = 7.78000815 + let required = MmNumber::from("7.78000815"); expect_not_sufficient_balance(&rc.1, available, required.to_decimal(), Some(BigDecimal::from(0))); } @@ -2018,10 +2020,10 @@ fn test_get_max_taker_vol() { .unwrap(); assert!(rc.0.is_success(), "!max_taker_vol: {}", rc.1); let json: MaxTakerVolResponse = serde_json::from_str(&rc.1).unwrap(); - // the result of equation `max_vol + max_vol / 777 + 0.00002 = 1` + // the result of equation `max_vol + max_vol / 777 + 0.00000274 + 0.00000245 = 1` // derived from `max_vol = balance - locked - trade_fee - fee_to_send_taker_fee - dex_fee(max_vol)` // where balance = 1, locked = 0, trade_fee = fee_to_send_taker_fee = 0.00001, dex_fee = max_vol / 777 - let expected = MmNumber::from((38849223, 38900000)).to_fraction(); + let expected = MmNumber::from((77699596737, 77800000000)).to_fraction(); assert_eq!(json.result, expected); assert_eq!(json.coin, "MYCOIN1"); @@ -2071,8 +2073,8 @@ fn test_get_max_taker_vol_dex_fee_min_tx_amount() { .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 + 0.00001 (dex fee) + 0.0002 (miner fee * 2) = 0.00532845 - assert_eq!(json["result"]["numer"], Json::from("102369")); + // the result of equation x + 0.00001 (dex fee) + 0.0000485 (miner fee 2740 + 2450) = 0.00532845 + assert_eq!(json["result"]["numer"], Json::from("105331")); assert_eq!(json["result"]["denom"], Json::from("20000000")); let rc = block_on(mm_alice.rpc(&json!({ @@ -2097,11 +2099,11 @@ fn test_get_max_taker_vol_dex_fee_min_tx_amount() { /// `volume + taker_fee + trade_fee + fee_to_send_taker_fee = x`. /// Let `dust = 0.000728` like for Qtum, `trade_fee = 0.0001`, `fee_to_send_taker_fee = 0.0001` and `taker_fee` is the `0.000728` threshold, /// therefore to find a minimum required balance, we should pass the `dust` as the `volume` into the equation above: -/// `2 * 0.000728 + 0.0002 = x`, so `x = 0.001656` +/// `2 * 0.000728 + 0.00002740 + 0.00002450 = x`, so `x = 0.0014041` #[test] fn test_get_max_taker_vol_dust_threshold() { // first, try to test with the balance slightly less than required - let (_ctx, coin, priv_key) = generate_utxo_coin_with_random_privkey("MYCOIN1", "0.001656".parse().unwrap()); + let (_ctx, coin, priv_key) = generate_utxo_coin_with_random_privkey("MYCOIN1", "0.0014041".parse().unwrap()); let coins = json!([ mycoin_conf(10000), {"coin":"MYCOIN1","asset":"MYCOIN1","txversion":4,"overwintered":1,"txfee":10000,"protocol":{"type":"UTXO"},"dust":72800} @@ -2137,7 +2139,7 @@ fn test_get_max_taker_vol_dust_threshold() { let result: MmNumber = serde_json::from_value(json["result"].clone()).unwrap(); assert!(result.is_zero()); - fill_address(&coin, &coin.my_address().unwrap(), "0.00001".parse().unwrap(), 30); + fill_address(&coin, &coin.my_address().unwrap(), "0.0002".parse().unwrap(), 30); //00699910 let rc = block_on(mm.rpc(&json!({ "userpass": mm.userpass, @@ -2147,9 +2149,9 @@ fn test_get_max_taker_vol_dust_threshold() { .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 + 0.000728 (dex fee) + 0.0002 (miner fee * 2) = 0.001666 - assert_eq!(json["result"]["numer"], Json::from("369")); - assert_eq!(json["result"]["denom"], Json::from("500000")); + // the result of equation x + 0.000728 (dex fee) + 0.00004220 + 0.00003930 (miner fees) = 0.0016041, x > dust + assert_eq!(json["result"]["numer"], Json::from("3973")); + assert_eq!(json["result"]["denom"], Json::from("5000000")); block_on(mm.stop()).unwrap(); } @@ -2197,9 +2199,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 + x * 9 / 7770 + 0.00002740 + 0.00002450 = 1 + assert_eq!(json["result"]["numer"], Json::from("2589865579")); + assert_eq!(json["result"]["denom"], Json::from("2593000000")); let rc = block_on(mm_alice.rpc(&json!({ "userpass": mm_alice.userpass, @@ -2229,8 +2231,8 @@ fn test_get_max_maker_vol() { log!("{:?}", block_on(enable_native(&mm, "MYCOIN", &[], None))); log!("{:?}", block_on(enable_native(&mm, "MYCOIN1", &[], None))); - // 1 - tx_fee - let expected_volume = MmNumber::from("0.99999"); + // 1 - tx_fee (274) + let expected_volume = MmNumber::from("0.99999726"); let expected = MaxMakerVolResponse { coin: "MYCOIN1".to_string(), volume: MmNumberMultiRepr::from(expected_volume.clone()), @@ -3113,9 +3115,9 @@ fn test_withdraw_not_sufficient_balance() { let (_ctx, coin) = utxo_coin_from_privkey("MYCOIN", privkey); fill_address(&coin, &coin.my_address().unwrap(), balance.clone(), 30); - // txfee = 0.00001, amount = 0.5 => required = 0.50001 + // txfee = 0.00000211, amount = 0.5 => required = 0.50000211 // but balance = 0.5 - let txfee = BigDecimal::from(1) / BigDecimal::from(100000); + let txfee = BigDecimal::from_str("0.00000211").unwrap(); let withdraw = block_on(mm.rpc(&json!({ "mmrpc": "2.0", "userpass": mm.userpass, @@ -3602,13 +3604,13 @@ fn test_locked_amount() { let locked_bob = block_on(get_locked_amount(&mm_bob, "MYCOIN")); assert_eq!(locked_bob.coin, "MYCOIN"); - let expected_result: MmNumberMultiRepr = MmNumber::from("777.00001").into(); + let expected_result: MmNumberMultiRepr = MmNumber::from("777.00000274").into(); // volume + txfee = 777 + 1 + 0.0000274 assert_eq!(expected_result, locked_bob.locked_amount); let locked_alice = block_on(get_locked_amount(&mm_alice, "MYCOIN1")); assert_eq!(locked_alice.coin, "MYCOIN1"); - let expected_result: MmNumberMultiRepr = MmNumber::from("778.00002").into(); + let expected_result: MmNumberMultiRepr = MmNumber::from("778.00000519").into(); // volume + dexfee + txfee + txfee = 777 + 1 + 0.0000245 + 0.00000274 assert_eq!(expected_result, locked_alice.locked_amount); } diff --git a/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs b/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs index 4a9147b5b0..930fdc4f88 100644 --- a/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs @@ -1014,8 +1014,7 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ // So we should deduct trade fee from the output. let max_trade_fee = block_on(coin.get_sender_trade_fee( TradePreimageValue::UpperBound(qtum_balance.clone()), - FeeApproxStage::TradePreimage, - true, + FeeApproxStage::TradePreimageMax, )) .expect("!get_sender_trade_fee"); let max_trade_fee = max_trade_fee.amount.to_decimal(); @@ -1030,7 +1029,7 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ // - `max_fee_to_send_taker_fee = fee_to_send_taker_fee(max_dex_fee)` // `taker_fee` is sent using general withdraw, and the fee get be obtained from withdraw result let max_fee_to_send_taker_fee = - block_on(coin.get_fee_to_send_taker_fee(max_dex_fee, FeeApproxStage::TradePreimage)) + block_on(coin.get_fee_to_send_taker_fee(max_dex_fee, FeeApproxStage::TradePreimageMax)) .expect("!get_fee_to_send_taker_fee"); let max_fee_to_send_taker_fee = max_fee_to_send_taker_fee.amount.to_decimal(); log!("max_fee_to_send_taker_fee: {}", max_fee_to_send_taker_fee); diff --git a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs index 55caf8e934..0ced4f84d4 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_proto_v2_tests.rs @@ -372,7 +372,7 @@ fn send_and_spend_taker_payment_dex_fee_burn_kmd() { assert_eq!(taker_payment_spend_preimage.preimage.outputs.len(), 3); assert_eq!(taker_payment_spend_preimage.preimage.outputs[0].value, 75000000); assert_eq!(taker_payment_spend_preimage.preimage.outputs[1].value, 25000000); - assert_eq!(taker_payment_spend_preimage.preimage.outputs[2].value, 77699998000); + assert_eq!(taker_payment_spend_preimage.preimage.outputs[2].value, 77699999008); block_on( maker_coin.validate_taker_payment_spend_preimage(&gen_taker_payment_spend_args, &taker_payment_spend_preimage), @@ -479,7 +479,7 @@ fn send_and_spend_taker_payment_dex_fee_burn_non_kmd() { assert_eq!(taker_payment_spend_preimage.preimage.outputs.len(), 3); assert_eq!(taker_payment_spend_preimage.preimage.outputs[0].value, 75_000_000); assert_eq!(taker_payment_spend_preimage.preimage.outputs[1].value, 25_000_000); - assert_eq!(taker_payment_spend_preimage.preimage.outputs[2].value, 77699998000); + assert_eq!(taker_payment_spend_preimage.preimage.outputs[2].value, 77699999008); block_on( maker_coin.validate_taker_payment_spend_preimage(&gen_taker_payment_spend_args, &taker_payment_spend_preimage), @@ -716,15 +716,15 @@ fn test_v2_swap_utxo_utxo_impl() { // coins must be virtually locked until swap transactions are sent let locked_bob = block_on(get_locked_amount(&mm_bob, MYCOIN)); assert_eq!(locked_bob.coin, MYCOIN); - let expected: MmNumberMultiRepr = MmNumber::from("777.00001").into(); + let expected: MmNumberMultiRepr = MmNumber::from("777.00000274").into(); assert_eq!(locked_bob.locked_amount, expected); let locked_alice = block_on(get_locked_amount(&mm_alice, MYCOIN1)); assert_eq!(locked_alice.coin, MYCOIN1); let expected: MmNumberMultiRepr = if SET_BURN_PUBKEY_TO_ALICE.get() { - MmNumber::from("777.00001").into() // no dex fee if dex pubkey is alice + MmNumber::from("777.00000274").into() } else { - MmNumber::from("778.00001").into() + MmNumber::from("778.00000274").into() }; assert_eq!(locked_alice.locked_amount, expected); @@ -851,12 +851,12 @@ fn test_v2_swap_utxo_utxo_kickstart() { // coins must be virtually locked after kickstart until swap transactions are sent let locked_alice = block_on(get_locked_amount(&mm_alice, MYCOIN1)); assert_eq!(locked_alice.coin, MYCOIN1); - let expected: MmNumberMultiRepr = MmNumber::from("778.00001").into(); + let expected: MmNumberMultiRepr = MmNumber::from("778.00000274").into(); assert_eq!(locked_alice.locked_amount, expected); let locked_bob = block_on(get_locked_amount(&mm_bob, MYCOIN)); assert_eq!(locked_bob.coin, MYCOIN); - let expected: MmNumberMultiRepr = MmNumber::from("777.00001").into(); + let expected: MmNumberMultiRepr = MmNumber::from("777.00000274").into(); assert_eq!(locked_bob.locked_amount, expected); // amount must unlocked after funding tx is sent diff --git a/mm2src/mm2_main/tests/docker_tests_main.rs b/mm2src/mm2_main/tests/docker_tests_main.rs index 5d7d4ddb94..2f32d66b4e 100644 --- a/mm2src/mm2_main/tests/docker_tests_main.rs +++ b/mm2src/mm2_main/tests/docker_tests_main.rs @@ -37,6 +37,13 @@ use docker_tests::qrc20_tests::{qtum_docker_node, QtumDockerOps, QTUM_REGTEST_DO #[allow(dead_code)] mod integration_tests_common; +const ENV_VAR_NO_UTXO_DOCKER: &str = "_KDF_NO_UTXO_DOCKER"; +const ENV_VAR_NO_QTUM_DOCKER: &str = "_KDF_NO_QTUM_DOCKER"; +const ENV_VAR_NO_SLP_DOCKER: &str = "_KDF_NO_SLP_DOCKER"; +const ENV_VAR_NO_ETH_DOCKER: &str = "_KDF_NO_ETH_DOCKER"; +const ENV_VAR_NO_COSMOS_DOCKER: &str = "_KDF_NO_COSMOS_DOCKER"; +const ENV_VAR_NO_ZOMBIE_DOCKER: &str = "_KDF_NO_ZOMBIE_DOCKER"; + // AP: custom test runner is intended to initialize the required environment (e.g. coin daemons in the docker containers) // and then gracefully clear it by dropping the RAII docker container handlers // I've tried to use static for such singleton initialization but it turned out that despite @@ -51,63 +58,119 @@ pub fn docker_tests_runner(tests: &[&TestDescAndFn]) { let mut containers = vec![]; // skip Docker containers initialization if we are intended to run test_mm_start only if env::var("_MM2_TEST_CONF").is_err() { - const IMAGES: &[&str] = &[ - UTXO_ASSET_DOCKER_IMAGE_WITH_TAG, - QTUM_REGTEST_DOCKER_IMAGE_WITH_TAG, - ZOMBIE_ASSET_DOCKER_IMAGE_WITH_TAG, - GETH_DOCKER_IMAGE_WITH_TAG, - NUCLEUS_IMAGE, - ATOM_IMAGE_WITH_TAG, - IBC_RELAYER_IMAGE_WITH_TAG, - ]; - - for image in IMAGES { + let mut images = vec![]; + + let disable_utxo: bool = env::var(ENV_VAR_NO_UTXO_DOCKER).is_ok(); + let disable_slp: bool = env::var(ENV_VAR_NO_SLP_DOCKER).is_ok(); + let disable_qtum: bool = env::var(ENV_VAR_NO_QTUM_DOCKER).is_ok(); + let disable_eth: bool = env::var(ENV_VAR_NO_ETH_DOCKER).is_ok(); + let disable_cosmos: bool = env::var(ENV_VAR_NO_COSMOS_DOCKER).is_ok(); + let disable_zombie: bool = env::var(ENV_VAR_NO_ZOMBIE_DOCKER).is_ok(); + + if !disable_utxo || !disable_slp { + images.push(UTXO_ASSET_DOCKER_IMAGE_WITH_TAG) + } + if !disable_qtum { + images.push(QTUM_REGTEST_DOCKER_IMAGE_WITH_TAG); + } + if !disable_eth { + images.push(GETH_DOCKER_IMAGE_WITH_TAG); + } + if !disable_cosmos { + images.push(NUCLEUS_IMAGE); + images.push(ATOM_IMAGE_WITH_TAG); + images.push(IBC_RELAYER_IMAGE_WITH_TAG); + } + if !disable_zombie { + images.push(ZOMBIE_ASSET_DOCKER_IMAGE_WITH_TAG); + } + + for image in images { pull_docker_image(image); remove_docker_containers(image); } - let runtime_dir = prepare_runtime_dir().unwrap(); - - let nucleus_node = nucleus_node(&docker, runtime_dir.clone()); - let atom_node = atom_node(&docker, runtime_dir.clone()); - let ibc_relayer_node = ibc_relayer_node(&docker, runtime_dir); - let utxo_node = utxo_asset_docker_node(&docker, "MYCOIN", 7000); - let utxo_node1 = utxo_asset_docker_node(&docker, "MYCOIN1", 8000); - let qtum_node = qtum_docker_node(&docker, 9000); - let for_slp_node = utxo_asset_docker_node(&docker, "FORSLP", 10000); - let geth_node = geth_docker_node(&docker, "ETH", 8545); - let zombie_node = zombie_asset_docker_node(&docker, 7090); - - let utxo_ops = UtxoAssetDockerOps::from_ticker("MYCOIN"); - let utxo_ops1 = UtxoAssetDockerOps::from_ticker("MYCOIN1"); - let qtum_ops = QtumDockerOps::new(); - let for_slp_ops = BchDockerOps::from_ticker("FORSLP"); - let zombie_ops = ZCoinAssetDockerOps::new(); - - zombie_ops.wait_ready(4); - qtum_ops.wait_ready(2); - qtum_ops.initialize_contracts(); - for_slp_ops.wait_ready(4); - for_slp_ops.initialize_slp(); - utxo_ops.wait_ready(4); - utxo_ops1.wait_ready(4); - - wait_for_geth_node_ready(); - init_geth_node(); - prepare_ibc_channels(ibc_relayer_node.container.id()); - - thread::sleep(Duration::from_secs(12)); - wait_until_relayer_container_is_ready(ibc_relayer_node.container.id()); - - containers.push(utxo_node); - containers.push(utxo_node1); - containers.push(qtum_node); - containers.push(for_slp_node); - containers.push(zombie_node); - containers.push(geth_node); - containers.push(nucleus_node); - containers.push(atom_node); - containers.push(ibc_relayer_node); + let (nucleus_node, atom_node, ibc_relayer_node) = if !disable_cosmos { + let runtime_dir = prepare_runtime_dir().unwrap(); + let nucleus_node = nucleus_node(&docker, runtime_dir.clone()); + let atom_node = atom_node(&docker, runtime_dir.clone()); + let ibc_relayer_node = ibc_relayer_node(&docker, runtime_dir); + (Some(nucleus_node), Some(atom_node), Some(ibc_relayer_node)) + } else { + (None, None, None) + }; + let (utxo_node, utxo_node1) = if !disable_utxo { + let utxo_node = utxo_asset_docker_node(&docker, "MYCOIN", 7000); + let utxo_node1 = utxo_asset_docker_node(&docker, "MYCOIN1", 8000); + (Some(utxo_node), Some(utxo_node1)) + } else { + (None, None) + }; + let qtum_node = if !disable_qtum { + let qtum_node = qtum_docker_node(&docker, 9000); + Some(qtum_node) + } else { + None + }; + let for_slp_node = if !disable_slp { + let for_slp_node = utxo_asset_docker_node(&docker, "FORSLP", 10000); + Some(for_slp_node) + } else { + None + }; + let geth_node = if !disable_eth { + let geth_node = geth_docker_node(&docker, "ETH", 8545); + Some(geth_node) + } else { + None + }; + let zombie_node = if !disable_zombie { + let zombie_node = zombie_asset_docker_node(&docker, 7090); + Some(zombie_node) + } else { + None + }; + + if let (Some(utxo_node), Some(utxo_node1)) = (utxo_node, utxo_node1) { + let utxo_ops = UtxoAssetDockerOps::from_ticker("MYCOIN"); + let utxo_ops1 = UtxoAssetDockerOps::from_ticker("MYCOIN1"); + utxo_ops.wait_ready(4); + utxo_ops1.wait_ready(4); + containers.push(utxo_node); + containers.push(utxo_node1); + } + if let Some(qtum_node) = qtum_node { + let qtum_ops = QtumDockerOps::new(); + qtum_ops.wait_ready(2); + qtum_ops.initialize_contracts(); + containers.push(qtum_node); + } + if let Some(for_slp_node) = for_slp_node { + let for_slp_ops = BchDockerOps::from_ticker("FORSLP"); + for_slp_ops.wait_ready(4); + for_slp_ops.initialize_slp(); + containers.push(for_slp_node); + } + if let Some(geth_node) = geth_node { + wait_for_geth_node_ready(); + init_geth_node(); + containers.push(geth_node); + } + if let Some(zombie_node) = zombie_node { + let zombie_ops = ZCoinAssetDockerOps::new(); + zombie_ops.wait_ready(4); + containers.push(zombie_node); + } + if let (Some(nucleus_node), Some(atom_node), Some(ibc_relayer_node)) = + (nucleus_node, atom_node, ibc_relayer_node) + { + prepare_ibc_channels(ibc_relayer_node.container.id()); + thread::sleep(Duration::from_secs(10)); + wait_until_relayer_container_is_ready(ibc_relayer_node.container.id()); + containers.push(nucleus_node); + containers.push(atom_node); + containers.push(ibc_relayer_node); + } } // detect if docker is installed // skip the tests that use docker if not installed diff --git a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs index aba82d15d4..9e118ce17e 100644 --- a/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs +++ b/mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs @@ -968,7 +968,7 @@ fn test_withdraw_and_send() { None, "RJTYiYeJ8eVvJ53n2YbrVmxWNNMVZjDGLh", &enable_res, - "-0.00101", + "-0.00100245", 0.001, ); @@ -2429,7 +2429,7 @@ fn test_electrum_tx_history() { None, &receiving_address, &enable_res_bob, - "-0.00101", + "-0.00100245", 0.001, );