238 auto const amount = tx[sfAmount];
240 auto const loanID = tx[sfLoanID];
244 std::int32_t const loanScale = loanSle->at(sfLoanScale);
246 auto const brokerID = loanSle->at(sfLoanBrokerID);
250 auto const brokerOwner = brokerSle->at(sfOwner);
251 auto const brokerPseudoAccount = brokerSle->at(sfAccount);
252 auto const vaultID = brokerSle->at(sfVaultID);
256 auto const vaultPseudoAccount = vaultSle->at(sfAccount);
257 auto const asset = *vaultSle->at(sfAsset);
260 auto coverAvailableProxy = brokerSle->at(sfCoverAvailable);
261 TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
262 auto debtTotalProxy = brokerSle->at(sfDebtTotal);
271 bool const sendBrokerFeeToOwner = [&]() {
276 return coverAvailableProxy >=
284 auto const brokerPayee =
285 sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount;
287 if (!sendBrokerFeeToOwner)
294 <<
"Both Loan Broker and Loan Broker pseudo-account "
295 "can not receive funds (deep frozen).";
323 asset,
view, loanSle, brokerSle, amount, paymentType,
j_);
328 paymentParts.
error(),
329 "xrpl::LoanPay::doApply",
330 "payment error is an error");
331 return paymentParts.
error();
340 paymentParts->principalPaid >= 0,
341 "xrpl::LoanPay::doApply",
342 "valid principal paid");
345 paymentParts->interestPaid >= 0,
346 "xrpl::LoanPay::doApply",
347 "valid interest paid");
350 paymentParts->principalPaid + paymentParts->interestPaid > 0,
351 "xrpl::LoanPay::doApply",
354 paymentParts->feePaid >= 0,
"xrpl::LoanPay::doApply",
"valid fee paid");
356 if (paymentParts->principalPaid < 0 || paymentParts->interestPaid < 0 ||
357 paymentParts->feePaid < 0)
360 JLOG(
j_.
fatal()) <<
"Loan payment computation returned invalid values.";
365 JLOG(
j_.
debug()) <<
"Loan Pay: principal paid: "
366 << paymentParts->principalPaid
367 <<
", interest paid: " << paymentParts->interestPaid
368 <<
", fee paid: " << paymentParts->feePaid
369 <<
", value change: " << paymentParts->valueChange;
375 auto assetsAvailableProxy = vaultSle->at(sfAssetsAvailable);
376 auto assetsTotalProxy = vaultSle->at(sfAssetsTotal);
380 auto const vaultScale = assetsTotalProxy.value().exponent();
382 auto const totalPaidToVaultRaw =
383 paymentParts->principalPaid + paymentParts->interestPaid;
384 auto const totalPaidToVaultRounded =
387 !asset.integral() || totalPaidToVaultRaw == totalPaidToVaultRounded,
388 "xrpl::LoanPay::doApply",
389 "rounding does nothing for integral asset");
395 auto const totalPaidToVaultForDebt =
396 totalPaidToVaultRaw - paymentParts->valueChange;
398 auto const totalPaidToBroker = paymentParts->feePaid;
401 (totalPaidToVaultRaw + totalPaidToBroker) ==
402 (paymentParts->principalPaid + paymentParts->interestPaid +
403 paymentParts->feePaid),
404 "xrpl::LoanPay::doApply",
411 isRounded(asset, totalPaidToVaultForDebt, loanScale),
412 "xrpl::LoanPay::doApply",
413 "totalPaidToVaultForDebt rounding good");
418 debtTotalProxy, -totalPaidToVaultForDebt, asset, vaultScale);
424 Number const assetsAvailableBefore = *assetsAvailableProxy;
435 assetsAvailableBefore == pseudoAccountBalanceBefore,
436 "xrpl::LoanPay::doApply",
437 "vault pseudo balance agrees before");
439 assetsAvailableProxy += totalPaidToVaultRounded;
440 assetsTotalProxy += paymentParts->valueChange;
443 *assetsAvailableProxy <= *assetsTotalProxy,
444 "xrpl::LoanPay::doApply",
445 "assets available must not be greater than assets outstanding");
447 if (*assetsAvailableProxy > *assetsTotalProxy)
455 JLOG(
j_.
debug()) <<
"total paid to vault raw: " << totalPaidToVaultRaw
456 <<
", total paid to vault rounded: "
457 << totalPaidToVaultRounded
458 <<
", total paid to broker: " << totalPaidToBroker
459 <<
", amount from transaction: " << amount;
463 totalPaidToVaultRounded + totalPaidToBroker <= amount,
464 "xrpl::LoanPay::doApply",
465 "amount is sufficient");
467 if (!sendBrokerFeeToOwner)
473 coverAvailableProxy += totalPaidToBroker;
479 auto const vaultBalanceBefore =
account_ == vaultPseudoAccount
488 auto const brokerBalanceBefore =
account_ == brokerPayee
494 if (totalPaidToVaultRounded != beast::zero)
501 if (totalPaidToBroker != beast::zero)
509 brokerPayeeSle->at(sfBalance).value().xrp(),
526 {{vaultPseudoAccount, totalPaidToVaultRounded},
527 {brokerPayee, totalPaidToBroker}},
532 Number const assetsAvailableAfter = *assetsAvailableProxy;
541 assetsAvailableAfter == pseudoAccountBalanceAfter,
542 "xrpl::LoanPay::doApply",
543 "vault pseudo balance agrees after");
548 auto const vaultBalanceAfter =
account_ == vaultPseudoAccount
557 auto const brokerBalanceAfter =
account_ == brokerPayee
563 accountBalanceBefore + vaultBalanceBefore + brokerBalanceBefore ==
564 accountBalanceAfter + vaultBalanceAfter + brokerBalanceAfter,
565 "xrpl::LoanPay::doApply",
566 "funds are conserved (with rounding)");
568 accountBalanceAfter >= beast::zero,
569 "xrpl::LoanPay::doApply",
570 "positive account balance");
572 accountBalanceAfter < accountBalanceBefore ||
574 "xrpl::LoanPay::doApply",
575 "account balance decreased");
577 vaultBalanceAfter >= beast::zero && brokerBalanceAfter >= beast::zero,
578 "xrpl::LoanPay::doApply",
579 "positive vault and broker balances");
581 vaultBalanceAfter >= vaultBalanceBefore,
582 "xrpl::LoanPay::doApply",
583 "vault balance did not decrease");
585 brokerBalanceAfter >= brokerBalanceBefore,
586 "xrpl::LoanPay::doApply",
587 "broker balance did not decrease");
589 vaultBalanceAfter > vaultBalanceBefore ||
590 brokerBalanceAfter > brokerBalanceBefore,
591 "xrpl::LoanPay::doApply",
592 "vault and/or broker balance increased");
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.