1#include <xrpl/tx/transactors/lending/LoanManage.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/core/ServiceRegistry.h>
7#include <xrpl/ledger/ApplyView.h>
8#include <xrpl/ledger/View.h>
9#include <xrpl/ledger/helpers/LendingHelpers.h>
10#include <xrpl/ledger/helpers/TokenHelpers.h>
11#include <xrpl/protocol/Asset.h>
12#include <xrpl/protocol/Feature.h>
13#include <xrpl/protocol/Indexes.h>
14#include <xrpl/protocol/LedgerFormats.h>
15#include <xrpl/protocol/Protocol.h>
16#include <xrpl/protocol/SField.h>
17#include <xrpl/protocol/STAmount.h>
18#include <xrpl/protocol/STLedgerEntry.h>
19#include <xrpl/protocol/STTakesAsset.h>
20#include <xrpl/protocol/STTx.h>
21#include <xrpl/protocol/TER.h>
22#include <xrpl/protocol/TxFlags.h>
23#include <xrpl/protocol/Units.h>
24#include <xrpl/protocol/XRPAmount.h>
25#include <xrpl/tx/Transactor.h>
40 return tfLoanManageMask;
50 if (
auto const flagField = ctx.
tx[~sfFlags]; flagField && (*flagField != 0u))
53 if ((flags & (flags - 1)) != 0)
55 JLOG(ctx.
j.
warn()) <<
"LoanManage: Only one of tfLoanDefault, tfLoanImpair, or "
56 "tfLoanUnimpair can be set.";
67 auto const& tx = ctx.
tx;
69 auto const account = tx[sfAccount];
70 auto const loanID = tx[sfLoanID];
75 JLOG(ctx.
j.
warn()) <<
"Loan does not exist.";
84 if (loanSle->isFlag(lsfLoanDefault))
86 JLOG(ctx.
j.
warn()) <<
"Loan is in default. A defaulted loan can not be modified.";
89 if (loanSle->isFlag(lsfLoanImpaired) && tx.isFlag(tfLoanImpair))
91 JLOG(ctx.
j.
warn()) <<
"Loan is impaired. A loan can not be impaired twice.";
94 if (!(loanSle->isFlag(lsfLoanImpaired) || loanSle->isFlag(lsfLoanDefault)) &&
95 (tx.isFlag(tfLoanUnimpair)))
97 JLOG(ctx.
j.
warn()) <<
"Loan is unimpaired. Can not be unimpaired again.";
100 if (loanSle->at(sfPaymentRemaining) == 0)
102 JLOG(ctx.
j.
warn()) <<
"Loan is fully paid. A loan can not be modified "
103 "after it is fully paid.";
106 if (tx.isFlag(tfLoanDefault) &&
109 loanSle->at(sfNextPaymentDueDate) + loanSle->at(sfGracePeriod),
113 JLOG(ctx.
j.
warn()) <<
"A loan can not be defaulted before the next payment due date.";
117 auto const loanBrokerID = loanSle->at(sfLoanBrokerID);
124 if (loanBrokerSle->at(sfOwner) != account)
126 JLOG(ctx.
j.
warn()) <<
"LoanBroker for Loan does not belong to the account. LoanManage "
127 "can only be submitted by the Loan Broker.";
140 Asset const& vaultAsset,
145 std::int32_t const loanScale = loanSle->at(sfLoanScale);
146 auto brokerDebtTotalProxy = brokerSle->at(sfDebtTotal);
151 TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
152 TenthBips32 const coverRateLiquidation{brokerSle->at(sfCoverRateLiquidation)};
153 auto const defaultCovered = [&]() {
156 auto const minimumCover =
tenthBipsOfValue(brokerDebtTotalProxy.value(), coverRateMinimum);
167 auto const coverAvailable = *brokerSle->at(sfCoverAvailable);
169 return std::min(covered, coverAvailable);
172 auto const vaultDefaultAmount = totalDefaultAmount - defaultCovered;
183 auto vaultTotalProxy = vaultSle->at(sfAssetsTotal);
184 auto vaultAvailableProxy = vaultSle->at(sfAssetsAvailable);
186 if (vaultTotalProxy < vaultDefaultAmount)
189 JLOG(j.
warn()) <<
"Vault total assets is less than the vault default amount";
196 vaultTotalProxy -= vaultDefaultRounded;
199 vaultAvailableProxy += defaultCovered;
200 if (*vaultAvailableProxy > *vaultTotalProxy && !vaultAsset.
integral())
202 auto const difference = vaultAvailableProxy - vaultTotalProxy;
203 JLOG(j.
debug()) <<
"Vault assets available: " << *vaultAvailableProxy <<
"("
204 << vaultAvailableProxy.value().exponent()
205 <<
"), Total: " << *vaultTotalProxy <<
"("
206 << vaultTotalProxy.value().exponent() <<
"), Difference: " << difference
207 <<
"(" << difference.exponent() <<
")";
208 if (vaultAvailableProxy.value().exponent() - difference.exponent() > 13)
212 JLOG(j.
debug()) <<
"Difference between vault assets available and total is "
213 "dust. Set both to the larger value.";
214 vaultTotalProxy = vaultAvailableProxy;
217 if (*vaultAvailableProxy > *vaultTotalProxy)
220 JLOG(j.
fatal()) <<
"Vault assets available must not be greater "
221 "than assets outstanding. Available: "
222 << *vaultAvailableProxy <<
", Total: " << *vaultTotalProxy;
228 if (loanSle->isFlag(lsfLoanImpaired))
230 auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
231 if (vaultLossUnrealizedProxy < totalDefaultAmount)
234 JLOG(j.
warn()) <<
"Vault unrealized loss is less than the default amount";
239 vaultLossUnrealizedProxy, -totalDefaultAmount, vaultAsset, vaultScale);
241 view.update(vaultSle);
250 auto coverAvailableProxy = brokerSle->at(sfCoverAvailable);
251 if (coverAvailableProxy < defaultCovered)
254 JLOG(j.
warn()) <<
"LoanBroker cover available is less than amount covered";
258 coverAvailableProxy -= defaultCovered;
259 view.update(brokerSle);
263 loanSle->setFlag(lsfLoanDefault);
265 loanSle->at(sfTotalValueOutstanding) = 0;
266 loanSle->at(sfPaymentRemaining) = 0;
267 loanSle->at(sfPrincipalOutstanding) = 0;
268 loanSle->at(sfManagementFeeOutstanding) = 0;
271 loanSle->at(sfNextPaymentDueDate) = 0;
272 view.update(loanSle);
278 brokerSle->at(sfAccount),
279 vaultSle->at(sfAccount),
280 STAmount{vaultAsset, defaultCovered},
291 Asset const& vaultAsset,
294 bool const fixEnabled340 =
view.rules().enabled(fixCleanup3_4_0);
298 JLOG(j.
warn()) <<
"Cannot impair a loan that is not late";
310 auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
312 if (vaultLossUnrealizedProxy > vaultSle->at(sfAssetsTotal) - vaultSle->at(sfAssetsAvailable))
316 JLOG(j.
warn()) <<
"Vault unrealized loss is too large, and will corrupt the vault.";
319 view.update(vaultSle);
322 loanSle->setFlag(lsfLoanImpaired);
326 auto loanNextDueProxy = loanSle->at(sfNextPaymentDueDate);
330 loanNextDueProxy =
view.parentCloseTime().time_since_epoch().count();
333 view.update(loanSle);
343 Asset const& vaultAsset,
352 auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
354 if (vaultLossUnrealizedProxy < lossReversed)
357 JLOG(j.
warn()) <<
"Vault unrealized loss is less than the amount to be cleared";
364 view.update(vaultSle);
367 loanSle->clearFlag(lsfLoanImpaired);
368 if (!
view.rules().enabled(fixCleanup3_4_0))
370 auto const paymentInterval = loanSle->at(sfPaymentInterval);
371 auto const normalPaymentDueDate =
372 std::max(loanSle->at(sfPreviousPaymentDueDate), loanSle->at(sfStartDate)) +
378 loanSle->at(sfNextPaymentDueDate) = normalPaymentDueDate;
383 loanSle->at(sfNextPaymentDueDate) =
384 view.parentCloseTime().time_since_epoch().count() + paymentInterval;
387 view.update(loanSle);
395 auto const& tx =
ctx_.tx;
398 auto const loanID = tx[sfLoanID];
403 auto const brokerID = loanSle->at(sfLoanBrokerID);
411 auto const vaultAsset = vaultSle->at(sfAsset);
413 auto const result = [&]() ->
TER {
416 if (tx.isFlag(tfLoanDefault))
418 if (tx.isFlag(tfLoanImpair))
420 if (tx.isFlag(tfLoanUnimpair))
A generic endpoint for log messages.
Writeable view to a ledger, for applying a transaction.
static TER impairLoan(ApplyView &view, SLE::Ref loanSle, SLE::Ref vaultSle, Asset const &vaultAsset, beast::Journal j)
Helper function that might be needed by other transactors.
static TER preclaim(PreclaimContext const &ctx)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static TER unimpairLoan(ApplyView &view, SLE::Ref loanSle, SLE::Ref vaultSle, Asset const &vaultAsset, beast::Journal j)
Helper function that might be needed by other transactors.
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
static TER defaultLoan(ApplyView &view, SLE::Ref loanSle, SLE::Ref brokerSle, SLE::Ref vaultSle, Asset const &vaultAsset, beast::Journal j)
Helper function that might be needed by other transactors.
static NotTEC preflight(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
Number is a floating point type that can represent a wide range of values.
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(UInt256 const &feature) const
Returns true if a feature is enabled.
std::shared_ptr< STLedgerEntry const > const & ConstRef
std::shared_ptr< STLedgerEntry > const & Ref
Keylet vault(AccountID const &owner, SeqProxy const &seq) noexcept
Keylet loanBroker(AccountID const &owner, SeqProxy const &seq) noexcept
Keylet loan(UInt256 const &loanBrokerID, SeqProxy const &loanSeq) noexcept
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp, ExpiryComparison comparison=ExpiryComparison::Inclusive)
Determines whether the given expiration time has passed.
constexpr T tenthBipsOfValue(T value, TenthBips< TBips > bips)
void adjustImpreciseNumber(NumberProxy value, Number const &adjustment, Asset const &asset, int vaultScale)
int getAssetsTotalScale(SLE::ConstRef vaultSle)
TenthBips< std::uint32_t > TenthBips32
TERSubset< CanCvtToNotTEC > NotTEC
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, SLE::Ref sponsorSle={}, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
bool isPaymentLate(ReadView const &view, SLE::ConstRef loanSle)
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
Number loanVaultExposure(SLE::ConstRef vaultSle, SLE::ConstRef loanSle)
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
constexpr FlagValue tfUniversalMask
bool checkLendingProtocolDependencies(Rules const &rules, STTx const &tx)
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.