242 auto const amount = tx[sfAmount];
244 auto const loanID = tx[sfLoanID];
248 std::int32_t const loanScale = loanSle->at(sfLoanScale);
250 auto const brokerID = loanSle->at(sfLoanBrokerID);
254 auto const brokerOwner = brokerSle->at(sfOwner);
255 auto const brokerPseudoAccount = brokerSle->at(sfAccount);
256 auto const vaultID = brokerSle->at(sfVaultID);
260 auto const vaultPseudoAccount = vaultSle->at(sfAccount);
261 auto const asset = *vaultSle->at(sfAsset);
264 auto coverAvailableProxy = brokerSle->at(sfCoverAvailable);
265 TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
266 auto debtTotalProxy = brokerSle->at(sfDebtTotal);
276 bool const sendBrokerFeeToOwner = [&]() {
281 return coverAvailableProxy >=
283 asset,
tenthBipsOfValue(debtTotalProxy.value(), coverRateMinimum), loanScale) &&
288 auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount;
290 if (!sendBrokerFeeToOwner)
296 JLOG(
j_.
warn()) <<
"Both Loan Broker and Loan Broker pseudo-account "
297 "can not receive funds (deep frozen).";
308 if (loanSle->isFlag(lsfLoanImpaired))
312 JLOG(
j_.
fatal()) <<
"Failed to unimpair loan before payment.";
319 if (tx.isFlag(tfLoanLatePayment))
321 if (tx.isFlag(tfLoanFullPayment))
323 if (tx.isFlag(tfLoanOverpayment))
334 paymentParts.
error(),
"xrpl::LoanPay::doApply",
"payment error is an error");
335 return paymentParts.
error();
344 paymentParts->principalPaid >= 0,
345 "xrpl::LoanPay::doApply",
346 "valid principal paid");
349 paymentParts->interestPaid >= 0,
350 "xrpl::LoanPay::doApply",
351 "valid interest paid");
354 paymentParts->principalPaid + paymentParts->interestPaid > 0,
355 "xrpl::LoanPay::doApply",
357 XRPL_ASSERT_PARTS(paymentParts->feePaid >= 0,
"xrpl::LoanPay::doApply",
"valid fee paid");
359 if (paymentParts->principalPaid < 0 || paymentParts->interestPaid < 0 ||
360 paymentParts->feePaid < 0)
363 JLOG(
j_.
fatal()) <<
"Loan payment computation returned invalid values.";
368 JLOG(
j_.
debug()) <<
"Loan Pay: principal paid: " << paymentParts->principalPaid
369 <<
", interest paid: " << paymentParts->interestPaid
370 <<
", fee paid: " << paymentParts->feePaid
371 <<
", value change: " << paymentParts->valueChange;
377 auto assetsAvailableProxy = vaultSle->at(sfAssetsAvailable);
378 auto assetsTotalProxy = vaultSle->at(sfAssetsTotal);
384 auto const totalPaidToVaultRaw = paymentParts->principalPaid + paymentParts->interestPaid;
385 auto const totalPaidToVaultRounded =
388 !asset.integral() || totalPaidToVaultRaw == totalPaidToVaultRounded,
389 "xrpl::LoanPay::doApply",
390 "rounding does nothing for integral asset");
396 auto const totalPaidToVaultForDebt = totalPaidToVaultRaw - paymentParts->valueChange;
398 auto const totalPaidToBroker = paymentParts->feePaid;
401 (totalPaidToVaultRaw + totalPaidToBroker) ==
402 (paymentParts->principalPaid + paymentParts->interestPaid + paymentParts->feePaid),
403 "xrpl::LoanPay::doApply",
410 isRounded(asset, totalPaidToVaultForDebt, loanScale),
411 "xrpl::LoanPay::doApply",
412 "totalPaidToVaultForDebt rounding good");
424 Number const assetsAvailableBefore = *assetsAvailableProxy;
434 assetsAvailableBefore == pseudoAccountBalanceBefore,
435 "xrpl::LoanPay::doApply",
436 "vault pseudo balance agrees before");
440 assetsAvailableProxy += totalPaidToVaultRounded;
441 assetsTotalProxy += paymentParts->valueChange;
444 *assetsAvailableProxy <= *assetsTotalProxy,
445 "xrpl::LoanPay::doApply",
446 "assets available must not be greater than assets outstanding");
448 if (*assetsAvailableProxy > *assetsTotalProxy)
451 JLOG(
j_.
fatal()) <<
"Vault assets available must not be greater "
452 "than assets outstanding. Available: "
453 << *assetsAvailableProxy <<
", Total: " << *assetsTotalProxy;
458 JLOG(
j_.
debug()) <<
"total paid to vault raw: " << totalPaidToVaultRaw
459 <<
", total paid to vault rounded: " << totalPaidToVaultRounded
460 <<
", total paid to broker: " << totalPaidToBroker
461 <<
", amount from transaction: " << amount;
465 totalPaidToVaultRounded + totalPaidToBroker <= amount,
466 "xrpl::LoanPay::doApply",
467 "amount is sufficient");
469 if (!sendBrokerFeeToOwner)
475 coverAvailableProxy += totalPaidToBroker;
484 *assetsAvailableProxy <= *assetsTotalProxy,
485 "xrpl::LoanPay::doApply",
486 "assets available must not be greater than assets outstanding");
497 auto const vaultBalanceBefore =
account_ == vaultPseudoAccount
507 auto const brokerBalanceBefore =
account_ == brokerPayee
519 if (totalPaidToVaultRounded != beast::zero)
525 if (totalPaidToBroker != beast::zero)
531 view, brokerPayee, brokerPayeeSle->at(sfBalance).value().xrp(), asset,
j_);
547 {{vaultPseudoAccount, totalPaidToVaultRounded}, {brokerPayee, totalPaidToBroker}},
553 Number const assetsAvailableAfter = *assetsAvailableProxy;
562 assetsAvailableAfter == pseudoAccountBalanceAfter,
563 "xrpl::LoanPay::doApply",
564 "vault pseudo balance agrees after");
574 auto const vaultBalanceAfter =
account_ == vaultPseudoAccount
584 auto const brokerBalanceAfter =
account_ == brokerPayee
596 accountBalanceBefore + vaultBalanceBefore + brokerBalanceBefore ==
597 accountBalanceAfter + vaultBalanceAfter + brokerBalanceAfter,
598 "xrpl::LoanPay::doApply",
599 "funds are conserved (with rounding)");
601 accountBalanceAfter >= beast::zero,
"xrpl::LoanPay::doApply",
"positive account balance");
603 accountBalanceAfter < accountBalanceBefore ||
account_ == asset.getIssuer(),
604 "xrpl::LoanPay::doApply",
605 "account balance decreased");
607 vaultBalanceAfter >= beast::zero && brokerBalanceAfter >= beast::zero,
608 "xrpl::LoanPay::doApply",
609 "positive vault and broker balances");
611 vaultBalanceAfter >= vaultBalanceBefore,
612 "xrpl::LoanPay::doApply",
613 "vault balance did not decrease");
615 brokerBalanceAfter >= brokerBalanceBefore,
616 "xrpl::LoanPay::doApply",
617 "broker balance did not decrease");
619 vaultBalanceAfter > vaultBalanceBefore || brokerBalanceAfter > brokerBalanceBefore,
620 "xrpl::LoanPay::doApply",
621 "vault and/or broker balance increased");
State information when determining if a tx is likely to claim a fee.