diff --git a/mm2src/coins/eth.rs b/mm2src/coins/eth.rs index c69ce6981f..53567b87eb 100644 --- a/mm2src/coins/eth.rs +++ b/mm2src/coins/eth.rs @@ -23,7 +23,7 @@ use super::eth::Action::{Call, Create}; use crate::lp_price::get_base_price_in_rel; use crate::nft::nft_structs::{ContractType, ConvertChain, TransactionNftDetails, WithdrawErc1155, WithdrawErc721}; -use crate::{ValidateWatcherSpendInput, WatcherSpendType}; +use crate::{DexFee, ValidateWatcherSpendInput, WatcherSpendType}; use async_trait::async_trait; use bitcrypto::{dhash160, keccak256, ripemd160, sha256}; use common::custom_futures::repeatable::{Ready, Retry, RetryOnError}; @@ -1058,12 +1058,15 @@ impl Deref for EthCoin { #[async_trait] impl SwapOps for EthCoin { - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { let address = try_tx_fus!(addr_from_raw_pubkey(fee_addr)); Box::new( - self.send_to_address(address, try_tx_fus!(wei_from_big_decimal(&amount, self.decimals))) - .map(TransactionEnum::from), + self.send_to_address( + address, + try_tx_fus!(wei_from_big_decimal(&dex_fee.fee_amount().into(), self.decimals)), + ) + .map(TransactionEnum::from), ) } @@ -1118,7 +1121,7 @@ impl SwapOps for EthCoin { fee_tx_hash: &tx.hash, expected_sender: validate_fee_args.expected_sender, fee_addr: validate_fee_args.fee_addr, - amount: validate_fee_args.amount, + amount: &validate_fee_args.dex_fee.fee_amount().into(), min_block_number: validate_fee_args.min_block_number, uuid: validate_fee_args.uuid, }) @@ -4901,10 +4904,10 @@ impl MmCoin for EthCoin { async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { - let dex_fee_amount = wei_from_big_decimal(&dex_fee_amount, self.decimals)?; + 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) diff --git a/mm2src/coins/eth/eth_tests.rs b/mm2src/coins/eth/eth_tests.rs index dfeff254f9..152f68436f 100644 --- a/mm2src/coins/eth/eth_tests.rs +++ b/mm2src/coins/eth/eth_tests.rs @@ -1,5 +1,5 @@ use super::*; -use crate::IguanaPrivKey; +use crate::{DexFee, IguanaPrivKey}; use common::{block_on, now_sec, wait_until_sec}; use crypto::privkey::key_pair_from_seed; use ethkey::{Generator, Random}; @@ -1104,8 +1104,11 @@ fn test_get_fee_to_send_taker_fee() { let dex_fee_amount = u256_to_big_decimal(DEX_FEE_AMOUNT.into(), 18).expect("!u256_to_big_decimal"); let (_ctx, coin) = eth_coin_for_test(EthCoinType::Eth, &["http://dummy.dummy"], None); - let actual = block_on(coin.get_fee_to_send_taker_fee(dex_fee_amount.clone(), FeeApproxStage::WithoutApprox)) - .expect("!get_fee_to_send_taker_fee"); + let actual = block_on(coin.get_fee_to_send_taker_fee( + DexFee::Standard(MmNumber::from(dex_fee_amount.clone())), + FeeApproxStage::WithoutApprox, + )) + .expect("!get_fee_to_send_taker_fee"); assert_eq!(actual, expected_fee); let (_ctx, coin) = eth_coin_for_test( @@ -1116,8 +1119,11 @@ fn test_get_fee_to_send_taker_fee() { &["http://dummy.dummy"], None, ); - let actual = block_on(coin.get_fee_to_send_taker_fee(dex_fee_amount, FeeApproxStage::WithoutApprox)) - .expect("!get_fee_to_send_taker_fee"); + let actual = block_on(coin.get_fee_to_send_taker_fee( + DexFee::Standard(MmNumber::from(dex_fee_amount)), + FeeApproxStage::WithoutApprox, + )) + .expect("!get_fee_to_send_taker_fee"); assert_eq!(actual, expected_fee); } @@ -1143,7 +1149,11 @@ fn test_get_fee_to_send_taker_fee_insufficient_balance() { ); let dex_fee_amount = u256_to_big_decimal(DEX_FEE_AMOUNT.into(), 18).expect("!u256_to_big_decimal"); - let error = block_on(coin.get_fee_to_send_taker_fee(dex_fee_amount, FeeApproxStage::WithoutApprox)).unwrap_err(); + let error = block_on(coin.get_fee_to_send_taker_fee( + DexFee::Standard(MmNumber::from(dex_fee_amount)), + FeeApproxStage::WithoutApprox, + )) + .unwrap_err(); log!("{}", error); assert!( matches!(error.get_inner(), TradePreimageError::NotSufficientBalance { .. }), @@ -1167,7 +1177,7 @@ fn validate_dex_fee_invalid_sender_eth() { fee_tx: &tx, expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], }; @@ -1201,7 +1211,7 @@ fn validate_dex_fee_invalid_sender_erc() { fee_tx: &tx, expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], }; @@ -1239,7 +1249,7 @@ fn validate_dex_fee_eth_confirmed_before_min_block() { fee_tx: &tx, expected_sender: &compressed_public, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 11784793, uuid: &[], }; @@ -1276,7 +1286,7 @@ fn validate_dex_fee_erc_confirmed_before_min_block() { fee_tx: &tx, expected_sender: &compressed_public, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 11823975, uuid: &[], }; diff --git a/mm2src/coins/lightning.rs b/mm2src/coins/lightning.rs index 74d5619c5d..447990e2b1 100644 --- a/mm2src/coins/lightning.rs +++ b/mm2src/coins/lightning.rs @@ -15,8 +15,8 @@ use crate::lightning::ln_utils::{filter_channels, pay_invoice_with_max_total_clt use crate::utxo::rpc_clients::UtxoRpcClientEnum; use crate::utxo::utxo_common::{big_decimal_from_sat, big_decimal_from_sat_unsigned}; use crate::utxo::{sat_from_big_decimal, utxo_common, BlockchainNetwork}; -use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, - FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, +use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, DexFee, + FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, RawTransactionError, RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, @@ -612,7 +612,7 @@ impl LightningCoin { #[async_trait] impl SwapOps for LightningCoin { // Todo: This uses dummy data for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning - fn send_taker_fee(&self, _fee_addr: &[u8], _amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { + fn send_taker_fee(&self, _fee_addr: &[u8], _dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { let fut = async move { Ok(TransactionEnum::LightningPayment(PaymentHash([1; 32]))) }; Box::new(fut.boxed().compat()) } @@ -1342,7 +1342,7 @@ impl MmCoin for LightningCoin { // Todo: This uses dummy data for now for the sake of swap P.O.C., this should be implemented probably after agreeing on how fees will work for lightning async fn get_fee_to_send_taker_fee( &self, - _dex_fee_amount: BigDecimal, + _dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { Ok(TradeFee { diff --git a/mm2src/coins/lp_coins.rs b/mm2src/coins/lp_coins.rs index 74303ba226..ad33246977 100644 --- a/mm2src/coins/lp_coins.rs +++ b/mm2src/coins/lp_coins.rs @@ -820,7 +820,7 @@ pub struct ValidateFeeArgs<'a> { pub fee_tx: &'a TransactionEnum, pub expected_sender: &'a [u8], pub fee_addr: &'a [u8], - pub amount: &'a BigDecimal, + pub dex_fee: &'a DexFee, pub min_block_number: u64, pub uuid: &'a [u8], } @@ -903,7 +903,7 @@ pub enum WatcherRewardError { /// Swap operations (mostly based on the Hash/Time locked transactions implemented by coin wallets). #[async_trait] pub trait SwapOps { - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, uuid: &[u8]) -> TransactionFut; + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, uuid: &[u8]) -> TransactionFut; fn send_maker_payment(&self, maker_payment_args: SendPaymentArgs<'_>) -> TransactionFut; @@ -2673,7 +2673,7 @@ pub trait MmCoin: /// Get transaction fee the Taker has to pay to send a `TakerFee` transaction and check if the wallet has sufficient balance to pay the fee. async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult; @@ -2951,6 +2951,72 @@ impl MmCoinStruct { } } +/// Represents the different types of DEX fees. +#[derive(Clone, Debug, PartialEq)] +pub enum DexFee { + /// Standard dex fee which will be sent to the dex fee address + Standard(MmNumber), + /// Dex fee with the burn amount. + /// - `fee_amount` goes to the dex fee address. + /// - `burn_amount` will be added as `OP_RETURN` output in the dex fee transaction. + WithBurn { + fee_amount: MmNumber, + burn_amount: MmNumber, + }, +} + +impl DexFee { + /// Creates a new `DexFee` with burn amounts. + pub fn with_burn(fee_amount: MmNumber, burn_amount: MmNumber) -> DexFee { + DexFee::WithBurn { + fee_amount, + burn_amount, + } + } + + /// Gets the fee amount associated with the dex fee. + pub fn fee_amount(&self) -> MmNumber { + match self { + DexFee::Standard(t) => t.clone(), + DexFee::WithBurn { fee_amount, .. } => fee_amount.clone(), + } + } + + /// Gets the burn amount associated with the dex fee, if applicable. + pub fn burn_amount(&self) -> Option { + match self { + DexFee::Standard(_) => None, + DexFee::WithBurn { burn_amount, .. } => Some(burn_amount.clone()), + } + } + + /// Calculates the total spend amount, considering both the fee and burn amounts. + pub fn total_spend_amount(&self) -> MmNumber { + match self { + DexFee::Standard(t) => t.clone(), + DexFee::WithBurn { + fee_amount, + burn_amount, + } => fee_amount + burn_amount, + } + } + + /// Converts the fee amount to micro-units based on the specified decimal places. + pub fn fee_uamount(&self, decimals: u8) -> NumConversResult { + let fee_amount = self.fee_amount(); + utxo::sat_from_big_decimal(&fee_amount.into(), decimals) + } + + /// Converts the burn amount to micro-units, if applicable, based on the specified decimal places. + pub fn burn_uamount(&self, decimals: u8) -> NumConversResult> { + if let Some(burn_amount) = self.burn_amount() { + Ok(Some(utxo::sat_from_big_decimal(&burn_amount.into(), decimals)?)) + } else { + Ok(None) + } + } +} + pub struct CoinsContext { /// A map from a currency ticker symbol to the corresponding coin. /// Similar to `LP_coins`. diff --git a/mm2src/coins/qrc20.rs b/mm2src/coins/qrc20.rs index f556ba5032..4043d299bb 100644 --- a/mm2src/coins/qrc20.rs +++ b/mm2src/coins/qrc20.rs @@ -16,18 +16,19 @@ use crate::utxo::{qtum, ActualTxFee, AdditionalTxData, AddrFromStrError, Broadca UtxoActivationParams, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps, UtxoFromLegacyReqErr, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom, UTXO_LOCK}; use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, - FeeApproxStage, FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MakerSwapTakerCoin, MarketCoinOps, - MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, - PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionFut, - RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, - SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureResult, SpendPaymentArgs, SwapOps, - TakerSwapMakerCoin, TradeFee, TradePreimageError, TradePreimageFut, TradePreimageResult, - TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, TransactionFut, - TransactionResult, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, - ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentFut, - ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, - WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, WithdrawRequest, WithdrawResult}; + DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MakerSwapTakerCoin, + MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, + PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, + RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureResult, + SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageError, TradePreimageFut, + TradePreimageResult, TradePreimageValue, TransactionDetails, TransactionEnum, TransactionErr, + TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, UnexpectedDerivationMethod, + ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, + ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, + WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFee, WithdrawFut, + WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use bitcrypto::{dhash160, sha256}; use chain::TransactionOutput; @@ -753,9 +754,9 @@ impl UtxoCommonOps for Qrc20Coin { #[async_trait] impl SwapOps for Qrc20Coin { - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { let to_address = try_tx_fus!(self.contract_address_from_raw_pubkey(fee_addr)); - let amount = try_tx_fus!(wei_from_big_decimal(&amount, self.utxo.decimals)); + let amount = try_tx_fus!(wei_from_big_decimal(&dex_fee.fee_amount().into(), self.utxo.decimals)); let transfer_output = try_tx_fus!(self.transfer_output(to_address, amount, QRC20_GAS_LIMIT_DEFAULT, QRC20_GAS_PRICE_DEFAULT)); let outputs = vec![transfer_output]; @@ -874,7 +875,10 @@ impl SwapOps for Qrc20Coin { let fee_addr = try_f!(self .contract_address_from_raw_pubkey(validate_fee_args.fee_addr) .map_to_mm(ValidatePaymentError::WrongPaymentTx)); - let expected_value = try_f!(wei_from_big_decimal(validate_fee_args.amount, self.utxo.decimals)); + let expected_value = try_f!(wei_from_big_decimal( + &validate_fee_args.dex_fee.fee_amount().into(), + self.utxo.decimals + )); let selfi = self.clone(); let fut = async move { @@ -1432,10 +1436,10 @@ impl MmCoin for Qrc20Coin { async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { - let amount = wei_from_big_decimal(&dex_fee_amount, self.utxo.decimals)?; + let amount = wei_from_big_decimal(&dex_fee_amount.fee_amount().into(), self.utxo.decimals)?; // pass the dummy params let to_addr = H160::default(); diff --git a/mm2src/coins/qrc20/qrc20_tests.rs b/mm2src/coins/qrc20/qrc20_tests.rs index f5aa39ba52..a837e18364 100644 --- a/mm2src/coins/qrc20/qrc20_tests.rs +++ b/mm2src/coins/qrc20/qrc20_tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::utxo::rpc_clients::UnspentInfo; -use crate::{TxFeeDetails, WaitForHTLCTxSpendArgs}; +use crate::{DexFee, TxFeeDetails, WaitForHTLCTxSpendArgs}; use chain::OutPoint; use common::{block_on, wait_until_sec, DEX_FEE_ADDR_RAW_PUBKEY}; use crypto::Secp256k1Secret; @@ -309,7 +309,7 @@ fn test_send_taker_fee() { let amount = BigDecimal::from_str("0.01").unwrap(); let tx = coin - .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, amount.clone(), &[]) + .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, DexFee::Standard(amount.clone().into()), &[]) .wait() .unwrap(); let tx_hash: H256Json = match tx { @@ -323,7 +323,7 @@ fn test_send_taker_fee() { fee_tx: &tx, expected_sender: coin.my_public_key().unwrap(), fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], }) @@ -351,7 +351,7 @@ fn test_validate_fee() { fee_tx: &tx, expected_sender: &sender_pub, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.clone().into()), min_block_number: 0, uuid: &[], }) @@ -364,7 +364,7 @@ fn test_validate_fee() { fee_tx: &tx, expected_sender: &sender_pub, fee_addr: &fee_addr_dif, - amount: &amount, + dex_fee: &DexFee::Standard(amount.clone().into()), min_block_number: 0, uuid: &[], }) @@ -382,7 +382,7 @@ fn test_validate_fee() { fee_tx: &tx, expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.clone().into()), min_block_number: 0, uuid: &[], }) @@ -400,7 +400,7 @@ fn test_validate_fee() { fee_tx: &tx, expected_sender: &sender_pub, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.clone().into()), min_block_number: 2000000, uuid: &[], }) @@ -419,7 +419,7 @@ fn test_validate_fee() { fee_tx: &tx, expected_sender: &sender_pub, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount_dif, + dex_fee: &DexFee::Standard(amount_dif.into()), min_block_number: 0, uuid: &[], }) @@ -442,7 +442,7 @@ fn test_validate_fee() { fee_tx: &tx, expected_sender: &sender_pub, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], }) @@ -949,8 +949,11 @@ fn test_taker_fee_tx_fee() { assert_eq!(coin.my_balance().wait().expect("!my_balance"), expected_balance); let dex_fee_amount = BigDecimal::from(5u32); - let actual = block_on(coin.get_fee_to_send_taker_fee(dex_fee_amount, FeeApproxStage::WithoutApprox)) - .expect("!get_fee_to_send_taker_fee"); + let actual = block_on(coin.get_fee_to_send_taker_fee( + DexFee::Standard(MmNumber::from(dex_fee_amount)), + FeeApproxStage::WithoutApprox, + )) + .expect("!get_fee_to_send_taker_fee"); // only one contract call should be included into the expected trade fee let expected_receiver_fee = big_decimal_from_sat(CONTRACT_CALL_GAS_FEE + EXPECTED_TX_FEE, coin.utxo.decimals); let expected = TradeFee { diff --git a/mm2src/coins/solana.rs b/mm2src/coins/solana.rs index 56178efa7a..025f4c6c7d 100644 --- a/mm2src/coins/solana.rs +++ b/mm2src/coins/solana.rs @@ -2,18 +2,18 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, Trade use crate::coin_errors::MyAddressError; use crate::solana::solana_common::{lamports_to_sol, PrepareTransferData, SufficientBalanceError}; use crate::solana::spl::SplTokenInfo; -use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, - FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, - PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, - RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, - SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureResult, - SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, TradePreimageValue, - TransactionDetails, TransactionFut, TransactionResult, TransactionType, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, - ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, - ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherReward, WatcherRewardError, - WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, - WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; +use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, DexFee, + FeeApproxStage, FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, + PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, + PrivKeyPolicyNotAllowed, RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, + RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, + SignatureResult, SpendPaymentArgs, TakerSwapMakerCoin, TradePreimageFut, TradePreimageResult, + TradePreimageValue, TransactionDetails, TransactionFut, TransactionResult, TransactionType, + TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, + ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, + ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, + WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + WatcherValidateTakerFeeInput, WithdrawError, WithdrawFut, WithdrawRequest, WithdrawResult}; use async_trait::async_trait; use base58::ToBase58; use bincode::{deserialize, serialize}; @@ -468,7 +468,7 @@ impl MarketCoinOps for SolanaCoin { #[async_trait] impl SwapOps for SolanaCoin { - fn send_taker_fee(&self, _fee_addr: &[u8], amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { unimplemented!() } + fn send_taker_fee(&self, _fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { unimplemented!() } fn send_maker_payment(&self, _maker_payment_args: SendPaymentArgs) -> TransactionFut { unimplemented!() } @@ -745,7 +745,7 @@ impl MmCoin for SolanaCoin { async fn get_fee_to_send_taker_fee( &self, - _dex_fee_amount: BigDecimal, + _dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { unimplemented!() diff --git a/mm2src/coins/solana/spl.rs b/mm2src/coins/solana/spl.rs index 9270302b0b..0777e278e5 100644 --- a/mm2src/coins/solana/spl.rs +++ b/mm2src/coins/solana/spl.rs @@ -2,7 +2,7 @@ use super::{CoinBalance, HistorySyncState, MarketCoinOps, MmCoin, SwapOps, Trade use crate::coin_errors::MyAddressError; use crate::solana::solana_common::{ui_amount_to_amount, PrepareTransferData, SufficientBalanceError}; use crate::solana::{solana_common, AccountError, SolanaCommonOps, SolanaFeeDetails}; -use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, +use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, @@ -289,7 +289,7 @@ impl MarketCoinOps for SplToken { #[async_trait] impl SwapOps for SplToken { - fn send_taker_fee(&self, _fee_addr: &[u8], amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { unimplemented!() } + fn send_taker_fee(&self, _fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { unimplemented!() } fn send_maker_payment(&self, _maker_payment_args: SendPaymentArgs) -> TransactionFut { unimplemented!() } @@ -539,7 +539,7 @@ impl MmCoin for SplToken { async fn get_fee_to_send_taker_fee( &self, - _dex_fee_amount: BigDecimal, + _dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { unimplemented!() diff --git a/mm2src/coins/tendermint/tendermint_coin.rs b/mm2src/coins/tendermint/tendermint_coin.rs index 6d0432d703..89b79573c3 100644 --- a/mm2src/coins/tendermint/tendermint_coin.rs +++ b/mm2src/coins/tendermint/tendermint_coin.rs @@ -15,8 +15,8 @@ use crate::tendermint::ibc::IBC_OUT_SOURCE_PORT; use crate::utxo::sat_from_big_decimal; use crate::utxo::utxo_common::big_decimal_from_sat; use crate::{big_decimal_from_sat_unsigned, BalanceError, BalanceFut, BigDecimal, CheckIfMyPaymentSentArgs, - CoinBalance, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, - MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, + CoinBalance, CoinFutSpawner, ConfirmPaymentInput, DexFee, FeeApproxStage, FoundSwapTxSpend, + HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, PrivKeyPolicy, PrivKeyPolicyNotAllowed, RawTransactionError, RawTransactionFut, RawTransactionRequest, RawTransactionRes, RefundError, RefundPaymentArgs, RefundResult, RpcCommonOps, SearchForSwapTxSpendInput, @@ -1612,11 +1612,11 @@ impl TendermintCoin { ticker: String, denom: Denom, decimals: u8, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, ) -> TradePreimageResult { let to_address = account_id_from_pubkey_hex(&self.account_prefix, DEX_FEE_ADDR_PUBKEY) .map_err(|e| MmError::new(TradePreimageError::InternalError(e.into_inner().to_string())))?; - let amount = sat_from_big_decimal(&dex_fee_amount, decimals)?; + let amount = sat_from_big_decimal(&dex_fee_amount.fee_amount().into(), decimals)?; let current_block = self.current_block().compat().await.map_err(|e| { MmError::new(TradePreimageError::InternalError(format!( @@ -2174,7 +2174,7 @@ impl MmCoin for TendermintCoin { async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { self.get_fee_to_send_taker_fee_for_denom(self.ticker.clone(), self.denom.clone(), self.decimals, dex_fee_amount) @@ -2421,8 +2421,14 @@ impl MarketCoinOps for TendermintCoin { #[async_trait] #[allow(unused_variables)] impl SwapOps for TendermintCoin { - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, uuid: &[u8]) -> TransactionFut { - self.send_taker_fee_for_denom(fee_addr, amount, self.denom.clone(), self.decimals, uuid) + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, uuid: &[u8]) -> TransactionFut { + self.send_taker_fee_for_denom( + fee_addr, + dex_fee.fee_amount().into(), + self.denom.clone(), + self.decimals, + uuid, + ) } fn send_maker_payment(&self, maker_payment_args: SendPaymentArgs) -> TransactionFut { @@ -2570,7 +2576,7 @@ impl SwapOps for TendermintCoin { validate_fee_args.fee_tx, validate_fee_args.expected_sender, validate_fee_args.fee_addr, - validate_fee_args.amount, + &validate_fee_args.dex_fee.fee_amount().into(), self.decimals, validate_fee_args.uuid, self.denom.to_string(), @@ -3185,13 +3191,13 @@ pub mod tendermint_coin_tests { data: TxRaw::decode(create_htlc_tx_bytes.as_slice()).unwrap(), }); - let invalid_amount = 1.into(); + let invalid_amount: MmNumber = 1.into(); let error = coin .validate_fee(ValidateFeeArgs { fee_tx: &create_htlc_tx, expected_sender: &[], fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &invalid_amount, + dex_fee: &DexFee::Standard(invalid_amount.clone()), min_block_number: 0, uuid: &[1; 16], }) @@ -3225,7 +3231,7 @@ pub mod tendermint_coin_tests { fee_tx: &random_transfer_tx, expected_sender: &[], fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &invalid_amount, + dex_fee: &DexFee::Standard(invalid_amount.clone()), min_block_number: 0, uuid: &[1; 16], }) @@ -3258,7 +3264,7 @@ pub mod tendermint_coin_tests { fee_tx: &dex_fee_tx, expected_sender: &[], fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &invalid_amount, + dex_fee: &DexFee::Standard(invalid_amount), min_block_number: 0, uuid: &[1; 16], }) @@ -3278,7 +3284,7 @@ pub mod tendermint_coin_tests { fee_tx: &dex_fee_tx, expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &valid_amount, + dex_fee: &DexFee::Standard(valid_amount.clone().into()), min_block_number: 0, uuid: &[1; 16], }) @@ -3297,7 +3303,7 @@ pub mod tendermint_coin_tests { fee_tx: &dex_fee_tx, expected_sender: &pubkey, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &valid_amount, + dex_fee: &DexFee::Standard(valid_amount.into()), min_block_number: 0, uuid: &[1; 16], }) diff --git a/mm2src/coins/tendermint/tendermint_token.rs b/mm2src/coins/tendermint/tendermint_token.rs index 061478aa09..93c5eb56f7 100644 --- a/mm2src/coins/tendermint/tendermint_token.rs +++ b/mm2src/coins/tendermint/tendermint_token.rs @@ -20,7 +20,7 @@ use crate::{big_decimal_from_sat_unsigned, utxo::sat_from_big_decimal, BalanceFu ValidatePaymentInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawError, WithdrawFrom, WithdrawFut, WithdrawRequest}; -use crate::{MmCoinEnum, PaymentInstructionArgs, ValidateWatcherSpendInput, WatcherReward, WatcherRewardError}; +use crate::{DexFee, MmCoinEnum, PaymentInstructionArgs, ValidateWatcherSpendInput, WatcherReward, WatcherRewardError}; use async_trait::async_trait; use bitcrypto::sha256; use common::executor::abortable_queue::AbortableQueue; @@ -268,9 +268,14 @@ impl TendermintToken { #[async_trait] #[allow(unused_variables)] impl SwapOps for TendermintToken { - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, uuid: &[u8]) -> TransactionFut { - self.platform_coin - .send_taker_fee_for_denom(fee_addr, amount, self.denom.clone(), self.decimals, uuid) + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, uuid: &[u8]) -> TransactionFut { + self.platform_coin.send_taker_fee_for_denom( + fee_addr, + dex_fee.fee_amount().into(), + self.denom.clone(), + self.decimals, + uuid, + ) } fn send_maker_payment(&self, maker_payment_args: SendPaymentArgs) -> TransactionFut { @@ -322,7 +327,7 @@ impl SwapOps for TendermintToken { validate_fee_args.fee_tx, validate_fee_args.expected_sender, validate_fee_args.fee_addr, - validate_fee_args.amount, + &validate_fee_args.dex_fee.fee_amount().into(), self.decimals, validate_fee_args.uuid, self.denom.to_string(), @@ -835,7 +840,7 @@ impl MmCoin for TendermintToken { async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { self.platform_coin diff --git a/mm2src/coins/test_coin.rs b/mm2src/coins/test_coin.rs index d21b438f58..d4da2109bd 100644 --- a/mm2src/coins/test_coin.rs +++ b/mm2src/coins/test_coin.rs @@ -16,7 +16,7 @@ use crate::{coin_errors::MyAddressError, BalanceFut, CanRefundHtlc, CheckIfMyPay ValidateTakerPaymentSpendPreimageResult, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, WithdrawRequest}; -use crate::{ToBytes, ValidateWatcherSpendInput}; +use crate::{DexFee, ToBytes, ValidateWatcherSpendInput}; use async_trait::async_trait; use common::executor::AbortedError; use futures01::Future; @@ -97,7 +97,7 @@ impl MarketCoinOps for TestCoin { fn display_priv_key(&self) -> Result { unimplemented!() } - fn min_tx_amount(&self) -> BigDecimal { unimplemented!() } + fn min_tx_amount(&self) -> BigDecimal { Default::default() } fn min_trading_vol(&self) -> MmNumber { MmNumber::from("0.00777") } } @@ -105,7 +105,7 @@ impl MarketCoinOps for TestCoin { #[async_trait] #[mockable] impl SwapOps for TestCoin { - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, uuid: &[u8]) -> TransactionFut { unimplemented!() } + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, uuid: &[u8]) -> TransactionFut { unimplemented!() } fn send_maker_payment(&self, _maker_payment_args: SendPaymentArgs) -> TransactionFut { unimplemented!() } @@ -351,7 +351,7 @@ impl MmCoin for TestCoin { async fn get_fee_to_send_taker_fee( &self, - _dex_fee_amount: BigDecimal, + _dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { unimplemented!() diff --git a/mm2src/coins/utxo/bch.rs b/mm2src/coins/utxo/bch.rs index f584ccb6bf..072719eeee 100644 --- a/mm2src/coins/utxo/bch.rs +++ b/mm2src/coins/utxo/bch.rs @@ -10,7 +10,7 @@ use crate::utxo::utxo_common::big_decimal_from_sat_unsigned; use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{BlockHeightAndTime, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinProtocol, - CoinWithDerivationMethod, ConfirmPaymentInput, IguanaPrivKey, MakerSwapTakerCoin, MmCoinEnum, + CoinWithDerivationMethod, ConfirmPaymentInput, DexFee, IguanaPrivKey, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, @@ -840,8 +840,8 @@ impl UtxoCommonOps for BchCoin { #[async_trait] impl SwapOps for BchCoin { #[inline] - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { - utxo_common::send_taker_fee(self.clone(), fee_addr, amount) + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { + utxo_common::send_taker_fee(self.clone(), fee_addr, dex_fee) } #[inline] @@ -884,7 +884,7 @@ impl SwapOps for BchCoin { tx, utxo_common::DEFAULT_FEE_VOUT, validate_fee_args.expected_sender, - validate_fee_args.amount, + validate_fee_args.dex_fee, validate_fee_args.min_block_number, validate_fee_args.fee_addr, ) @@ -1268,7 +1268,7 @@ impl MmCoin for BchCoin { async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { utxo_common::get_fee_to_send_taker_fee(self, dex_fee_amount, stage).await diff --git a/mm2src/coins/utxo/qtum.rs b/mm2src/coins/utxo/qtum.rs index e901a53e94..a66ae04331 100644 --- a/mm2src/coins/utxo/qtum.rs +++ b/mm2src/coins/utxo/qtum.rs @@ -25,15 +25,15 @@ use crate::utxo::utxo_builder::{MergeUtxoArcOps, UtxoCoinBuildError, UtxoCoinBui use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{eth, CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithDerivationMethod, ConfirmPaymentInput, - DelegationError, DelegationFut, GetWithdrawSenderAddress, IguanaPrivKey, MakerSwapTakerCoin, MmCoinEnum, - NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, - PrivKeyBuildPolicy, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, - SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureResult, SpendPaymentArgs, StakingInfosFut, - SwapOps, TakerSwapMakerCoin, TradePreimageValue, TransactionFut, TransactionResult, TxMarshalingErr, - UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, - ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, - ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, - WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, + DelegationError, DelegationFut, DexFee, GetWithdrawSenderAddress, IguanaPrivKey, MakerSwapTakerCoin, + MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, + PaymentInstructionsErr, PrivKeyBuildPolicy, RefundError, RefundPaymentArgs, RefundResult, + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureResult, + SpendPaymentArgs, StakingInfosFut, SwapOps, TakerSwapMakerCoin, TradePreimageValue, TransactionFut, + TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, + ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, + ValidatePaymentInput, ValidateWatcherSpendInput, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, + WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, WithdrawSenderAddress}; use common::executor::{AbortableSystem, AbortedError}; use crypto::Bip44Chain; @@ -526,8 +526,8 @@ impl UtxoStandardOps for QtumCoin { #[async_trait] impl SwapOps for QtumCoin { #[inline] - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { - utxo_common::send_taker_fee(self.clone(), fee_addr, amount) + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { + utxo_common::send_taker_fee(self.clone(), fee_addr, dex_fee) } #[inline] @@ -570,7 +570,7 @@ impl SwapOps for QtumCoin { tx, utxo_common::DEFAULT_FEE_VOUT, validate_fee_args.expected_sender, - validate_fee_args.amount, + validate_fee_args.dex_fee, validate_fee_args.min_block_number, validate_fee_args.fee_addr, ) @@ -939,7 +939,7 @@ impl MmCoin for QtumCoin { async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { utxo_common::get_fee_to_send_taker_fee(self, dex_fee_amount, stage).await diff --git a/mm2src/coins/utxo/slp.rs b/mm2src/coins/utxo/slp.rs index e400b3e128..da2a51224b 100644 --- a/mm2src/coins/utxo/slp.rs +++ b/mm2src/coins/utxo/slp.rs @@ -13,8 +13,8 @@ use crate::utxo::utxo_common::{self, big_decimal_from_sat_unsigned, payment_scri use crate::utxo::{generate_and_send_tx, sat_from_big_decimal, ActualTxFee, AdditionalTxData, BroadcastTxErr, FeePolicy, GenerateTxError, RecentlySpentOutPointsGuard, UtxoCoinConf, UtxoCoinFields, UtxoCommonOps, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps}; -use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, FeeApproxStage, - FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, +use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, DexFee, + FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, NumConversError, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyPolicyNotAllowed, RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, @@ -750,7 +750,7 @@ impl SlpToken { tx, SLP_FEE_VOUT, expected_sender, - &self.platform_dust_dec(), + &DexFee::Standard(self.platform_dust_dec().into()), min_block_number, fee_addr, ); @@ -1193,11 +1193,11 @@ impl MarketCoinOps for SlpToken { #[async_trait] impl SwapOps for SlpToken { - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { let coin = self.clone(); let fee_pubkey = try_tx_fus!(Public::from_slice(fee_addr)); let script_pubkey = ScriptBuilder::build_p2pkh(&fee_pubkey.address_hash().into()).into(); - let amount = try_tx_fus!(sat_from_big_decimal(&amount, self.decimals())); + let amount = try_tx_fus!(dex_fee.fee_uamount(self.decimals())); let fut = async move { let slp_out = SlpOutput { amount, script_pubkey }; @@ -1326,11 +1326,11 @@ impl SwapOps for SlpToken { let coin = self.clone(); let expected_sender = validate_fee_args.expected_sender.to_owned(); let fee_addr = validate_fee_args.fee_addr.to_owned(); - let amount = validate_fee_args.amount.to_owned(); + let amount = validate_fee_args.dex_fee.fee_amount(); let min_block_number = validate_fee_args.min_block_number; let fut = async move { - coin.validate_dex_fee(tx, &expected_sender, &fee_addr, amount, min_block_number) + coin.validate_dex_fee(tx, &expected_sender, &fee_addr, amount.into(), min_block_number) .await .map_err(|e| MmError::new(ValidatePaymentError::WrongPaymentTx(e.into_inner().to_string())))?; Ok(()) @@ -1818,10 +1818,10 @@ impl MmCoin for SlpToken { async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { - let slp_amount = sat_from_big_decimal(&dex_fee_amount, self.decimals())?; + 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(); let slp_out = SlpOutput { diff --git a/mm2src/coins/utxo/utxo_common.rs b/mm2src/coins/utxo/utxo_common.rs index 82e33e4b36..2354d8ba44 100644 --- a/mm2src/coins/utxo/utxo_common.rs +++ b/mm2src/coins/utxo/utxo_common.rs @@ -15,7 +15,7 @@ use crate::utxo::spv::SimplePaymentVerification; use crate::utxo::tx_cache::TxCacheResult; use crate::utxo::utxo_withdraw::{InitUtxoWithdraw, StandardUtxoWithdraw, UtxoWithdraw}; use crate::watcher_common::validate_watcher_reward; -use crate::{CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, ConfirmPaymentInput, GenPreimageResult, +use crate::{CanRefundHtlc, CoinBalance, CoinWithDerivationMethod, ConfirmPaymentInput, DexFee, GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, HDAccountAddressId, RawTransactionError, RawTransactionRequest, RawTransactionRes, RefundFundingSecretArgs, RefundPaymentArgs, RewardTarget, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, @@ -1668,7 +1668,7 @@ pub async fn sign_and_broadcast_taker_payment_spend( Ok(final_tx.into()) } -pub fn send_taker_fee(coin: T, fee_pub_key: &[u8], amount: BigDecimal) -> TransactionFut +pub fn send_taker_fee(coin: T, fee_pub_key: &[u8], dex_fee: DexFee) -> TransactionFut where T: UtxoCommonOps + GetUtxoListOps, { @@ -1680,12 +1680,36 @@ where coin.as_ref().conf.bech32_hrp.clone(), coin.addr_format().clone(), )); - let amount = try_tx_fus!(sat_from_big_decimal(&amount, coin.as_ref().decimals)); - let output = TransactionOutput { - value: amount, - script_pubkey: Builder::build_p2pkh(&address.hash).to_bytes(), - }; - send_outputs_from_my_address(coin, vec![output]) + + let outputs = try_tx_fus!(generate_taker_fee_tx_outputs( + coin.as_ref().decimals, + &address.hash, + dex_fee, + )); + + send_outputs_from_my_address(coin, outputs) +} + +fn generate_taker_fee_tx_outputs( + decimals: u8, + address_hash: &AddressHashEnum, + dex_fee: DexFee, +) -> Result, MmError> { + let fee_amount = dex_fee.fee_uamount(decimals)?; + + let mut outputs = vec![TransactionOutput { + value: fee_amount, + script_pubkey: Builder::build_p2pkh(address_hash).to_bytes(), + }]; + + if let Some(burn_amount) = dex_fee.burn_uamount(decimals)? { + outputs.push(TransactionOutput { + value: burn_amount, + script_pubkey: Builder::default().push_opcode(Opcode::OP_RETURN).into_bytes(), + }); + } + + Ok(outputs) } pub fn send_maker_payment(coin: T, args: SendPaymentArgs) -> TransactionFut @@ -2329,11 +2353,10 @@ pub fn validate_fee( tx: UtxoTx, output_index: usize, sender_pubkey: &[u8], - amount: &BigDecimal, + dex_amount: &DexFee, min_block_number: u64, fee_addr: &[u8], ) -> ValidatePaymentFut<()> { - let amount = amount.clone(); let address = try_f!(address_from_raw_pubkey( fee_addr, coin.as_ref().conf.pub_addr_prefix, @@ -2354,8 +2377,10 @@ pub fn validate_fee( )); } + let fee_amount = try_f!(dex_amount.fee_uamount(coin.as_ref().decimals)); + let burn_amount = try_f!(dex_amount.burn_uamount(coin.as_ref().decimals)); + let fut = async move { - let amount = sat_from_big_decimal(&amount, coin.as_ref().decimals)?; let tx_from_rpc = coin .as_ref() .rpc_client @@ -2391,10 +2416,10 @@ pub fn validate_fee( INVALID_RECEIVER_ERR_LOG, out.script_pubkey, expected_script_pubkey ))); } - if out.value < amount { + if out.value < fee_amount { return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( "Provided dex fee tx output value is less than expected {:?} {:?}", - out.value, amount + out.value, fee_amount ))); } }, @@ -2405,6 +2430,35 @@ pub fn validate_fee( ))) }, } + + if let Some(burn_amount) = burn_amount { + match tx.outputs.get(output_index + 1) { + Some(out) => { + let expected_script_pubkey = Builder::default().push_opcode(Opcode::OP_RETURN).into_bytes(); + + if out.script_pubkey != expected_script_pubkey { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "{}: Provided burn tx output script_pubkey doesn't match expected {:?} {:?}", + INVALID_RECEIVER_ERR_LOG, out.script_pubkey, expected_script_pubkey + ))); + } + + if out.value < burn_amount { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Provided burn tx output value is less than expected {:?} {:?}", + out.value, burn_amount + ))); + } + }, + None => { + return MmError::err(ValidatePaymentError::WrongPaymentTx(format!( + "Provided burn tx output {:?} does not have output {}", + tx, output_index + ))) + }, + } + } + Ok(()) }; Box::new(fut.boxed().compat()) @@ -3999,21 +4053,19 @@ pub fn get_receiver_trade_fee(coin: T) -> TradePreimageFut( coin: &T, - dex_fee_amount: BigDecimal, + dex_fee: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult where T: MarketCoinOps + UtxoCommonOps, { let decimals = coin.as_ref().decimals; - let value = sat_from_big_decimal(&dex_fee_amount, decimals)?; - let output = TransactionOutput { - value, - script_pubkey: Builder::build_p2pkh(&AddressHashEnum::default_address_hash()).to_bytes(), - }; + + let outputs = generate_taker_fee_tx_outputs(decimals, &AddressHashEnum::default_address_hash(), dex_fee)?; + let gas_fee = None; let fee_amount = coin - .preimage_trade_fee_required_to_send_outputs(vec![output], FeePolicy::SendExact, gas_fee, &stage) + .preimage_trade_fee_required_to_send_outputs(outputs, FeePolicy::SendExact, gas_fee, &stage) .await?; Ok(TradeFee { coin: coin.ticker().to_owned(), @@ -5057,3 +5109,42 @@ fn test_tx_v_size() { let v_size = tx_size_in_v_bytes(&UtxoAddressFormat::Segwit, &tx); assert_eq!(v_size, 209) } + +#[test] +fn test_generate_taker_fee_tx_outputs() { + let amount = BigDecimal::from(6150); + let fee_amount = sat_from_big_decimal(&amount, 8).unwrap(); + + let outputs = generate_taker_fee_tx_outputs( + 8, + &AddressHashEnum::default_address_hash(), + DexFee::Standard(amount.into()), + ) + .unwrap(); + + assert_eq!(outputs.len(), 1); + + assert_eq!(outputs[0].value, fee_amount); +} + +#[test] +fn test_generate_taker_fee_tx_outputs_with_burn() { + let fee_amount = BigDecimal::from(6150); + let burn_amount = &(&fee_amount / &BigDecimal::from_str("0.75").unwrap()) - &fee_amount; + + let fee_uamount = sat_from_big_decimal(&fee_amount, 8).unwrap(); + let burn_uamount = sat_from_big_decimal(&burn_amount, 8).unwrap(); + + let outputs = generate_taker_fee_tx_outputs( + 8, + &AddressHashEnum::default_address_hash(), + DexFee::with_burn(fee_amount.into(), burn_amount.into()), + ) + .unwrap(); + + assert_eq!(outputs.len(), 2); + + assert_eq!(outputs[0].value, fee_uamount); + + assert_eq!(outputs[1].value, burn_uamount); +} diff --git a/mm2src/coins/utxo/utxo_standard.rs b/mm2src/coins/utxo/utxo_standard.rs index 26bc6bdc88..ab809ccfc5 100644 --- a/mm2src/coins/utxo/utxo_standard.rs +++ b/mm2src/coins/utxo/utxo_standard.rs @@ -23,7 +23,7 @@ use crate::utxo::utxo_builder::{UtxoArcBuilder, UtxoCoinBuilder}; use crate::utxo::utxo_tx_history_v2::{UtxoMyAddressesHistoryError, UtxoTxDetailsError, UtxoTxDetailsParams, UtxoTxHistoryOps}; use crate::{CanRefundHtlc, CheckIfMyPaymentSentArgs, CoinBalance, CoinWithDerivationMethod, ConfirmPaymentInput, - GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, + DexFee, GenPreimageResult, GenTakerFundingSpendArgs, GenTakerPaymentSpendArgs, GetWithdrawSenderAddress, IguanaPrivKey, MakerSwapTakerCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, PrivKeyBuildPolicy, RefundError, RefundFundingSecretArgs, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, @@ -294,8 +294,8 @@ impl UtxoStandardOps for UtxoStandardCoin { #[async_trait] impl SwapOps for UtxoStandardCoin { #[inline] - fn send_taker_fee(&self, fee_addr: &[u8], amount: BigDecimal, _uuid: &[u8]) -> TransactionFut { - utxo_common::send_taker_fee(self.clone(), fee_addr, amount) + fn send_taker_fee(&self, fee_addr: &[u8], dex_fee: DexFee, _uuid: &[u8]) -> TransactionFut { + utxo_common::send_taker_fee(self.clone(), fee_addr, dex_fee) } #[inline] @@ -338,7 +338,7 @@ impl SwapOps for UtxoStandardCoin { tx, utxo_common::DEFAULT_FEE_VOUT, validate_fee_args.expected_sender, - validate_fee_args.amount, + validate_fee_args.dex_fee, validate_fee_args.min_block_number, validate_fee_args.fee_addr, ) @@ -804,7 +804,7 @@ impl MmCoin for UtxoStandardCoin { async fn get_fee_to_send_taker_fee( &self, - dex_fee_amount: BigDecimal, + dex_fee_amount: DexFee, stage: FeeApproxStage, ) -> TradePreimageResult { utxo_common::get_fee_to_send_taker_fee(self, dex_fee_amount, stage).await diff --git a/mm2src/coins/utxo/utxo_tests.rs b/mm2src/coins/utxo/utxo_tests.rs index 265b1d94d2..88d60142bd 100644 --- a/mm2src/coins/utxo/utxo_tests.rs +++ b/mm2src/coins/utxo/utxo_tests.rs @@ -28,7 +28,7 @@ use crate::utxo::utxo_common_tests::{self, utxo_coin_fields_for_test, utxo_coin_ use crate::utxo::utxo_standard::{utxo_standard_coin_with_priv_key, UtxoStandardCoin}; use crate::utxo::utxo_tx_history_v2::{UtxoTxDetailsParams, UtxoTxHistoryOps}; #[cfg(not(target_arch = "wasm32"))] use crate::WithdrawFee; -use crate::{BlockHeightAndTime, CoinBalance, ConfirmPaymentInput, IguanaPrivKey, PrivKeyBuildPolicy, +use crate::{BlockHeightAndTime, CoinBalance, ConfirmPaymentInput, DexFee, IguanaPrivKey, PrivKeyBuildPolicy, SearchForSwapTxSpendInput, SpendPaymentArgs, StakingInfosDetails, SwapOps, TradePreimageValue, TxFeeDetails, TxMarshalingErr, ValidateFeeArgs, WaitForHTLCTxSpendArgs, INVALID_SENDER_ERR_LOG}; use chain::{BlockHeader, BlockHeaderBits, OutPoint}; @@ -2520,7 +2520,7 @@ fn test_validate_fee_wrong_sender() { fee_tx: &taker_fee_tx, expected_sender: &DEX_FEE_ADDR_RAW_PUBKEY, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], }; @@ -2545,7 +2545,7 @@ fn test_validate_fee_min_block() { fee_tx: &taker_fee_tx, expected_sender: &sender_pub, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 278455, uuid: &[], }; @@ -2574,7 +2574,7 @@ fn test_validate_fee_bch_70_bytes_signature() { fee_tx: &taker_fee_tx, expected_sender: &sender_pub, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &amount, + dex_fee: &DexFee::Standard(amount.into()), min_block_number: 0, uuid: &[], }; diff --git a/mm2src/coins/z_coin.rs b/mm2src/coins/z_coin.rs index af7d816435..65e2729361 100644 --- a/mm2src/coins/z_coin.rs +++ b/mm2src/coins/z_coin.rs @@ -9,23 +9,23 @@ use crate::utxo::utxo_builder::UtxoCoinBuildError; use crate::utxo::utxo_builder::{UtxoCoinBuilder, UtxoCoinBuilderCommonOps, UtxoFieldsWithGlobalHDBuilder, UtxoFieldsWithHardwareWalletBuilder, UtxoFieldsWithIguanaSecretBuilder}; use crate::utxo::utxo_common::{big_decimal_from_sat_unsigned, payment_script}; -use crate::utxo::{sat_from_big_decimal, utxo_common, ActualTxFee, AdditionalTxData, AddrFromStrError, Address, - BroadcastTxErr, FeePolicy, GetUtxoListOps, HistoryUtxoTx, HistoryUtxoTxMap, MatureUnspentList, - RecentlySpentOutPointsGuard, UtxoActivationParams, UtxoAddressFormat, UtxoArc, UtxoCoinFields, - UtxoCommonOps, UtxoRpcMode, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom}; +use crate::utxo::{utxo_common, ActualTxFee, AdditionalTxData, AddrFromStrError, Address, BroadcastTxErr, FeePolicy, + GetUtxoListOps, HistoryUtxoTx, HistoryUtxoTxMap, MatureUnspentList, RecentlySpentOutPointsGuard, + UtxoActivationParams, UtxoAddressFormat, UtxoArc, UtxoCoinFields, UtxoCommonOps, UtxoRpcMode, + UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom}; use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, - FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, MmCoinEnum, - NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, PaymentInstructionsErr, - PrivKeyActivationPolicy, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, RawTransactionFut, - RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, SearchForSwapTxSpendInput, - SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureError, SignatureResult, SpendPaymentArgs, - SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageFut, TradePreimageResult, TradePreimageValue, - TransactionEnum, TransactionFut, TransactionResult, TxMarshalingErr, UnexpectedDerivationMethod, - ValidateAddressResult, ValidateFeeArgs, ValidateInstructionsErr, ValidateOtherPubKeyErr, - ValidatePaymentError, ValidatePaymentFut, ValidatePaymentInput, ValidateWatcherSpendInput, - VerificationError, VerificationResult, WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, - WatcherRewardError, WatcherSearchForSwapTxSpendInput, WatcherValidatePaymentInput, - WatcherValidateTakerFeeInput, WithdrawFut, WithdrawRequest}; + DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin, + MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs, PaymentInstructions, + PaymentInstructionsErr, PrivKeyActivationPolicy, PrivKeyBuildPolicy, PrivKeyPolicyNotAllowed, + RawTransactionFut, RawTransactionRequest, RefundError, RefundPaymentArgs, RefundResult, + SearchForSwapTxSpendInput, SendMakerPaymentSpendPreimageInput, SendPaymentArgs, SignatureError, + SignatureResult, SpendPaymentArgs, SwapOps, TakerSwapMakerCoin, TradeFee, TradePreimageFut, + TradePreimageResult, TradePreimageValue, TransactionEnum, TransactionFut, TransactionResult, + TxMarshalingErr, UnexpectedDerivationMethod, ValidateAddressResult, ValidateFeeArgs, + ValidateInstructionsErr, ValidateOtherPubKeyErr, ValidatePaymentError, ValidatePaymentFut, + ValidatePaymentInput, ValidateWatcherSpendInput, VerificationError, VerificationResult, + WaitForHTLCTxSpendArgs, WatcherOps, WatcherReward, WatcherRewardError, WatcherSearchForSwapTxSpendInput, + WatcherValidatePaymentInput, WatcherValidateTakerFeeInput, WithdrawFut, WithdrawRequest}; use crate::{Transaction, WithdrawError}; use async_trait::async_trait; use bitcrypto::dhash256; @@ -82,7 +82,7 @@ pub use z_rpc::{FirstSyncBlock, SyncStatus}; cfg_native!( use crate::{NumConversError, TransactionDetails, TxFeeDetails}; - use crate::utxo::UtxoFeeDetails; + use crate::utxo::{UtxoFeeDetails, sat_from_big_decimal}; use crate::utxo::utxo_common::{addresses_from_script, big_decimal_from_sat}; use common::{async_blocking, calc_total_pages, PagingOptionsEnum}; @@ -1180,11 +1180,11 @@ impl MarketCoinOps for ZCoin { #[async_trait] impl SwapOps for ZCoin { - fn send_taker_fee(&self, _fee_addr: &[u8], amount: BigDecimal, uuid: &[u8]) -> TransactionFut { + fn send_taker_fee(&self, _fee_addr: &[u8], dex_fee: DexFee, uuid: &[u8]) -> TransactionFut { let selfi = self.clone(); let uuid = uuid.to_owned(); let fut = async move { - let tx = try_tx_s!(z_send_dex_fee(&selfi, amount, &uuid).await); + let tx = try_tx_s!(z_send_dex_fee(&selfi, dex_fee.fee_amount().into(), &uuid).await); Ok(tx.into()) }; Box::new(fut.boxed().compat()) @@ -1354,7 +1354,7 @@ impl SwapOps for ZCoin { TransactionEnum::ZTransaction(t) => t.clone(), _ => panic!("Unexpected tx {:?}", validate_fee_args.fee_tx), }; - let amount_sat = try_f!(sat_from_big_decimal(validate_fee_args.amount, self.utxo_arc.decimals)); + let amount_sat = try_f!(validate_fee_args.dex_fee.fee_uamount(self.utxo_arc.decimals)); let expected_memo = MemoBytes::from_bytes(validate_fee_args.uuid).expect("Uuid length < 512"); let min_block_number = validate_fee_args.min_block_number; @@ -1717,7 +1717,7 @@ impl MmCoin for ZCoin { async fn get_fee_to_send_taker_fee( &self, - _dex_fee_amount: BigDecimal, + _dex_fee_amount: DexFee, _stage: FeeApproxStage, ) -> TradePreimageResult { Ok(TradeFee { diff --git a/mm2src/coins/z_coin/z_coin_native_tests.rs b/mm2src/coins/z_coin/z_coin_native_tests.rs index 12789b2090..c2a0b66201 100644 --- a/mm2src/coins/z_coin/z_coin_native_tests.rs +++ b/mm2src/coins/z_coin/z_coin_native_tests.rs @@ -11,6 +11,8 @@ use super::{z_coin_from_conf_and_params_with_z_key, z_mainnet_constants, Future, ZTransaction}; use crate::z_coin::{z_htlc::z_send_dex_fee, ZcoinActivationParams, ZcoinRpcMode}; use crate::CoinProtocol; +use crate::DexFee; +use mm2_number::MmNumber; #[test] fn zombie_coin_send_and_refund_maker_payment() { @@ -228,7 +230,7 @@ fn zombie_coin_validate_dex_fee() { fee_tx: &tx, expected_sender: &[], fee_addr: &[], - amount: &"0.001".parse().unwrap(), + dex_fee: &DexFee::Standard(MmNumber::from("0.001")), min_block_number: 12000, uuid: &[1; 16], }; @@ -244,7 +246,7 @@ fn zombie_coin_validate_dex_fee() { fee_tx: &tx, expected_sender: &[], fee_addr: &[], - amount: &"0.01".parse().unwrap(), + dex_fee: &DexFee::Standard(MmNumber::from("0.01")), min_block_number: 12000, uuid: &[2; 16], }; @@ -259,7 +261,7 @@ fn zombie_coin_validate_dex_fee() { fee_tx: &tx, expected_sender: &[], fee_addr: &[], - amount: &"0.01".parse().unwrap(), + dex_fee: &DexFee::Standard(MmNumber::from("0.01")), min_block_number: 14000, uuid: &[1; 16], }; @@ -274,7 +276,7 @@ fn zombie_coin_validate_dex_fee() { fee_tx: &tx, expected_sender: &[], fee_addr: &[], - amount: &"0.01".parse().unwrap(), + dex_fee: &DexFee::Standard(MmNumber::from("0.01")), min_block_number: 12000, uuid: &[1; 16], }; diff --git a/mm2src/mm2_bitcoin/script/src/builder.rs b/mm2src/mm2_bitcoin/script/src/builder.rs index 4dfc3e4d07..281cc0f185 100644 --- a/mm2src/mm2_bitcoin/script/src/builder.rs +++ b/mm2src/mm2_bitcoin/script/src/builder.rs @@ -78,7 +78,7 @@ impl Builder { pub fn push_bytes(mut self, bytes: &[u8]) -> Self { let len = bytes.len(); if !(1..=75).contains(&len) { - panic!("Canot push {} bytes", len); + panic!("Can not push {} bytes", len); } let opcode: Opcode = Opcode::from_u8(((Opcode::OP_PUSHBYTES_1 as usize) + len - 1) as u8) diff --git a/mm2src/mm2_main/src/lp_ordermatch.rs b/mm2src/mm2_main/src/lp_ordermatch.rs index 8dfe4b7e30..92055cf43d 100644 --- a/mm2src/mm2_main/src/lp_ordermatch.rs +++ b/mm2src/mm2_main/src/lp_ordermatch.rs @@ -2974,7 +2974,10 @@ fn lp_connect_start_bob(ctx: MmArc, maker_match: MakerMatch, maker_order: MakerO maker_volume: maker_amount, secret, taker_coin: t.clone(), - dex_fee_amount: dex_fee_amount_from_taker_coin(&t, m.ticker(), &taker_amount), + // TODO: + // Support KMD burning for v2 + dex_fee_amount: dex_fee_amount_from_taker_coin(&t, m.ticker(), &taker_amount) + .total_spend_amount(), taker_volume: taker_amount, taker_premium: Default::default(), conf_settings: my_conf_settings, @@ -3121,7 +3124,10 @@ fn lp_connected_alice(ctx: MmArc, taker_order: TakerOrder, taker_match: TakerMat maker_coin: m.clone(), maker_volume: maker_amount, taker_coin: t.clone(), - dex_fee: dex_fee_amount_from_taker_coin(&t, maker_coin_ticker, &taker_amount), + // TODO: + // Support KMD burning for v2 + dex_fee: dex_fee_amount_from_taker_coin(&t, maker_coin_ticker, &taker_amount) + .total_spend_amount(), taker_volume: taker_amount, taker_premium: Default::default(), secret_hash_algo, diff --git a/mm2src/mm2_main/src/lp_swap.rs b/mm2src/mm2_main/src/lp_swap.rs index 4f0a704f16..8d207542fc 100644 --- a/mm2src/mm2_main/src/lp_swap.rs +++ b/mm2src/mm2_main/src/lp_swap.rs @@ -60,7 +60,7 @@ use super::lp_network::P2PRequestResult; use crate::mm2::lp_network::{broadcast_p2p_msg, Libp2pPeerId, P2PProcessError, P2PProcessResult, P2PRequestError}; use bitcrypto::{dhash160, sha256}; -use coins::{lp_coinfind, lp_coinfind_or_err, CoinFindError, MmCoin, MmCoinEnum, TradeFee, TransactionEnum}; +use coins::{lp_coinfind, lp_coinfind_or_err, CoinFindError, DexFee, MmCoin, MmCoinEnum, TradeFee, TransactionEnum}; use common::log::{debug, warn}; use common::now_sec; use common::time_cache::DuplicateCache; @@ -742,18 +742,44 @@ 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) -> MmNumber { +pub fn dex_fee_amount(base: &str, rel: &str, trade_amount: &MmNumber, min_tx_amount: &MmNumber) -> DexFee { let rate = dex_fee_rate(base, rel); - let fee_amount = trade_amount * &rate; - if &fee_amount < min_tx_amount { - min_tx_amount.clone() - } else { - fee_amount + 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) } /// Calculates DEX fee with a threshold based on min tx amount of the taker coin. -pub fn dex_fee_amount_from_taker_coin(taker_coin: &dyn MmCoin, maker_coin: &str, trade_amount: &MmNumber) -> MmNumber { +pub fn dex_fee_amount_from_taker_coin(taker_coin: &dyn MmCoin, maker_coin: &str, trade_amount: &MmNumber) -> DexFee { let min_tx_amount = MmNumber::from(taker_coin.min_tx_amount()); dex_fee_amount(taker_coin.ticker(), maker_coin, trade_amount, &min_tx_amount) } @@ -1650,28 +1676,43 @@ 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 / 777u64.into(); + let expected_fee = DexFee::Standard(amount / 777u64.into()); assert_eq!(expected_fee, actual_fee); let base = "KMD"; let rel = "ETH"; let amount = 1.into(); let actual_fee = dex_fee_amount(base, rel, &amount, &min_tx_amount); - let expected_fee = amount * (9, 7770).into(); - assert_eq!(expected_fee, actual_fee); + 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); + + // check the case when KMD taker fee is close to dust + let base = "KMD"; + let rel = "BTC"; + 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 + ); 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 = amount * (9, 7770).into(); + let expected_fee = DexFee::Standard(amount * (9, 7770).into()); assert_eq!(expected_fee, 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!(min_tx_amount, actual_fee); + assert_eq!(DexFee::Standard(min_tx_amount), actual_fee); } #[test] @@ -2182,4 +2223,55 @@ mod lp_swap_tests { let _: SavedSwap = json::from_str(include_str!("for_tests/iris_nimda_rick_taker_swap.json")).unwrap(); let _: SavedSwap = json::from_str(include_str!("for_tests/iris_nimda_rick_maker_swap.json")).unwrap(); } + + #[test] + fn test_kmd_taker_dex_fee_calculation() { + 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 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 { .. } => { + 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; + + assert_eq!(expected_mycoin_taker_fee, mycoin_taker_fee); + assert_eq!(expected_kmd_burn_amount, kmd_burn_amount); + } + + #[test] + fn test_dex_fee_amount_from_taker_coin_discount() { + 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)) { + DexFee::Standard(t) => t, + DexFee::WithBurn { .. } => { + panic!("Wrong variant returned for MYCOIN from `dex_fee_amount_from_taker_coin`.") + }, + }; + + 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 { .. } => { + panic!("Wrong variant returned for TEST coin from `dex_fee_amount_from_taker_coin`.") + }, + }; + + assert_eq!(testcoin_taker_fee * MmNumber::from("0.90"), mycoin_taker_fee); + } } diff --git a/mm2src/mm2_main/src/lp_swap/maker_swap.rs b/mm2src/mm2_main/src/lp_swap/maker_swap.rs index 67f1b6285a..9608213eaa 100644 --- a/mm2src/mm2_main/src/lp_swap/maker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/maker_swap.rs @@ -748,8 +748,7 @@ impl MakerSwap { info!("Taker fee tx {:02x}", hash); let taker_amount = MmNumber::from(self.taker_amount.clone()); - let fee_amount = - dex_fee_amount_from_taker_coin(self.taker_coin.deref(), &self.r().data.maker_coin, &taker_amount); + 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; @@ -761,7 +760,7 @@ impl MakerSwap { fee_tx: &taker_fee, expected_sender: &*other_taker_coin_htlc_pub, fee_addr: &DEX_FEE_ADDR_RAW_PUBKEY, - amount: &fee_amount.clone().into(), + dex_fee: &dex_fee, min_block_number: taker_coin_start_block, uuid: self.uuid.as_bytes(), }) diff --git a/mm2src/mm2_main/src/lp_swap/taker_swap.rs b/mm2src/mm2_main/src/lp_swap/taker_swap.rs index f16bdd129d..a60ff2e149 100644 --- a/mm2src/mm2_main/src/lp_swap/taker_swap.rs +++ b/mm2src/mm2_main/src/lp_swap/taker_swap.rs @@ -998,7 +998,7 @@ impl TakerSwap { let fee_to_send_dex_fee_fut = self .taker_coin - .get_fee_to_send_taker_fee(dex_fee.to_decimal(), stage.clone()); + .get_fee_to_send_taker_fee(dex_fee.clone(), stage.clone()); let fee_to_send_dex_fee = match fee_to_send_dex_fee_fut.await { Ok(fee) => fee, Err(e) => { @@ -1027,7 +1027,7 @@ impl TakerSwap { }; let params = TakerSwapPreparedParams { - dex_fee: dex_fee.clone(), + dex_fee: dex_fee.total_spend_amount(), fee_to_send_dex_fee: fee_to_send_dex_fee.clone(), taker_payment_trade_fee: taker_payment_trade_fee.clone(), maker_payment_spend_trade_fee: maker_payment_spend_trade_fee.clone(), @@ -1272,7 +1272,7 @@ impl TakerSwap { dex_fee_amount_from_taker_coin(self.taker_coin.deref(), &self.r().data.maker_coin, &self.taker_amount); let fee_tx = self .taker_coin - .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount.into(), self.uuid.as_bytes()) + .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount, self.uuid.as_bytes()) .compat() .await; let transaction = match fee_tx { @@ -2291,7 +2291,7 @@ impl AtomicSwap for TakerSwap { if self.r().taker_fee.is_none() { result.push(LockedAmount { coin: self.taker_coin.ticker().to_owned(), - amount: taker_fee_amount, + amount: taker_fee_amount.total_spend_amount(), trade_fee, }); } @@ -2356,7 +2356,7 @@ pub async fn check_balance_for_taker_swap( None => { let dex_fee = dex_fee_amount_from_taker_coin(my_coin, other_coin.ticker(), &volume); let fee_to_send_dex_fee = my_coin - .get_fee_to_send_taker_fee(dex_fee.to_decimal(), stage.clone()) + .get_fee_to_send_taker_fee(dex_fee.clone(), stage.clone()) .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, my_coin.ticker()))?; let preimage_value = TradePreimageValue::Exact(volume.to_decimal()); @@ -2370,7 +2370,7 @@ pub async fn check_balance_for_taker_swap( .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, other_coin.ticker()))?; TakerSwapPreparedParams { - dex_fee, + dex_fee: dex_fee.total_spend_amount(), fee_to_send_dex_fee, taker_payment_trade_fee, maker_payment_spend_trade_fee, @@ -2445,12 +2445,12 @@ pub async fn taker_swap_trade_preimage( let dex_amount = dex_fee_amount_from_taker_coin(my_coin.deref(), other_coin_ticker, &my_coin_volume); let taker_fee = TradeFee { coin: my_coin_ticker.to_owned(), - amount: dex_amount.clone(), + amount: dex_amount.total_spend_amount(), paid_from_trading_vol: false, }; let fee_to_send_taker_fee = my_coin - .get_fee_to_send_taker_fee(dex_amount.to_decimal(), stage.clone()) + .get_fee_to_send_taker_fee(dex_amount.clone(), stage.clone()) .await .mm_err(|e| TradePreimageRpcError::from_trade_preimage_error(e, my_coin_ticker))?; @@ -2466,7 +2466,7 @@ pub async fn taker_swap_trade_preimage( .mm_err(|e| TradePreimageRpcError::from_trade_preimage_error(e, other_coin_ticker))?; let prepared_params = TakerSwapPreparedParams { - dex_fee: dex_amount, + dex_fee: dex_amount.total_spend_amount(), fee_to_send_dex_fee: fee_to_send_taker_fee.clone(), taker_payment_trade_fee: my_coin_trade_fee.clone(), maker_payment_spend_trade_fee: other_coin_trade_fee.clone(), @@ -2590,7 +2590,7 @@ pub async fn calc_max_taker_vol( let max_possible_2 = &max_possible - &max_trade_fee.amount; let max_dex_fee = dex_fee_amount_from_taker_coin(coin.deref(), other_coin, &max_possible_2); let max_fee_to_send_taker_fee = coin - .get_fee_to_send_taker_fee(max_dex_fee.to_decimal(), stage) + .get_fee_to_send_taker_fee(max_dex_fee.clone(), stage) .await .mm_err(|e| CheckBalanceError::from_trade_preimage_error(e, my_coin))?; let min_max_possible = &max_possible_2 - &max_fee_to_send_taker_fee.amount; @@ -2601,7 +2601,7 @@ pub async fn calc_max_taker_vol( balance.to_fraction(), locked.to_fraction(), max_trade_fee.amount.to_fraction(), - max_dex_fee.to_fraction(), + max_dex_fee.total_spend_amount().to_fraction(), max_fee_to_send_taker_fee.amount.to_fraction() ); max_taker_vol_from_available(min_max_possible, my_coin, other_coin, &min_tx_amount) @@ -3078,7 +3078,7 @@ mod taker_swap_tests { let max_taker_vol = max_taker_vol_from_available(available.clone(), "RICK", "MORTY", &min_tx_amount) .expect("!max_taker_vol_from_available"); - let dex_fee = dex_fee_amount(base, "MORTY", &max_taker_vol, &min_tx_amount); + let dex_fee = dex_fee_amount(base, "MORTY", &max_taker_vol, &min_tx_amount).fee_amount(); assert!(min_tx_amount < dex_fee); assert!(min_tx_amount <= max_taker_vol); assert_eq!(max_taker_vol + dex_fee, available); @@ -3098,7 +3098,7 @@ mod taker_swap_tests { let base = if is_kmd { "KMD" } else { "RICK" }; let max_taker_vol = max_taker_vol_from_available(available.clone(), base, "MORTY", &min_tx_amount) .expect("!max_taker_vol_from_available"); - let dex_fee = dex_fee_amount(base, "MORTY", &max_taker_vol, &min_tx_amount); + let dex_fee = dex_fee_amount(base, "MORTY", &max_taker_vol, &min_tx_amount).fee_amount(); println!( "available={:?} max_taker_vol={:?} dex_fee={:?}", available.to_decimal(), diff --git a/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs b/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs index 03c484275d..109e31b6cc 100644 --- a/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/qrc20_tests.rs @@ -1054,12 +1054,12 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ let max_possible_2 = &qtum_balance - &max_trade_fee; // - `max_dex_fee = dex_fee(max_possible_2)` let max_dex_fee = dex_fee_amount("QTUM", "MYCOIN", &MmNumber::from(max_possible_2), &qtum_min_tx_amount); - debug!("max_dex_fee: {:?}", max_dex_fee.to_fraction()); + debug!("max_dex_fee: {:?}", max_dex_fee.fee_amount().to_fraction()); // - `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.to_decimal(), FeeApproxStage::TradePreimage)) + block_on(coin.get_fee_to_send_taker_fee(max_dex_fee, FeeApproxStage::TradePreimage)) .expect("!get_fee_to_send_taker_fee"); let max_fee_to_send_taker_fee = max_fee_to_send_taker_fee.amount.to_decimal(); debug!("max_fee_to_send_taker_fee: {}", max_fee_to_send_taker_fee); @@ -1072,7 +1072,7 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ let expected_max_taker_vol = max_taker_vol_from_available(MmNumber::from(available), "QTUM", "MYCOIN", &min_tx_amount) .expect("max_taker_vol_from_available"); - let real_dex_fee = dex_fee_amount("QTUM", "MYCOIN", &expected_max_taker_vol, &qtum_min_tx_amount); + let real_dex_fee = dex_fee_amount("QTUM", "MYCOIN", &expected_max_taker_vol, &qtum_min_tx_amount).fee_amount(); debug!("real_max_dex_fee: {:?}", real_dex_fee.to_fraction()); // check if the actual max_taker_vol equals to the expected @@ -1105,9 +1105,9 @@ fn test_get_max_taker_vol_and_trade_with_dynamic_trade_fee(coin: QtumCoin, priv_ let timelock = now_sec() - 200; let secret_hash = &[0; 20]; - let dex_fee_amount = dex_fee_amount("QTUM", "MYCOIN", &expected_max_taker_vol, &qtum_min_tx_amount); + let dex_fee = dex_fee_amount("QTUM", "MYCOIN", &expected_max_taker_vol, &qtum_min_tx_amount); let _taker_fee_tx = coin - .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, dex_fee_amount.to_decimal(), &[]) + .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, dex_fee, &[]) .wait() .expect("!send_taker_fee"); let taker_payment_args = SendPaymentArgs { diff --git a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs index 4f3a10593f..00348f66c0 100644 --- a/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs +++ b/mm2src/mm2_main/tests/docker_tests/swap_watcher_tests.rs @@ -738,8 +738,9 @@ fn test_watcher_spends_maker_payment_eth_utxo() { let mycoin_volume = BigDecimal::from_str("1").unwrap(); let min_tx_amount = BigDecimal::from_str("0.00001").unwrap().into(); - let dex_fee: BigDecimal = - dex_fee_amount("MYCOIN", "ETH", &MmNumber::from(mycoin_volume.clone()), &min_tx_amount).into(); + let dex_fee: BigDecimal = dex_fee_amount("MYCOIN", "ETH", &MmNumber::from(mycoin_volume.clone()), &min_tx_amount) + .fee_amount() + .into(); let alice_mycoin_reward_sent = balances.alice_acoin_balance_before - balances.alice_acoin_balance_after.clone() - mycoin_volume.clone() @@ -878,8 +879,9 @@ fn test_watcher_spends_maker_payment_erc20_utxo() { let jst_volume = BigDecimal::from_str("1").unwrap(); let min_tx_amount = BigDecimal::from_str("0.00001").unwrap().into(); - let dex_fee: BigDecimal = - dex_fee_amount("MYCOIN", "JST", &MmNumber::from(mycoin_volume.clone()), &min_tx_amount).into(); + let dex_fee: BigDecimal = dex_fee_amount("MYCOIN", "JST", &MmNumber::from(mycoin_volume.clone()), &min_tx_amount) + .fee_amount() + .into(); let alice_mycoin_reward_sent = balances.alice_acoin_balance_before - balances.alice_acoin_balance_after.clone() - mycoin_volume.clone() @@ -1130,7 +1132,7 @@ fn test_watcher_validate_taker_fee_utxo() { let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, maker_coin.ticker(), &taker_amount); let taker_fee = taker_coin - .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount.into(), Uuid::new_v4().as_bytes()) + .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount, Uuid::new_v4().as_bytes()) .wait() .unwrap(); @@ -1251,7 +1253,7 @@ fn test_watcher_validate_taker_fee_eth() { let taker_amount = MmNumber::from((1, 1)); let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, "ETH", &taker_amount); let taker_fee = taker_coin - .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount.into(), Uuid::new_v4().as_bytes()) + .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount, Uuid::new_v4().as_bytes()) .wait() .unwrap(); @@ -1354,7 +1356,7 @@ fn test_watcher_validate_taker_fee_erc20() { let taker_amount = MmNumber::from((1, 1)); let fee_amount = dex_fee_amount_from_taker_coin(&taker_coin, "ETH", &taker_amount); let taker_fee = taker_coin - .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount.into(), Uuid::new_v4().as_bytes()) + .send_taker_fee(&DEX_FEE_ADDR_RAW_PUBKEY, fee_amount, Uuid::new_v4().as_bytes()) .wait() .unwrap();