From 12524bea355e924fa70d882c889015a930143392 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Mon, 12 May 2025 20:03:53 +0100 Subject: [PATCH] [WIP] Test single payments, does not pass tests, may not build --- src/test/app/Loan_test.cpp | 448 +++++++++++++------ src/xrpld/app/misc/LendingHelpers.h | 112 +++-- src/xrpld/app/misc/detail/LendingHelpers.cpp | 48 -- src/xrpld/app/tx/detail/LoanPay.cpp | 12 +- src/xrpld/app/tx/detail/LoanSet.cpp | 2 +- 5 files changed, 412 insertions(+), 210 deletions(-) diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 88af1d8b30..df8cec82ac 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -1039,7 +1039,8 @@ class Loan_test : public beast::unit_test::suite return state; }; - auto defaultBeforeStartDate = [&](std::uint32_t baseFlag) { + auto defaultBeforeStartDate = [&](std::uint32_t baseFlag, + bool impair = true) { return [&, baseFlag]( Keylet const& loanKeylet, VerifyLoanStatus const& verifyLoanStatus) { @@ -1051,17 +1052,20 @@ class Loan_test : public beast::unit_test::suite auto state = currentState(loanKeylet, verifyLoanStatus); BEAST_EXPECT(state.flags == baseFlag); - // Impair the loan - env(manage(lender, loanKeylet.key, tfLoanImpair)); + if (impair) + { + // Impair the loan + env(manage(lender, loanKeylet.key, tfLoanImpair)); - state.flags |= tfLoanImpair; - state.nextPaymentDate = - env.now().time_since_epoch().count(); - verifyLoanStatus(state); + state.flags |= tfLoanImpair; + state.nextPaymentDate = + env.now().time_since_epoch().count(); + verifyLoanStatus(state); - // Once the loan is impaired, it can't be impaired again - env(manage(lender, loanKeylet.key, tfLoanImpair), - ter(tecNO_PERMISSION)); + // Once the loan is impaired, it can't be impaired again + env(manage(lender, loanKeylet.key, tfLoanImpair), + ter(tecNO_PERMISSION)); + } auto const nextDueDate = tp{d{state.nextPaymentDate}}; @@ -1074,9 +1078,12 @@ class Loan_test : public beast::unit_test::suite // defaulted env.close(nextDueDate + 60s); - // Impaired loans can't be drawn against - env(draw(borrower, loanKeylet.key, broker.asset(100)), - ter(tecNO_PERMISSION)); + if (impair) + { + // Impaired loans can't be drawn against + env(draw(borrower, loanKeylet.key, broker.asset(100)), + ter(tecNO_PERMISSION)); + } // Default the loan env(manage(lender, loanKeylet.key, tfLoanDefault)); @@ -1099,11 +1106,154 @@ class Loan_test : public beast::unit_test::suite }; }; + auto immediatePayoff = [&](std::uint32_t baseFlag) { + return [&, baseFlag]( + Keylet const& loanKeylet, + VerifyLoanStatus const& verifyLoanStatus) { + // toEndOfLife + // + auto state = currentState(loanKeylet, verifyLoanStatus); + BEAST_EXPECT(state.flags == baseFlag); + auto const borrowerStartingBalance = + env.balance(borrower, broker.asset); + + // Try to make a payment before the loan starts + env(pay(borrower, loanKeylet.key, broker.asset(500)), + ter(tecTOO_SOON)); + + // Advance to the start date of the loan + env.close(state.startDate + 5s); + + verifyLoanStatus(state); + + // Need to account for fees if the loan is in XRP + PrettyAmount adjustment = broker.asset(0); + if (broker.asset.raw().native()) + { + adjustment = 2 * env.current()->fees().base; + } + + // Draw the entire available balance + // Need to create the STAmount directly to avoid + // PrettyAsset scaling. + STAmount const drawAmount{ + broker.asset, state.assetsAvailable}; + env(draw(borrower, loanKeylet.key, drawAmount)); + env.close(state.startDate + 20s); + auto const loanAge = (env.now() - state.startDate).count(); + BEAST_EXPECT(loanAge == 30); + + state.assetsAvailable -= drawAmount; + verifyLoanStatus(state); + BEAST_EXPECT( + env.balance(borrower, broker.asset) == + borrowerStartingBalance + drawAmount - adjustment); + + // Send some bogus pay transactions + env(pay(borrower, + keylet::loan(uint256(0)).key, + broker.asset(10)), + ter(temINVALID)); + env(pay(borrower, loanKeylet.key, broker.asset(-100)), + ter(temBAD_AMOUNT)); + env(pay(borrower, broker.brokerID, broker.asset(100)), + ter(tecNO_ENTRY)); + env(pay(evan, loanKeylet.key, broker.asset(500)), + ter(tecNO_PERMISSION)); + + { + auto const otherAsset = + broker.asset.raw() == assets[0].raw() ? assets[1] + : assets[0]; + env(pay(borrower, loanKeylet.key, otherAsset(100)), + ter(tecWRONG_ASSET)); + } + + // Amount doesn't cover a single payment + env(pay(borrower, + loanKeylet.key, + STAmount{broker.asset, 1}), + ter(tecINSUFFICIENT_PAYMENT)); + + // Get the balance after these failed transactions take + // fees + auto const borrowerBalanceBeforePayment = + env.balance(borrower, broker.asset); + + // Full payoff amount will consist of + // 1. principal outstanding (1000) + // 2. accrued interest (at 12%) + // 3. prepayment penalty (closeInterest at 3.6%) + // 4. close payment fee (4) + // Calculate these values without the helper functions + // to verify they're working correctly The numbers in + // the below BEAST_EXPECTs may not hold across assets. + Number const interval = state.paymentInterval; + auto const periodicRate = + interval * Number(12, -2) / (365 * 24 * 60 * 60); + BEAST_EXPECT( + periodicRate == + Number(2283105022831050, -21, Number::unchecked{})); + STAmount const accruedInterest{ + broker.asset, + state.principalOutstanding * periodicRate * loanAge / + interval}; + BEAST_EXPECT( + accruedInterest == + broker.asset(Number(1141552511415525, -19))); + STAmount const prepaymentPenalty{ + broker.asset, + state.principalOutstanding * Number(36, -3)}; + BEAST_EXPECT(prepaymentPenalty == broker.asset(36)); + STAmount const closePaymentFee = broker.asset(4); + auto const payoffAmount = + STAmount{broker.asset, state.principalOutstanding} + + accruedInterest + prepaymentPenalty + closePaymentFee; + BEAST_EXPECT( + payoffAmount == + broker.asset(Number(1040000114155251, -12))); + BEAST_EXPECT(payoffAmount > drawAmount); + // Try to pay a little extra to show that it's _not_ + // taken + auto const transactionAmount = + payoffAmount + broker.asset(10); + BEAST_EXPECT( + transactionAmount == + broker.asset(Number(1050000114155251, -12))); + env(pay(borrower, loanKeylet.key, transactionAmount)); + + env.close(); + + // Need to account for fees if the loan is in XRP + adjustment = broker.asset(0); + if (broker.asset.raw().native()) + { + adjustment = env.current()->fees().base; + } + + state.paymentRemaining = 0; + state.principalOutstanding = 0; + verifyLoanStatus(state); + + BEAST_EXPECT( + env.balance(borrower, broker.asset) == + borrowerBalanceBeforePayment - payoffAmount - + adjustment); + + // Can't impair or default a paid off loan + env(manage(lender, loanKeylet.key, tfLoanImpair), + ter(tecNO_PERMISSION)); + env(manage(lender, loanKeylet.key, tfLoanDefault), + ter(tecNO_PERMISSION)); + }; + }; + // There are a lot of fields that can be set on a loan, but most of // them only affect the "math" when a payment is made. The only one // that really affects behavior is the `tfLoanOverpayment` flag. lifecycle( - "Loan overpayment allowed - Default before start date", + "Loan overpayment allowed - Impair and Default before start " + "date", env, lender, borrower, @@ -1114,7 +1264,8 @@ class Loan_test : public beast::unit_test::suite defaultBeforeStartDate(lsfLoanOverpayment)); lifecycle( - "Loan overpayment prohibited - Default before start date", + "Loan overpayment prohibited - Impair and Default before start " + "date", env, lender, borrower, @@ -1124,6 +1275,32 @@ class Loan_test : public beast::unit_test::suite 0, defaultBeforeStartDate(0)); + lifecycle( + "Loan overpayment allowed - Default without Impair before " + "start " + "date", + env, + lender, + borrower, + evan, + broker, + pseudoAcct, + tfLoanOverpayment, + defaultBeforeStartDate(lsfLoanOverpayment, false)); + + lifecycle( + "Loan overpayment prohibited - Default without Impair before " + "start " + "date", + env, + lender, + borrower, + evan, + broker, + pseudoAcct, + 0, + defaultBeforeStartDate(0, false)); + lifecycle( "Loan overpayment allowed - Draw then default", env, @@ -1224,34 +1401,53 @@ class Loan_test : public beast::unit_test::suite broker, pseudoAcct, 0, + immediatePayoff(0)); + + lifecycle( + "Loan overpayment allowed - Pay off immediately", + env, + lender, + borrower, + evan, + broker, + pseudoAcct, + tfLoanOverpayment, + immediatePayoff(lsfLoanOverpayment)); + + lifecycle( + "Loan overpayment prohibited - Make payments", + env, + lender, + borrower, + evan, + broker, + pseudoAcct, + 0, [&](Keylet const& loanKeylet, VerifyLoanStatus const& verifyLoanStatus) { // toEndOfLife // + // Draw and make multiple payments auto state = currentState(loanKeylet, verifyLoanStatus); BEAST_EXPECT(state.flags == 0); - auto const borrowerStartingBalance = - env.balance(borrower, broker.asset); - - // Try to make a payment before the loan starts - env(pay(borrower, loanKeylet.key, broker.asset(500)), - ter(tecTOO_SOON)); - // Advance to the start date of the loan env.close(state.startDate + 5s); verifyLoanStatus(state); + auto const borrowerStartingBalance = + env.balance(borrower, broker.asset); + // Need to account for fees if the loan is in XRP PrettyAmount adjustment = broker.asset(0); if (broker.asset.raw().native()) { - adjustment = 2 * env.current()->fees().base; + adjustment = env.current()->fees().base; } // Draw the entire available balance - // Need to create the STAmount directly to avoid PrettyAsset - // scaling. + // Need to create the STAmount directly to avoid + // PrettyAsset scaling. STAmount const drawAmount{ broker.asset, state.assetsAvailable}; env(draw(borrower, loanKeylet.key, drawAmount)); @@ -1265,95 +1461,119 @@ class Loan_test : public beast::unit_test::suite env.balance(borrower, broker.asset) == borrowerStartingBalance + drawAmount - adjustment); - // Send some bogus pay transactions - env(pay(borrower, - keylet::loan(uint256(0)).key, - broker.asset(10)), - ter(temINVALID)); - env(pay(borrower, loanKeylet.key, broker.asset(-100)), - ter(temBAD_AMOUNT)); - env(pay(borrower, broker.brokerID, broker.asset(100)), - ter(tecNO_ENTRY)); - env(pay(evan, loanKeylet.key, broker.asset(500)), - ter(tecNO_PERMISSION)); - - { - auto const otherAsset = - broker.asset.raw() == assets[0].raw() ? assets[1] - : assets[0]; - env(pay(borrower, loanKeylet.key, otherAsset(100)), - ter(tecWRONG_ASSET)); - } - - // Amount doesn't cover a single payment - env(pay(borrower, - loanKeylet.key, - STAmount{broker.asset, 1}), - ter(tecINSUFFICIENT_PAYMENT)); - - // Get the balance after these failed transactions take fees - auto const borrowerBalanceBeforePayment = - env.balance(borrower, broker.asset); - - // Full payoff amount will consist of + // Periodic payment amount will consist of // 1. principal outstanding (1000) - // 2. accrued interest (at 12%) - // 3. prepayment penalty (closeInterest at 3.6%) - // 4. close payment fee (4) - // Calculate these values without the helper functions to - // verify they're working correctly - // The numbers in the below BEAST_EXPECTs may not hold - // across assets. + // 2. interest interest rate (at 12%) + // 3. payment interval (600s) + // 4. loan service fee (2) + // Calculate these values without the helper functions + // to verify they're working correctly The numbers in + // the below BEAST_EXPECTs may not hold across assets. Number const interval = state.paymentInterval; auto const periodicRate = interval * Number(12, -2) / (365 * 24 * 60 * 60); BEAST_EXPECT( periodicRate == Number(2283105022831050, -21, Number::unchecked{})); - STAmount const accruedInterest{ - broker.asset, - state.principalOutstanding * periodicRate * loanAge / - interval}; - BEAST_EXPECT( - accruedInterest == - broker.asset(Number(1141552511415525, -19))); - STAmount const prepaymentPenalty{ - broker.asset, - state.principalOutstanding * Number(36, -3)}; - BEAST_EXPECT(prepaymentPenalty == broker.asset(36)); - STAmount const closePaymentFee = broker.asset(4); - auto const payoffAmount = - STAmount{broker.asset, state.principalOutstanding} + - accruedInterest + prepaymentPenalty + closePaymentFee; - BEAST_EXPECT( - payoffAmount == - broker.asset(Number(1040000114155251, -12))); - BEAST_EXPECT(payoffAmount > drawAmount); - // Try to pay a little extra to show that it's _not_ taken - auto const transactionAmount = - payoffAmount + broker.asset(10); - BEAST_EXPECT( - transactionAmount == - broker.asset(Number(1050000114155251, -12))); - env(pay(borrower, loanKeylet.key, transactionAmount)); - env.close(); - - // Need to account for fees if the loan is in XRP - adjustment = broker.asset(0); - if (broker.asset.raw().native()) + while (state.paymentRemaining > 0) { - adjustment = env.current()->fees().base; + testcase << "Payments remaining: " + << state.paymentRemaining; + // Compute the payment based on the number of payments + // remaining + auto const rateFactor = + power(1 + periodicRate, state.paymentRemaining); + STAmount const periodicPayment{ + broker.asset, + state.principalOutstanding * periodicRate * + rateFactor / (rateFactor - 1)}; + // Only check the first payment since the rounding may + // drift as payments are made + BEAST_EXPECT( + state.paymentRemaining < 12 || + periodicPayment == + broker.asset(Number(8333457001162141, -14))); + // Include the service fee + STAmount const totalDue{ + broker.asset, + periodicPayment + broker.asset(2).value()}; + // Only check the first payment since the rounding may + // drift as payments are made + BEAST_EXPECT( + state.paymentRemaining < 12 || + totalDue == + broker.asset(Number(8533457001162141, -14))); + + // Try to pay a little extra to show that it's _not_ + // taken + STAmount const transactionAmount = + STAmount{broker.asset, totalDue} + broker.asset(10); + // Only check the first payment since the rounding may + // drift as payments are made + BEAST_EXPECT( + state.paymentRemaining < 12 || + transactionAmount == + broker.asset(Number(9533457001162141, -14))); + + auto const totalDueAmount = + STAmount{broker.asset, totalDue}; + + // Compute the expected principal amount + STAmount const interest{ + broker.asset, + state.principalOutstanding * periodicRate}; + BEAST_EXPECT( + state.paymentRemaining < 12 || + interest == + broker.asset(Number(2283105022831050, -18))); + BEAST_EXPECT(interest >= 0); + + auto const principal = roundToAsset( + broker.asset, periodicPayment - interest); + BEAST_EXPECT( + state.paymentRemaining < 12 || + principal == + broker.asset(Number(8333228700000000, -14))); + BEAST_EXPECT( + principal > 0 && + principal <= state.principalOutstanding); + BEAST_EXPECT( + state.paymentRemaining > 1 || + principal == state.principalOutstanding); + + auto const borrowerBalanceBeforePayment = + env.balance(borrower, broker.asset); + + // Make the payment + env(pay(borrower, loanKeylet.key, transactionAmount)); + + env.close(); + + // Need to account for fees if the loan is in XRP + adjustment = broker.asset(0); + if (broker.asset.raw().native()) + { + adjustment = env.current()->fees().base; + } + + // Check the result + BEAST_EXPECT( + env.balance(borrower, broker.asset) == + borrowerBalanceBeforePayment - totalDueAmount - + adjustment); + + --state.paymentRemaining; + state.previousPaymentDate = state.nextPaymentDate; + state.nextPaymentDate += state.paymentInterval; + state.principalOutstanding -= principal; + + verifyLoanStatus(state); } - state.paymentRemaining = 0; - state.principalOutstanding = 0; - verifyLoanStatus(state); - - BEAST_EXPECT( - env.balance(borrower, broker.asset) == - borrowerBalanceBeforePayment - payoffAmount - - adjustment); + // Loan is paid off + BEAST_EXPECT(state.paymentRemaining == 0); + BEAST_EXPECT(state.principalOutstanding == 0); // Can't impair or default a paid off loan env(manage(lender, loanKeylet.key, tfLoanImpair), @@ -1362,28 +1582,6 @@ class Loan_test : public beast::unit_test::suite ter(tecNO_PERMISSION)); }); -#if 0 - lifecycle( - "Loan overpayment prohibited - Pay off", - env, - lender, - borrower, - evan, - broker, - pseudoAcct, - 0, - [&](Keylet const& loanKeylet, - VerifyLoanStatus const& verifyLoanStatus) { - // toEndOfLife - // - // TODO: Draw and make some payments - - // Make payments down to 0 - - // TODO: Try to impair a paid off loan - }); -#endif - if (auto brokerSle = env.le(keylet::loanbroker(broker.brokerID)); BEAST_EXPECT(brokerSle)) { diff --git a/src/xrpld/app/misc/LendingHelpers.h b/src/xrpld/app/misc/LendingHelpers.h index 861bc603b9..77bdf55135 100644 --- a/src/xrpld/app/misc/LendingHelpers.h +++ b/src/xrpld/app/misc/LendingHelpers.h @@ -33,6 +33,8 @@ #include #include +#include + namespace ripple { struct PreflightContext; @@ -69,29 +71,62 @@ loanPeriodicPayment( std::uint32_t paymentInterval, std::uint32_t paymentsRemaining); +template Number loanTotalValueOutstanding( + A asset, Number periodicPayment, - std::uint32_t paymentsRemaining); + std::uint32_t paymentsRemaining) +{ + return roundToAsset( + asset, periodicPayment * paymentsRemaining, Number::upward); +} +template Number loanTotalValueOutstanding( + A asset, Number principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, - std::uint32_t paymentsRemaining); + std::uint32_t paymentsRemaining) +{ + return loanTotalValueOutstanding( + asset, + loanPeriodicPayment( + principalOutstanding, + interestRate, + paymentInterval, + paymentsRemaining), + paymentsRemaining); +} -Number +inline Number loanTotalInterestOutstanding( Number principalOutstanding, - Number totalValueOutstanding); + Number totalValueOutstanding) +{ + return totalValueOutstanding - principalOutstanding; +} +template Number loanTotalInterestOutstanding( + A asset, Number principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, - std::uint32_t paymentsRemaining); + std::uint32_t paymentsRemaining) +{ + return loanTotalInterestOutstanding( + principalOutstanding, + loanTotalValueOutstanding( + asset, + principalOutstanding, + interestRate, + paymentInterval, + paymentsRemaining)); +} Number loanLatePaymentInterest( @@ -147,6 +182,17 @@ minusManagementFee(Number value, TenthBips32 managementFeeRate) } // namespace detail +template +Number +valueMinusManagementFee( + A const& asset, + Number value, + TenthBips32 managementFeeRate) +{ + return roundToAsset( + asset, detail::minusManagementFee(value, managementFeeRate)); +} + template Number loanInterestOutstandingMinusFee( @@ -157,15 +203,15 @@ loanInterestOutstandingMinusFee( std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate) { - return roundToAsset( + return valueMinusManagementFee( asset, - detail::minusManagementFee( - detail::loanTotalInterestOutstanding( - principalOutstanding, - interestRate, - paymentInterval, - paymentsRemaining), - managementFeeRate)); + detail::loanTotalInterestOutstanding( + asset, + principalOutstanding, + interestRate, + paymentInterval, + paymentsRemaining), + managementFeeRate); } template @@ -270,19 +316,23 @@ loanComputePaymentParts( auto const periodic = detail::computePeriodicPaymentParts( asset, principalOutstandingField, periodicPaymentAmount, periodicRate); - Number const totalValueOutstanding = roundToAsset( - asset, - detail::loanTotalValueOutstanding( - periodicPaymentAmount, paymentRemainingField)); + Number const totalValueOutstanding = detail::loanTotalValueOutstanding( + asset, periodicPaymentAmount, paymentRemainingField); XRPL_ASSERT( totalValueOutstanding > 0, "ripple::loanComputePaymentParts : valid total value"); Number const totalInterestOutstanding = detail::loanTotalInterestOutstanding( principalOutstandingField, totalValueOutstanding); - XRPL_ASSERT( + XRPL_ASSERT_PARTS( totalInterestOutstanding >= 0, - "ripple::loanComputePaymentParts : valid total interest"); + "ripple::loanComputePaymentParts", + "valid total interest"); + XRPL_ASSERT_PARTS( + totalValueOutstanding - totalInterestOutstanding == + principalOutstandingField, + "ripple::loanComputePaymentParts", + "valid principal computation"); view.update(loan); @@ -388,8 +438,13 @@ loanComputePaymentParts( // periodic one, with possible overpayments std::optional mg(Number::downward); - std::int64_t const fullPeriodicPayments{ - amount / roundToAsset(asset, periodicPaymentAmount, Number::upward)}; + std::int64_t const fullPeriodicPayments = [&]() { + std::int64_t const full{ + amount / + roundToAsset( + asset, (periodicPaymentAmount + serviceFee), Number::upward)}; + return full < paymentRemainingField ? full : paymentRemainingField; + }(); mg.reset(); // Temporary asserts XRPL_ASSERT( @@ -424,10 +479,10 @@ loanComputePaymentParts( periodicPaymentAmount, periodicRate); XRPL_ASSERT( - future->interest < periodic.interest, + future->interest <= periodic.interest, "ripple::loanComputePaymentParts : decreasing interest"); XRPL_ASSERT( - future->principal > periodic.principal, + future->principal >= periodic.principal, "ripple::loanComputePaymentParts : increasing principal"); totalPrincipalPaid += future->principal; @@ -465,13 +520,12 @@ loanComputePaymentParts( principalOutstandingField -= remainder; - Number const newInterest = roundToAsset( + Number const newInterest = detail::loanTotalInterestOutstanding( asset, - detail::loanTotalInterestOutstanding( - principalOutstandingField, - interestRate, - paymentInterval, - paymentRemainingField)); + principalOutstandingField, + interestRate, + paymentInterval, + paymentRemainingField); loanValueChange = (newInterest - totalInterestOutstanding) + interestPortion; diff --git a/src/xrpld/app/misc/detail/LendingHelpers.cpp b/src/xrpld/app/misc/detail/LendingHelpers.cpp index fbfb76e0e8..06c1e750d3 100644 --- a/src/xrpld/app/misc/detail/LendingHelpers.cpp +++ b/src/xrpld/app/misc/detail/LendingHelpers.cpp @@ -70,54 +70,6 @@ loanPeriodicPayment( principalOutstanding, periodicRate, paymentsRemaining); } -Number -loanTotalValueOutstanding( - Number periodicPayment, - std::uint32_t paymentsRemaining) -{ - return periodicPayment * paymentsRemaining; -} - -Number -loanTotalValueOutstanding( - Number principalOutstanding, - TenthBips32 interestRate, - std::uint32_t paymentInterval, - std::uint32_t paymentsRemaining) -{ - return loanTotalValueOutstanding( - loanPeriodicPayment( - principalOutstanding, - interestRate, - paymentInterval, - paymentsRemaining), - paymentsRemaining); -} - -Number -loanTotalInterestOutstanding( - Number principalOutstanding, - Number totalValueOutstanding) -{ - return totalValueOutstanding - principalOutstanding; -} - -Number -loanTotalInterestOutstanding( - Number principalOutstanding, - TenthBips32 interestRate, - std::uint32_t paymentInterval, - std::uint32_t paymentsRemaining) -{ - return loanTotalInterestOutstanding( - principalOutstanding, - loanTotalValueOutstanding( - principalOutstanding, - interestRate, - paymentInterval, - paymentsRemaining)); -} - Number loanLatePaymentInterest( Number principalOutstanding, diff --git a/src/xrpld/app/tx/detail/LoanPay.cpp b/src/xrpld/app/tx/detail/LoanPay.cpp index c20b9acca3..e1519344f3 100644 --- a/src/xrpld/app/tx/detail/LoanPay.cpp +++ b/src/xrpld/app/tx/detail/LoanPay.cpp @@ -212,7 +212,7 @@ LoanPay::doApply() auto const totalPaidToVault = paymentParts->principalPaid + paymentParts->interestPaid - managementFee; - auto const totalFee = paymentParts->feePaid + managementFee; + auto const totalPaidToBroker = paymentParts->feePaid + managementFee; // If there is not enough first-loss capital auto coverAvailableField = brokerSle->at(sfCoverAvailable); @@ -226,15 +226,13 @@ LoanPay::doApply() if (!sufficientCover) { // Add the fee to to First Loss Cover Pool - coverAvailableField += totalFee; + coverAvailableField += totalPaidToBroker; } // Decrease LoanBroker Debt by the amount paid, add the Loan value change, // and subtract the change in the management fee - auto const vaultValueChange = paymentParts->valueChange - - roundToAsset(asset, - tenthBipsOfValue( - paymentParts->valueChange, managementFeeRate)); + auto const vaultValueChange = valueMinusManagementFee( + asset, paymentParts->valueChange, managementFeeRate); debtTotalField += vaultValueChange - totalPaidToVault; //------------------------------------------------------ @@ -246,7 +244,7 @@ LoanPay::doApply() // Move funds STAmount const paidToVault(asset, totalPaidToVault); - STAmount const paidToBroker(asset, totalFee); + STAmount const paidToBroker(asset, totalPaidToBroker); XRPL_ASSERT_PARTS( paidToVault + paidToBroker <= amount, "ripple::LoanPay::doApply", diff --git a/src/xrpld/app/tx/detail/LoanSet.cpp b/src/xrpld/app/tx/detail/LoanSet.cpp index 6f6f0ed6bf..5385450474 100644 --- a/src/xrpld/app/tx/detail/LoanSet.cpp +++ b/src/xrpld/app/tx/detail/LoanSet.cpp @@ -246,7 +246,7 @@ LoanSet::preclaim(PreclaimContext const& ctx) if (auto const originationFee = tx[~sfLoanOriginationFee]) { // Check that the lender will not make an unfair profit on the lending - // fee if the loan defaults. (Not yet in spec. May not be included.) + // fee if the loan defaults. (Not yet in spec. May need to be removed.) TenthBips32 const coverRateLiquidation{ brokerSle->at(sfCoverRateLiquidation)};