Skip to content

Commit 9b7e059

Browse files
authored
Merge branch 'ximinez/lending-XLS-66-ongoing' into ximinez/lending-transitive-amendments
2 parents 846015f + 5ceb915 commit 9b7e059

File tree

10 files changed

+816
-154
lines changed

10 files changed

+816
-154
lines changed

src/test/app/LendingHelpers_test.cpp

Lines changed: 642 additions & 0 deletions
Large diffs are not rendered by default.

src/test/app/Loan_test.cpp

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class Loan_test : public beast::unit_test::suite
144144
using namespace jtx;
145145

146146
auto const vaultSle = env.le(keylet::vault(vaultID));
147-
return getVaultScale(vaultSle);
147+
return getAssetsTotalScale(vaultSle);
148148
}
149149
};
150150

@@ -554,12 +554,15 @@ class Loan_test : public beast::unit_test::suite
554554
broker.vaultScale(env),
555555
state.principalOutstanding.exponent())));
556556
BEAST_EXPECT(state.paymentInterval == 600);
557-
BEAST_EXPECT(
558-
state.totalValue ==
559-
roundToAsset(
560-
broker.asset,
561-
state.periodicPayment * state.paymentRemaining,
562-
state.loanScale));
557+
{
558+
NumberRoundModeGuard mg(Number::upward);
559+
BEAST_EXPECT(
560+
state.totalValue ==
561+
roundToAsset(
562+
broker.asset,
563+
state.periodicPayment * state.paymentRemaining,
564+
state.loanScale));
565+
}
563566
BEAST_EXPECT(
564567
state.managementFeeOutstanding ==
565568
computeManagementFee(
@@ -700,7 +703,8 @@ class Loan_test : public beast::unit_test::suite
700703
interval,
701704
total,
702705
feeRate,
703-
asset(brokerParams.vaultDeposit).number().exponent());
706+
asset(brokerParams.vaultDeposit).number().exponent(),
707+
env.journal);
704708
log << "Loan properties:\n"
705709
<< "\tPrincipal: " << principal << std::endl
706710
<< "\tInterest rate: " << interest << std::endl
@@ -1481,7 +1485,8 @@ class Loan_test : public beast::unit_test::suite
14811485
state.paymentInterval,
14821486
state.paymentRemaining,
14831487
broker.params.managementFeeRate,
1484-
state.loanScale);
1488+
state.loanScale,
1489+
env.journal);
14851490

14861491
verifyLoanStatus(
14871492
0,
@@ -2452,13 +2457,18 @@ class Loan_test : public beast::unit_test::suite
24522457
// Make all the payments in one transaction
24532458
// service fee is 2
24542459
auto const startingPayments = state.paymentRemaining;
2455-
auto const rawPayoff = startingPayments *
2456-
(state.periodicPayment + broker.asset(2).value());
2457-
STAmount const payoffAmount{broker.asset, rawPayoff};
2458-
BEAST_EXPECT(
2459-
payoffAmount ==
2460-
broker.asset(Number(1024014840139457, -12)));
2461-
BEAST_EXPECT(payoffAmount > state.principalOutstanding);
2460+
STAmount const payoffAmount = [&]() {
2461+
NumberRoundModeGuard mg(Number::upward);
2462+
auto const rawPayoff = startingPayments *
2463+
(state.periodicPayment + broker.asset(2).value());
2464+
STAmount const payoffAmount{broker.asset, rawPayoff};
2465+
BEAST_EXPECTS(
2466+
payoffAmount ==
2467+
broker.asset(Number(1024014840139457, -12)),
2468+
to_string(payoffAmount));
2469+
BEAST_EXPECT(payoffAmount > state.principalOutstanding);
2470+
return payoffAmount;
2471+
}();
24622472

24632473
singlePayment(
24642474
loanKeylet,
@@ -4013,7 +4023,7 @@ class Loan_test : public beast::unit_test::suite
40134023
createJson = env.json(createJson, sig(sfCounterpartySignature, lender));
40144024
// Fails in preclaim because principal requested can't be
40154025
// represented as XRP
4016-
env(createJson, ter(tecPRECISION_LOSS));
4026+
env(createJson, ter(tecPRECISION_LOSS), THISLINE);
40174027
env.close();
40184028

40194029
BEAST_EXPECT(!env.le(keylet));
@@ -4025,7 +4035,7 @@ class Loan_test : public beast::unit_test::suite
40254035
createJson = env.json(createJson, sig(sfCounterpartySignature, lender));
40264036
// Fails in doApply because the payment is too small to be
40274037
// represented as XRP.
4028-
env(createJson, ter(tecPRECISION_LOSS));
4038+
env(createJson, ter(tecPRECISION_LOSS), THISLINE);
40294039
env.close();
40304040
}
40314041

@@ -5000,7 +5010,7 @@ class Loan_test : public beast::unit_test::suite
50005010
auto const keylet = keylet::loan(broker.brokerID, loanSequence);
50015011

50025012
createJson = env.json(createJson, sig(sfCounterpartySignature, lender));
5003-
env(createJson, ter(tecPRECISION_LOSS));
5013+
env(createJson, ter(tecPRECISION_LOSS), THISLINE);
50045014
env.close(startDate);
50055015

50065016
auto loanPayTx = env.json(
@@ -6148,15 +6158,16 @@ class Loan_test : public beast::unit_test::suite
61486158
// Accrued + prepayment-penalty interest based on current periodic
61496159
// schedule
61506160
auto const fullPaymentInterest = computeFullPaymentInterest(
6151-
after.periodicPayment,
6161+
detail::loanPrincipalFromPeriodicPayment(
6162+
after.periodicPayment, periodicRate2, after.paymentRemaining),
61526163
periodicRate2,
6153-
after.paymentRemaining,
61546164
env.current()->parentCloseTime(),
61556165
after.paymentInterval,
61566166
after.previousPaymentDate,
61576167
static_cast<std::uint32_t>(
61586168
after.startDate.time_since_epoch().count()),
61596169
closeInterestRate);
6170+
61606171
// Round to asset scale and split interest/fee parts
61616172
auto const roundedInterest =
61626173
roundToAsset(asset.raw(), fullPaymentInterest, after.loanScale);
@@ -6184,9 +6195,9 @@ class Loan_test : public beast::unit_test::suite
61846195
// window by clamping prevPaymentDate to 'now' for the full-pay path.
61856196
auto const prevClamped = std::min(after.previousPaymentDate, nowSecs);
61866197
auto const fullPaymentInterestClamped = computeFullPaymentInterest(
6187-
after.periodicPayment,
6198+
detail::loanPrincipalFromPeriodicPayment(
6199+
after.periodicPayment, periodicRate2, after.paymentRemaining),
61886200
periodicRate2,
6189-
after.paymentRemaining,
61906201
env.current()->parentCloseTime(),
61916202
after.paymentInterval,
61926203
prevClamped,

src/xrpld/app/misc/LendingHelpers.h

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,12 @@ adjustImpreciseNumber(
179179
}
180180

181181
inline int
182-
getVaultScale(SLE::const_ref vaultSle)
182+
getAssetsTotalScale(SLE::const_ref vaultSle)
183183
{
184184
if (!vaultSle)
185185
return Number::minExponent - 1; // LCOV_EXCL_LINE
186-
return vaultSle->at(sfAssetsTotal).exponent();
186+
return STAmount{vaultSle->at(sfAsset), vaultSle->at(sfAssetsTotal)}
187+
.exponent();
187188
}
188189

189190
TER
@@ -202,14 +203,6 @@ computeRawLoanState(
202203
std::uint32_t const paymentRemaining,
203204
TenthBips32 const managementFeeRate);
204205

205-
LoanState
206-
computeRawLoanState(
207-
Number const& periodicPayment,
208-
TenthBips32 interestRate,
209-
std::uint32_t paymentInterval,
210-
std::uint32_t const paymentRemaining,
211-
TenthBips32 const managementFeeRate);
212-
213206
// Constructs a valid LoanState object from arbitrary inputs
214207
LoanState
215208
constructLoanState(
@@ -239,17 +232,6 @@ computeFullPaymentInterest(
239232
std::uint32_t startDate,
240233
TenthBips32 closeInterestRate);
241234

242-
Number
243-
computeFullPaymentInterest(
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-
253235
namespace detail {
254236
// These classes and functions should only be accessed by LendingHelper
255237
// functions and unit tests
@@ -387,6 +369,58 @@ struct LoanStateDeltas
387369
nonNegative();
388370
};
389371

372+
Number
373+
computeRaisedRate(Number const& periodicRate, std::uint32_t paymentsRemaining);
374+
375+
Number
376+
computePaymentFactor(
377+
Number const& periodicRate,
378+
std::uint32_t paymentsRemaining);
379+
380+
std::pair<Number, Number>
381+
computeInterestAndFeeParts(
382+
Asset const& asset,
383+
Number const& interest,
384+
TenthBips16 managementFeeRate,
385+
std::int32_t loanScale);
386+
387+
Number
388+
loanPeriodicPayment(
389+
Number const& principalOutstanding,
390+
Number const& periodicRate,
391+
std::uint32_t paymentsRemaining);
392+
393+
Number
394+
loanPrincipalFromPeriodicPayment(
395+
Number const& periodicPayment,
396+
Number const& periodicRate,
397+
std::uint32_t paymentsRemaining);
398+
399+
Number
400+
loanLatePaymentInterest(
401+
Number const& principalOutstanding,
402+
TenthBips32 lateInterestRate,
403+
NetClock::time_point parentCloseTime,
404+
std::uint32_t nextPaymentDueDate);
405+
406+
Number
407+
loanAccruedInterest(
408+
Number const& principalOutstanding,
409+
Number const& periodicRate,
410+
NetClock::time_point parentCloseTime,
411+
std::uint32_t startDate,
412+
std::uint32_t prevPaymentDate,
413+
std::uint32_t paymentInterval);
414+
415+
ExtendedPaymentComponents
416+
computeOverpaymentComponents(
417+
Asset const& asset,
418+
int32_t const loanScale,
419+
Number const& overpayment,
420+
TenthBips32 const overpaymentInterestRate,
421+
TenthBips32 const overpaymentFeeRate,
422+
TenthBips16 const managementFeeRate);
423+
390424
PaymentComponents
391425
computePaymentComponents(
392426
Asset const& asset,
@@ -418,7 +452,8 @@ computeLoanProperties(
418452
std::uint32_t paymentInterval,
419453
std::uint32_t paymentsRemaining,
420454
TenthBips32 managementFeeRate,
421-
std::int32_t minimumScale);
455+
std::int32_t minimumScale,
456+
beast::Journal j);
422457

423458
bool
424459
isRounded(Asset const& asset, Number const& value, std::int32_t scale);

0 commit comments

Comments
 (0)