rippled
Loading...
Searching...
No Matches
LendingHelpers.h
1#ifndef XRPL_APP_MISC_LENDINGHELPERS_H_INCLUDED
2#define XRPL_APP_MISC_LENDINGHELPERS_H_INCLUDED
3
4#include <xrpl/ledger/View.h>
5#include <xrpl/protocol/st.h>
6
7namespace ripple {
8
9struct PreflightContext;
10
11// Lending protocol has dependencies, so capture them here.
12bool
13checkLendingProtocolDependencies(PreflightContext const& ctx);
14
15static constexpr std::uint32_t secondsInYear = 365 * 24 * 60 * 60;
16
18loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval);
19
21inline Number
23 Asset const& asset,
24 Number const& periodicPayment,
25 std::int32_t scale)
26{
27 return roundToAsset(asset, periodicPayment, scale, Number::upward);
28}
29
30/* Represents the breakdown of amounts to be paid and changes applied to the
31 * Loan object while processing a loan payment.
32 *
33 * This structure is returned after processing a loan payment transaction and
34 * captures the amounts that need to be paid. The actual ledger entry changes
35 * are made in LoanPay based on this structure values.
36 *
37 * The sum of principalPaid, interestPaid, and feePaid represents the total
38 * amount to be deducted from the borrower's account. The valueChange field
39 * tracks whether the loan's total value increased or decreased beyond normal
40 * amortization.
41 *
42 * This structure is explained in the XLS-66 spec, section 3.2.4.2 (Payment
43 * Processing).
44 */
46{
47 // The amount of principal paid that reduces the loan balance.
48 // This amount is subtracted from sfPrincipalOutstanding in the Loan object
49 // and paid to the Vault
51
52 // The total amount of interest paid to the Vault.
53 // This includes:
54 // - Tracked interest from the amortization schedule
55 // - Untracked interest (e.g., late payment penalty interest)
56 // This value is always non-negative.
58
59 // The change in the loan's total value outstanding.
60 // - If valueChange < 0: Loan value decreased
61 // - If valueChange > 0: Loan value increased
62 // - If valueChange = 0: No value adjustment
63 //
64 // For regular on-time payments, this is always 0. Non-zero values occur
65 // when:
66 // - Overpayments reduce the loan balance beyond the scheduled amount
67 // - Late payments add penalty interest to the loan value
68 // - Early full payment may increase or decrease the loan value based on
69 // terms
71
72 /* The total amount of fees paid to the Broker.
73 * This includes:
74 * - Tracked management fees from the amortization schedule
75 * - Untracked fees (e.g., late payment fees, service fees, origination
76 * fees) This value is always non-negative.
77 */
79
81 operator+=(LoanPaymentParts const& other);
82
83 bool
84 operator==(LoanPaymentParts const& other) const;
85};
86
87/* Describes the initial computed properties of a loan.
88 *
89 * This structure contains the fundamental calculated values that define a
90 * loan's payment structure and amortization schedule. These properties are
91 * computed:
92 * - At loan creation (LoanSet transaction)
93 * - When loan terms change (e.g., after an overpayment that reduces the loan
94 * balance)
95 */
97{
98 // The unrounded amount to be paid at each regular payment period.
99 // Calculated using the standard amortization formula based on principal,
100 // interest rate, and number of payments.
101 // The actual amount paid in the LoanPay transaction must be rounded up to
102 // the precision of the asset and loan.
104
105 // The total amount the borrower will pay over the life of the loan.
106 // Equal to periodicPayment * paymentsRemaining.
107 // This includes principal, interest, and management fees.
109
110 // The total management fee that will be paid to the broker over the
111 // loan's lifetime. This is a percentage of the total interest (gross)
112 // as specified by the broker's management fee rate.
114
115 // The scale (decimal places) used for rounding all loan amounts.
116 // This is the maximum of:
117 // - The asset's native scale
118 // - A minimum scale required to represent the periodic payment accurately
119 // All loan state values (principal, interest, fees) are rounded to this
120 // scale.
122
123 // The principal portion of the first payment.
125};
126
139{
140 // Total value still due to be paid by the borrower.
142 // Principal still due to be paid by the borrower.
144 // Interest still due to be paid to the Vault.
145 // This is a portion of interestOutstanding
147 // Management fee still due to be paid to the broker.
148 // This is a portion of interestOutstanding
150
151 // Interest still due to be paid by the borrower.
152 Number
154 {
155 XRPL_ASSERT_PARTS(
158 "ripple::LoanState::interestOutstanding",
159 "other values add up correctly");
161 }
162};
163
164// Some values get re-rounded to the vault scale any time they are adjusted. In
165// addition, they are prevented from ever going below zero. This helps avoid
166// accumulated rounding errors and leftover dust amounts.
167template <class NumberProxy>
168void
170 NumberProxy value,
171 Number const& adjustment,
172 Asset const& asset,
173 int vaultScale)
174{
175 value = roundToAsset(asset, value + adjustment, vaultScale);
176
177 if (*value < beast::zero)
178 value = 0;
179}
180
181inline int
183{
184 if (!vaultSle)
185 return Number::minExponent - 1; // LCOV_EXCL_LINE
186 return vaultSle->at(sfAssetsTotal).exponent();
187}
188
189TER
191 Asset const& vaultAsset,
192 Number const& principalRequested,
193 bool expectInterest,
194 std::uint32_t paymentTotal,
195 LoanProperties const& properties,
197
198LoanState
200 Number const& periodicPayment,
201 Number const& periodicRate,
202 std::uint32_t const paymentRemaining,
203 TenthBips32 const managementFeeRate);
204
205LoanState
207 Number const& periodicPayment,
208 TenthBips32 interestRate,
209 std::uint32_t paymentInterval,
210 std::uint32_t const paymentRemaining,
211 TenthBips32 const managementFeeRate);
212
213// Constructs a valid LoanState object from arbitrary inputs
214LoanState
216 Number const& totalValueOutstanding,
217 Number const& principalOutstanding,
218 Number const& managementFeeOutstanding);
219
220// Constructs a valid LoanState object from a Loan object, which always has
221// rounded values
222LoanState
224
225Number
227 Asset const& asset,
228 Number const& interest,
229 TenthBips32 managementFeeRate,
230 std::int32_t scale);
231
232Number
234 Number const& rawPrincipalOutstanding,
235 Number const& periodicRate,
236 NetClock::time_point parentCloseTime,
237 std::uint32_t paymentInterval,
238 std::uint32_t prevPaymentDate,
239 std::uint32_t startDate,
240 TenthBips32 closeInterestRate);
241
242Number
244 Number const& periodicPayment,
245 Number const& periodicRate,
246 std::uint32_t paymentRemaining,
247 NetClock::time_point parentCloseTime,
248 std::uint32_t paymentInterval,
249 std::uint32_t prevPaymentDate,
250 std::uint32_t startDate,
251 TenthBips32 closeInterestRate);
252
253namespace detail {
254// These classes and functions should only be accessed by LendingHelper
255// functions and unit tests
256
257enum class PaymentSpecialCase { none, final, extra };
258
259/* Represents a single loan payment component parts.
260
261* This structure captures the "delta" (change) values that will be applied to
262* the tracked fields in the Loan ledger object when a payment is processed.
263*
264* These are called "deltas" because they represent the amount by which each
265* corresponding field in the Loan object will be reduced.
266* They are "tracked" as they change tracked loan values.
267*/
269{
270 // The change in total value outstanding for this payment.
271 // This amount will be subtracted from sfTotalValueOutstanding in the Loan
272 // object. Equal to the sum of trackedPrincipalDelta,
273 // trackedInterestPart(), and trackedManagementFeeDelta.
275
276 // The change in principal outstanding for this payment.
277 // This amount will be subtracted from sfPrincipalOutstanding in the Loan
278 // object, representing the portion of the payment that reduces the
279 // original loan amount.
281
282 // The change in management fee outstanding for this payment.
283 // This amount will be subtracted from sfManagementFeeOutstanding in the
284 // Loan object. This represents only the tracked management fees from the
285 // amortization schedule and does not include additional untracked fees
286 // (such as late payment fees) that go directly to the broker.
288
289 // Indicates if this payment has special handling requirements.
290 // - none: Regular scheduled payment
291 // - final: The last payment that closes out the loan
292 // - extra: An additional payment beyond the regular schedule (overpayment)
294
295 // Calculates the tracked interest portion of this payment.
296 // This is derived from the other components as:
297 // trackedValueDelta - trackedPrincipalDelta - trackedManagementFeeDelta
298 //
299 // @return The amount of tracked interest included in this payment that
300 // will be paid to the vault.
301 Number
302 trackedInterestPart() const;
303};
304
305/* Extends PaymentComponents with untracked payment amounts.
306 *
307 * This structure adds untracked fees and interest to the base
308 * PaymentComponents, representing amounts that don't affect the Loan object's
309 * tracked state but are still part of the total payment due from the borrower.
310 *
311 * Untracked amounts include:
312 * - Late payment fees that go directly to the Broker
313 * - Late payment penalty interest that goes directly to the Vault
314 * - Service fees
315 *
316 * The key distinction is that tracked amounts reduce the Loan object's state
317 * (sfTotalValueOutstanding, sfPrincipalOutstanding,
318 * sfManagementFeeOutstanding), while untracked amounts are paid directly to the
319 * recipient without affecting the loan's amortization schedule.
320 */
322{
323 // Additional management fees that go directly to the Broker.
324 // This includes fees not part of the standard amortization schedule
325 // (e.g., late fees, service fees, origination fees).
326 // This value may be negative, though the final value returned in
327 // LoanPaymentParts.feePaid will never be negative.
329
330 // Additional interest that goes directly to the Vault.
331 // This includes interest not part of the standard amortization schedule
332 // (e.g., late payment penalty interest).
333 // This value may be negative, though the final value returned in
334 // LoanPaymentParts.interestPaid will never be negative.
336
337 // The complete amount due from the borrower for this payment.
338 // Calculated as: trackedValueDelta + untrackedInterest +
339 // untrackedManagementFee
340 //
341 // This value is used to validate that the payment amount provided by the
342 // borrower is sufficient to cover all components of the payment.
344
356};
357
358/* Represents the differences between two loan states.
359 *
360 * This structure is used to capture the change in each component of a loan's
361 * state, typically when computing the difference between two LoanState objects
362 * (e.g., before and after a payment). It is a convenient way to capture changes
363 * in each component. How that difference is used depends on the context.
364 */
366{
367 // The difference in principal outstanding between two loan states.
369
370 // The difference in interest due between two loan states.
372
373 // The difference in management fee outstanding between two loan states.
375
376 /* Calculates the total change across all components.
377 * @return The sum of principal, interest, and management fee deltas.
378 */
379 Number
380 total() const
381 {
383 }
384
385 // Ensures all delta values are non-negative.
386 void
387 nonNegative();
388};
389
390PaymentComponents
392 Asset const& asset,
393 std::int32_t scale,
394 Number const& totalValueOutstanding,
395 Number const& principalOutstanding,
396 Number const& managementFeeOutstanding,
397 Number const& periodicPayment,
398 Number const& periodicRate,
399 std::uint32_t paymentRemaining,
400 TenthBips16 managementFeeRate);
401
402} // namespace detail
403
404detail::LoanStateDeltas
405operator-(LoanState const& lhs, LoanState const& rhs);
406
407LoanState
408operator-(LoanState const& lhs, detail::LoanStateDeltas const& rhs);
409
410LoanState
411operator+(LoanState const& lhs, detail::LoanStateDeltas const& rhs);
412
413LoanProperties
415 Asset const& asset,
416 Number principalOutstanding,
417 TenthBips32 interestRate,
418 std::uint32_t paymentInterval,
419 std::uint32_t paymentsRemaining,
420 TenthBips32 managementFeeRate,
421 std::int32_t minimumScale);
422
423bool
424isRounded(Asset const& asset, Number const& value, std::int32_t scale);
425
426// Indicates what type of payment is being made.
427// regular, late, and full are mutually exclusive.
428// overpayment is an "add on" to a regular payment, and follows that path with
429// potential extra work at the end.
431
432Expected<LoanPaymentParts, TER>
434 Asset const& asset,
435 ApplyView& view,
436 SLE::ref loan,
437 SLE::const_ref brokerSle,
438 STAmount const& amount,
439 LoanPaymentType const paymentType,
441
442} // namespace ripple
443
444#endif // XRPL_APP_MISC_LENDINGHELPERS_H_INCLUDED
A generic endpoint for log messages.
Definition Journal.h:41
std::chrono::time_point< NetClock > time_point
Definition chrono.h:50
static constexpr int minExponent
Definition Number.h:39
std::shared_ptr< STLedgerEntry const > const & const_ref
std::shared_ptr< STLedgerEntry > 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)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
static constexpr Number numZero
Definition Number.h:191
TenthBips< std::uint32_t > TenthBips32
Definition Units.h:444
Number loanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval)
LoanProperties computeLoanProperties(Asset const &asset, Number principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
int getVaultScale(SLE::const_ref vaultSle)
Number roundPeriodicPayment(Asset const &asset, Number const &periodicPayment, std::int32_t scale)
Ensure the periodic payment is always rounded consistently.
void adjustImpreciseNumber(NumberProxy value, Number const &adjustment, Asset const &asset, int vaultScale)
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
static constexpr std::uint32_t secondsInYear
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.
Definition STAmount.h:722
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)
bool isRounded(Asset const &asset, Number const &value, std::int32_t scale)
Number computeManagementFee(Asset const &asset, Number const &value, TenthBips32 managementFeeRate, std::int32_t scale)
Number operator-(Number const &x, Number const &y)
Definition Number.h:279
LoanState constructRoundedLoanState(SLE::const_ref loan)
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)
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
Definition base_uint.h:603
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:630
LoanState computeRawLoanState(Number const &periodicPayment, Number const &periodicRate, std::uint32_t const paymentRemaining, TenthBips32 const managementFeeRate)
LoanPaymentParts & operator+=(LoanPaymentParts const &other)
bool operator==(LoanPaymentParts const &other) const
This structure captures the parts of a loan state.
Number interestOutstanding() const
ExtendedPaymentComponents(PaymentComponents const &p, Number fee, Number interest=numZero)