Skip to content

Commit

Permalink
fix: fee estimate (#4656)
Browse files Browse the repository at this point in the history
Description
---
Allows fee estimate to always return an estimated fee. 

Motivation and Context
---
Currently, when calling fee estimate and you do not have enough funds, or funds pending, the fee estimate will error. 
We do not require such a high level of accuracy when calculating a fee estimate for an external call for an estimation. 
When the wallet does not have enough funds available, the API will return the fee estimate for default with 1 input and 1 kernel. 


How Has This Been Tested?
---
unit test
  • Loading branch information
SWvheerden authored Sep 13, 2022
1 parent 3fcc6a0 commit d9de2e0
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 20 deletions.
23 changes: 21 additions & 2 deletions base_layer/wallet/src/output_manager_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,15 +865,34 @@ where
Covenant::new().consensus_encode_exact_size(),
);

let utxo_selection = self
let utxo_selection = match self
.select_utxos(
amount,
selection_criteria,
fee_per_gram,
num_outputs,
metadata_byte_size * num_outputs,
)
.await?;
.await
{
Ok(v) => Ok(v),
Err(OutputManagerError::FundsPending | OutputManagerError::NotEnoughFunds) => {
debug!(
target: LOG_TARGET,
"We dont have enough funds available to make a fee estimate, so we estimate 1 input, no change"
);
let fee_calc = self.get_fee_calc();
let output_features_estimate = OutputFeatures::default();
let default_metadata_size = fee_calc.weighting().round_up_metadata_size(
output_features_estimate.consensus_encode_exact_size() +
Covenant::new().consensus_encode_exact_size() +
script![Nop].consensus_encode_exact_size(),
);
let fee = fee_calc.calculate(fee_per_gram, 1, 1, num_outputs, default_metadata_size);
return Ok(Fee::normalize(fee));
},
Err(e) => Err(e),
}?;

debug!(target: LOG_TARGET, "{} utxos selected.", utxo_selection.utxos.len());

Expand Down
30 changes: 12 additions & 18 deletions base_layer/wallet/tests/output_manager_service_tests/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ async fn fee_estimate() {
}

// not enough funds
let err = oms
let fee = oms
.output_manager_handle
.fee_estimate(
MicroTari::from(2750),
Expand All @@ -407,8 +407,8 @@ async fn fee_estimate() {
1,
)
.await
.unwrap_err();
assert!(matches!(err, OutputManagerError::NotEnoughFunds));
.unwrap();
assert_eq!(fee, MicroTari::from(360));
}

#[allow(clippy::identity_op)]
Expand Down Expand Up @@ -496,23 +496,19 @@ async fn test_utxo_selection_no_chain_metadata() {
let expected_fee = fee_calc.calculate(fee_per_gram, 1, 1, 3, default_metadata_byte_size() * 3);
assert_eq!(fee, expected_fee);

// test if a fee estimate would be possible with pending funds included
// at this point 52000 uT is still spendable, with pending change incoming of 1690 uT
// so instead of returning "not enough funds".to_string(), return "funds pending"
let spendable_amount = (3..=10).sum::<u64>() * amount;
let err = oms
let fee = oms
.fee_estimate(spendable_amount, UtxoSelectionCriteria::default(), fee_per_gram, 1, 2)
.await
.unwrap_err();
assert!(matches!(err, OutputManagerError::FundsPending));
.unwrap();
assert_eq!(fee, MicroTari::from(250));

// test not enough funds
let broke_amount = spendable_amount + MicroTari::from(2000);
let err = oms
let fee = oms
.fee_estimate(broke_amount, UtxoSelectionCriteria::default(), fee_per_gram, 1, 2)
.await
.unwrap_err();
assert!(matches!(err, OutputManagerError::NotEnoughFunds));
.unwrap();
assert_eq!(fee, MicroTari::from(250));

// coin split uses the "Largest" selection strategy
let (_, tx, utxos_total_value) = oms.create_coin_split(vec![], amount, 5, fee_per_gram).await.unwrap();
Expand Down Expand Up @@ -593,14 +589,12 @@ async fn test_utxo_selection_with_chain_metadata() {
let expected_fee = fee_calc.calculate(fee_per_gram, 1, 2, 3, default_metadata_byte_size() * 3);
assert_eq!(fee, expected_fee);

// test fee estimates are maturity aware
// even though we have utxos for the fee, they can't be spent because they are not mature yet
let spendable_amount = (1..=6).sum::<u64>() * amount;
let err = oms
let fee = oms
.fee_estimate(spendable_amount, UtxoSelectionCriteria::default(), fee_per_gram, 1, 2)
.await
.unwrap_err();
assert!(matches!(err, OutputManagerError::NotEnoughFunds));
.unwrap();
assert_eq!(fee, MicroTari::from(250));

// test coin split is maturity aware
let (_, tx, utxos_total_value) = oms.create_coin_split(vec![], amount, 5, fee_per_gram).await.unwrap();
Expand Down

0 comments on commit d9de2e0

Please sign in to comment.