Skip to content

Commit

Permalink
fix (mariocynicys): recalc kmd interest when tx inputs reselected and…
Browse files Browse the repository at this point in the history
… update txfee
  • Loading branch information
dimxy committed Feb 24, 2025
1 parent 3f709fc commit 459a379
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 116 deletions.
21 changes: 8 additions & 13 deletions mm2src/coins/qrc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ use crate::utxo::utxo_builder::{UtxoCoinBuildError, UtxoCoinBuildResult, UtxoCoi
UtxoFieldsWithGlobalHDBuilder, UtxoFieldsWithHardwareWalletBuilder,
UtxoFieldsWithIguanaSecretBuilder};
use crate::utxo::utxo_common::{self, big_decimal_from_sat, check_all_utxo_inputs_signed_by_pub, UtxoTxBuilder};
use crate::utxo::{qtum, ActualFeeRate, AdditionalTxData, AddrFromStrError, BroadcastTxErr, FeePolicy, GenerateTxError,
GetUtxoListOps, HistoryUtxoTx, HistoryUtxoTxMap, MatureUnspentList, RecentlySpentOutPointsGuard,
UnsupportedAddr, UtxoActivationParams, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps,
UtxoFromLegacyReqErr, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom,
UTXO_LOCK};
use crate::utxo::{qtum, ActualFeeRate, AddrFromStrError, BroadcastTxErr, FeePolicy, GenerateTxError, GetUtxoListOps,
HistoryUtxoTx, HistoryUtxoTxMap, MatureUnspentList, RecentlySpentOutPointsGuard, UnsupportedAddr,
UtxoActivationParams, UtxoAddressFormat, UtxoCoinFields, UtxoCommonOps, UtxoFromLegacyReqErr,
UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps, VerboseTransactionFrom, UTXO_LOCK};
use crate::{BalanceError, BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput,
DexFee, Eip1559Ops, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, IguanaPrivKey, MakerSwapTakerCoin,
MarketCoinOps, MmCoin, MmCoinEnum, NegotiateSwapContractAddrErr, PaymentInstructionArgs,
Expand Down Expand Up @@ -615,15 +614,11 @@ impl UtxoTxGenerationOps for Qrc20Coin {
/// Get only QTUM transaction fee.
async fn get_fee_rate(&self) -> UtxoRpcResult<ActualFeeRate> { utxo_common::get_fee_rate(&self.utxo).await }

async fn calc_interest_if_required(
&self,
unsigned: TransactionInputSigner,
data: AdditionalTxData,
my_script_pub: ScriptBytes,
dust: u64,
) -> UtxoRpcResult<(TransactionInputSigner, AdditionalTxData)> {
utxo_common::calc_interest_if_required(self, unsigned, data, my_script_pub, dust).await
async fn calc_interest_if_required(&self, unsigned: &mut TransactionInputSigner) -> UtxoRpcResult<u64> {
utxo_common::calc_interest_if_required(self, unsigned).await
}

fn is_kmd(&self) -> bool { utxo_common::is_kmd(self) }
}

#[async_trait]
Expand Down
11 changes: 4 additions & 7 deletions mm2src/coins/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -878,13 +878,10 @@ pub trait UtxoTxGenerationOps {
/// Calculates interest if the coin is KMD
/// Adds the value to existing output to my_script_pub or creates additional interest output
/// returns transaction and data as is if the coin is not KMD
async fn calc_interest_if_required(
&self,
mut unsigned: TransactionInputSigner,
mut data: AdditionalTxData,
my_script_pub: Bytes,
dust: u64,
) -> UtxoRpcResult<(TransactionInputSigner, AdditionalTxData)>;
async fn calc_interest_if_required(&self, unsigned: &mut TransactionInputSigner) -> UtxoRpcResult<u64>;

/// Is KMD coin
fn is_kmd(&self) -> bool;
}

/// The UTXO address balance scanner.
Expand Down
12 changes: 4 additions & 8 deletions mm2src/coins/utxo/bch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,15 +699,11 @@ impl UtxoTxBroadcastOps for BchCoin {
impl UtxoTxGenerationOps for BchCoin {
async fn get_fee_rate(&self) -> UtxoRpcResult<ActualFeeRate> { utxo_common::get_fee_rate(&self.utxo_arc).await }

async fn calc_interest_if_required(
&self,
unsigned: TransactionInputSigner,
data: AdditionalTxData,
my_script_pub: Bytes,
dust: u64,
) -> UtxoRpcResult<(TransactionInputSigner, AdditionalTxData)> {
utxo_common::calc_interest_if_required(self, unsigned, data, my_script_pub, dust).await
async fn calc_interest_if_required(&self, unsigned: &mut TransactionInputSigner) -> UtxoRpcResult<u64> {
utxo_common::calc_interest_if_required(self, unsigned).await
}

fn is_kmd(&self) -> bool { utxo_common::is_kmd(self) }
}

#[async_trait]
Expand Down
12 changes: 4 additions & 8 deletions mm2src/coins/utxo/qtum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,15 +317,11 @@ impl UtxoTxBroadcastOps for QtumCoin {
impl UtxoTxGenerationOps for QtumCoin {
async fn get_fee_rate(&self) -> UtxoRpcResult<ActualFeeRate> { utxo_common::get_fee_rate(&self.utxo_arc).await }

async fn calc_interest_if_required(
&self,
unsigned: TransactionInputSigner,
data: AdditionalTxData,
my_script_pub: Bytes,
dust: u64,
) -> UtxoRpcResult<(TransactionInputSigner, AdditionalTxData)> {
utxo_common::calc_interest_if_required(self, unsigned, data, my_script_pub, dust).await
async fn calc_interest_if_required(&self, unsigned: &mut TransactionInputSigner) -> UtxoRpcResult<u64> {
utxo_common::calc_interest_if_required(self, unsigned).await
}

fn is_kmd(&self) -> bool { utxo_common::is_kmd(self) }
}

#[async_trait]
Expand Down
20 changes: 7 additions & 13 deletions mm2src/coins/utxo/slp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use crate::utxo::bch::BchCoin;
use crate::utxo::bchd_grpc::{check_slp_transaction, validate_slp_utxos, ValidateSlpUtxosErr};
use crate::utxo::rpc_clients::{UnspentInfo, UtxoRpcClientEnum, UtxoRpcError, UtxoRpcResult};
use crate::utxo::utxo_common::{self, big_decimal_from_sat_unsigned, payment_script, UtxoTxBuilder};
use crate::utxo::{generate_and_send_tx, sat_from_big_decimal, ActualFeeRate, AdditionalTxData, BroadcastTxErr,
FeePolicy, GenerateTxError, RecentlySpentOutPointsGuard, UtxoCoinConf, UtxoCoinFields,
UtxoCommonOps, UtxoTx, UtxoTxBroadcastOps, UtxoTxGenerationOps};
use crate::utxo::{generate_and_send_tx, sat_from_big_decimal, ActualFeeRate, BroadcastTxErr, FeePolicy,
GenerateTxError, RecentlySpentOutPointsGuard, UtxoCoinConf, UtxoCoinFields, UtxoCommonOps, UtxoTx,
UtxoTxBroadcastOps, UtxoTxGenerationOps};
use crate::{BalanceFut, CheckIfMyPaymentSentArgs, CoinBalance, CoinFutSpawner, ConfirmPaymentInput, DerivationMethod,
DexFee, FeeApproxStage, FoundSwapTxSpend, HistorySyncState, MakerSwapTakerCoin, MarketCoinOps, MmCoin,
MmCoinEnum, NegotiateSwapContractAddrErr, NumConversError, PaymentInstructionArgs, PaymentInstructions,
Expand Down Expand Up @@ -1080,17 +1080,11 @@ impl UtxoTxBroadcastOps for SlpToken {
impl UtxoTxGenerationOps for SlpToken {
async fn get_fee_rate(&self) -> UtxoRpcResult<ActualFeeRate> { self.platform_coin.get_fee_rate().await }

async fn calc_interest_if_required(
&self,
unsigned: TransactionInputSigner,
data: AdditionalTxData,
my_script_pub: Bytes,
dust: u64,
) -> UtxoRpcResult<(TransactionInputSigner, AdditionalTxData)> {
self.platform_coin
.calc_interest_if_required(unsigned, data, my_script_pub, dust)
.await
async fn calc_interest_if_required(&self, unsigned: &mut TransactionInputSigner) -> UtxoRpcResult<u64> {
self.platform_coin.calc_interest_if_required(unsigned).await
}

fn is_kmd(&self) -> bool { self.platform_coin.is_kmd() }
}

#[async_trait]
Expand Down
81 changes: 31 additions & 50 deletions mm2src/coins/utxo/utxo_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ pub struct UtxoTxBuilder<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> {
tx_fee: u64,
min_relay_fee_rate: Option<u64>,
dust: Option<u64>,
interest: u64,
}

impl<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> {
Expand All @@ -492,6 +493,7 @@ impl<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> {
tx_fee: 0,
min_relay_fee_rate: None,
dust: None,
interest: 0,
}
}

Expand Down Expand Up @@ -597,24 +599,34 @@ impl<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> {
total
}

fn make_kmd_rewards_data(coin: &T, interest: u64) -> Option<KmdRewardsDetails> {
let rewards_amount = big_decimal_from_sat_unsigned(interest, coin.as_ref().decimals);
if coin.is_kmd() {
Some(KmdRewardsDetails::claimed_by_me(rewards_amount))
} else {
None
}
}

/// Adds change output.
/// Returns change value and dust change
fn add_change(&mut self, change_script_pubkey: &Bytes) -> (u64, u64) {
async fn add_change(&mut self, coin: &T, change_script_pubkey: &Bytes) -> UtxoRpcResult<(u64, u64)> {
let sum_output_with_fee = self.sum_outputs + self.total_tx_fee();
if self.sum_inputs < sum_output_with_fee {
return (0u64, 0u64);
return Ok((0u64, 0u64));
}
let change = self.sum_inputs - sum_output_with_fee;
self.interest = coin.calc_interest_if_required(&mut self.tx).await?;
let change = self.sum_inputs + self.interest - sum_output_with_fee;
if change < self.dust() {
return (0u64, change);
return Ok((0u64, change));
};
self.tx.outputs.push({
TransactionOutput {
value: change,
script_pubkey: change_script_pubkey.clone(),
}
});
(change, 0u64)
Ok((change, 0u64))
}

/// Recalculates tx fee for tx size.
Expand Down Expand Up @@ -689,7 +701,6 @@ impl<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> {
/// Also returns additional transaction data
pub async fn build(mut self) -> GenerateTxResult {
let coin = self.coin;
let dust: u64 = self.dust();
let from = self
.from
.clone()
Expand Down Expand Up @@ -722,7 +733,6 @@ impl<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> {

#[allow(unused_assignments)]
let mut unused_change = 0u64;

let mut one_time_fee_update = false;
loop {
let required_amount_0 = self.required_amount();
Expand All @@ -743,11 +753,11 @@ impl<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> {
.sum_outputs
.checked_sub(self.deduct_txfee_from_output()?)
.or_mm_err(|| GenerateTxError::Internal("sum_outputs underflow".to_owned()))?;
let (change, unused) = self.add_change(&change_script_pubkey);
self.sum_outputs += change;
let (change, unused) = self.add_change(coin, &change_script_pubkey).await?;
unused_change = unused;
self.sum_outputs += change;
self.update_tx_fee(from.addr_format(), &actual_fee_rate); // recalculate txfee with the change output, if added
if self.sum_inputs >= self.sum_outputs + self.total_tx_fee() {
if self.sum_inputs + self.interest >= self.sum_outputs + self.total_tx_fee() {
break;
}
}
Expand All @@ -758,12 +768,10 @@ impl<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> {
spent_by_me: self.sum_inputs,
unused_change,
// will be changed if the ticker is KMD
kmd_rewards: None,
kmd_rewards: Self::make_kmd_rewards_data(coin, self.interest),
};

Ok(coin
.calc_interest_if_required(self.tx, data, change_script_pubkey, dust)
.await?)
Ok((self.tx, data))
}

/// Generates unsigned transaction (TransactionInputSigner) from specified utxos and outputs.
Expand Down Expand Up @@ -815,13 +823,10 @@ impl<'a, T: AsRef<UtxoCoinFields> + UtxoTxGenerationOps> UtxoTxBuilder<'a, T> {
/// returns transaction and data as is if the coin is not KMD
pub async fn calc_interest_if_required<T: UtxoCommonOps>(
coin: &T,
mut unsigned: TransactionInputSigner,
mut data: AdditionalTxData,
my_script_pub: Bytes,
dust: u64,
) -> UtxoRpcResult<(TransactionInputSigner, AdditionalTxData)> {
if coin.as_ref().conf.ticker != "KMD" {
return Ok((unsigned, data));
unsigned: &mut TransactionInputSigner,
) -> UtxoRpcResult<u64> {
if !coin.is_kmd() {
return Ok(0);
}
unsigned.lock_time = coin.get_current_mtp().await?;
let mut interest = 0;
Expand All @@ -839,39 +844,15 @@ pub async fn calc_interest_if_required<T: UtxoCommonOps>(
interest += output_interest;
};
}
if interest > 0 {
data.received_by_me += interest;
let mut output_to_me = unsigned
.outputs
.iter_mut()
.find(|out| out.script_pubkey == my_script_pub);
// add calculated interest to existing output to my address
// or create the new one if it's not found
match output_to_me {
Some(ref mut output) => output.value += interest,
None => {
let maybe_change_output_value = interest + data.unused_change;
if maybe_change_output_value > dust {
let change_output = TransactionOutput {
script_pubkey: my_script_pub,
value: maybe_change_output_value,
};
unsigned.outputs.push(change_output);
data.unused_change = 0;
} else {
data.unused_change += interest;
}
},
};
} else {
if interest == 0 {
// if interest is zero attempt to set the lowest possible lock_time to claim it later
unsigned.lock_time = now_sec_u32() - 3600 + 777 * 2;
}
let rewards_amount = big_decimal_from_sat_unsigned(interest, coin.as_ref().decimals);
data.kmd_rewards = Some(KmdRewardsDetails::claimed_by_me(rewards_amount));
Ok((unsigned, data))
Ok(interest)
}

pub fn is_kmd<T: UtxoCommonOps>(coin: &T) -> bool { &coin.as_ref().conf.ticker == "KMD" }

pub struct P2SHSpendingTxInput<'a> {
prev_transaction: UtxoTx,
redeem_script: Bytes,
Expand Down Expand Up @@ -3773,7 +3754,7 @@ pub async fn calc_interest_of_tx<T: UtxoCommonOps>(
tx: &UtxoTx,
input_transactions: &mut HistoryUtxoTxMap,
) -> UtxoRpcResult<u64> {
if coin.as_ref().conf.ticker != "KMD" {
if !coin.is_kmd() {
let error = format!("Expected KMD ticker, found {}", coin.as_ref().conf.ticker);
return MmError::err(UtxoRpcError::Internal(error));
}
Expand Down
12 changes: 4 additions & 8 deletions mm2src/coins/utxo/utxo_standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,11 @@ impl UtxoTxBroadcastOps for UtxoStandardCoin {
impl UtxoTxGenerationOps for UtxoStandardCoin {
async fn get_fee_rate(&self) -> UtxoRpcResult<ActualFeeRate> { utxo_common::get_fee_rate(&self.utxo_arc).await }

async fn calc_interest_if_required(
&self,
unsigned: TransactionInputSigner,
data: AdditionalTxData,
my_script_pub: Bytes,
dust: u64,
) -> UtxoRpcResult<(TransactionInputSigner, AdditionalTxData)> {
utxo_common::calc_interest_if_required(self, unsigned, data, my_script_pub, dust).await
async fn calc_interest_if_required(&self, unsigned: &mut TransactionInputSigner) -> UtxoRpcResult<u64> {
utxo_common::calc_interest_if_required(self, unsigned).await
}

fn is_kmd(&self) -> bool { utxo_common::is_kmd(self) }
}

#[async_trait]
Expand Down
13 changes: 4 additions & 9 deletions mm2src/coins/z_coin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ use mm2_err_handle::prelude::*;
use mm2_event_stream::behaviour::{EventBehaviour, EventInitStatus};
use mm2_number::{BigDecimal, MmNumber};
#[cfg(test)] use mocktopus::macros::*;
use primitives::bytes::Bytes;
use rpc::v1::types::{Bytes as BytesJson, Transaction as RpcTransaction, H256 as H256Json};
use script::{Builder as ScriptBuilder, Opcode, Script, TransactionInputSigner};
use serde_json::Value as Json;
Expand Down Expand Up @@ -1801,15 +1800,11 @@ impl MmCoin for ZCoin {
impl UtxoTxGenerationOps for ZCoin {
async fn get_fee_rate(&self) -> UtxoRpcResult<ActualFeeRate> { utxo_common::get_fee_rate(&self.utxo_arc).await }

async fn calc_interest_if_required(
&self,
unsigned: TransactionInputSigner,
data: AdditionalTxData,
my_script_pub: Bytes,
dust: u64,
) -> UtxoRpcResult<(TransactionInputSigner, AdditionalTxData)> {
utxo_common::calc_interest_if_required(self, unsigned, data, my_script_pub, dust).await
async fn calc_interest_if_required(&self, unsigned: &mut TransactionInputSigner) -> UtxoRpcResult<u64> {
utxo_common::calc_interest_if_required(self, unsigned).await
}

fn is_kmd(&self) -> bool { utxo_common::is_kmd(self) }
}

#[async_trait]
Expand Down

0 comments on commit 459a379

Please sign in to comment.