rippled
Loading...
Searching...
No Matches
LendingHelpers.h
1#pragma once
2
3#include <xrpl/ledger/View.h>
4#include <xrpl/protocol/st.h>
5
6namespace xrpl {
7
8struct PreflightContext;
9
10// Lending protocol has dependencies, so capture them here.
11bool
12checkLendingProtocolDependencies(PreflightContext const& ctx);
13
14static constexpr std::uint32_t secondsInYear = 365 * 24 * 60 * 60;
15
17loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval);
18
20inline Number
21roundPeriodicPayment(Asset const& asset, Number const& periodicPayment, std::int32_t scale)
22{
23 return roundToAsset(asset, periodicPayment, scale, Number::upward);
24}
25
26/* Represents the breakdown of amounts to be paid and changes applied to the
27 * Loan object while processing a loan payment.
28 *
29 * This structure is returned after processing a loan payment transaction and
30 * captures the amounts that need to be paid. The actual ledger entry changes
31 * are made in LoanPay based on this structure values.
32 *
33 * The sum of principalPaid, interestPaid, and feePaid represents the total
34 * amount to be deducted from the borrower's account. The valueChange field
35 * tracks whether the loan's total value increased or decreased beyond normal
36 * amortization.
37 *
38 * This structure is explained in the XLS-66 spec, section 3.2.4.2 (Payment
39 * Processing).
40 */
42{
43 // The amount of principal paid that reduces the loan balance.
44 // This amount is subtracted from sfPrincipalOutstanding in the Loan object
45 // and paid to the Vault
47
48 // The total amount of interest paid to the Vault.
49 // This includes:
50 // - Tracked interest from the amortization schedule
51 // - Untracked interest (e.g., late payment penalty interest)
52 // This value is always non-negative.
54
55 // The change in the loan's total value outstanding.
56 // - If valueChange < 0: Loan value decreased
57 // - If valueChange > 0: Loan value increased
58 // - If valueChange = 0: No value adjustment
59 //
60 // For regular on-time payments, this is always 0. Non-zero values occur
61 // when:
62 // - Overpayments reduce the loan balance beyond the scheduled amount
63 // - Late payments add penalty interest to the loan value
64 // - Early full payment may increase or decrease the loan value based on
65 // terms
67
68 /* The total amount of fees paid to the Broker.
69 * This includes:
70 * - Tracked management fees from the amortization schedule
71 * - Untracked fees (e.g., late payment fees, service fees, origination
72 * fees) This value is always non-negative.
73 */
75
77 operator+=(LoanPaymentParts const& other);
78
79 bool
80 operator==(LoanPaymentParts const& other) const;
81};
82
95{
96 // Total value still due to be paid by the borrower.
98 // Principal still due to be paid by the borrower.
100 // Interest still due to be paid to the Vault.
101 // This is a portion of interestOutstanding
103 // Management fee still due to be paid to the broker.
104 // This is a portion of interestOutstanding
106
107 // Interest still due to be paid by the borrower.
108 Number
110 {
111 XRPL_ASSERT_PARTS(
113 "xrpl::LoanState::interestOutstanding",
114 "other values add up correctly");
116 }
117};
118
119/* Describes the initial computed properties of a loan.
120 *
121 * This structure contains the fundamental calculated values that define a
122 * loan's payment structure and amortization schedule. These properties are
123 * computed:
124 * - At loan creation (LoanSet transaction)
125 * - When loan terms change (e.g., after an overpayment that reduces the loan
126 * balance)
127 */
129{
130 // The unrounded amount to be paid at each regular payment period.
131 // Calculated using the standard amortization formula based on principal,
132 // interest rate, and number of payments.
133 // The actual amount paid in the LoanPay transaction must be rounded up to
134 // the precision of the asset and loan.
136
137 // The loan's current state, with all values rounded to the loan's scale.
139
140 // The scale (decimal places) used for rounding all loan amounts.
141 // This is the maximum of:
142 // - The asset's native scale
143 // - A minimum scale required to represent the periodic payment accurately
144 // All loan state values (principal, interest, fees) are rounded to this
145 // scale.
147
148 // The principal portion of the first payment.
150};
151
152// Some values get re-rounded to the vault scale any time they are adjusted. In
153// addition, they are prevented from ever going below zero. This helps avoid
154// accumulated rounding errors and leftover dust amounts.
155template <class NumberProxy>
156void
157adjustImpreciseNumber(NumberProxy value, Number const& adjustment, Asset const& asset, int vaultScale)
158{
159 value = roundToAsset(asset, value + adjustment, vaultScale);
160
161 if (*value < beast::zero)
162 value = 0;
163}
164
165inline int
167{
168 if (!vaultSle)
169 return Number::minExponent - 1; // LCOV_EXCL_LINE
170 return STAmount{vaultSle->at(sfAsset), vaultSle->at(sfAssetsTotal)}.exponent();
171}
172
173TER
175 Asset const& vaultAsset,
176 Number const& principalRequested,
177 bool expectInterest,
178 std::uint32_t paymentTotal,
179 LoanProperties const& properties,
181
182LoanState
184 Number const& periodicPayment,
185 Number const& periodicRate,
186 std::uint32_t const paymentRemaining,
187 TenthBips32 const managementFeeRate);
188
189// Constructs a valid LoanState object from arbitrary inputs
190LoanState
192 Number const& totalValueOutstanding,
193 Number const& principalOutstanding,
194 Number const& managementFeeOutstanding);
195
196// Constructs a valid LoanState object from a Loan object, which always has
197// rounded values
198LoanState
200
201Number
202computeManagementFee(Asset const& asset, Number const& interest, TenthBips32 managementFeeRate, std::int32_t scale);
203
204Number
206 Number const& theoreticalPrincipalOutstanding,
207 Number const& periodicRate,
208 NetClock::time_point parentCloseTime,
209 std::uint32_t paymentInterval,
210 std::uint32_t prevPaymentDate,
211 std::uint32_t startDate,
212 TenthBips32 closeInterestRate);
213
214namespace detail {
215// These classes and functions should only be accessed by LendingHelper
216// functions and unit tests
217
218enum class PaymentSpecialCase { none, final, extra };
219
220/* Represents a single loan payment component parts.
221
222* This structure captures the "delta" (change) values that will be applied to
223* the tracked fields in the Loan ledger object when a payment is processed.
224*
225* These are called "deltas" because they represent the amount by which each
226* corresponding field in the Loan object will be reduced.
227* They are "tracked" as they change tracked loan values.
228*/
230{
231 // The change in total value outstanding for this payment.
232 // This amount will be subtracted from sfTotalValueOutstanding in the Loan
233 // object. Equal to the sum of trackedPrincipalDelta,
234 // trackedInterestPart(), and trackedManagementFeeDelta.
236
237 // The change in principal outstanding for this payment.
238 // This amount will be subtracted from sfPrincipalOutstanding in the Loan
239 // object, representing the portion of the payment that reduces the
240 // original loan amount.
242
243 // The change in management fee outstanding for this payment.
244 // This amount will be subtracted from sfManagementFeeOutstanding in the
245 // Loan object. This represents only the tracked management fees from the
246 // amortization schedule and does not include additional untracked fees
247 // (such as late payment fees) that go directly to the broker.
249
250 // Indicates if this payment has special handling requirements.
251 // - none: Regular scheduled payment
252 // - final: The last payment that closes out the loan
253 // - extra: An additional payment beyond the regular schedule (overpayment)
255
256 // Calculates the tracked interest portion of this payment.
257 // This is derived from the other components as:
258 // trackedValueDelta - trackedPrincipalDelta - trackedManagementFeeDelta
259 //
260 // @return The amount of tracked interest included in this payment that
261 // will be paid to the vault.
262 Number
263 trackedInterestPart() const;
264};
265
266/* Extends PaymentComponents with untracked payment amounts.
267 *
268 * This structure adds untracked fees and interest to the base
269 * PaymentComponents, representing amounts that don't affect the Loan object's
270 * tracked state but are still part of the total payment due from the borrower.
271 *
272 * Untracked amounts include:
273 * - Late payment fees that go directly to the Broker
274 * - Late payment penalty interest that goes directly to the Vault
275 * - Service fees
276 *
277 * The key distinction is that tracked amounts reduce the Loan object's state
278 * (sfTotalValueOutstanding, sfPrincipalOutstanding,
279 * sfManagementFeeOutstanding), while untracked amounts are paid directly to the
280 * recipient without affecting the loan's amortization schedule.
281 */
283{
284 // Additional management fees that go directly to the Broker.
285 // This includes fees not part of the standard amortization schedule
286 // (e.g., late fees, service fees, origination fees).
287 // This value may be negative, though the final value returned in
288 // LoanPaymentParts.feePaid will never be negative.
290
291 // Additional interest that goes directly to the Vault.
292 // This includes interest not part of the standard amortization schedule
293 // (e.g., late payment penalty interest).
294 // This value may be negative, though the final value returned in
295 // LoanPaymentParts.interestPaid will never be negative.
297
298 // The complete amount due from the borrower for this payment.
299 // Calculated as: trackedValueDelta + untrackedInterest +
300 // untrackedManagementFee
301 //
302 // This value is used to validate that the payment amount provided by the
303 // borrower is sufficient to cover all components of the payment.
305
313};
314
315/* Represents the differences between two loan states.
316 *
317 * This structure is used to capture the change in each component of a loan's
318 * state, typically when computing the difference between two LoanState objects
319 * (e.g., before and after a payment). It is a convenient way to capture changes
320 * in each component. How that difference is used depends on the context.
321 */
323{
324 // The difference in principal outstanding between two loan states.
326
327 // The difference in interest due between two loan states.
329
330 // The difference in management fee outstanding between two loan states.
332
333 /* Calculates the total change across all components.
334 * @return The sum of principal, interest, and management fee deltas.
335 */
336 Number
337 total() const
338 {
340 }
341
342 // Ensures all delta values are non-negative.
343 void
344 nonNegative();
345};
346
349 Asset const& asset,
350 std::int32_t loanScale,
351 ExtendedPaymentComponents const& overpaymentComponents,
352 LoanState const& roundedLoanState,
353 Number const& periodicPayment,
354 Number const& periodicRate,
355 std::uint32_t paymentRemaining,
356 TenthBips16 const managementFeeRate,
358
359Number
360computeRaisedRate(Number const& periodicRate, std::uint32_t paymentsRemaining);
361
362Number
363computePaymentFactor(Number const& periodicRate, std::uint32_t paymentsRemaining);
364
367 Asset const& asset,
368 Number const& interest,
369 TenthBips16 managementFeeRate,
370 std::int32_t loanScale);
371
372Number
373loanPeriodicPayment(Number const& principalOutstanding, Number const& periodicRate, std::uint32_t paymentsRemaining);
374
375Number
377 Number const& periodicPayment,
378 Number const& periodicRate,
379 std::uint32_t paymentsRemaining);
380
381Number
383 Number const& principalOutstanding,
384 TenthBips32 lateInterestRate,
385 NetClock::time_point parentCloseTime,
386 std::uint32_t nextPaymentDueDate);
387
388Number
390 Number const& principalOutstanding,
391 Number const& periodicRate,
392 NetClock::time_point parentCloseTime,
393 std::uint32_t startDate,
394 std::uint32_t prevPaymentDate,
395 std::uint32_t paymentInterval);
396
397ExtendedPaymentComponents
399 Asset const& asset,
400 int32_t const loanScale,
401 Number const& overpayment,
402 TenthBips32 const overpaymentInterestRate,
403 TenthBips32 const overpaymentFeeRate,
404 TenthBips16 const managementFeeRate);
405
406PaymentComponents
408 Asset const& asset,
409 std::int32_t scale,
410 Number const& totalValueOutstanding,
411 Number const& principalOutstanding,
412 Number const& managementFeeOutstanding,
413 Number const& periodicPayment,
414 Number const& periodicRate,
415 std::uint32_t paymentRemaining,
416 TenthBips16 managementFeeRate);
417
418} // namespace detail
419
420detail::LoanStateDeltas
421operator-(LoanState const& lhs, LoanState const& rhs);
422
423LoanState
424operator-(LoanState const& lhs, detail::LoanStateDeltas const& rhs);
425
426LoanState
427operator+(LoanState const& lhs, detail::LoanStateDeltas const& rhs);
428
429LoanProperties
431 Asset const& asset,
432 Number const& principalOutstanding,
433 TenthBips32 interestRate,
434 std::uint32_t paymentInterval,
435 std::uint32_t paymentsRemaining,
436 TenthBips32 managementFeeRate,
437 std::int32_t minimumScale);
438
439LoanProperties
441 Asset const& asset,
442 Number const& principalOutstanding,
443 Number const& periodicRate,
444 std::uint32_t paymentsRemaining,
445 TenthBips32 managementFeeRate,
446 std::int32_t minimumScale);
447
448bool
449isRounded(Asset const& asset, Number const& value, std::int32_t scale);
450
451// Indicates what type of payment is being made.
452// regular, late, and full are mutually exclusive.
453// overpayment is an "add on" to a regular payment, and follows that path with
454// potential extra work at the end.
456
457Expected<LoanPaymentParts, TER>
459 Asset const& asset,
460 ApplyView& view,
461 SLE::ref loan,
462 SLE::const_ref brokerSle,
463 STAmount const& amount,
464 LoanPaymentType const paymentType,
466
467} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
std::chrono::time_point< NetClock > time_point
Definition chrono.h:45
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
static constexpr int minExponent
Definition Number.h:217
std::shared_ptr< STLedgerEntry > const & ref
std::shared_ptr< STLedgerEntry const > const & const_ref
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)
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)
Number loanAccruedInterest(Number const &principalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t startDate, std::uint32_t prevPaymentDate, std::uint32_t paymentInterval)
std::pair< Number, Number > computeInterestAndFeeParts(Asset const &asset, Number const &interest, TenthBips16 managementFeeRate, std::int32_t loanScale)
Number loanLatePaymentInterest(Number const &principalOutstanding, TenthBips32 lateInterestRate, NetClock::time_point parentCloseTime, std::uint32_t nextPaymentDueDate)
Number computePaymentFactor(Number const &periodicRate, std::uint32_t paymentsRemaining)
Number loanPrincipalFromPeriodicPayment(Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentsRemaining)
Expected< std::pair< LoanPaymentParts, LoanProperties >, TER > tryOverpayment(Asset const &asset, std::int32_t loanScale, ExtendedPaymentComponents const &overpaymentComponents, LoanState const &roundedOldState, Number const &periodicPayment, Number const &periodicRate, std::uint32_t paymentRemaining, TenthBips16 const managementFeeRate, beast::Journal j)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
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)
Definition base_uint.h:589
Number operator-(Number const &x, Number const &y)
Definition Number.h:638
static constexpr Number numZero
Definition Number.h:515
int getAssetsTotalScale(SLE::const_ref vaultSle)
void adjustImpreciseNumber(NumberProxy value, Number const &adjustment, Asset const &asset, int vaultScale)
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
static constexpr std::uint32_t secondsInYear
LoanState computeTheoreticalLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:429
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
TERSubset< CanCvtToTER > TER
Definition TER.h:620
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)
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
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)
LoanProperties computeLoanProperties(Asset const &asset, Number const &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 &theoreticalPrincipalOutstanding, Number const &periodicRate, NetClock::time_point parentCloseTime, std::uint32_t paymentInterval, std::uint32_t prevPaymentDate, std::uint32_t startDate, TenthBips32 closeInterestRate)
bool isRounded(Asset const &asset, Number const &value, std::int32_t scale)
bool operator==(LoanPaymentParts const &other) const
LoanPaymentParts & operator+=(LoanPaymentParts const &other)
This structure captures the parts of a loan state.
Number principalOutstanding
Number interestOutstanding() const
ExtendedPaymentComponents(PaymentComponents const &p, Number fee, Number interest=numZero)