mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-26 14:05:51 +00:00
Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-transitive-amendments
This commit is contained in:
@@ -1357,28 +1357,6 @@ protected:
|
|||||||
auto const borrowerStartbalance = env.balance(borrower, broker.asset);
|
auto const borrowerStartbalance = env.balance(borrower, broker.asset);
|
||||||
|
|
||||||
auto createJtx = loanParams(env, broker);
|
auto createJtx = loanParams(env, broker);
|
||||||
#if LOANCOMPLETE
|
|
||||||
{
|
|
||||||
auto createJtxOld = env.jt(
|
|
||||||
set(borrower, broker.brokerID, principalRequest, flags),
|
|
||||||
sig(sfCounterpartySignature, lender),
|
|
||||||
loanOriginationFee(originationFee),
|
|
||||||
loanServiceFee(serviceFee),
|
|
||||||
latePaymentFee(lateFee),
|
|
||||||
closePaymentFee(closeFee),
|
|
||||||
overpaymentFee(overFee),
|
|
||||||
interestRate(interest),
|
|
||||||
lateInterestRate(lateInterest),
|
|
||||||
closeInterestRate(closeInterest),
|
|
||||||
overpaymentInterestRate(overpaymentInterest),
|
|
||||||
paymentTotal(total),
|
|
||||||
paymentInterval(interval),
|
|
||||||
gracePeriod(grace),
|
|
||||||
fee(loanSetFee));
|
|
||||||
BEAST_EXPECT(
|
|
||||||
createJtx.stx->getJson(0) == createJtxOld.stx->getJson(0));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Successfully create a Loan
|
// Successfully create a Loan
|
||||||
env(createJtx);
|
env(createJtx);
|
||||||
|
|
||||||
@@ -7041,13 +7019,6 @@ class LoanArbitrary_test : public LoanBatch_test
|
|||||||
{
|
{
|
||||||
using namespace jtx;
|
using namespace jtx;
|
||||||
|
|
||||||
#if LOANCOMPLETE
|
|
||||||
BEAST_EXPECT(
|
|
||||||
std::numeric_limits<std::int64_t>::max() > INITIAL_XRP.drops());
|
|
||||||
BEAST_EXPECT(Number::maxMantissa > INITIAL_XRP.drops());
|
|
||||||
Number initalXrp{INITIAL_XRP};
|
|
||||||
BEAST_EXPECT(initalXrp.exponent() <= 0);
|
|
||||||
#endif
|
|
||||||
BrokerParameters const brokerParams{
|
BrokerParameters const brokerParams{
|
||||||
.vaultDeposit = 10000,
|
.vaultDeposit = 10000,
|
||||||
.debtMax = 0,
|
.debtMax = 0,
|
||||||
|
|||||||
@@ -4555,7 +4555,7 @@ class Vault_test : public beast::unit_test::suite
|
|||||||
BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
|
BEAST_EXPECT(checkString(vault, sfAssetsAvailable, "50"));
|
||||||
BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
|
BEAST_EXPECT(checkString(vault, sfAssetsMaximum, "1000"));
|
||||||
BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
|
BEAST_EXPECT(checkString(vault, sfAssetsTotal, "50"));
|
||||||
BEAST_EXPECT(checkString(vault, sfLossUnrealized, "0"));
|
BEAST_EXPECT(!vault.isMember(sfLossUnrealized.getJsonName()));
|
||||||
|
|
||||||
auto const strShareID = strHex(sle->at(sfShareMPTID));
|
auto const strShareID = strHex(sle->at(sfShareMPTID));
|
||||||
BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
|
BEAST_EXPECT(checkString(vault, sfShareMPTID, strShareID));
|
||||||
|
|||||||
@@ -164,12 +164,6 @@ enum class PaymentSpecialCase { none, final, extra };
|
|||||||
/// single loan payment
|
/// single loan payment
|
||||||
struct PaymentComponents
|
struct PaymentComponents
|
||||||
{
|
{
|
||||||
#if LOANCOMPLETE
|
|
||||||
// raw values are unrounded, and are based on pure math
|
|
||||||
Number rawInterest;
|
|
||||||
Number rawPrincipal;
|
|
||||||
Number rawManagementFee;
|
|
||||||
#endif
|
|
||||||
// tracked values are rounded to the asset and loan scale, and correspond to
|
// tracked values are rounded to the asset and loan scale, and correspond to
|
||||||
// fields in the Loan ledger object.
|
// fields in the Loan ledger object.
|
||||||
// trackedValueDelta modifies sfTotalValueOutstanding.
|
// trackedValueDelta modifies sfTotalValueOutstanding.
|
||||||
|
|||||||
@@ -219,244 +219,6 @@ loanAccruedInterest(
|
|||||||
paymentInterval;
|
paymentInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LOANCOMPLETE
|
|
||||||
Number
|
|
||||||
computeRoundedPrincipalComponent(
|
|
||||||
Asset const& asset,
|
|
||||||
Number const& principalOutstanding,
|
|
||||||
Number const& rawPrincipalOutstanding,
|
|
||||||
Number const& rawPrincipal,
|
|
||||||
Number const& roundedPeriodicPayment,
|
|
||||||
std::int32_t scale)
|
|
||||||
{
|
|
||||||
// Adjust the principal payment by the rounding error between the true
|
|
||||||
// and rounded principal outstanding
|
|
||||||
auto const diff = roundToAsset(
|
|
||||||
asset,
|
|
||||||
principalOutstanding - rawPrincipalOutstanding,
|
|
||||||
scale,
|
|
||||||
asset.integral() ? Number::downward : Number::towards_zero);
|
|
||||||
|
|
||||||
// If the rounded principal outstanding is greater than the true
|
|
||||||
// principal outstanding, we need to pay more principal to reduce
|
|
||||||
// the rounded principal outstanding
|
|
||||||
//
|
|
||||||
// If the rounded principal outstanding is less than the true
|
|
||||||
// principal outstanding, we need to pay less principal to allow the
|
|
||||||
// rounded principal outstanding to catch up
|
|
||||||
|
|
||||||
auto const p =
|
|
||||||
roundToAsset(asset, rawPrincipal + diff, scale, Number::downward);
|
|
||||||
|
|
||||||
// For particular loans, it's entirely possible for many of the first
|
|
||||||
// rounded payments to be all interest.
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
p >= 0,
|
|
||||||
"rippled::detail::computeRoundedPrincipalComponent",
|
|
||||||
"principal part not negative");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
p <= principalOutstanding,
|
|
||||||
"rippled::detail::computeRoundedPrincipalComponent",
|
|
||||||
"principal part not larger than outstanding principal");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
!asset.integral() || abs(p - rawPrincipal) <= 1,
|
|
||||||
"rippled::detail::computeRoundedPrincipalComponent",
|
|
||||||
"principal part not larger than outstanding principal");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
p <= roundedPeriodicPayment,
|
|
||||||
"rippled::detail::computeRoundedPrincipalComponent",
|
|
||||||
"principal part not larger than total payment");
|
|
||||||
|
|
||||||
// The asserts will be skipped in release builds, so check here to make
|
|
||||||
// sure nothing goes negative
|
|
||||||
if (p > roundedPeriodicPayment || p > principalOutstanding)
|
|
||||||
return std::min(roundedPeriodicPayment, principalOutstanding);
|
|
||||||
else if (p < 0)
|
|
||||||
return Number{};
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the interest component of a payment WITHOUT accounting for
|
|
||||||
** management fees
|
|
||||||
*
|
|
||||||
* In other words, it returns the combined value of the interest part that will
|
|
||||||
* go to the Vault and the management fee that will go to the Broker.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Number
|
|
||||||
computeRoundedInterestComponent(
|
|
||||||
Asset const& asset,
|
|
||||||
Number const& interestOutstanding,
|
|
||||||
Number const& roundedPrincipal,
|
|
||||||
Number const& rawInterestOutstanding,
|
|
||||||
Number const& roundedPeriodicPayment,
|
|
||||||
std::int32_t scale)
|
|
||||||
{
|
|
||||||
// Start by just using the non-principal part of the payment for interest
|
|
||||||
Number roundedInterest = roundedPeriodicPayment - roundedPrincipal;
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
isRounded(asset, roundedInterest, scale),
|
|
||||||
"ripple::detail::computeRoundedInterestComponent",
|
|
||||||
"initial interest computation is rounded");
|
|
||||||
|
|
||||||
{
|
|
||||||
// Adjust the interest payment by the rounding error between the true
|
|
||||||
// and rounded interest outstanding
|
|
||||||
//
|
|
||||||
// If the rounded interest outstanding is greater than the true interest
|
|
||||||
// outstanding, we need to pay more interest to reduce the rounded
|
|
||||||
// interest outstanding
|
|
||||||
//
|
|
||||||
// If the rounded interest outstanding is less than the true interest
|
|
||||||
// outstanding, we need to pay less interest to allow the rounded
|
|
||||||
// interest outstanding to catch up
|
|
||||||
auto const diff = roundToAsset(
|
|
||||||
asset,
|
|
||||||
interestOutstanding - rawInterestOutstanding,
|
|
||||||
scale,
|
|
||||||
asset.integral() ? Number::downward : Number::towards_zero);
|
|
||||||
roundedInterest += diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
// However, we cannot allow negative interest payments, therefore we need to
|
|
||||||
// cap the interest payment at 0.
|
|
||||||
//
|
|
||||||
// Ensure interest payment is non-negative and does not exceed the remaining
|
|
||||||
// payment after principal
|
|
||||||
return std::max(Number{}, roundedInterest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Interest and Fee components need to be calculated together, because they
|
|
||||||
// can affect each other during computation in both directions.
|
|
||||||
|
|
||||||
std::pair<Number, Number>
|
|
||||||
computeRoundedInterestAndFeeComponents(
|
|
||||||
Asset const& asset,
|
|
||||||
Number const& interestOutstanding,
|
|
||||||
Number const& managementFeeOutstanding,
|
|
||||||
Number const& roundedPrincipal,
|
|
||||||
Number const& rawInterestOutstanding,
|
|
||||||
Number const& rawManagementFeeOutstanding,
|
|
||||||
Number const& roundedPeriodicPayment,
|
|
||||||
Number const& periodicRate,
|
|
||||||
TenthBips16 managementFeeRate,
|
|
||||||
std::int32_t scale)
|
|
||||||
{
|
|
||||||
// Zero interest means ZERO interest
|
|
||||||
if (periodicRate == 0)
|
|
||||||
return std::make_pair(Number{}, Number{});
|
|
||||||
|
|
||||||
Number roundedInterest = computeRoundedInterestComponent(
|
|
||||||
asset,
|
|
||||||
interestOutstanding,
|
|
||||||
roundedPrincipal,
|
|
||||||
rawInterestOutstanding,
|
|
||||||
roundedPeriodicPayment,
|
|
||||||
scale);
|
|
||||||
|
|
||||||
Number roundedFee =
|
|
||||||
computeFee(asset, roundedInterest, managementFeeRate, scale);
|
|
||||||
|
|
||||||
{
|
|
||||||
// Adjust the interest fee by the rounding error between the true and
|
|
||||||
// rounded interest fee outstanding
|
|
||||||
auto const diff = roundToAsset(
|
|
||||||
asset,
|
|
||||||
managementFeeOutstanding - rawManagementFeeOutstanding,
|
|
||||||
scale,
|
|
||||||
asset.integral() ? Number::downward : Number::towards_zero);
|
|
||||||
|
|
||||||
roundedFee += diff;
|
|
||||||
|
|
||||||
// But again, we cannot allow negative interest fees, therefore we need
|
|
||||||
// to cap the interest fee at 0
|
|
||||||
roundedFee = std::max(Number{}, roundedFee);
|
|
||||||
|
|
||||||
// Finally, the rounded interest fee cannot exceed the outstanding
|
|
||||||
// interest fee
|
|
||||||
roundedFee = std::min(roundedFee, managementFeeOutstanding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the fee portion from the interest payment, as the fee is paid
|
|
||||||
// separately
|
|
||||||
|
|
||||||
// Ensure that the interest payment does not become negative, this may
|
|
||||||
// happen with high interest fees
|
|
||||||
roundedInterest = std::max(Number{}, roundedInterest - roundedFee);
|
|
||||||
|
|
||||||
// Finally, ensure that the interest payment does not exceed the
|
|
||||||
// interest outstanding
|
|
||||||
roundedInterest = std::min(interestOutstanding, roundedInterest);
|
|
||||||
|
|
||||||
// Make sure the parts don't add up to too much
|
|
||||||
auto const initialTotal = roundedPrincipal + roundedInterest + roundedFee;
|
|
||||||
Number excess = roundedPeriodicPayment - initialTotal;
|
|
||||||
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
isRounded(asset, excess, scale),
|
|
||||||
"ripple::detail::computeRoundedInterestAndFeeComponents",
|
|
||||||
"excess is rounded");
|
|
||||||
|
|
||||||
#if LOANCOMPLETE
|
|
||||||
if (excess != beast::zero)
|
|
||||||
std::cout << "computeRoundedInterestAndFeeComponents excess is "
|
|
||||||
<< excess << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (excess < beast::zero)
|
|
||||||
{
|
|
||||||
// Take as much of the excess as we can out of the interest
|
|
||||||
#if LOANCOMPLETE
|
|
||||||
std::cout << "\tApplying excess to interest\n";
|
|
||||||
#endif
|
|
||||||
auto part = std::min(roundedInterest, -excess);
|
|
||||||
roundedInterest -= part;
|
|
||||||
excess += part;
|
|
||||||
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
excess <= beast::zero,
|
|
||||||
"ripple::detail::computeRoundedInterestAndFeeComponents",
|
|
||||||
"excess not positive (interest)");
|
|
||||||
}
|
|
||||||
if (excess < beast::zero)
|
|
||||||
{
|
|
||||||
// If there's any left, take as much of the excess as we can out of the
|
|
||||||
// fee
|
|
||||||
#if LOANCOMPLETE
|
|
||||||
std::cout << "\tApplying excess to fee\n";
|
|
||||||
#endif
|
|
||||||
auto part = std::min(roundedFee, -excess);
|
|
||||||
roundedFee -= part;
|
|
||||||
excess += part;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The excess should never be negative, which indicates that the parts are
|
|
||||||
// trying to take more than the whole payment. The excess can be positive,
|
|
||||||
// which indicates that we're not going to take the whole payment amount,
|
|
||||||
// but if so, it must be small.
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
excess == beast::zero ||
|
|
||||||
(excess > beast::zero &&
|
|
||||||
((asset.integral() && excess < 3) ||
|
|
||||||
(roundedPeriodicPayment.exponent() - excess.exponent() > 6))),
|
|
||||||
"ripple::detail::computeRoundedInterestAndFeeComponents",
|
|
||||||
"excess is extremely small (fee)");
|
|
||||||
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
roundedFee >= beast::zero,
|
|
||||||
"ripple::detail::computeRoundedInterestAndFeeComponents",
|
|
||||||
"non-negative fee");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
roundedInterest >= beast::zero,
|
|
||||||
"ripple::detail::computeRoundedInterestAndFeeComponents",
|
|
||||||
"non-negative interest");
|
|
||||||
|
|
||||||
return std::make_pair(
|
|
||||||
std::max(Number{}, roundedInterest), std::max(Number{}, roundedFee));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct PaymentComponentsPlus : public PaymentComponents
|
struct PaymentComponentsPlus : public PaymentComponents
|
||||||
{
|
{
|
||||||
// untrackedManagementFeeDelta includes any fees that go directly to the
|
// untrackedManagementFeeDelta includes any fees that go directly to the
|
||||||
@@ -846,10 +608,6 @@ computeLatePayment(
|
|||||||
view.parentCloseTime(),
|
view.parentCloseTime(),
|
||||||
nextDueDate);
|
nextDueDate);
|
||||||
|
|
||||||
#if LOANCOMPLETE
|
|
||||||
auto const [rawLateInterest, rawLateManagementFee] =
|
|
||||||
computeInterestAndFeeParts(latePaymentInterest, managementFeeRate);
|
|
||||||
#endif
|
|
||||||
auto const [roundedLateInterest, roundedLateManagementFee] = [&]() {
|
auto const [roundedLateInterest, roundedLateManagementFee] = [&]() {
|
||||||
auto const interest =
|
auto const interest =
|
||||||
roundToAsset(asset, latePaymentInterest, loanScale);
|
roundToAsset(asset, latePaymentInterest, loanScale);
|
||||||
@@ -868,9 +626,6 @@ computeLatePayment(
|
|||||||
// This preserves all the other fields without having to enumerate them.
|
// This preserves all the other fields without having to enumerate them.
|
||||||
PaymentComponentsPlus const late = [&]() {
|
PaymentComponentsPlus const late = [&]() {
|
||||||
auto inner = periodic;
|
auto inner = periodic;
|
||||||
#if LOANCOMPLETE
|
|
||||||
inner.rawInterest += rawLateInterest;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return PaymentComponentsPlus{
|
return PaymentComponentsPlus{
|
||||||
inner,
|
inner,
|
||||||
@@ -943,11 +698,6 @@ computeFullPayment(
|
|||||||
startDate,
|
startDate,
|
||||||
closeInterestRate);
|
closeInterestRate);
|
||||||
|
|
||||||
#if LOANCOMPLETE
|
|
||||||
auto const [rawFullInterest, rawFullManagementFee] =
|
|
||||||
computeInterestAndFeeParts(fullPaymentInterest, managementFeeRate);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto const [roundedFullInterest, roundedFullManagementFee] = [&]() {
|
auto const [roundedFullInterest, roundedFullManagementFee] = [&]() {
|
||||||
auto const interest =
|
auto const interest =
|
||||||
roundToAsset(asset, fullPaymentInterest, loanScale);
|
roundToAsset(asset, fullPaymentInterest, loanScale);
|
||||||
@@ -960,11 +710,6 @@ computeFullPayment(
|
|||||||
|
|
||||||
PaymentComponentsPlus const full{
|
PaymentComponentsPlus const full{
|
||||||
PaymentComponents{
|
PaymentComponents{
|
||||||
#if LOANCOMPLETE
|
|
||||||
.rawInterest = rawFullInterest,
|
|
||||||
.rawPrincipal = rawPrincipalOutstanding,
|
|
||||||
.rawManagementFee = rawFullManagementFee,
|
|
||||||
#endif
|
|
||||||
.trackedValueDelta = principalOutstanding +
|
.trackedValueDelta = principalOutstanding +
|
||||||
totalInterestOutstanding + managementFeeOutstanding,
|
totalInterestOutstanding + managementFeeOutstanding,
|
||||||
.trackedPrincipalDelta = principalOutstanding,
|
.trackedPrincipalDelta = principalOutstanding,
|
||||||
@@ -1121,107 +866,12 @@ computePaymentComponents(
|
|||||||
|
|
||||||
// Pay everything off
|
// Pay everything off
|
||||||
return PaymentComponents{
|
return PaymentComponents{
|
||||||
#if LOANCOMPLETE
|
|
||||||
.rawInterest = raw.interestOutstanding,
|
|
||||||
.rawPrincipal = raw.principalOutstanding,
|
|
||||||
.rawManagementFee = raw.managementFeeDue,
|
|
||||||
#endif
|
|
||||||
.trackedValueDelta = totalValueOutstanding,
|
.trackedValueDelta = totalValueOutstanding,
|
||||||
.trackedPrincipalDelta = principalOutstanding,
|
.trackedPrincipalDelta = principalOutstanding,
|
||||||
.trackedManagementFeeDelta = managementFeeOutstanding,
|
.trackedManagementFeeDelta = managementFeeOutstanding,
|
||||||
.specialCase = PaymentSpecialCase::final};
|
.specialCase = PaymentSpecialCase::final};
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LOANCOMPLETE
|
|
||||||
/*
|
|
||||||
* From the spec, once the periodicPayment is computed:
|
|
||||||
*
|
|
||||||
* The principal and interest portions can be derived as follows:
|
|
||||||
* interest = principalOutstanding * periodicRate
|
|
||||||
* principal = periodicPayment - interest
|
|
||||||
*/
|
|
||||||
Number const rawInterest = raw.principalOutstanding * periodicRate;
|
|
||||||
Number const rawPrincipal = periodicPayment - rawInterest;
|
|
||||||
Number const rawFee = tenthBipsOfValue(rawInterest, managementFeeRate);
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
rawInterest >= 0,
|
|
||||||
"ripple::detail::computePaymentComponents",
|
|
||||||
"valid raw interest");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
rawPrincipal >= 0 && rawPrincipal <= raw.principalOutstanding,
|
|
||||||
"ripple::detail::computePaymentComponents",
|
|
||||||
"valid raw principal");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
rawFee >= 0 && rawFee <= raw.managementFeeDue,
|
|
||||||
"ripple::detail::computePaymentComponents",
|
|
||||||
"valid raw fee");
|
|
||||||
|
|
||||||
/*
|
|
||||||
Critical Calculation: Balancing Principal and Interest Outstanding
|
|
||||||
|
|
||||||
This calculation maintains a delicate balance between keeping
|
|
||||||
principal outstanding and interest outstanding as close as possible to
|
|
||||||
reference values. However, we cannot perfectly match the reference
|
|
||||||
values due to rounding issues.
|
|
||||||
|
|
||||||
Key considerations:
|
|
||||||
1. Since the periodic payment is rounded up, we have excess funds
|
|
||||||
that can be used to pay down the loan faster than the reference
|
|
||||||
calculation.
|
|
||||||
|
|
||||||
2. We must ensure that loan repayment is not too fast, otherwise we
|
|
||||||
will end up with negative principal outstanding or negative
|
|
||||||
interest outstanding.
|
|
||||||
|
|
||||||
3. We cannot allow the borrower to repay interest ahead of schedule.
|
|
||||||
If the borrower makes an overpayment, the interest portion could
|
|
||||||
go negative, requiring complex recalculation to refund the borrower by
|
|
||||||
reflecting the overpayment in the principal portion of the loan.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Number const roundedPrincipal = detail::computeRoundedPrincipalComponent(
|
|
||||||
asset,
|
|
||||||
principalOutstanding,
|
|
||||||
raw.principalOutstanding,
|
|
||||||
rawPrincipal,
|
|
||||||
roundedPeriodicPayment,
|
|
||||||
scale);
|
|
||||||
|
|
||||||
auto const [roundedInterest, roundedFee] =
|
|
||||||
detail::computeRoundedInterestAndFeeComponents(
|
|
||||||
asset,
|
|
||||||
totalValueOutstanding - principalOutstanding,
|
|
||||||
managementFeeOutstanding,
|
|
||||||
roundedPrincipal,
|
|
||||||
raw.interestOutstanding,
|
|
||||||
raw.managementFeeDue,
|
|
||||||
roundedPeriodicPayment,
|
|
||||||
periodicRate,
|
|
||||||
managementFeeRate,
|
|
||||||
scale);
|
|
||||||
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
roundedInterest >= 0 && isRounded(asset, roundedInterest, scale),
|
|
||||||
"ripple::detail::computePaymentComponents",
|
|
||||||
"valid rounded interest");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
roundedFee >= 0 && roundedFee <= managementFeeOutstanding &&
|
|
||||||
isRounded(asset, roundedFee, scale),
|
|
||||||
"ripple::detail::computePaymentComponents",
|
|
||||||
"valid rounded fee");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
roundedPrincipal >= 0 && roundedPrincipal <= principalOutstanding &&
|
|
||||||
roundedPrincipal <= roundedPeriodicPayment &&
|
|
||||||
isRounded(asset, roundedPrincipal, scale),
|
|
||||||
"ripple::detail::computePaymentComponents",
|
|
||||||
"valid rounded principal");
|
|
||||||
XRPL_ASSERT_PARTS(
|
|
||||||
roundedPrincipal + roundedInterest + roundedFee <=
|
|
||||||
roundedPeriodicPayment,
|
|
||||||
"ripple::detail::computePaymentComponents",
|
|
||||||
"payment parts fit within payment limit");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The shortage must never be negative, which indicates that the parts are
|
// The shortage must never be negative, which indicates that the parts are
|
||||||
// trying to take more than the whole payment. The excess can be positive,
|
// trying to take more than the whole payment. The excess can be positive,
|
||||||
// which indicates that we're not going to take the whole payment amount,
|
// which indicates that we're not going to take the whole payment amount,
|
||||||
@@ -1284,16 +934,6 @@ computePaymentComponents(
|
|||||||
shortage >= beast::zero,
|
shortage >= beast::zero,
|
||||||
"ripple::detail::computePaymentComponents",
|
"ripple::detail::computePaymentComponents",
|
||||||
"no shortage or excess");
|
"no shortage or excess");
|
||||||
#if LOANCOMPLETE
|
|
||||||
/*
|
|
||||||
// This used to be part of the above assert. It will eventually be removed
|
|
||||||
// if proved accurate
|
|
||||||
||
|
|
||||||
(shortage > beast::zero &&
|
|
||||||
((asset.integral() && shortage < 3) ||
|
|
||||||
(scale - shortage.exponent() > 14)))
|
|
||||||
*/
|
|
||||||
#endif
|
|
||||||
|
|
||||||
XRPL_ASSERT_PARTS(
|
XRPL_ASSERT_PARTS(
|
||||||
deltas.valueDelta() ==
|
deltas.valueDelta() ==
|
||||||
@@ -1326,11 +966,6 @@ computePaymentComponents(
|
|||||||
"payment parts add to payment");
|
"payment parts add to payment");
|
||||||
|
|
||||||
return PaymentComponents{
|
return PaymentComponents{
|
||||||
#if LOANCOMPLETE
|
|
||||||
.rawInterest = rawInterest - rawFee,
|
|
||||||
.rawPrincipal = rawPrincipal,
|
|
||||||
.rawManagementFee = rawFee,
|
|
||||||
#endif
|
|
||||||
// As a final safety check, ensure the value is non-negative, and won't
|
// As a final safety check, ensure the value is non-negative, and won't
|
||||||
// make the corresponding item negative
|
// make the corresponding item negative
|
||||||
.trackedValueDelta = std::clamp(
|
.trackedValueDelta = std::clamp(
|
||||||
@@ -1380,11 +1015,6 @@ computeOverpaymentComponents(
|
|||||||
|
|
||||||
return detail::PaymentComponentsPlus{
|
return detail::PaymentComponentsPlus{
|
||||||
detail::PaymentComponents{
|
detail::PaymentComponents{
|
||||||
#if LOANCOMPLETE
|
|
||||||
.rawInterest = rawOverpaymentInterest,
|
|
||||||
.rawPrincipal = payment - rawOverpaymentInterest,
|
|
||||||
.rawManagementFee = 0,
|
|
||||||
#endif
|
|
||||||
.trackedValueDelta = payment,
|
.trackedValueDelta = payment,
|
||||||
.trackedPrincipalDelta = payment - roundedOverpaymentInterest -
|
.trackedPrincipalDelta = payment - roundedOverpaymentInterest -
|
||||||
roundedOverpaymentManagementFee,
|
roundedOverpaymentManagementFee,
|
||||||
|
|||||||
Reference in New Issue
Block a user