1#include <xrpld/app/tx/detail/LoanManage.h>
3#include <xrpld/app/misc/LendingHelpers.h>
5#include <xrpl/protocol/STTakesAsset.h>
6#include <xrpl/protocol/TxFlags.h>
25 if (ctx.
tx[sfLoanID] == beast::zero)
29 if (
auto const flagField = ctx.
tx[~sfFlags]; flagField && *flagField)
32 if ((flags & (flags - 1)) != 0)
34 JLOG(ctx.
j.
warn()) <<
"LoanManage: Only one of tfLoanDefault, tfLoanImpair, or "
35 "tfLoanUnimpair can be set.";
46 auto const& tx = ctx.
tx;
48 auto const account = tx[sfAccount];
49 auto const loanID = tx[sfLoanID];
54 JLOG(ctx.
j.
warn()) <<
"Loan does not exist.";
65 JLOG(ctx.
j.
warn()) <<
"Loan is in default. A defaulted loan can not be modified.";
70 JLOG(ctx.
j.
warn()) <<
"Loan is impaired. A loan can not be impaired twice.";
75 JLOG(ctx.
j.
warn()) <<
"Loan is unimpaired. Can not be unimpaired again.";
78 if (loanSle->at(sfPaymentRemaining) == 0)
80 JLOG(ctx.
j.
warn()) <<
"Loan is fully paid. A loan can not be modified "
81 "after it is fully paid.";
85 !
hasExpired(ctx.
view, loanSle->at(sfNextPaymentDueDate) + loanSle->at(sfGracePeriod)))
87 JLOG(ctx.
j.
warn()) <<
"A loan can not be defaulted before the next payment due date.";
91 auto const loanBrokerID = loanSle->at(sfLoanBrokerID);
98 if (loanBrokerSle->at(sfOwner) != account)
100 JLOG(ctx.
j.
warn()) <<
"LoanBroker for Loan does not belong to the account. LoanManage "
101 "can only be submitted by the Loan Broker.";
122 return loanSle->at(sfTotalValueOutstanding) - loanSle->at(sfManagementFeeOutstanding);
131 Asset const& vaultAsset,
136 std::int32_t const loanScale = loanSle->at(sfLoanScale);
137 auto brokerDebtTotalProxy = brokerSle->at(sfDebtTotal);
142 TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
143 TenthBips32 const coverRateLiquidation{brokerSle->at(sfCoverRateLiquidation)};
144 auto const defaultCovered = [&]() {
147 auto const minimumCover =
tenthBipsOfValue(brokerDebtTotalProxy.value(), coverRateMinimum);
158 auto const coverAvailable = *brokerSle->at(sfCoverAvailable);
160 return std::min(covered, coverAvailable);
163 auto const vaultDefaultAmount = totalDefaultAmount - defaultCovered;
174 auto vaultTotalProxy = vaultSle->at(sfAssetsTotal);
175 auto vaultAvailableProxy = vaultSle->at(sfAssetsAvailable);
177 if (vaultTotalProxy < vaultDefaultAmount)
180 JLOG(j.
warn()) <<
"Vault total assets is less than the vault default amount";
186 vaultTotalProxy -= vaultDefaultRounded;
189 vaultAvailableProxy += defaultCovered;
190 if (*vaultAvailableProxy > *vaultTotalProxy && !vaultAsset.
integral())
192 auto const difference = vaultAvailableProxy - vaultTotalProxy;
193 JLOG(j.
debug()) <<
"Vault assets available: " << *vaultAvailableProxy <<
"("
194 << vaultAvailableProxy.value().exponent() <<
"), Total: " << *vaultTotalProxy <<
"("
195 << vaultTotalProxy.value().exponent() <<
"), Difference: " << difference <<
"("
196 << difference.exponent() <<
")";
197 if (vaultAvailableProxy.value().exponent() - difference.exponent() > 13)
201 JLOG(j.
debug()) <<
"Difference between vault assets available and total is "
202 "dust. Set both to the larger value.";
203 vaultTotalProxy = vaultAvailableProxy;
206 if (*vaultAvailableProxy > *vaultTotalProxy)
209 JLOG(j.
fatal()) <<
"Vault assets available must not be greater "
210 "than assets outstanding. Available: "
211 << *vaultAvailableProxy <<
", Total: " << *vaultTotalProxy;
219 auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
220 if (vaultLossUnrealizedProxy < totalDefaultAmount)
223 JLOG(j.
warn()) <<
"Vault unrealized loss is less than the default amount";
238 auto coverAvailableProxy = brokerSle->at(sfCoverAvailable);
239 if (coverAvailableProxy < defaultCovered)
242 JLOG(j.
warn()) <<
"LoanBroker cover available is less than amount covered";
246 coverAvailableProxy -= defaultCovered;
253 loanSle->at(sfTotalValueOutstanding) = 0;
254 loanSle->at(sfPaymentRemaining) = 0;
255 loanSle->at(sfPrincipalOutstanding) = 0;
256 loanSle->at(sfManagementFeeOutstanding) = 0;
259 loanSle->at(sfNextPaymentDueDate) = 0;
266 brokerSle->at(sfAccount),
267 vaultSle->at(sfAccount),
268 STAmount{vaultAsset, defaultCovered},
284 auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
286 if (vaultLossUnrealizedProxy > vaultSle->at(sfAssetsTotal) - vaultSle->at(sfAssetsAvailable))
290 JLOG(j.
warn()) <<
"Vault unrealized loss is too large, and will "
291 "corrupt the vault.";
298 auto loanNextDueProxy = loanSle->at(sfNextPaymentDueDate);
315 Asset const& vaultAsset,
324 auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
326 if (vaultLossUnrealizedProxy < lossReversed)
329 JLOG(j.
warn()) <<
"Vault unrealized loss is less than the amount to be cleared";
340 auto const paymentInterval = loanSle->at(sfPaymentInterval);
341 auto const normalPaymentDueDate =
342 std::max(loanSle->at(sfPreviousPaymentDueDate), loanSle->at(sfStartDate)) + paymentInterval;
346 loanSle->at(sfNextPaymentDueDate) = normalPaymentDueDate;
364 auto const loanID = tx[sfLoanID];
369 auto const brokerID = loanSle->at(sfLoanBrokerID);
377 auto const vaultAsset = vaultSle->at(sfAsset);
A generic endpoint for log messages.
Writeable view to a ledger, for applying a transaction.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
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 TER preclaim(PreclaimContext const &ctx)
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.
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 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.
NetClock::time_point parentCloseTime() const
Returns the close time of the previous ledger.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
Keylet loanbroker(AccountID const &owner, std::uint32_t seq) noexcept
Keylet loan(uint256 const &loanBrokerID, std::uint32_t loanSeq) noexcept
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t const tfLoanImpair
constexpr T tenthBipsOfValue(T value, TenthBips< TBips > bips)
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
int getAssetsTotalScale(SLE::const_ref vaultSle)
void adjustImpreciseNumber(NumberProxy value, Number const &adjustment, Asset const &asset, int vaultScale)
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
TERSubset< CanCvtToTER > TER
static Number owedToVault(SLE::ref loanSle)
constexpr std::uint32_t const tfLoanDefault
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
constexpr std::uint32_t const tfLoanUnimpair
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
constexpr std::uint32_t tfUniversalMask
constexpr std::uint32_t const tfLoanManageMask
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.
T time_since_epoch(T... args)