226 auto const amount = tx[sfAmount];
228 auto const loanID = tx[sfLoanID];
232 std::int32_t const loanScale = loanSle->at(sfLoanScale);
234 auto const brokerID = loanSle->at(sfLoanBrokerID);
238 auto const brokerOwner = brokerSle->at(sfOwner);
239 auto const brokerPseudoAccount = brokerSle->at(sfAccount);
240 auto const vaultID = brokerSle->at(sfVaultID);
244 auto const vaultPseudoAccount = vaultSle->at(sfAccount);
245 auto const asset = *vaultSle->at(sfAsset);
248 auto coverAvailableProxy = brokerSle->at(sfCoverAvailable);
249 TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
250 auto debtTotalProxy = brokerSle->at(sfDebtTotal);
260 bool const sendBrokerFeeToOwner = [&]() {
265 return coverAvailableProxy >=
270 auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount;
272 if (!sendBrokerFeeToOwner)
278 JLOG(
j_.
warn()) <<
"Both Loan Broker and Loan Broker pseudo-account "
279 "can not receive funds (deep frozen).";
294 JLOG(
j_.
fatal()) <<
"Failed to unimpair loan before payment.";
315 XRPL_ASSERT_PARTS(paymentParts.
error(),
"xrpl::LoanPay::doApply",
"payment error is an error");
316 return paymentParts.
error();
325 paymentParts->principalPaid >= 0,
326 "xrpl::LoanPay::doApply",
327 "valid principal paid");
330 paymentParts->interestPaid >= 0,
331 "xrpl::LoanPay::doApply",
332 "valid interest paid");
335 paymentParts->principalPaid + paymentParts->interestPaid > 0,
336 "xrpl::LoanPay::doApply",
338 XRPL_ASSERT_PARTS(paymentParts->feePaid >= 0,
"xrpl::LoanPay::doApply",
"valid fee paid");
340 if (paymentParts->principalPaid < 0 || paymentParts->interestPaid < 0 || paymentParts->feePaid < 0)
343 JLOG(
j_.
fatal()) <<
"Loan payment computation returned invalid values.";
348 JLOG(
j_.
debug()) <<
"Loan Pay: principal paid: " << paymentParts->principalPaid
349 <<
", interest paid: " << paymentParts->interestPaid <<
", fee paid: " << paymentParts->feePaid
350 <<
", value change: " << paymentParts->valueChange;
356 auto assetsAvailableProxy = vaultSle->at(sfAssetsAvailable);
357 auto assetsTotalProxy = vaultSle->at(sfAssetsTotal);
363 auto const totalPaidToVaultRaw = paymentParts->principalPaid + paymentParts->interestPaid;
366 !asset.integral() || totalPaidToVaultRaw == totalPaidToVaultRounded,
367 "xrpl::LoanPay::doApply",
368 "rounding does nothing for integral asset");
374 auto const totalPaidToVaultForDebt = totalPaidToVaultRaw - paymentParts->valueChange;
376 auto const totalPaidToBroker = paymentParts->feePaid;
379 (totalPaidToVaultRaw + totalPaidToBroker) ==
380 (paymentParts->principalPaid + paymentParts->interestPaid + paymentParts->feePaid),
381 "xrpl::LoanPay::doApply",
388 isRounded(asset, totalPaidToVaultForDebt, loanScale),
389 "xrpl::LoanPay::doApply",
390 "totalPaidToVaultForDebt rounding good");
402 Number const assetsAvailableBefore = *assetsAvailableProxy;
407 assetsAvailableBefore == pseudoAccountBalanceBefore,
408 "xrpl::LoanPay::doApply",
409 "vault pseudo balance agrees before");
413 assetsAvailableProxy += totalPaidToVaultRounded;
414 assetsTotalProxy += paymentParts->valueChange;
417 *assetsAvailableProxy <= *assetsTotalProxy,
418 "xrpl::LoanPay::doApply",
419 "assets available must not be greater than assets outstanding");
421 if (*assetsAvailableProxy > *assetsTotalProxy)
424 JLOG(
j_.
fatal()) <<
"Vault assets available must not be greater "
425 "than assets outstanding. Available: "
426 << *assetsAvailableProxy <<
", Total: " << *assetsTotalProxy;
431 JLOG(
j_.
debug()) <<
"total paid to vault raw: " << totalPaidToVaultRaw
432 <<
", total paid to vault rounded: " << totalPaidToVaultRounded
433 <<
", total paid to broker: " << totalPaidToBroker <<
", amount from transaction: " << amount;
437 totalPaidToVaultRounded + totalPaidToBroker <= amount,
"xrpl::LoanPay::doApply",
"amount is sufficient");
439 if (!sendBrokerFeeToOwner)
445 coverAvailableProxy += totalPaidToBroker;
454 *assetsAvailableProxy <= *assetsTotalProxy,
455 "xrpl::LoanPay::doApply",
456 "assets available must not be greater than assets outstanding");
459 auto const accountBalanceBefore =
461 auto const vaultBalanceBefore =
account_ == vaultPseudoAccount
465 auto const brokerBalanceBefore =
account_ == brokerPayee
470 if (totalPaidToVaultRounded != beast::zero)
476 if (totalPaidToBroker != beast::zero)
496 {{vaultPseudoAccount, totalPaidToVaultRounded}, {brokerPayee, totalPaidToBroker}},
502 Number const assetsAvailableAfter = *assetsAvailableProxy;
503 Number const pseudoAccountBalanceAfter =
506 assetsAvailableAfter == pseudoAccountBalanceAfter,
507 "xrpl::LoanPay::doApply",
508 "vault pseudo balance agrees after");
510 auto const accountBalanceAfter =
512 auto const vaultBalanceAfter =
account_ == vaultPseudoAccount
516 auto const brokerBalanceAfter =
account_ == brokerPayee
521 accountBalanceBefore + vaultBalanceBefore + brokerBalanceBefore ==
522 accountBalanceAfter + vaultBalanceAfter + brokerBalanceAfter,
523 "xrpl::LoanPay::doApply",
524 "funds are conserved (with rounding)");
525 XRPL_ASSERT_PARTS(accountBalanceAfter >= beast::zero,
"xrpl::LoanPay::doApply",
"positive account balance");
527 accountBalanceAfter < accountBalanceBefore ||
account_ == asset.getIssuer(),
528 "xrpl::LoanPay::doApply",
529 "account balance decreased");
531 vaultBalanceAfter >= beast::zero && brokerBalanceAfter >= beast::zero,
532 "xrpl::LoanPay::doApply",
533 "positive vault and broker balances");
535 vaultBalanceAfter >= vaultBalanceBefore,
"xrpl::LoanPay::doApply",
"vault balance did not decrease");
537 brokerBalanceAfter >= brokerBalanceBefore,
"xrpl::LoanPay::doApply",
"broker balance did not decrease");
539 vaultBalanceAfter > vaultBalanceBefore || brokerBalanceAfter > brokerBalanceBefore,
540 "xrpl::LoanPay::doApply",
541 "vault and/or broker balance increased");
State information when determining if a tx is likely to claim a fee.