Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(UTXO swaps): kmd burn plan impl #2006

Merged
merged 12 commits into from
Nov 22, 2023
17 changes: 10 additions & 7 deletions mm2src/coins/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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),
)
}

Expand Down Expand Up @@ -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,
})
Expand Down Expand Up @@ -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<TradeFee> {
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)
Expand Down
30 changes: 20 additions & 10 deletions mm2src/coins/eth/eth_tests.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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(
Expand All @@ -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);
}

Expand All @@ -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 { .. }),
Expand All @@ -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: &[],
};
Expand Down Expand Up @@ -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: &[],
};
Expand Down Expand Up @@ -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: &[],
};
Expand Down Expand Up @@ -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: &[],
};
Expand Down
8 changes: 4 additions & 4 deletions mm2src/coins/lightning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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())
}
Expand Down Expand Up @@ -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<TradeFee> {
Ok(TradeFee {
Expand Down
72 changes: 69 additions & 3 deletions mm2src/coins/lp_coins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
}
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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<TradeFee>;

Expand Down Expand Up @@ -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<MmNumber> {
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<u64> {
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<Option<u64>> {
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`.
Expand Down
38 changes: 21 additions & 17 deletions mm2src/coins/qrc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<TradeFee> {
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();
Expand Down
Loading
Loading