Fix offer crossing via single path AMM with transfer fee:

Single path AMM offer has to factor in the transfer in rate
when calculating the upper bound quality and the quality function
because single path AMM's offer quality is not constant.
This fix factors in the transfer fee in
BookStep::adjustQualityWithFees().
This commit is contained in:
Gregory Tsipenyuk
2024-05-04 17:36:49 -04:00
parent 2a25f58d40
commit 7f6a079aa4
2 changed files with 245 additions and 6 deletions

View File

@@ -275,6 +275,7 @@ public:
using BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>::BookStep;
using BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>::qualityUpperBound;
using typename BookStep<TIn, TOut, BookPaymentStep<TIn, TOut>>::OfferType;
// Never limit self cross quality on a payment.
template <template <typename, typename> typename Offer>
@@ -328,7 +329,9 @@ public:
ReadView const& v,
Quality const& ofrQ,
DebtDirection prevStepDir,
WaiveTransferFee waiveFee) const
WaiveTransferFee waiveFee,
OfferType,
Rules const&) const
{
// Charge the offer owner, not the sender
// Charge a fee even if the owner is the same as the issuer
@@ -368,6 +371,8 @@ class BookOfferCrossingStep
{
using BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>::
qualityUpperBound;
using typename BookStep<TIn, TOut, BookOfferCrossingStep<TIn, TOut>>::
OfferType;
private:
// Helper function that throws if the optional passed to the constructor
@@ -513,7 +518,9 @@ public:
ReadView const& v,
Quality const& ofrQ,
DebtDirection prevStepDir,
WaiveTransferFee waiveFee) const
WaiveTransferFee waiveFee,
OfferType offerType,
Rules const& rules) const
{
// Offer x-ing does not charge a transfer fee when the offer's owner
// is the same as the strand dst. It is important that
@@ -521,7 +528,30 @@ public:
// ignore strands whose quality cannot meet a minimum threshold). When
// calculating quality assume no fee is charged, or the estimate will no
// longer be an upper bound.
return ofrQ;
// Single path AMM offer has to factor in the transfer in rate
// when calculating the upper bound quality and the quality function
// because single path AMM's offer quality is not constant.
if (!rules.enabled(fixAMMRounding))
return ofrQ;
else if (
offerType == OfferType::CLOB ||
(this->ammLiquidity_ && this->ammLiquidity_->multiPath()))
return ofrQ;
auto rate = [&](AccountID const& id) {
if (isXRP(id) || id == this->strandDst_)
return parityRate;
return transferRate(v, id);
};
auto const trIn =
redeems(prevStepDir) ? rate(this->book_.in.account) : parityRate;
// AMM doesn't pay the transfer fee on the out amount
auto const trOut = parityRate;
Quality const q1{getRate(STAmount(trOut.value), STAmount(trIn.value))};
return composed_quality(q1, ofrQ);
}
std::string
@@ -563,7 +593,12 @@ BookStep<TIn, TOut, TDerived>::qualityUpperBound(
: WaiveTransferFee::No;
Quality const q = static_cast<TDerived const*>(this)->adjustQualityWithFees(
v, std::get<Quality>(*res), prevStepDir, waiveFee);
v,
std::get<Quality>(*res),
prevStepDir,
waiveFee,
std::get<OfferType>(*res),
v.rules());
return {q, dir};
}
@@ -585,7 +620,12 @@ BookStep<TIn, TOut, TDerived>::getQualityFunc(
auto static const qOne = Quality{STAmount::uRateOne};
auto const q =
static_cast<TDerived const*>(this)->adjustQualityWithFees(
v, qOne, prevStepDir, WaiveTransferFee::Yes);
v,
qOne,
prevStepDir,
WaiveTransferFee::Yes,
OfferType::AMM,
v.rules());
if (q == qOne)
return {res, dir};
QualityFunction qf{q, QualityFunction::CLOBLikeTag{}};
@@ -595,7 +635,12 @@ BookStep<TIn, TOut, TDerived>::getQualityFunc(
// CLOB
Quality const q = static_cast<TDerived const*>(this)->adjustQualityWithFees(
v, *(res->quality()), prevStepDir, WaiveTransferFee::No);
v,
*(res->quality()),
prevStepDir,
WaiveTransferFee::No,
OfferType::CLOB,
v.rules());
return {QualityFunction{q, QualityFunction::CLOBLikeTag{}}, dir};
}