diff --git a/src/libxrpl/tx/transactors/lending/LoanSet.cpp b/src/libxrpl/tx/transactors/lending/LoanSet.cpp index c5b9c420a9..a4575e40b0 100644 --- a/src/libxrpl/tx/transactors/lending/LoanSet.cpp +++ b/src/libxrpl/tx/transactors/lending/LoanSet.cpp @@ -439,9 +439,8 @@ LoanSet::doApply() principalRequested, properties.loanState.managementFeeDue); - auto const vaultMaximum = *vaultSle->at(sfAssetsMaximum); XRPL_ASSERT_PARTS( - vaultMaximum == 0 || vaultMaximum > *vaultTotalProxy, + *vaultSle->at(sfAssetsMaximum) == 0 || *vaultSle->at(sfAssetsMaximum) > *vaultTotalProxy, "xrpl::LoanSet::doApply", "Vault is below maximum limit"); @@ -492,8 +491,9 @@ LoanSet::doApply() auto const loanAssetsToBorrower = principalRequested - originationFee; - auto const newDebtDelta = principalRequested + state.interestDue; - auto const newDebtTotal = brokerSle->at(sfDebtTotal) + newDebtDelta; + auto const [assetsTotalDelta, debtTotalDelta] = + loanOriginationDeltas(ctx_.view().rules(), vaultSle, principalRequested, state.interestDue); + auto const newDebtTotal = brokerSle->at(sfDebtTotal) + debtTotalDelta; if (auto const debtMaximum = brokerSle->at(sfDebtMaximum); debtMaximum != 0 && debtMaximum < newDebtTotal) { @@ -635,8 +635,6 @@ LoanSet::doApply() view.insert(loan); // Update the balances in the vault - auto const [assetsTotalDelta, debtTotalDelta] = - loanOriginationDeltas(ctx_.view().rules(), vaultSle, principalRequested, state.interestDue); vaultAvailableProxy -= principalRequested; vaultTotalProxy += assetsTotalDelta; XRPL_ASSERT_PARTS( diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index ad21a82462..8a6f1669df 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -8684,8 +8684,7 @@ protected: } // AssetsMaximum guard checks interestDue headroom only under - // whole-life accounting (cash-basis never adds interest to - // AssetsTotal). DebtMaximum guard is unconditional either way. + // whole-life accounting; DebtMaximum guard also varies by model. auto runVaultGuard = [&](FeatureBitset features, Number const& slack, TER expected) { Env env(*this, features); @@ -8761,12 +8760,16 @@ protected: runVaultGuard(all_ | featureLendingProtocolV1_1, oneDrop, tesSUCCESS); } - for (auto const features : {all_ | featureLendingProtocolV1_1, all_}) + // DebtMaximum guard: cash-basis projects principal-only DebtTotal; + // whole-life projects principal + interestDue. + for (auto const cashBasis : {true, false}) { testcase( - std::string("cash-basis: LoanSet DebtMaximum guard unaffected by amendment (") + - (features[featureLendingProtocolV1_1] ? "enabled)" : "disabled)")); - Number const newDebtTotal = principalOutstandingCash + interestDueCash; + std::string("LoanSet DebtMaximum guard (") + + (cashBasis ? "cash-basis)" : "whole-life)")); + auto const features = cashBasis ? all_ | featureLendingProtocolV1_1 : all_; + Number const newDebtTotal = + principalOutstandingCash + (cashBasis ? Number{} : interestDueCash); runBrokerGuard(features, newDebtTotal - oneDrop, tecLIMIT_EXCEEDED); runBrokerGuard(features, newDebtTotal, tesSUCCESS); }