Start implementing LoanPay transaction

This commit is contained in:
Ed Hennis
2025-05-07 18:01:59 -04:00
parent f2b78454da
commit 37f365a053
14 changed files with 608 additions and 49 deletions

View File

@@ -776,7 +776,6 @@ TRANSACTION(ttLOAN_DRAW, 81, LoanDraw, noPriv, ({
{sfAmount, soeREQUIRED, soeMPTSupported},
}))
#if 0
/** The Borrower uses this transaction to make a Payment on the Loan. */
#if TRANSACTION_INCLUDE
# include <xrpld/app/tx/detail/LoanPay.h>
@@ -785,7 +784,6 @@ TRANSACTION(ttLOAN_PAY, 82, LoanPay, noPriv, ({
{sfLoanID, soeREQUIRED},
{sfAmount, soeREQUIRED, soeMPTSupported},
}))
#endif
/** This system-generated transaction type is used to update the status of the various amendments.

View File

@@ -159,7 +159,7 @@ class Loan_test : public beast::unit_test::suite
{
TenthBips16 const managementFeeRate{
brokerSle->at(sfManagementFeeRate)};
auto const loanInterest = LoanInterestOutstandingToVault(
auto const loanInterest = LoanInterestOutstandingMinusFee(
broker.asset,
principalOutstanding,
interestRate,
@@ -261,7 +261,7 @@ class Loan_test : public beast::unit_test::suite
env.test.BEAST_EXPECT(
vaultSle->at(sfLossUnrealized) ==
principalOutstanding +
LoanInterestOutstandingToVault(
LoanInterestOutstandingMinusFee(
broker.asset,
principalOutstanding,
interestRate,

View File

@@ -20,6 +20,8 @@
#ifndef RIPPLE_APP_MISC_LENDINGHELPERS_H_INCLUDED
#define RIPPLE_APP_MISC_LENDINGHELPERS_H_INCLUDED
#include <xrpld/ledger/ApplyView.h>
#include <xrpl/basics/Number.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Feature.h>
@@ -31,21 +33,22 @@ struct PreflightContext;
// Lending protocol has dependencies, so capture them here.
bool
LendingProtocolEnabled(PreflightContext const& ctx);
lendingProtocolEnabled(PreflightContext const& ctx);
namespace detail {
// These functions should rarely be used directly. More often, the ultimate
// result needs to be roundToAsset'd.
Number
LoanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval);
struct LoanPaymentParts
{
Number principalPaid;
Number interestPaid;
Number valueChange;
Number feePaid;
};
Number
LoanPeriodicPayment(
Number principalOutstanding,
TenthBips32 interestRate,
std::uint32_t paymentInterval,
std::uint32_t paymentsRemaining);
LoanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval);
Number
LoanTotalValueOutstanding(
@@ -61,27 +64,110 @@ LoanTotalInterestOutstanding(
std::uint32_t paymentInterval,
std::uint32_t paymentsRemaining);
Number
LoanPeriodicPayment(
Number principalOutstanding,
TenthBips32 interestRate,
std::uint32_t paymentInterval,
std::uint32_t paymentsRemaining);
Number
LoanLatePaymentInterest(
Number principalOutstanding,
TenthBips32 lateInterestRate,
NetClock::time_point parentCloseTime,
std::uint32_t startDate,
std::uint32_t prevPaymentDate);
LoanPaymentParts
LoanComputePaymentParts(ApplyView& view, SLE::ref loan);
} // namespace detail
template <AssetType A>
Number
LoanInterestOutstandingToVault(
MinusFee(A const& asset, Number value, TenthBips32 managementFeeRate)
{
return roundToAsset(
asset, tenthBipsOfValue(value, tenthBipsPerUnity - managementFeeRate));
}
template <AssetType A>
Number
LoanInterestOutstandingMinusFee(
A const& asset,
Number principalOutstanding,
TenthBips32 interestRate,
std::uint32_t paymentInterval,
std::uint32_t paymentsRemaining,
TenthBips32 managementFeeRate)
{
return MinusFee(
asset,
detail::LoanTotalInterestOutstanding(
principalOutstanding,
interestRate,
paymentInterval,
paymentsRemaining),
managementFeeRate);
}
template <AssetType A>
Number
LoanPeriodicPayment(
A const& asset,
Number principalOutstanding,
TenthBips32 interestRate,
std::uint32_t paymentInterval,
std::uint32_t paymentsRemaining)
{
return roundToAsset(
asset,
tenthBipsOfValue(
detail::LoanTotalInterestOutstanding(
principalOutstanding,
interestRate,
paymentInterval,
paymentsRemaining),
tenthBipsPerUnity - managementFeeRate));
detail::LoanPeriodicPayment(
principalOutstanding,
interestRate,
paymentInterval,
paymentsRemaining));
}
template <AssetType A>
Number
LoanLatePaymentInterest(
A const& asset,
Number principalOutstanding,
TenthBips32 lateInterestRate,
NetClock::time_point parentCloseTime,
std::uint32_t startDate,
std::uint32_t prevPaymentDate)
{
return roundToAsset(
asset,
detail::LoanLatePaymentInterest(
principalOutstanding,
lateInterestRate,
parentCloseTime,
startDate,
prevPaymentDate));
}
struct LoanPaymentParts
{
STAmount principalPaid;
STAmount interestPaid;
STAmount valueChange;
STAmount feePaid;
};
template <AssetType A>
LoanPaymentParts
LoanComputePaymentParts(A const& asset, ApplyView& view, SLE::ref loan)
{
auto const parts = detail::LoanComputePaymentParts(view, loan);
return LoanPaymentParts{
roundToAsset(asset, parts.principalPaid),
roundToAsset(asset, parts.interestPaid),
roundToAsset(asset, parts.valueChange),
roundToAsset(asset, parts.feePaid)};
}
} // namespace ripple

View File

@@ -21,13 +21,15 @@
//
#include <xrpld/app/tx/detail/Transactor.h>
#include <xrpld/app/tx/detail/VaultCreate.h>
#include <xrpld/ledger/View.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/st.h>
namespace ripple {
bool
LendingProtocolEnabled(PreflightContext const& ctx)
lendingProtocolEnabled(PreflightContext const& ctx)
{
return ctx.rules.enabled(featureLendingProtocol) &&
VaultCreate::isEnabled(ctx);
@@ -44,24 +46,6 @@ LoanPeriodicRate(TenthBips32 interestRate, std::uint32_t paymentInterval)
(365 * 24 * 60 * 60);
}
Number
LoanPeriodicPayment(
Number principalOutstanding,
TenthBips32 interestRate,
std::uint32_t paymentInterval,
std::uint32_t paymentsRemaining)
{
if (principalOutstanding == 0 || paymentsRemaining == 0)
return 0;
Number const periodicRate = LoanPeriodicRate(interestRate, paymentInterval);
// TODO: Need a better name
Number const timeFactor = power(1 + periodicRate, paymentsRemaining);
return principalOutstanding * (periodicRate * timeFactor) /
(timeFactor - 1);
}
Number
LoanTotalValueOutstanding(
Number principalOutstanding,
@@ -92,6 +76,216 @@ LoanTotalInterestOutstanding(
principalOutstanding;
}
Number
LoanPeriodicPayment(
Number principalOutstanding,
Number periodicRate,
std::uint32_t paymentsRemaining)
{
// TODO: Need a better name
Number const timeFactor = power(1 + periodicRate, paymentsRemaining);
return principalOutstanding * (periodicRate * timeFactor) /
(timeFactor - 1);
}
Number
LoanPeriodicPayment(
Number principalOutstanding,
TenthBips32 interestRate,
std::uint32_t paymentInterval,
std::uint32_t paymentsRemaining)
{
if (principalOutstanding == 0 || paymentsRemaining == 0)
return 0;
Number const periodicRate = LoanPeriodicRate(interestRate, paymentInterval);
return LoanPeriodicPayment(
principalOutstanding, periodicRate, paymentsRemaining);
}
Number
LoanLatePaymentInterest(
Number principalOutstanding,
TenthBips32 lateInterestRate,
NetClock::time_point parentCloseTime,
std::uint32_t startDate,
std::uint32_t prevPaymentDate)
{
auto const lastPaymentDate = std::max(prevPaymentDate, startDate);
auto const secondsSinceLastPayment =
parentCloseTime.time_since_epoch().count() - lastPaymentDate;
auto const rate =
LoanPeriodicRate(lateInterestRate, secondsSinceLastPayment);
return principalOutstanding * rate;
}
Number
LoanAccruedInterest(
Number principalOutstanding,
TenthBips32 periodicRate,
NetClock::time_point parentCloseTime,
std::uint32_t startDate,
std::uint32_t prevPaymentDate,
std::uint32_t paymentInterval)
{
auto const lastPaymentDate = std::max(prevPaymentDate, startDate);
auto const secondsSinceLastPayment =
parentCloseTime.time_since_epoch().count() - lastPaymentDate;
return tenthBipsOfValue(
principalOutstanding * secondsSinceLastPayment, periodicRate) /
paymentInterval;
}
LoanPaymentParts
LoanComputePaymentParts(ApplyView& view, SLE::ref loan)
{
Number const principalOutstanding = loan->at(sfPrincipalOutstanding);
TenthBips32 const interestRate{loan->at(sfInterestRate)};
TenthBips32 const lateInterestRate{loan->at(sfLateInterestRate)};
TenthBips32 const closeInterestRate{loan->at(sfCloseInterestRate)};
Number const latePaymentFee = loan->at(sfLatePaymentFee);
Number const closePaymentFee = loan->at(sfClosePaymentFee);
std::uint32_t const paymentInterval = loan->at(sfPaymentInterval);
std::uint32_t const paymentRemaining = loan->at(sfPaymentRemaining);
std::uint32_t const prevPaymentDate = loan->at(sfPreviousPaymentDate);
std::uint32_t const startDate = loan->at(sfStartDate);
std::uint32_t const nextDueDate = loan->at(sfNextPaymentDueDate);
// Compute the normal periodic rate, payment, etc.
// We'll need it in the remaining calculations
Number const periodicRate = LoanPeriodicRate(interestRate, paymentInterval);
Number const periodicPaymentAmount = LoanPeriodicPayment(
principalOutstanding, periodicRate, paymentRemaining);
Number const periodicInterest = principalOutstanding * periodicRate;
Number const periodicPrincipal = periodicPaymentAmount - periodicInterest;
// the payment is late
if (hasExpired(view, nextDueDate))
{
auto const latePaymentInterest = LoanLatePaymentInterest(
principalOutstanding,
lateInterestRate,
view.parentCloseTime(),
startDate,
prevPaymentDate);
auto const latePaymentAmount =
periodicPaymentAmount + latePaymentInterest + latePaymentFee;
loan->at(sfPaymentRemaining) -= 1;
// A single payment always pays the same amount of principal. Only the
// interest and fees are extra
loan->at(sfPrincipalOutstanding) -= periodicPrincipal;
// Make sure this does an assignment
loan->at(sfPreviousPaymentDate) = loan->at(sfNextPaymentDueDate);
loan->at(sfNextPaymentDueDate) += paymentInterval;
// A late payment increases the value of the loan by the difference
// between periodic and late payment interest
return {
periodicPrincipal,
latePaymentInterest + periodicInterest,
latePaymentInterest,
latePaymentFee};
}
auto const accruedInterest = LoanAccruedInterest(
principalOutstanding,
interestRate,
view.parentCloseTime(),
startDate,
prevPaymentDate,
paymentInterval);
auto const prepaymentPenalty =
tenthBipsOfValue(principalOutstanding, closeInterestRate);
assert(0);
return {0, 0, 0, 0};
/*
function make_payment(amount, current_time) -> (principal_paid, interest_paid,
value_change, fee_paid): if loan.payments_remaining is 0 ||
loan.principal_outstanding is 0 { return "loan complete" error
}
.....
let full_payment = loan.compute_full_payment(current_time)
// if the payment is equal or higher than full payment amount
// and there is more than one payment remaining, make a full payment
if amount >= full_payment && loan.payments_remaining > 1 {
loan.payments_remaining = 0
loan.principal_outstanding = 0
// A full payment decreases the value of the loan by the difference
between the interest paid and the expected outstanding interest return
(full_payment.principal, full_payment.interest, full_payment.interest -
loan.compute_current_value().interest, full_payment.fee)
}
// if the payment is not late nor if it's a full payment, then it must be a
periodic once
let periodic_payment = loan.compute_periodic_payment()
let full_periodic_payments = floor(amount / periodic_payment)
if full_periodic_payments < 1 {
return "insufficient amount paid" error
}
loan.payments_remaining -= full_periodic_payments
loan.next_payment_due_date = loan.next_payment_due_date +
loan.payment_interval * full_periodic_payments loan.last_payment_date =
loan.next_payment_due_date - loan.payment_interval
let total_principal_paid = 0
let total_interest_paid = 0
let loan_value_change = 0
let total_fee_paid = loan.service_fee * full_periodic_payments
while full_periodic_payments > 0 {
total_principal_paid += periodic_payment.principal
total_interest_paid += periodic_payment.interest
periodic_payment = loan.compute_periodic_payment()
full_periodic_payments -= 1
}
loan.principal_outstanding -= total_principal_paid
let overpayment = min(loan.principal_outstanding, amount % periodic_payment)
if overpayment > 0 && is_set(lsfOverpayment) {
let interest_portion = overpayment * loan.overpayment_interest_rate
let fee_portion = overpayment * loan.overpayment_fee
let remainder = overpayment - interest_portion - fee_portion
total_principal_paid += remainder
total_interest_paid += interest_portion
total_fee_paid += fee_portion
let current_value = loan.compute_current_value()
loan.principal_outstanding -= remainder
let new_value = loan.compute_current_value()
loan_value_change = (new_value.interest - current_value.interest) +
interest_portion
}
return (total_principal_paid, total_interest_paid, loan_value_change,
total_fee_paid)
*/
}
} // namespace detail
} // namespace ripple

View File

@@ -45,7 +45,7 @@ namespace ripple {
bool
LoanBrokerCoverDeposit::isEnabled(PreflightContext const& ctx)
{
return LendingProtocolEnabled(ctx);
return lendingProtocolEnabled(ctx);
}
NotTEC

View File

@@ -45,7 +45,7 @@ namespace ripple {
bool
LoanBrokerCoverWithdraw::isEnabled(PreflightContext const& ctx)
{
return LendingProtocolEnabled(ctx);
return lendingProtocolEnabled(ctx);
}
NotTEC

View File

@@ -45,7 +45,7 @@ namespace ripple {
bool
LoanBrokerDelete::isEnabled(PreflightContext const& ctx)
{
return LendingProtocolEnabled(ctx);
return lendingProtocolEnabled(ctx);
}
NotTEC

View File

@@ -48,7 +48,7 @@ namespace ripple {
bool
LoanBrokerSet::isEnabled(PreflightContext const& ctx)
{
return LendingProtocolEnabled(ctx);
return lendingProtocolEnabled(ctx);
}
NotTEC

View File

@@ -47,7 +47,7 @@ namespace ripple {
bool
LoanDelete::isEnabled(PreflightContext const& ctx)
{
return LendingProtocolEnabled(ctx);
return lendingProtocolEnabled(ctx);
}
NotTEC

View File

@@ -47,7 +47,7 @@ namespace ripple {
bool
LoanDraw::isEnabled(PreflightContext const& ctx)
{
return LendingProtocolEnabled(ctx);
return lendingProtocolEnabled(ctx);
}
NotTEC

View File

@@ -48,7 +48,7 @@ namespace ripple {
bool
LoanManage::isEnabled(PreflightContext const& ctx)
{
return LendingProtocolEnabled(ctx);
return lendingProtocolEnabled(ctx);
}
std::uint32_t
@@ -377,7 +377,7 @@ LoanManage::doApply()
TenthBips32 const managementFeeRate{brokerSle->at(sfManagementFeeRate)};
auto const paymentInterval = loanSle->at(sfPaymentInterval);
auto const paymentsRemaining = loanSle->at(sfPaymentRemaining);
auto const interestOutstanding = LoanInterestOutstandingToVault(
auto const interestOutstanding = LoanInterestOutstandingMinusFee(
vaultAsset,
principalOutstanding.value(),
interestRate,

View File

@@ -0,0 +1,228 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpld/app/tx/detail/LoanPay.h>
//
#include <xrpld/app/misc/LendingHelpers.h>
#include <xrpld/ledger/ApplyView.h>
#include <xrpld/ledger/View.h>
#include <xrpl/basics/Log.h>
#include <xrpl/basics/Number.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/PublicKey.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STNumber.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STXChainBridge.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/XRPAmount.h>
namespace ripple {
bool
LoanPay::isEnabled(PreflightContext const& ctx)
{
return lendingProtocolEnabled(ctx);
}
NotTEC
LoanPay::doPreflight(PreflightContext const& ctx)
{
if (ctx.tx[sfLoanID] == beast::zero)
return temINVALID;
if (ctx.tx[sfAmount] <= beast::zero)
return temBAD_AMOUNT;
return tesSUCCESS;
}
TER
LoanPay::preclaim(PreclaimContext const& ctx)
{
auto const& tx = ctx.tx;
auto const account = tx[sfAccount];
auto const loanID = tx[sfLoanID];
auto const amount = tx[sfAmount];
auto const loanSle = ctx.view.read(keylet::loan(loanID));
if (!loanSle)
{
JLOG(ctx.j.warn()) << "Loan does not exist.";
return tecNO_ENTRY;
}
auto const principalOutstanding = loanSle->at(sfPrincipalOutstanding);
TenthBips32 const interestRate{loanSle->at(sfInterestRate)};
auto const paymentInterval = loanSle->at(sfPaymentInterval);
auto const paymentRemaining = loanSle->at(sfPaymentRemaining);
TenthBips32 const lateInterestRate{loanSle->at(sfLateInterestRate)};
auto const latePaymentFee = loanSle->at(sfLatePaymentFee);
auto const prevPaymentDate = loanSle->at(sfPreviousPaymentDate);
auto const startDate = loanSle->at(sfStartDate);
auto const nextDueDate = loanSle->at(sfNextPaymentDueDate);
if (loanSle->at(sfBorrower) != account)
{
JLOG(ctx.j.warn()) << "Loan does not belong to the account.";
return tecNO_PERMISSION;
}
if (!hasExpired(ctx.view, startDate))
{
JLOG(ctx.j.warn()) << "Loan has not started yet.";
return tecTOO_SOON;
}
if (paymentRemaining == 0 || principalOutstanding == 0)
{
JLOG(ctx.j.warn()) << "Loan is already paid off.";
return tecKILLED;
}
auto const loanBrokerID = loanSle->at(sfLoanBrokerID);
auto const loanBrokerSle = ctx.view.read(keylet::loanbroker(loanBrokerID));
if (!loanBrokerSle)
{
// This should be impossible
// LCOV_EXCL_START
JLOG(ctx.j.fatal()) << "LoanBroker does not exist.";
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
auto const brokerPseudoAccount = loanBrokerSle->at(sfAccount);
auto const vaultID = loanBrokerSle->at(sfVaultID);
auto const vaultSle = ctx.view.read(keylet::vault(vaultID));
if (!vaultSle)
{
// This should be impossible
// LCOV_EXCL_START
JLOG(ctx.j.fatal()) << "Vault does not exist.";
return tefBAD_LEDGER;
// LCOV_EXCL_STOP
}
auto const asset = vaultSle->at(sfAsset);
if (amount.asset() != asset)
{
JLOG(ctx.j.warn()) << "Loan amount does not match the Vault asset.";
return tecWRONG_ASSET;
}
if (isFrozen(ctx.view, brokerPseudoAccount, asset))
{
JLOG(ctx.j.warn()) << "Loan Broker pseudo-account is frozen.";
return asset.holds<Issue>() ? tecFROZEN : tecLOCKED;
}
if (asset.holds<Issue>())
{
auto const issue = asset.get<Issue>();
if (isDeepFrozen(ctx.view, account, issue.currency, issue.account))
{
JLOG(ctx.j.warn()) << "Borrower account is frozen.";
return tecFROZEN;
}
}
auto const periodicPaymentAmount = LoanPeriodicPayment(
asset,
principalOutstanding,
interestRate,
paymentInterval,
paymentRemaining);
if (hasExpired(ctx.view, nextDueDate))
{
// Need to pay the late payment amount
auto const latePaymentInterest = LoanLatePaymentInterest(
asset,
principalOutstanding,
lateInterestRate,
ctx.view.parentCloseTime(),
startDate,
prevPaymentDate);
auto const latePaymentAmount =
periodicPaymentAmount + latePaymentInterest + latePaymentFee;
if (amount < latePaymentAmount)
{
JLOG(ctx.j.warn())
<< "Late loan payment amount is insufficient. Due: "
<< latePaymentAmount << ", paid: " << amount;
return tecINSUFFICIENT_PAYMENT;
}
}
else if (amount < periodicPaymentAmount)
{
// Need to pay the regular payment amount
JLOG(ctx.j.warn())
<< "Periodic loan payment amount is insufficient. Due: "
<< periodicPaymentAmount << ", paid: " << amount;
return tecINSUFFICIENT_PAYMENT;
}
return tesSUCCESS;
}
TER
LoanPay::doApply()
{
auto const& tx = ctx_.tx;
auto& view = ctx_.view();
auto const amount = tx[sfAmount];
auto const loanID = tx[sfLoanID];
auto const loanSle = view.peek(keylet::loan(loanID));
if (!loanSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const brokerID = loanSle->at(sfLoanBrokerID);
auto const brokerSle = view.peek(keylet::loanbroker(brokerID));
if (!brokerSle)
return tefBAD_LEDGER; // LCOV_EXCL_LINE
auto const brokerPseudoAccount = brokerSle->at(sfAccount);
if (auto const ter = accountSend(
view,
brokerPseudoAccount,
account_,
amount,
j_,
WaiveTransferFee::Yes))
return ter;
loanSle->at(sfAssetsAvailable) -= amount;
view.update(loanSle);
return tesSUCCESS;
}
//------------------------------------------------------------------------------
} // namespace ripple

View File

@@ -0,0 +1,53 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TX_LOANPAY_H_INCLUDED
#define RIPPLE_TX_LOANPAY_H_INCLUDED
#include <xrpld/app/tx/detail/Transactor.h>
namespace ripple {
class LoanPay : public Transactor
{
public:
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
explicit LoanPay(ApplyContext& ctx) : Transactor(ctx)
{
}
static bool
isEnabled(PreflightContext const& ctx);
static NotTEC
doPreflight(PreflightContext const& ctx);
static TER
preclaim(PreclaimContext const& ctx);
TER
doApply() override;
};
//------------------------------------------------------------------------------
} // namespace ripple
#endif

View File

@@ -49,7 +49,7 @@ namespace ripple {
bool
LoanSet::isEnabled(PreflightContext const& ctx)
{
return LendingProtocolEnabled(ctx);
return lendingProtocolEnabled(ctx);
}
std::uint32_t
@@ -359,7 +359,7 @@ LoanSet::doApply()
TenthBips32 const managementFeeRate{brokerSle->at(sfManagementFeeRate)};
// The portion of the loan interest that will go to the vault (total
// interest minus the management fee)
auto const loanInterestToVault = LoanInterestOutstandingToVault(
auto const loanInterestToVault = LoanInterestOutstandingMinusFee(
vaultAsset,
principalRequested,
interestRate,