1#include <xrpld/app/misc/LendingHelpers.h>
3#include <xrpld/app/tx/detail/VaultCreate.h>
20 "xrpl::LoanPaymentParts::operator+= : other principal "
24 "xrpl::LoanPaymentParts::operator+= : other interest paid "
28 "xrpl::LoanPaymentParts::operator+= : other fee paid "
90 return power(1 + periodicRate, paymentsRemaining);
100 Number const& periodicRate,
104 if (periodicRate == beast::zero)
105 return Number{1} / paymentsRemaining;
110 return (periodicRate * raisedRate) / (raisedRate - 1);
120 Number const& principalOutstanding,
121 Number const& periodicRate,
124 if (principalOutstanding == 0 || paymentsRemaining == 0)
128 if (periodicRate == beast::zero)
129 return principalOutstanding / paymentsRemaining;
131 return principalOutstanding *
142 Number const& principalOutstanding,
147 if (principalOutstanding == 0 || paymentsRemaining == 0)
153 principalOutstanding, periodicRate, paymentsRemaining);
163 Number const& periodicPayment,
164 Number const& periodicRate,
167 if (periodicRate == 0)
168 return periodicPayment * paymentsRemaining;
170 return periodicPayment /
214 Number const& principalOutstanding,
223 if (now <= nextPaymentDueDate)
227 auto const secondsOverdue = now - nextPaymentDueDate;
231 return principalOutstanding * rate;
241 Number const& principalOutstanding,
242 Number const& periodicRate,
248 if (periodicRate == beast::zero)
251 auto const lastPaymentDate =
std::max(prevPaymentDate, startDate);
256 if (now <= lastPaymentDate)
260 auto const secondsSinceLastPayment = now - lastPaymentDate;
265 return principalOutstanding * periodicRate * secondsSinceLastPayment /
278template <
class NumberProxy,
class UInt32Proxy,
class UInt32OptionalProxy>
282 NumberProxy& totalValueOutstandingProxy,
283 NumberProxy& principalOutstandingProxy,
284 NumberProxy& managementFeeOutstandingProxy,
285 UInt32Proxy& paymentRemainingProxy,
286 UInt32Proxy& prevPaymentDateProxy,
287 UInt32OptionalProxy& nextDueDateProxy,
291 nextDueDateProxy,
"xrpl::detail::doPayment",
"Next due date proxy set");
297 "xrpl::detail::doPayment",
298 "Full principal payment");
301 "xrpl::detail::doPayment",
302 "Full value payment");
305 "xrpl::detail::doPayment",
306 "Full management fee payment");
309 paymentRemainingProxy = 0;
312 prevPaymentDateProxy = *nextDueDateProxy;
316 nextDueDateProxy = 0;
321 principalOutstandingProxy = 0;
322 totalValueOutstandingProxy = 0;
323 managementFeeOutstandingProxy = 0;
330 paymentRemainingProxy -= 1;
332 prevPaymentDateProxy = nextDueDateProxy;
333 nextDueDateProxy += paymentInterval;
337 "xrpl::detail::doPayment",
338 "Partial principal payment");
341 "xrpl::detail::doPayment",
342 "Partial value payment");
347 "xrpl::detail::doPayment",
348 "Valid management fee");
360 static_cast<Number>(principalOutstandingProxy) <=
361 static_cast<Number>(totalValueOutstandingProxy),
362 "xrpl::detail::doPayment",
363 "principal does not exceed total");
368 static_cast<Number>(managementFeeOutstandingProxy) >= beast::zero,
369 "xrpl::detail::doPayment",
370 "fee outstanding stays valid");
409 Number& totalValueOutstanding,
410 Number& principalOutstanding,
411 Number& managementFeeOutstanding,
415 Number const& periodicRate,
424 periodicPayment, periodicRate, paymentRemaining, managementFeeRate);
428 totalValueOutstanding, principalOutstanding, managementFeeOutstanding);
434 auto const errors = rounded - raw;
438 auto const newRawPrincipal =
std::max(
454 JLOG(j.
debug()) <<
"new periodic payment: "
455 << newLoanProperties.periodicPayment
456 <<
", new total value: "
457 << newLoanProperties.totalValueOutstanding
458 <<
", first payment principal: "
459 << newLoanProperties.firstPaymentPrincipal;
463 newLoanProperties.periodicPayment,
469 JLOG(j.
debug()) <<
"new raw value: " << newRaw.valueOutstanding
470 <<
", principal: " << newRaw.principalOutstanding
471 <<
", interest gross: " << newRaw.interestOutstanding();
480 rounded.principalOutstanding);
484 principalOutstanding + newRaw.interestOutstanding(),
488 rounded.valueOutstanding);
490 roundToAsset(asset, newRaw.managementFeeDue, loanScale),
492 rounded.managementFeeDue);
495 totalValueOutstanding, principalOutstanding, managementFeeOutstanding);
499 newLoanProperties.totalValueOutstanding = newRounded.valueOutstanding;
501 JLOG(j.
debug()) <<
"new rounded value: " << newRounded.valueOutstanding
502 <<
", principal: " << newRounded.principalOutstanding
503 <<
", interest gross: " << newRounded.interestOutstanding();
506 periodicPayment = newLoanProperties.periodicPayment;
511 principalOutstanding,
516 newRounded.interestOutstanding() != beast::zero,
521 JLOG(j.
warn()) <<
"Principal overpayment would cause the loan to be in "
522 "an invalid state. Ignore the overpayment";
529 if (newLoanProperties.periodicPayment <= 0 ||
530 newLoanProperties.totalValueOutstanding <= 0 ||
531 newLoanProperties.managementFeeOwedToBroker < 0)
534 JLOG(j.
warn()) <<
"Overpayment not allowed: Computed loan "
535 "properties are invalid. Does "
536 "not compute. TotalValueOutstanding: "
537 << newLoanProperties.totalValueOutstanding
538 <<
", PeriodicPayment : "
539 << newLoanProperties.periodicPayment
540 <<
", ManagementFeeOwedToBroker: "
541 << newLoanProperties.managementFeeOwedToBroker;
546 auto const deltas = rounded - newRounded;
548 auto const hypotheticalValueOutstanding =
549 rounded.valueOutstanding - deltas.principal;
554 auto const valueChange =
555 newRounded.valueOutstanding - hypotheticalValueOutstanding;
558 JLOG(j.
warn()) <<
"Principal overpayment would increase the value of "
559 "the loan. Ignore the overpayment";
576 .feePaid = deltas.managementFee +
591template <
class NumberProxy>
597 NumberProxy& totalValueOutstandingProxy,
598 NumberProxy& principalOutstandingProxy,
599 NumberProxy& managementFeeOutstandingProxy,
600 NumberProxy& periodicPaymentProxy,
603 Number const& periodicRate,
613 Number totalValueOutstanding = totalValueOutstandingProxy;
614 Number principalOutstanding = principalOutstandingProxy;
615 Number managementFeeOutstanding = managementFeeOutstandingProxy;
616 Number periodicPayment = periodicPaymentProxy;
619 <<
"overpayment components:"
620 <<
", totalValue before: " << *totalValueOutstandingProxy
623 <<
", managementFeeDelta: "
627 <<
", totalDue: " << overpaymentComponents.
totalDue
628 <<
", payments remaining :" << paymentRemaining;
635 overpaymentComponents,
636 totalValueOutstanding,
637 principalOutstanding,
638 managementFeeOutstanding,
651 auto const& loanPaymentParts = *ret;
656 if (principalOutstandingProxy <= principalOutstanding)
659 JLOG(j.
warn()) <<
"Overpayment not allowed: principal "
660 <<
"outstanding did not decrease. Before: "
661 << *principalOutstandingProxy
662 <<
". After: " << principalOutstanding;
673 principalOutstandingProxy - principalOutstanding,
674 "xrpl::detail::doOverpayment",
675 "principal change agrees");
679 managementFeeOutstandingProxy - managementFeeOutstanding,
680 "xrpl::detail::doOverpayment",
686 JLOG(j.
debug()) <<
"valueChange: " << loanPaymentParts.valueChange
687 <<
", totalValue before: " << *totalValueOutstandingProxy
688 <<
", totalValue after: " << totalValueOutstanding
689 <<
", totalValue delta: "
690 << (totalValueOutstandingProxy - totalValueOutstanding)
691 <<
", principalDelta: "
693 <<
", principalPaid: " << loanPaymentParts.principalPaid
694 <<
", Computed difference: "
696 (totalValueOutstandingProxy - totalValueOutstanding);
699 loanPaymentParts.valueChange ==
700 totalValueOutstanding -
701 (totalValueOutstandingProxy -
704 "xrpl::detail::doOverpayment",
705 "interest paid agrees");
709 loanPaymentParts.principalPaid,
710 "xrpl::detail::doOverpayment",
711 "principal payment matches");
714 loanPaymentParts.feePaid ==
717 "xrpl::detail::doOverpayment",
718 "fee payment matches");
722 totalValueOutstandingProxy = totalValueOutstanding;
723 principalOutstandingProxy = principalOutstanding;
724 managementFeeOutstandingProxy = managementFeeOutstanding;
725 periodicPaymentProxy = periodicPayment;
727 return loanPaymentParts;
747 Number const& principalOutstanding,
752 Number const& latePaymentFee,
764 principalOutstanding,
772 auto const [roundedLateInterest, roundedLateManagementFee] = [&]() {
773 auto const interest =
776 asset, interest, managementFeeRate, loanScale);
780 roundedLateInterest >= 0,
781 "xrpl::detail::computeLatePayment : valid late interest");
784 "xrpl::detail::computeLatePayment",
785 "no extra parts to this payment");
793 auto inner = periodic;
802 roundedLateManagementFee,
814 "xrpl::detail::computeLatePayment",
815 "total due is rounded");
820 if (amount <
late.totalDue)
822 JLOG(j.
warn()) <<
"Late loan payment amount is insufficient. Due: "
823 <<
late.totalDue <<
", paid: " << amount;
853 Number const& principalOutstanding,
854 Number const& managementFeeOutstanding,
855 Number const& periodicPayment,
862 Number const& totalInterestOutstanding,
863 Number const& periodicRate,
864 Number const& closePaymentFee,
870 if (paymentRemaining <= 1)
873 JLOG(j.
warn()) <<
"Last payment cannot be a full payment.";
881 periodicPayment, periodicRate, paymentRemaining);
886 rawPrincipalOutstanding,
896 auto const [roundedFullInterest, roundedFullManagementFee] = [&]() {
900 asset, interest, managementFeeRate, loanScale);
910 totalInterestOutstanding + managementFeeOutstanding,
911 .trackedPrincipalDelta = principalOutstanding,
915 .trackedManagementFeeDelta = managementFeeOutstanding,
926 closePaymentFee + roundedFullManagementFee - managementFeeOutstanding,
935 roundedFullInterest - totalInterestOutstanding,
940 "xrpl::detail::computeFullPayment",
941 "total due is rounded");
943 JLOG(j.
trace()) <<
"computeFullPayment result: periodicPayment: "
944 << periodicPayment <<
", periodicRate: " << periodicRate
945 <<
", paymentRemaining: " << paymentRemaining
946 <<
", rawPrincipalOutstanding: " << rawPrincipalOutstanding
947 <<
", fullPaymentInterest: " << fullPaymentInterest
948 <<
", roundedFullInterest: " << roundedFullInterest
949 <<
", roundedFullManagementFee: "
950 << roundedFullManagementFee
951 <<
", untrackedInterest: " <<
full.untrackedInterest;
953 if (amount <
full.totalDue)
988 Number const& totalValueOutstanding,
989 Number const& principalOutstanding,
990 Number const& managementFeeOutstanding,
991 Number const& periodicPayment,
992 Number const& periodicRate,
997 isRounded(asset, totalValueOutstanding, scale) &&
998 isRounded(asset, principalOutstanding, scale) &&
999 isRounded(asset, managementFeeOutstanding, scale),
1000 "xrpl::detail::computePaymentComponents",
1001 "Outstanding values are rounded");
1003 paymentRemaining > 0,
1004 "xrpl::detail::computePaymentComponents",
1005 "some payments remaining");
1007 auto const roundedPeriodicPayment =
1012 if (paymentRemaining == 1 ||
1013 totalValueOutstanding <= roundedPeriodicPayment)
1019 .trackedPrincipalDelta = principalOutstanding,
1020 .trackedManagementFeeDelta = managementFeeOutstanding,
1027 periodicPayment, periodicRate, paymentRemaining - 1, managementFeeRate);
1034 .principalOutstanding =
1042 totalValueOutstanding, principalOutstanding, managementFeeOutstanding);
1055 "xrpl::detail::computePaymentComponents",
1056 "principal delta not greater than outstanding");
1064 "xrpl::detail::computePaymentComponents",
1065 "interest due delta not greater than outstanding");
1076 "xrpl::detail::computePaymentComponents",
1077 "management fee due delta not greater than outstanding");
1090 auto takeFrom = [](
Number& component,
Number& excess) {
1091 if (excess > beast::zero)
1093 auto part =
std::min(component, excess);
1098 excess >= beast::zero,
1099 "xrpl::detail::computePaymentComponents",
1100 "excess non-negative");
1114 Number totalOverpayment =
1117 if (totalOverpayment > beast::zero)
1121 "xrpl::detail::computePaymentComponents : payment exceeded loan "
1123 addressExcess(deltas, totalOverpayment);
1128 Number shortage = roundedPeriodicPayment - deltas.
total();
1132 "xrpl::detail::computePaymentComponents",
1133 "shortage is rounded");
1135 if (shortage < beast::zero)
1138 Number excess = -shortage;
1139 addressExcess(deltas, excess);
1147 shortage >= beast::zero,
1148 "xrpl::detail::computePaymentComponents",
1149 "no shortage or excess");
1155 "xrpl::detail::computePaymentComponents",
1156 "total value adds up");
1161 "xrpl::detail::computePaymentComponents",
1162 "valid principal result");
1166 "xrpl::detail::computePaymentComponents",
1167 "valid interest result");
1171 "xrpl::detail::computePaymentComponents",
1172 "valid fee result");
1176 "xrpl::detail::computePaymentComponents",
1177 "payment parts add to payment");
1208ExtendedPaymentComponents
1211 int32_t
const loanScale,
1219 "xrpl::detail::computeOverpaymentComponents : valid overpayment "
1232 auto const [rawOverpaymentInterest, _] = [&]() {
1239 auto const [roundedOverpaymentInterest, roundedOverpaymentManagementFee] =
1242 roundToAsset(asset, rawOverpaymentInterest, loanScale);
1244 asset, interest, managementFeeRate, loanScale);
1253 .trackedPrincipalDelta =
overpayment - roundedOverpaymentInterest -
1254 roundedOverpaymentManagementFee - overpaymentFee,
1255 .trackedManagementFeeDelta = roundedOverpaymentManagementFee,
1265 roundedOverpaymentInterest};
1267 result.trackedInterestPart() == roundedOverpaymentInterest,
1268 "xrpl::detail::computeOverpaymentComponents",
1269 "valid interest computation");
1275detail::LoanStateDeltas
1315 Asset const& vaultAsset,
1316 Number const& principalRequested,
1317 bool expectInterest,
1322 auto const totalInterestOutstanding =
1327 if (expectInterest && totalInterestOutstanding <= 0)
1331 JLOG(j.
warn()) <<
"Loan for " << principalRequested
1332 <<
" with interest has no interest due";
1337 if (!expectInterest && totalInterestOutstanding > 0)
1340 JLOG(j.
warn()) <<
"Loan for " << principalRequested
1341 <<
" with no interest has interest due";
1355 JLOG(j.
warn()) <<
"Loan is unable to pay principal.";
1364 if (roundedPayment == beast::zero)
1366 JLOG(j.
warn()) <<
"Loan Periodic payment ("
1379 computedPayments != paymentTotal)
1381 JLOG(j.
warn()) <<
"Loan Periodic payment ("
1383 << roundedPayment <<
") on a total value of "
1385 <<
" can not complete the loan in the specified "
1386 "number of payments ("
1387 << computedPayments <<
" != " << paymentTotal <<
")";
1402 Number const& rawPrincipalOutstanding,
1403 Number const& periodicRate,
1411 rawPrincipalOutstanding,
1418 accruedInterest >= 0,
1419 "xrpl::detail::computeFullPaymentInterest : valid accrued "
1423 auto const prepaymentPenalty = closeInterestRate == beast::zero
1428 prepaymentPenalty >= 0,
1429 "xrpl::detail::computeFullPaymentInterest : valid prepayment "
1433 return accruedInterest + prepaymentPenalty;
1438 Number const& periodicPayment,
1439 Number const& periodicRate,
1447 Number const rawPrincipalOutstanding =
1449 periodicPayment, periodicRate, paymentRemaining);
1452 rawPrincipalOutstanding,
1482 Number const& periodicPayment,
1483 Number const& periodicRate,
1487 if (paymentRemaining == 0)
1491 .principalOutstanding = 0,
1493 .managementFeeDue = 0};
1497 Number const rawTotalValueOutstanding = periodicPayment * paymentRemaining;
1499 Number const rawPrincipalOutstanding =
1501 periodicPayment, periodicRate, paymentRemaining);
1504 Number const rawInterestOutstandingGross =
1505 rawTotalValueOutstanding - rawPrincipalOutstanding;
1508 Number const rawManagementFeeOutstanding =
1512 Number const rawInterestOutstandingNet =
1513 rawInterestOutstandingGross - rawManagementFeeOutstanding;
1517 .principalOutstanding = rawPrincipalOutstanding,
1518 .interestDue = rawInterestOutstandingNet,
1519 .managementFeeDue = rawManagementFeeOutstanding};
1524 Number const& periodicPayment,
1558 Number const& totalValueOutstanding,
1559 Number const& principalOutstanding,
1560 Number const& managementFeeOutstanding)
1566 .principalOutstanding = principalOutstanding,
1567 .interestDue = totalValueOutstanding - principalOutstanding -
1568 managementFeeOutstanding,
1569 .managementFeeDue = managementFeeOutstanding};
1576 loan->at(sfTotalValueOutstanding),
1577 loan->at(sfPrincipalOutstanding),
1578 loan->at(sfManagementFeeOutstanding));
1607 Number principalOutstanding,
1616 interestRate == 0 || periodicRate > 0,
1617 "xrpl::computeLoanProperties : valid rate");
1620 principalOutstanding, periodicRate, paymentsRemaining);
1622 auto const [totalValueOutstanding, loanScale] = [&]() {
1629 STAmount amount{asset, periodicPayment * paymentsRemaining};
1634 auto const loanScale =
std::max(minimumScale, amount.exponent());
1636 (amount.integral() && loanScale == 0) ||
1637 (!amount.integral() &&
1639 "xrpl::computeLoanProperties",
1640 "loanScale value fits expectations");
1656 auto const totalInterestOutstanding =
1657 totalValueOutstanding - principalOutstanding;
1659 asset, totalInterestOutstanding, managementFeeRate, loanScale);
1664 auto const firstPaymentPrincipal = [&]() {
1676 paymentsRemaining - 1,
1681 return startingState.principalOutstanding -
1682 firstPaymentState.principalOutstanding;
1687 .totalValueOutstanding = totalValueOutstanding,
1688 .managementFeeOwedToBroker = feeOwedToBroker,
1689 .loanScale = loanScale,
1690 .firstPaymentPrincipal = firstPaymentPrincipal};
1699Expected<LoanPaymentParts, TER>
1709 using namespace Lending;
1711 auto principalOutstandingProxy = loan->at(sfPrincipalOutstanding);
1712 auto paymentRemainingProxy = loan->at(sfPaymentRemaining);
1714 if (paymentRemainingProxy == 0 || principalOutstandingProxy == 0)
1718 JLOG(j.
warn()) <<
"Loan is already paid off.";
1723 auto totalValueOutstandingProxy = loan->at(sfTotalValueOutstanding);
1724 auto managementFeeOutstandingProxy = loan->at(sfManagementFeeOutstanding);
1727 auto nextDueDateProxy = loan->at(sfNextPaymentDueDate);
1728 if (*nextDueDateProxy == 0)
1730 JLOG(j.
warn()) <<
"Loan next payment due date is not set.";
1736 TenthBips32 const interestRate{loan->at(sfInterestRate)};
1738 Number const serviceFee = loan->at(sfLoanServiceFee);
1739 TenthBips16 const managementFeeRate{brokerSle->at(sfManagementFeeRate)};
1741 Number const periodicPayment = loan->at(sfPeriodicPayment);
1743 auto prevPaymentDateProxy = loan->at(sfPreviousPaymentDate);
1746 std::uint32_t const paymentInterval = loan->at(sfPaymentInterval);
1752 interestRate == 0 || periodicRate > 0,
1753 "xrpl::loanMakePayment : valid rate");
1756 *totalValueOutstandingProxy > 0,
1757 "xrpl::loanMakePayment : valid total value");
1768 JLOG(j.
warn()) <<
"Loan payment is overdue. Use the tfLoanLatePayment "
1770 "flag to make a late payment. Loan was created on "
1771 << startDate <<
", prev payment due date is "
1772 << prevPaymentDateProxy <<
", next payment due date is "
1773 << nextDueDateProxy <<
", ledger time is "
1782 TenthBips32 const closeInterestRate{loan->at(sfCloseInterestRate)};
1783 Number const closePaymentFee =
1784 roundToAsset(asset, loan->at(sfClosePaymentFee), loanScale);
1787 totalValueOutstandingProxy,
1788 principalOutstandingProxy,
1789 managementFeeOutstandingProxy);
1794 principalOutstandingProxy,
1795 managementFeeOutstandingProxy,
1797 paymentRemainingProxy,
1798 prevPaymentDateProxy,
1811 *fullPaymentComponents,
1812 totalValueOutstandingProxy,
1813 principalOutstandingProxy,
1814 managementFeeOutstandingProxy,
1815 paymentRemainingProxy,
1816 prevPaymentDateProxy,
1820 else if (fullPaymentComponents.error())
1824 return Unexpected(fullPaymentComponents.error());
1827 UNREACHABLE(
"xrpl::loanMakePayment : invalid full payment result");
1828 JLOG(j.
error()) <<
"Full payment computation failed unexpectedly.";
1840 totalValueOutstandingProxy,
1841 principalOutstandingProxy,
1842 managementFeeOutstandingProxy,
1845 paymentRemainingProxy,
1849 periodic.trackedPrincipalDelta >= 0,
1850 "xrpl::loanMakePayment",
1851 "regular payment valid principal");
1857 TenthBips32 const lateInterestRate{loan->at(sfLateInterestRate)};
1858 Number const latePaymentFee = loan->at(sfLatePaymentFee);
1863 principalOutstandingProxy,
1874 *latePaymentComponents,
1875 totalValueOutstandingProxy,
1876 principalOutstandingProxy,
1877 managementFeeOutstandingProxy,
1878 paymentRemainingProxy,
1879 prevPaymentDateProxy,
1883 else if (latePaymentComponents.error())
1887 return Unexpected(latePaymentComponents.error());
1891 UNREACHABLE(
"xrpl::loanMakePayment : invalid late payment result");
1892 JLOG(j.
error()) <<
"Late payment computation failed unexpectedly.";
1903 "xrpl::loanMakePayment",
1904 "regular payment type");
1911 while ((amount >= (totalPaid + periodic.totalDue)) &&
1912 paymentRemainingProxy > 0 &&
1913 numPayments < loanMaximumPaymentsPerTransaction)
1917 periodic.trackedPrincipalDelta >= 0,
1918 "xrpl::loanMakePayment",
1919 "payment pays non-negative principal");
1921 totalPaid += periodic.totalDue;
1924 totalValueOutstandingProxy,
1925 principalOutstandingProxy,
1926 managementFeeOutstandingProxy,
1927 paymentRemainingProxy,
1928 prevPaymentDateProxy,
1935 (paymentRemainingProxy == 0),
1936 "xrpl::loanMakePayment",
1937 "final payment is the final payment");
1947 totalValueOutstandingProxy,
1948 principalOutstandingProxy,
1949 managementFeeOutstandingProxy,
1952 paymentRemainingProxy,
1957 if (numPayments == 0)
1959 JLOG(j.
warn()) <<
"Regular loan payment amount is insufficient. Due: "
1960 << periodic.totalDue <<
", paid: " << amount;
1968 "xrpl::loanMakePayment",
1969 "payment parts add up");
1972 "xrpl::loanMakePayment",
1979 totalPaid < amount && numPayments < loanMaximumPaymentsPerTransaction)
1982 loan->at(sfOverpaymentInterestRate)};
1983 TenthBips32 const overpaymentFeeRate{loan->at(sfOverpaymentFee)};
1989 std::min(amount - totalPaid, *totalValueOutstandingProxy);
1996 overpaymentInterestRate,
2006 "xrpl::loanMakePayment",
2007 "overpayment penalty did not reduce value of loan");
2010 auto periodicPaymentProxy = loan->at(sfPeriodicPayment);
2014 overpaymentComponents,
2015 totalValueOutstandingProxy,
2016 principalOutstandingProxy,
2017 managementFeeOutstandingProxy,
2018 periodicPaymentProxy,
2022 paymentRemainingProxy,
2023 prevPaymentDateProxy,
2027 totalParts += *overResult;
2028 else if (overResult.error())
2042 "xrpl::loanMakePayment : total principal paid is valid");
2046 "xrpl::loanMakePayment : total interest paid is valid");
2049 "xrpl::loanMakePayment : loan value change is valid");
2052 totalParts.
feePaid >= beast::zero,
2053 "xrpl::loanMakePayment : fee paid is valid");
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
Writeable view to a ledger, for applying a transaction.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
constexpr int exponent() const noexcept
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
static bool checkExtraFeatures(PreflightContext const &ctx)
PaymentComponents computePaymentComponents(Asset const &asset, std::int32_t scale, Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 managementFeeRate)
Expected< ExtendedPaymentComponents, TER > computeFullPayment(Asset const &asset, ApplyView &view, Number const &principalOutstanding, Number const &managementFeeOutstanding, Number const &periodicPayment, std::uint32_t paymentRemaining, std::uint32_t prevPaymentDate, std::uint32_t const startDate, std::uint32_t const paymentInterval, TenthBips32 const closeInterestRate, std::int32_t loanScale, Number const &totalInterestOutstanding, Number const &periodicRate, Number const &closePaymentFee, STAmount const &amount, TenthBips16 managementFeeRate, beast::Journal j)
Number computeRaisedRate(Number const &periodicRate, std::uint32_t paymentsRemaining)
Number loanPeriodicPayment(Number const &principalOutstanding, Number const &periodicRate, std::uint32_t paymentsRemaining)
ExtendedPaymentComponents computeOverpaymentComponents(Asset const &asset, int32_t const loanScale, Number const &overpayment, TenthBips32 const overpaymentInterestRate, TenthBips32 const overpaymentFeeRate, TenthBips16 const managementFeeRate)
Expected< ExtendedPaymentComponents, TER > computeLatePayment(Asset const &asset, ApplyView const &view, Number const &principalOutstanding, std::int32_t nextDueDate, ExtendedPaymentComponents const &periodic, TenthBips32 lateInterestRate, std::int32_t loanScale, Number const &latePaymentFee, STAmount const &amount, TenthBips16 managementFeeRate, beast::Journal j)
Number loanAccruedInterest(Number const &principalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t startDate, std::uint32_t prevPaymentDate, std::uint32_t paymentInterval)
Number loanLatePaymentInterest(Number const &principalOutstanding, TenthBips32 lateInterestRate, NetClock::time_point parentCloseTime, std::uint32_t nextPaymentDueDate)
Number computePaymentFactor(Number const &periodicRate, std::uint32_t paymentsRemaining)
Expected< LoanPaymentParts, TER > tryOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, Number &totalValueOutstanding, Number &principalOutstanding, Number &managementFeeOutstanding, Number &periodicPayment, TenthBips32 interestRate, std::uint32_t paymentInterval, Number const &periodicRate, std::uint32_t paymentRemaining, std::uint32_t prevPaymentDate, std::optional< std::uint32_t > nextDueDate, TenthBips16 const managementFeeRate, beast::Journal j)
Number loanPrincipalFromPeriodicPayment(Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentsRemaining)
std::pair< Number, Number > computeInterestAndFeeParts(Number const &interest, TenthBips16 managementFeeRate)
Expected< LoanPaymentParts, TER > doOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, NumberProxy &totalValueOutstandingProxy, NumberProxy &principalOutstandingProxy, NumberProxy &managementFeeOutstandingProxy, NumberProxy &periodicPaymentProxy, TenthBips32 const interestRate, std::uint32_t const paymentInterval, Number const &periodicRate, std::uint32_t const paymentRemaining, std::uint32_t const prevPaymentDate, std::optional< std::uint32_t > const nextDueDate, TenthBips16 const managementFeeRate, beast::Journal j)
LoanPaymentParts doPayment(ExtendedPaymentComponents const &payment, NumberProxy &totalValueOutstandingProxy, NumberProxy &principalOutstandingProxy, NumberProxy &managementFeeOutstandingProxy, UInt32Proxy &paymentRemainingProxy, UInt32Proxy &prevPaymentDateProxy, UInt32OptionalProxy &nextDueDateProxy, std::uint32_t paymentInterval)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval)
Number computeManagementFee(Asset const &asset, Number const &value, TenthBips32 managementFeeRate, std::int32_t scale)
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
Number operator-(Number const &x, Number const &y)
static constexpr Number numZero
Number roundToAsset(A const &asset, Number const &value, std::int32_t scale, Number::rounding_mode rounding=Number::getround())
Round an arbitrary precision Number to the precision of a given Asset.
constexpr T tenthBipsOfValue(T value, TenthBips< TBips > bips)
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
static constexpr std::uint32_t secondsInYear
Number power(Number const &f, unsigned n)
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
TERSubset< CanCvtToTER > TER
Expected< LoanPaymentParts, TER > loanMakePayment(Asset const &asset, ApplyView &view, SLE::ref loan, SLE::const_ref brokerSle, STAmount const &amount, LoanPaymentType const paymentType, beast::Journal j)
Number roundPeriodicPayment(Asset const &asset, Number const &periodicPayment, std::int32_t scale)
Ensure the periodic payment is always rounded consistently.
LoanState constructRoundedLoanState(SLE::const_ref loan)
@ tecINSUFFICIENT_PAYMENT
LoanProperties computeLoanProperties(Asset const &asset, Number principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
Number computeFullPaymentInterest(Number const &rawPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
LoanState computeRawLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
bool isRounded(Asset const &asset, Number const &value, std::int32_t scale)
bool operator==(LoanPaymentParts const &other) const
LoanPaymentParts & operator+=(LoanPaymentParts const &other)
Number totalValueOutstanding
Number firstPaymentPrincipal
This structure captures the parts of a loan state.
Number principalOutstanding
State information when preflighting a tx.
Number untrackedManagementFee
Number trackedPrincipalDelta
PaymentSpecialCase specialCase
Number trackedManagementFeeDelta
Number trackedInterestPart() const
T time_since_epoch(T... args)