Compare commits

...

28 Commits

Author SHA1 Message Date
Ed Hennis
03a2e10600 Merge branch 'ximinez/lending-XLS-66-ongoing' into ximinez/lending-XLS-66-test-wip 2025-12-05 21:29:32 -05:00
Ed Hennis
d475cb96c1 Merge branch 'ximinez/lending-XLS-66-ongoing' into ximinez/lending-XLS-66-test-wip 2025-12-05 21:13:33 -05:00
Ed Hennis
24a0af37d8 Merge branch 'ximinez/lending-XLS-66-ongoing' into ximinez/lending-XLS-66-test-wip 2025-12-03 15:33:58 -05:00
Ed Hennis
c740b06861 Merge branch 'ximinez/lending-XLS-66-2' into ximinez/lending-XLS-66-test-wip 2025-12-02 12:33:41 -05:00
Ed Hennis
8e4be94f4a Merge remote-tracking branch 'XRPLF/ximinez/develop-nolending' into ximinez/lending-XLS-66-2
* XRPLF/ximinez/develop-nolending:
  Revert "Implement Lending Protocol (unsupported) (5270)"
  Implement Lending Protocol (unsupported) (5270)
2025-12-02 12:26:11 -05:00
Ed Hennis
1f3ded7116 Test updates - show balances in runLoan() 2025-12-02 10:50:57 -05:00
Ed Hennis
aa1234199a Review feedback from @dangell7: early return & coverage
- Exclude LogicError lines in ApplyView.cpp (specifically directory
  operations) from code coverage.
- Replace the ability to set the next page on a new directory page with
  an assert, because nothing uses it right now.
- Early return with success for batch inner transactions in preflight2.
2025-12-02 10:48:22 -05:00
Ed Hennis
14888c6d1f Merge branch 'ximinez/lending-XLS-66-2' into ximinez/lending-XLS-66-test-wip 2025-12-01 16:50:24 -05:00
Ed Hennis
da9a483b79 Merge branch 'ximinez/develop-nolending' into ximinez/lending-XLS-66-2 2025-12-01 16:49:54 -05:00
Vito Tumas
f447827474 Fix overpayment asserts (#6084) 2025-12-01 14:59:12 -05:00
Ed Hennis
aa93a779a4 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-12-01 14:41:10 -05:00
Ed Hennis
19c72b30e4 Merge branch 'develop' into ximinez/lending-XLS-66 2025-12-01 14:40:31 -05:00
Ed Hennis
07497322de Merge branch 'ximinez/develop-nolending' into ximinez/lending-XLS-66-2 2025-12-01 14:40:27 -05:00
Ed Hennis
530fccaa7e Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-28 15:52:52 -05:00
Ed Hennis
e8cb14c522 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-26 00:25:43 -05:00
Ed Hennis
12a5c0a698 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-25 14:55:32 -05:00
Ed Hennis
30cda21f24 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-24 21:49:34 -05:00
Ed Hennis
fc280d42bf Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-24 21:42:52 -05:00
Ed Hennis
683c9c31c9 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-24 21:30:46 -05:00
Ed Hennis
a9793b2565 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-21 14:34:57 -05:00
Ed Hennis
4ef7f18f20 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-18 22:51:29 -05:00
Ed Hennis
e6c6d0f5d1 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-15 03:09:04 -05:00
Ed Hennis
f0326dcbb4 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-13 14:45:30 -05:00
Ed Hennis
85af14295c Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-13 12:20:30 -05:00
Ed Hennis
8a16afc23b Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-13 01:53:28 -05:00
Ed Hennis
8b7da79f64 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-12 20:12:23 -05:00
Ed Hennis
f9c9b1d2e3 Merge branch 'ximinez/lending-XLS-66' into ximinez/lending-XLS-66-test-wip 2025-11-12 14:17:10 -05:00
Ed Hennis
d0c4adf202 Compute Loan unit test values dynamically
- Not quite working
2025-11-10 19:54:00 -05:00

View File

@@ -521,6 +521,35 @@ protected:
.paymentInterval = loan->at(sfPaymentInterval),
.interestRate = TenthBips32{loan->at(sfInterestRate)},
};
BEAST_EXPECT(state.previousPaymentDate == 0);
BEAST_EXPECT(
tp{d{state.nextPaymentDate}} == state.startDate + 600s);
BEAST_EXPECT(state.paymentRemaining == 12);
BEAST_EXPECT(
state.principalOutstanding == broker.asset(loanAmount).value());
BEAST_EXPECT(
state.loanScale ==
(broker.asset.integral()
? 0
: state.principalOutstanding.exponent()));
BEAST_EXPECT(state.paymentInterval == 600);
BEAST_EXPECT(
state.totalValue ==
roundToAsset(
broker.asset,
state.periodicPayment * state.paymentRemaining,
state.loanScale));
BEAST_EXPECT(
state.managementFeeOutstanding ==
computeFee(
broker.asset,
state.totalValue - state.principalOutstanding,
managementFeeRateParameter,
state.loanScale));
verifyLoanStatus(state);
return state;
}
return LoanState{};
}
@@ -532,6 +561,7 @@ protected:
jtx::Env const& env,
BrokerInfo const& broker,
Keylet const& loanKeylet,
Number const& loanAmount,
VerifyLoanStatus const& verifyLoanStatus)
{
using namespace std::chrono_literals;
@@ -1369,10 +1399,10 @@ protected:
.counterpartyExplicit = false,
.principalRequest = loanAmount,
.setFee = loanSetFee,
.originationFee = 1,
.serviceFee = 2,
.lateFee = 3,
.closeFee = 4,
.originationFee = loanAmount * Number(1, -3),
.serviceFee = loanAmount * Number(2, -3),
.lateFee = loanAmount * Number(3, -3),
.closeFee = loanAmount * Number(4, -3),
.overFee = applyExponent(percentageToTenthBips(5) / 10),
.interest = applyExponent(percentageToTenthBips(12)),
// 2.4%
@@ -1473,7 +1503,8 @@ protected:
loan->at(sfPrincipalOutstanding) == principalRequestAmount);
}
auto state = getCurrentState(env, broker, keylet, verifyLoanStatus);
auto state =
getCurrentState(env, broker, keylet, loanAmount, verifyLoanStatus);
auto const loanProperties = computeLoanProperties(
broker.asset.raw(),
@@ -1648,8 +1679,8 @@ protected:
auto const currencyLabel = getCurrencyLabel(asset);
auto const caseLabel = [&]() {
std::stringstream ss;
ss << "Lifecycle: " << loanAmount << " " << currencyLabel
<< " Scale interest to: " << interestExponent << " ";
ss << "Lifecycle: " << " " << currencyLabel << " Interest scale: "
<< Number(1, interestExponent) " Amount: " << loanAmount;
return ss.str();
}();
testcase << caseLabel;
@@ -2150,8 +2181,8 @@ protected:
// Default the loan
// Initialize values with the current state
auto state =
getCurrentState(env, broker, loanKeylet, verifyLoanStatus);
auto state = getCurrentState(
env, broker, loanKeylet, loanAmount, verifyLoanStatus);
BEAST_EXPECT(state.flags == baseFlag);
auto const& broker = verifyLoanStatus.broker;
@@ -2379,8 +2410,9 @@ protected:
VerifyLoanStatus const& verifyLoanStatus) {
// toEndOfLife
//
auto state =
getCurrentState(env, broker, loanKeylet, verifyLoanStatus);
auto state = getCurrentState(
env, broker, loanKeylet, loanAmount, verifyLoanStatus);
BEAST_EXPECT(state.flags == baseFlag);
env.close(state.startDate + 20s);
auto const loanAge = (env.now() - state.startDate).count();
BEAST_EXPECT(loanAge == 30);
@@ -2407,22 +2439,38 @@ protected:
interval};
BEAST_EXPECT(
accruedInterest ==
broker.asset(Number(1141552511415525, -19)));
broker.asset(loanAmount * Number(1141552511415525, -22)));
STAmount const prepaymentPenalty{
broker.asset, state.principalOutstanding * Number(36, -3)};
BEAST_EXPECT(prepaymentPenalty == broker.asset(36));
STAmount const closePaymentFee = broker.asset(4);
broker.asset,
state.principalOutstanding *
Number(36, interestExponent - 3)};
BEAST_EXPECT(
prepaymentPenalty ==
broker.asset(
loanAmount * Number(36, interestExponent - 3)));
STAmount const closePaymentFee =
broker.asset(loanAmount * Number(4, -3));
auto const payoffAmount = roundToScale(
principalOutstanding + accruedInterest + prepaymentPenalty +
closePaymentFee,
state.loanScale);
// TODO: Figure out what's wrong with this calculation
// STAmount{broker.asset, state.principalOutstanding} +
// accruedInterest + prepaymentPenalty + closePaymentFee;
BEAST_EXPECT(
payoffAmount ==
roundToAsset(
broker.asset,
broker.asset(Number(1040000114155251, -12)).number(),
broker.asset(loanAmount * Number(1040000114155251, -15))
.number(),
state.loanScale));
// Try to pay a little extra to show that it's _not_
// taken
auto const transactionAmount =
payoffAmount + broker.asset(loanAmount * Number(1, -2));
env(pay(borrower, loanKeylet.key, transactionAmount));
// The terms of this loan actually make the early payoff
// more expensive than just making payments
BEAST_EXPECT(
@@ -2611,8 +2659,8 @@ protected:
// toEndOfLife
//
// Draw and make multiple payments
auto state =
getCurrentState(env, broker, loanKeylet, verifyLoanStatus);
auto state = getCurrentState(
env, broker, loanKeylet, loanAmount, verifyLoanStatus);
BEAST_EXPECT(state.flags == 0);
env.close();
@@ -2724,6 +2772,21 @@ protected:
while (state.paymentRemaining > 0)
{
// Try to pay a little extra to show that it's _not_
// taken
STAmount const transactionAmount =
STAmount{broker.asset, totalDue} +
broker.asset(loanAmount * Number(1, -2));
// Only check the first payment since the rounding may
// drift as payments are made
BEAST_EXPECT(
transactionAmount ==
roundToScale(
broker.asset(
Number(9533457001162141, -14), Number::upward),
state.loanScale,
Number::upward));
// Compute the expected principal amount
auto const paymentComponents =
detail::computePaymentComponents(
@@ -2805,7 +2868,7 @@ protected:
Number::upward) ==
roundToScale(
broker.asset(
Number(8333228695260180, -14),
loanAmount * Number(8333228695260180, -17),
Number::upward),
state.loanScale,
Number::upward));
@@ -2917,6 +2980,10 @@ protected:
ter(tecNO_PERMISSION));
env(manage(lender, loanKeylet.key, tfLoanDefault),
ter(tecNO_PERMISSION));
// Can't make a payment on it either
env(pay(borrower, loanKeylet.key, broker.asset(loanAmount)),
ter(tecKILLED));
});
#if LOANTODO
@@ -3659,19 +3726,23 @@ protected:
// Create and update Loans
for (auto const& broker : brokers)
{
for (int amountExponent = 3; amountExponent >= 3; --amountExponent)
for (int amountMantissa : {1, 3, 7})
{
Number const loanAmount{1, amountExponent};
for (int interestExponent = 0; interestExponent >= 0;
--interestExponent)
for (int amountExponent = 3; amountExponent >= -5;
amountExponent -= 4)
{
testCaseWrapper(
env,
mptt,
assets,
broker,
loanAmount,
interestExponent);
Number const loanAmount{amountMantissa, amountExponent};
for (int interestExponent = 1 - 1; interestExponent >= -2;
--interestExponent)
{
testCaseWrapper(
env,
mptt,
assets,
broker,
loanAmount,
interestExponent);
}
}
}