mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
[WIP] Start implementing LoanSet transactor
- Add some more values and functions to make it easier to work with basis point values / bips. - Fix several earlier mistakes.
This commit is contained in:
@@ -82,30 +82,86 @@ std::size_t constexpr maxDeletableTokenOfferEntries = 500;
|
||||
*/
|
||||
std::uint16_t constexpr maxTransferFee = 50000;
|
||||
|
||||
/** The maximum management fee rate allowed in lending.
|
||||
|
||||
TODO: Is this a good name?
|
||||
|
||||
Valid values for the the management fee charged by the Lending Protocol are
|
||||
between 0 and 10000 inclusive. A value of 1 is equivalent to 1/10 basis
|
||||
point fee or 0.001%.
|
||||
*/
|
||||
std::uint16_t constexpr maxFeeRate = 10'000;
|
||||
|
||||
/** The maximum coverage rate allowed in lending.
|
||||
|
||||
TODO: Is this a good name?
|
||||
|
||||
Valid values for the coverage rate charged by the Lending Protocol for first
|
||||
loss capital operations are between 0 and 100000 inclusive. A value of 1 is
|
||||
equivalent to 1/10 bps or 0.001%.
|
||||
*/
|
||||
std::uint32_t constexpr maxCoverRate = 100'000;
|
||||
|
||||
/** Basis points (bps) represent 0.01% of a thing. Given a value X, to find the
|
||||
* amount for B bps, use X * B / bpsPerOne
|
||||
/** There are 10,000 basis points (bips) in 100%.
|
||||
*
|
||||
* Basis points represent 0.01%.
|
||||
*
|
||||
* Given a value X, to find the amount for B bps,
|
||||
* use X * B / bipsPerUnity
|
||||
*
|
||||
* Example: If a loan broker has 999 XRP of debt, and must maintain 1,000 bps of
|
||||
* that debt as cover (10%), then the minimum cover amount is 999,000,000 drops
|
||||
* * 1000 / bipsPerUnity = 99,900,00 drops or 99.9 XRP.
|
||||
*
|
||||
* Given a percentage P, to find the number of bps that percentage represents,
|
||||
* use P * bipsPerUnity.
|
||||
*
|
||||
* Example: 50% is 0.50 * bipsPerUnity = 5,000 bps.
|
||||
*/
|
||||
std::uint32_t constexpr bpsPerOne = 10'000;
|
||||
std::uint32_t constexpr bipsPerUnity = 100 * 100;
|
||||
std::uint32_t constexpr tenthBipsPerUnity = bipsPerUnity * 10;
|
||||
|
||||
constexpr std::uint32_t
|
||||
percentageToBips(std::uint32_t percentage)
|
||||
{
|
||||
return percentage * bipsPerUnity / 100;
|
||||
}
|
||||
constexpr std::uint32_t
|
||||
percentageToTenthBips(std::uint32_t percentage)
|
||||
{
|
||||
return percentage * tenthBipsPerUnity / 100;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr T
|
||||
bipsOfValue(T value, std::uint32_t bips)
|
||||
{
|
||||
return value * bips / bipsPerUnity;
|
||||
}
|
||||
template <typename T>
|
||||
constexpr T
|
||||
tenthBipsOfValue(T value, std::uint32_t bips)
|
||||
{
|
||||
return value * bips / tenthBipsPerUnity;
|
||||
}
|
||||
|
||||
/** The maximum management fee rate allowed by a loan broker in 1/10 bips.
|
||||
|
||||
Valid values are between 0 and 10% inclusive.
|
||||
*/
|
||||
std::uint16_t constexpr maxManagementFeeRate = percentageToTenthBips(10);
|
||||
static_assert(maxManagementFeeRate == 10'000);
|
||||
|
||||
/** The maximum coverage rate required of a loan broker in 1/10 bips.
|
||||
|
||||
Valid values are between 0 and 100% inclusive.
|
||||
*/
|
||||
std::uint32_t constexpr maxCoverRate = percentageToTenthBips(100);
|
||||
static_assert(maxCoverRate == 100'000);
|
||||
|
||||
/** The maximum overpayment fee on a loan in 1/10 bips.
|
||||
*
|
||||
Valid values are between 0 and 100% inclusive.
|
||||
*/
|
||||
std::uint32_t constexpr maxOverpaymentFee = percentageToTenthBips(100);
|
||||
|
||||
/** The maximum premium added to the interest rate for late payments on a loan
|
||||
* in 1/10 bips.
|
||||
*
|
||||
* Valid values are between 0 and 100% inclusive.
|
||||
*/
|
||||
std::uint32_t constexpr maxLateInterestRate = percentageToTenthBips(100);
|
||||
|
||||
/** The maximum interest rate charged for repaying a loan early in 1/10 bips.
|
||||
*
|
||||
* Valid values are between 0 and 100% inclusive.
|
||||
*/
|
||||
std::uint32_t constexpr maxCloseInterestRate = percentageToTenthBips(100);
|
||||
|
||||
/** The maximum interest rate charged on loan overpayments in 1/10 bips.
|
||||
*
|
||||
* Valid values are between 0 and 100% inclusive.
|
||||
*/
|
||||
std::uint32_t constexpr maxOverpaymentInterestRate = percentageToTenthBips(100);
|
||||
|
||||
/** The maximum length of a URI inside an NFT */
|
||||
std::size_t constexpr maxTokenURILength = 256;
|
||||
|
||||
@@ -234,14 +234,14 @@ constexpr std::uint32_t const tfVaultShareNonTransferable = 0x00020000;
|
||||
constexpr std::uint32_t const tfVaultCreateMask = ~(tfUniversal | tfVaultPrivate | tfVaultShareNonTransferable);
|
||||
|
||||
// LoanSet flags:
|
||||
// True, indicates the load supports overpayments
|
||||
// True, indicates the loan supports overpayments
|
||||
constexpr std::uint32_t const tfLoanOverpayment = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanSetMask = ~(tfUniversal | tfLoanOverpayment);
|
||||
|
||||
// LoanManage flags:
|
||||
constexpr std::uint32_t const tfLoanDefault = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanImpair = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanUnimpair = 0x00010000;
|
||||
constexpr std::uint32_t const tfLoanImpair = 0x00020000;
|
||||
constexpr std::uint32_t const tfLoanUnimpair = 0x00040000;
|
||||
constexpr std::uint32_t const tfLoanManageMask = ~(tfUniversal | tfLoanDefault | tfLoanImpair | tfLoanUnimpair);
|
||||
|
||||
// clang-format on
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#error "undefined macro: TYPED_SFIELD"
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
|
||||
// untyped
|
||||
UNTYPED_SFIELD(sfLedgerEntry, LEDGERENTRY, 257)
|
||||
UNTYPED_SFIELD(sfTransaction, TRANSACTION, 257)
|
||||
@@ -60,10 +62,6 @@ TYPED_SFIELD(sfHookExecutionIndex, UINT16, 19)
|
||||
TYPED_SFIELD(sfHookApiVersion, UINT16, 20)
|
||||
TYPED_SFIELD(sfLedgerFixType, UINT16, 21)
|
||||
TYPED_SFIELD(sfManagementFeeRate, UINT16, 22)
|
||||
TYPED_SFIELD(sfInterestRate, UINT16, 25)
|
||||
TYPED_SFIELD(sfLateInterestRate, UINT16, 26)
|
||||
TYPED_SFIELD(sfCloseInterestRate, UINT16, 27)
|
||||
TYPED_SFIELD(sfOverpaymentInterestRate, UINT16, 28)
|
||||
|
||||
// 32-bit integers (common)
|
||||
TYPED_SFIELD(sfNetworkID, UINT32, 1)
|
||||
@@ -127,6 +125,10 @@ TYPED_SFIELD(sfPaymentRemaining, UINT32, 57)
|
||||
TYPED_SFIELD(sfPaymentTotal, UINT32, 58)
|
||||
TYPED_SFIELD(sfCoverRateMinimum, UINT32, 59)
|
||||
TYPED_SFIELD(sfCoverRateLiquidation, UINT32, 60)
|
||||
TYPED_SFIELD(sfInterestRate, UINT32, 61)
|
||||
TYPED_SFIELD(sfLateInterestRate, UINT32, 62)
|
||||
TYPED_SFIELD(sfCloseInterestRate, UINT32, 63)
|
||||
TYPED_SFIELD(sfOverpaymentInterestRate, UINT32, 64)
|
||||
|
||||
// 64-bit integers (common)
|
||||
TYPED_SFIELD(sfIndexNext, UINT64, 1)
|
||||
@@ -418,3 +420,5 @@ UNTYPED_SFIELD(sfAuthAccounts, ARRAY, 25)
|
||||
UNTYPED_SFIELD(sfAuthorizeCredentials, ARRAY, 26)
|
||||
UNTYPED_SFIELD(sfUnauthorizeCredentials, ARRAY, 27)
|
||||
UNTYPED_SFIELD(sfAcceptedCredentials, ARRAY, 28)
|
||||
|
||||
// clang-format on
|
||||
@@ -726,7 +726,6 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, noPriv, (
|
||||
{sfAmount, soeREQUIRED, soeMPTSupported},
|
||||
}))
|
||||
|
||||
#if 0
|
||||
/** This transaction creates a Loan */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanSet.h>
|
||||
@@ -750,6 +749,7 @@ TRANSACTION(ttLOAN_SET, 78, LoanSet, noPriv, ({
|
||||
{sfGracePeriod, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
#if 0
|
||||
/** This transaction deletes an existing Loan */
|
||||
#if TRANSACTION_INCLUDE
|
||||
# include <xrpld/app/tx/detail/LoanDelete.h>
|
||||
|
||||
@@ -28,12 +28,6 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
// inner objects with the default fields have to be
|
||||
// constructed with STObject::makeInnerObject()
|
||||
|
||||
std::initializer_list<SOElement> const signingFields = {
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfSigningPubKey, soeREQUIRED},
|
||||
{sfTxnSignature, soeREQUIRED},
|
||||
};
|
||||
|
||||
add(sfSignerEntry.jsonName,
|
||||
sfSignerEntry.getCode(),
|
||||
{
|
||||
@@ -42,7 +36,13 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
{sfWalletLocator, soeOPTIONAL},
|
||||
});
|
||||
|
||||
add(sfSigner.jsonName, sfSigner.getCode(), signingFields);
|
||||
add(sfSigner.jsonName,
|
||||
sfSigner.getCode(),
|
||||
{
|
||||
{sfAccount, soeREQUIRED},
|
||||
{sfSigningPubKey, soeREQUIRED},
|
||||
{sfTxnSignature, soeREQUIRED},
|
||||
});
|
||||
|
||||
add(sfMajority.jsonName,
|
||||
sfMajority.getCode(),
|
||||
@@ -157,7 +157,11 @@ InnerObjectFormats::InnerObjectFormats()
|
||||
|
||||
add(sfCounterpartySignature.jsonName,
|
||||
sfCounterpartySignature.getCode(),
|
||||
signingFields);
|
||||
{
|
||||
{sfSigningPubKey, soeREQUIRED},
|
||||
{sfTxnSignature, soeOPTIONAL},
|
||||
{sfSigners, soeOPTIONAL},
|
||||
});
|
||||
}
|
||||
|
||||
InnerObjectFormats const&
|
||||
|
||||
@@ -416,12 +416,12 @@ class LoanBroker_test : public beast::unit_test::suite
|
||||
ter(temINVALID));
|
||||
// sfManagementFeeRate: good value, bad account
|
||||
env(set(evan, vault.vaultID),
|
||||
managementFeeRate(maxFeeRate),
|
||||
managementFeeRate(maxManagementFeeRate),
|
||||
fee(increment),
|
||||
ter(tecNO_PERMISSION));
|
||||
// sfManagementFeeRate: too big
|
||||
env(set(evan, vault.vaultID),
|
||||
managementFeeRate(maxFeeRate + 1),
|
||||
managementFeeRate(maxManagementFeeRate + 1),
|
||||
fee(increment),
|
||||
ter(temINVALID));
|
||||
// sfCoverRateMinimum: good value, bad account
|
||||
@@ -504,17 +504,17 @@ class LoanBroker_test : public beast::unit_test::suite
|
||||
// ManagementFeeRate
|
||||
env(set(alice, vault.vaultID),
|
||||
loanBrokerID(broker->key()),
|
||||
managementFeeRate(maxFeeRate),
|
||||
managementFeeRate(maxManagementFeeRate),
|
||||
ter(temINVALID));
|
||||
// CoverRateMinimum
|
||||
env(set(alice, vault.vaultID),
|
||||
loanBrokerID(broker->key()),
|
||||
coverRateMinimum(maxFeeRate),
|
||||
coverRateMinimum(maxManagementFeeRate),
|
||||
ter(temINVALID));
|
||||
// CoverRateLiquidation
|
||||
env(set(alice, vault.vaultID),
|
||||
loanBrokerID(broker->key()),
|
||||
coverRateLiquidation(maxFeeRate),
|
||||
coverRateLiquidation(maxManagementFeeRate),
|
||||
ter(temINVALID));
|
||||
|
||||
// fields that can be changed
|
||||
|
||||
@@ -109,9 +109,9 @@ LoanBrokerCoverWithdraw::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
|
||||
auto const coverAvail = sleBroker->at(sfCoverAvailable);
|
||||
// Cover Rate is in 1/10 bps units
|
||||
auto const minimumCover = sleBroker->at(sfDebtTotal) *
|
||||
sleBroker->at(sfCoverRateMinimum) / bpsPerOne / 10;
|
||||
// Cover Rate is in 1/10 bips units
|
||||
auto const minimumCover = tenthBipsOfValue(
|
||||
sleBroker->at(sfDebtTotal), sleBroker->at(sfCoverRateMinimum));
|
||||
if (coverAvail < amount)
|
||||
return tecINSUFFICIENT_FUNDS;
|
||||
if ((coverAvail - amount) < minimumCover)
|
||||
|
||||
@@ -69,7 +69,7 @@ LoanBrokerSet::doPreflight(PreflightContext const& ctx)
|
||||
if (auto const data = tx[~sfData]; data && !data->empty() &&
|
||||
!validDataLength(tx[~sfData], maxDataPayloadLength))
|
||||
return temINVALID;
|
||||
if (!validNumericRange(tx[~sfManagementFeeRate], maxFeeRate))
|
||||
if (!validNumericRange(tx[~sfManagementFeeRate], maxManagementFeeRate))
|
||||
return temINVALID;
|
||||
if (!validNumericRange(tx[~sfCoverRateMinimum], maxCoverRate))
|
||||
return temINVALID;
|
||||
|
||||
213
src/xrpld/app/tx/detail/LoanSet.cpp
Normal file
213
src/xrpld/app/tx/detail/LoanSet.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/LoanSet.h>
|
||||
//
|
||||
#include <xrpld/app/tx/detail/LoanBrokerSet.h>
|
||||
#include <xrpld/app/tx/detail/SignerEntries.h>
|
||||
#include <xrpld/app/tx/detail/VaultCreate.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/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
|
||||
LoanSet::isEnabled(PreflightContext const& ctx)
|
||||
{
|
||||
return lendingProtocolEnabled(ctx);
|
||||
}
|
||||
|
||||
std::uint32_t
|
||||
LoanSet::getFlagsMask(PreflightContext const& ctx)
|
||||
{
|
||||
return tfLoanSetMask;
|
||||
}
|
||||
|
||||
NotTEC
|
||||
LoanSet::doPreflight(PreflightContext const& ctx)
|
||||
{
|
||||
auto const& tx = ctx.tx;
|
||||
if (auto const data = tx[~sfData]; data && !data->empty() &&
|
||||
!validDataLength(tx[~sfData], maxDataPayloadLength))
|
||||
return temINVALID;
|
||||
if (!validNumericRange(tx[~sfLateInterestRate], maxLateInterestRate))
|
||||
return temINVALID;
|
||||
if (!validNumericRange(tx[~sfCloseInterestRate], maxCloseInterestRate))
|
||||
return temINVALID;
|
||||
if (!validNumericRange(
|
||||
tx[~sfOverpaymentInterestRate], maxOverpaymentInterestRate))
|
||||
return temINVALID;
|
||||
|
||||
if (auto const paymentTotal = tx[~sfPaymentTotal];
|
||||
paymentTotal && *paymentTotal == 0)
|
||||
return temINVALID;
|
||||
if (auto const paymentInterval =
|
||||
tx[~sfPaymentInterval].value_or(LoanSet::defaultPaymentInterval);
|
||||
paymentInterval < LoanSet::defaultPaymentInterval)
|
||||
return temINVALID;
|
||||
else if (auto const gracePeriod =
|
||||
tx[~sfGracePeriod].value_or(LoanSet::defaultGracePeriod);
|
||||
gracePeriod > paymentInterval)
|
||||
return temINVALID;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
return temDISABLED;
|
||||
|
||||
auto const& tx = ctx.tx;
|
||||
|
||||
auto const account = tx[sfAccount];
|
||||
if (auto const ID = tx[~sfLoanID])
|
||||
{
|
||||
auto const sle = ctx.view.read(keylet::loan(*ID));
|
||||
if (!sle)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Loan does not exist.";
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
if (tx[sfVaultID] != sle->at(sfVaultID))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Can not change VaultID on an existing Loan.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
if (account != sle->at(sfOwner))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Account is not the owner of the Loan.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const vaultID = tx[sfVaultID];
|
||||
auto const sleVault = ctx.view.read(keylet::vault(vaultID));
|
||||
if (!sleVault)
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Vault does not exist.";
|
||||
return tecNO_ENTRY;
|
||||
}
|
||||
if (account != sleVault->at(sfOwner))
|
||||
{
|
||||
JLOG(ctx.j.warn()) << "Account is not the owner of the Vault.";
|
||||
return tecNO_PERMISSION;
|
||||
}
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
LoanSet::doApply()
|
||||
{
|
||||
return temDISABLED;
|
||||
|
||||
auto const& tx = ctx_.tx;
|
||||
auto& view = ctx_.view();
|
||||
|
||||
#if 0
|
||||
if (auto const ID = tx[~sfLoanID])
|
||||
{
|
||||
// Modify an existing Loan
|
||||
auto loan = view.peek(keylet::loan(*ID));
|
||||
|
||||
if (auto const data = tx[~sfData])
|
||||
loan->at(sfData) = *data;
|
||||
if (auto const debtMax = tx[~sfDebtMaximum])
|
||||
loan->at(sfDebtMaximum) = *debtMax;
|
||||
|
||||
view.update();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new Loan pointing back to the given Vault
|
||||
auto const vaultID = tx[sfVaultID];
|
||||
auto const sleVault = view.read(keylet::vault(vaultID));
|
||||
auto const vaultPseudoID = sleVault->at(sfAccount);
|
||||
auto const sequence = tx.getSeqValue();
|
||||
|
||||
auto owner = view.peek(keylet::account(account_));
|
||||
auto loan = std::make_shared<SLE>(keylet::loan(account_, sequence));
|
||||
|
||||
if (auto const ter = dirLink(view, account_, ))
|
||||
return ter;
|
||||
if (auto const ter = dirLink(view, vaultPseudoID, , sfVaultNode))
|
||||
return ter;
|
||||
|
||||
adjustOwnerCount(view, owner, 1, j_);
|
||||
auto ownerCount = owner->at(sfOwnerCount);
|
||||
if (mPriorBalance < view.fees().accountReserve(ownerCount))
|
||||
return tecINSUFFICIENT_RESERVE;
|
||||
|
||||
auto maybePseudo =
|
||||
createPseudoAccount(view, loan->key(), PseudoAccountOwnerType::Loan);
|
||||
if (!maybePseudo)
|
||||
return maybePseudo.error();
|
||||
auto& pseudo = *maybePseudo;
|
||||
auto pseudoId = pseudo->at(sfAccount);
|
||||
|
||||
if (auto ter = addEmptyHolding(
|
||||
view, pseudoId, mPriorBalance, sleVault->at(sfAsset), j_))
|
||||
return ter;
|
||||
|
||||
// Initialize data fields:
|
||||
loan->at(sfSequence) = sequence;
|
||||
loan->at(sfVaultID) = vaultID;
|
||||
loan->at(sfOwner) = account_;
|
||||
loan->at(sfAccount) = pseudoId;
|
||||
if (auto const data = tx[~sfData])
|
||||
loan->at(sfData) = *data;
|
||||
if (auto const rate = tx[~sfManagementFeeRate])
|
||||
loan->at(sfManagementFeeRate) = *rate;
|
||||
if (auto const debtMax = tx[~sfDebtMaximum])
|
||||
loan->at(sfDebtMaximum) = *debtMax;
|
||||
if (auto const coverMin = tx[~sfCoverRateMinimum])
|
||||
loan->at(sfCoverRateMinimum) = *coverMin;
|
||||
if (auto const coverLiq = tx[~sfCoverRateLiquidation])
|
||||
loan->at(sfCoverRateLiquidation) = *coverLiq;
|
||||
|
||||
view.insert(loan);
|
||||
}
|
||||
#endif
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace ripple
|
||||
61
src/xrpld/app/tx/detail/LoanSet.h
Normal file
61
src/xrpld/app/tx/detail/LoanSet.h
Normal file
@@ -0,0 +1,61 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_LOANSET_H_INCLUDED
|
||||
#define RIPPLE_TX_LOANSET_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/tx/detail/Transactor.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class LoanSet : public Transactor
|
||||
{
|
||||
public:
|
||||
static constexpr ConsequencesFactoryType ConsequencesFactory{Normal};
|
||||
|
||||
explicit LoanSet(ApplyContext& ctx) : Transactor(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static bool
|
||||
isEnabled(PreflightContext const& ctx);
|
||||
|
||||
static std::uint32_t
|
||||
getFlagsMask(PreflightContext const& ctx);
|
||||
|
||||
static NotTEC
|
||||
doPreflight(PreflightContext const& ctx);
|
||||
|
||||
static TER
|
||||
preclaim(PreclaimContext const& ctx);
|
||||
|
||||
TER
|
||||
doApply() override;
|
||||
|
||||
private:
|
||||
static std::uint32_t constexpr defaultPaymentTotal = 1;
|
||||
static std::uint32_t constexpr defaultPaymentInterval = 60;
|
||||
static std::uint32_t constexpr defaultGracePeriod = 60;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user