54 bool const lpV11Enabled = view.
rules().
enabled(featureLendingProtocolV1_1);
88 JLOG(j.
fatal()) <<
"Invariant failed: closed-ended loan final payment "
89 "must precede RedemptionDate by at least "
90 "kLoanRedemptionBuffer";
99 if (
after->at(sfPaymentRemaining) == 0 &&
104 JLOG(j.
fatal()) <<
"Invariant failed: Loan with zero payments "
105 "remaining has not been paid off";
110 if (
after->at(sfPaymentRemaining) != 0 &&
115 JLOG(j.
fatal()) <<
"Invariant failed: Fully paid off Loan still has payments remaining";
121 if (!lpV11Enabled && before &&
122 (before->isFlag(lsfLoanOverpayment) !=
after->isFlag(lsfLoanOverpayment)))
124 JLOG(j.
fatal()) <<
"Invariant failed: Loan Overpayment flag changed";
128 for (
auto const field :
132 &sfPrincipalOutstanding,
133 &sfTotalValueOutstanding,
134 &sfManagementFeeOutstanding})
136 if (
after->at(*field) < 0)
138 JLOG(j.
fatal()) <<
"Invariant failed: " << field->getName() <<
" is negative ";
143 for (
auto const field : {
147 if (
after->at(*field) <= 0)
149 JLOG(j.
fatal()) <<
"Invariant failed: " << field->getName()
150 <<
" is zero or negative ";
157 if (!before && txType != ttLOAN_SET)
159 JLOG(j.
fatal()) <<
"Invariant failed: Loan created by a transaction "
160 "other than LoanSet";
164 if (
after->at(sfPaymentRemaining) == 0 &&
165 after->at(~sfNextPaymentDueDate).value_or(0) != 0)
167 JLOG(j.
fatal()) <<
"Invariant failed: Loan with zero payments must have zero next "
174 bool const wasImpaired = before->isFlag(lsfLoanImpaired);
175 bool const isImpaired =
after->isFlag(lsfLoanImpaired);
176 bool const wasDefaulted = before->isFlag(lsfLoanDefault);
177 bool const isDefaulted =
after->isFlag(lsfLoanDefault);
179 if (wasImpaired != isImpaired && txType != ttLOAN_MANAGE && txType != ttLOAN_PAY)
181 JLOG(j.
fatal()) <<
"Invariant failed: lsfLoanImpaired changed "
182 "outside LoanManage or LoanPay";
185 if (wasDefaulted != isDefaulted && txType != ttLOAN_MANAGE)
187 JLOG(j.
fatal()) <<
"Invariant failed: lsfLoanDefault changed "
188 "outside LoanManage";
199 JLOG(j.
fatal()) <<
"Invariant failed: Loan broker does not exist";
205 JLOG(j.
fatal()) <<
"Invariant failed: Loan broker vault does not exist";
215 auto const interestDue =
after->at(sfTotalValueOutstanding) -
216 after->at(sfPrincipalOutstanding) -
after->at(sfManagementFeeOutstanding);
220 bool const integral =
Asset{vaultSle->at(sfAsset)}.
integral();
223 if (interestDue < tolerance)
225 JLOG(j.
fatal()) <<
"Invariant failed: Loan interest due is negative";
243 if (before &&
after->at(sfPaymentRemaining) != 0)
245 if (
after->at(sfPrincipalOutstanding) > before->at(sfPrincipalOutstanding))
247 JLOG(j.
fatal()) <<
"Invariant failed: loan pay must not increase "
248 "PrincipalOutstanding on a non-full-repayment";
251 if (
after->at(sfTotalValueOutstanding) > before->at(sfTotalValueOutstanding))
253 JLOG(j.
fatal()) <<
"Invariant failed: loan pay must not increase "
254 "TotalValueOutstanding on a non-full-repayment";
257 if (
after->at(sfPrincipalOutstanding) == before->at(sfPrincipalOutstanding) &&
258 after->at(sfTotalValueOutstanding) == before->at(sfTotalValueOutstanding))
260 JLOG(j.
fatal()) <<
"Invariant failed: loan pay must decrease "
261 "PrincipalOutstanding or TotalValueOutstanding "
262 "on a non-full-repayment";
265 if (
after->at(sfPaymentRemaining) >= before->at(sfPaymentRemaining))
267 JLOG(j.
fatal()) <<
"Invariant failed: loan pay must decrease "
268 "PaymentRemaining on a non-full-repayment";
272 std::uint32_t const beforeDue = before->at(~sfNextPaymentDueDate).value_or(0);
275 if (afterDue <= beforeDue || interval == 0 ||
276 (afterDue - beforeDue) % interval != 0)
278 JLOG(j.
fatal()) <<
"Invariant failed: loan pay must advance "
279 "NextPaymentDueDate by a positive multiple of "
280 "PaymentInterval on a non-full-repayment";
289 if (lpV11Enabled && txType != ttLOAN_DELETE && !
deletedLoans_.empty())
291 JLOG(j.
fatal()) <<
"Invariant failed: Loan deleted by a transaction "
292 "other than LoanDelete";
std::shared_ptr< STLedgerEntry const > const & ConstRef