From 30e2650ff985a5bbc93e2f7374b2258e385dd990 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 22 Oct 2025 17:15:34 -0400 Subject: [PATCH] Use a loan scale of 0 for integral asset types - Loan scale is completely irrelevant to integral types (XRP, MPT), and the field is "soeDEFAULT", so when set to 0, it won't be stored on ledger, saving a little bit of space. --- src/test/app/Loan_test.cpp | 20 ++++++++++++++------ src/xrpld/app/misc/LendingHelpers.h | 9 ++++++++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 9fd0f8541a..45a076b2ed 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -404,7 +404,10 @@ class Loan_test : public beast::unit_test::suite BEAST_EXPECT( state.principalOutstanding == broker.asset(1000).value()); BEAST_EXPECT( - state.principalOutstanding.exponent() == state.loanScale); + state.loanScale == + (broker.asset.integral() + ? 0 + : state.principalOutstanding.exponent())); BEAST_EXPECT(state.paymentInterval == 600); BEAST_EXPECT( state.totalValue == @@ -636,7 +639,9 @@ class Loan_test : public beast::unit_test::suite BEAST_EXPECT( loan->at(sfNextPaymentDueDate) == startDate + interval); BEAST_EXPECT(loan->at(sfPaymentRemaining) == total); - BEAST_EXPECT(loan->at(sfLoanScale) == principalRequest.exponent()); + BEAST_EXPECT( + loan->at(sfLoanScale) == + (broker.asset.integral() ? 0 : principalRequest.exponent())); BEAST_EXPECT(loan->at(sfPrincipalOutstanding) == principalRequest); } @@ -654,7 +659,7 @@ class Loan_test : public beast::unit_test::suite 0, startDate + interval, total, - principalRequest.exponent(), + broker.asset.integral() ? 0 : principalRequest.exponent(), loanProperties.totalValueOutstanding, principalRequest, loanProperties.managementFeeOwedToBroker, @@ -711,7 +716,7 @@ class Loan_test : public beast::unit_test::suite 0, nextDueDate, total, - principalRequest.exponent(), + broker.asset.integral() ? 0 : principalRequest.exponent(), loanProperties.totalValueOutstanding, principalRequest, loanProperties.managementFeeOwedToBroker, @@ -1196,7 +1201,10 @@ class Loan_test : public beast::unit_test::suite BEAST_EXPECT(brokerSle)) { BEAST_EXPECT( - state.loanScale == state.principalOutstanding.exponent()); + state.loanScale == + (broker.asset.integral() + ? 0 + : state.principalOutstanding.exponent())); auto const defaultAmount = roundToAsset( broker.asset, std::min( @@ -2166,7 +2174,7 @@ class Loan_test : public beast::unit_test::suite BEAST_EXPECT(!loan.isMember(sfPreviousPaymentDate)); BEAST_EXPECT(loan[sfPrincipalOutstanding] == "1000000000"); BEAST_EXPECT(loan[sfTotalValueOutstanding] == "1000000000"); - BEAST_EXPECT(loan[sfLoanScale] == -6); + BEAST_EXPECT(!loan.isMember(sfLoanScale)); BEAST_EXPECT( loan[sfStartDate].asUInt() == startDate.time_since_epoch().count()); diff --git a/src/xrpld/app/misc/LendingHelpers.h b/src/xrpld/app/misc/LendingHelpers.h index b843bfc344..b63cdcb505 100644 --- a/src/xrpld/app/misc/LendingHelpers.h +++ b/src/xrpld/app/misc/LendingHelpers.h @@ -1158,7 +1158,7 @@ computeLoanProperties( auto const periodicPayment = detail::loanPeriodicPayment( principalOutstanding, periodicRate, paymentsRemaining); - Number const totalValueOutstanding = [&]() { + STAmount const totalValueOutstanding = [&]() { NumberRoundModeGuard mg(Number::to_nearest); // Use STAmount's internal rounding instead of roundToAsset, because // we're going to use this result to determine the scale for all the @@ -1176,6 +1176,13 @@ computeLoanProperties( // biggest number involved (barring unusual parameters for late, full, or // over payments) auto const loanScale = totalValueOutstanding.exponent(); + XRPL_ASSERT_PARTS( + totalValueOutstanding.integral() && loanScale == 0 || + !totalValueOutstanding.integral() && + loanScale == + static_cast(totalValueOutstanding).exponent(), + "ripple::computeLoanProperties", + "loanScale value fits expectations"); // Since we just figured out the loan scale, we haven't been able to // validate that the principal fits in it, so to allow this function to