rippled
Loading...
Searching...
No Matches
LoanSet.cpp
1#include <xrpld/app/tx/detail/LoanSet.h>
2//
3#include <xrpld/app/misc/LendingHelpers.h>
4
5#include <xrpl/protocol/STTakesAsset.h>
6#include <xrpl/protocol/TxFlags.h>
7
8namespace xrpl {
9
10bool
15
21
24{
25 using namespace Lending;
26
27 auto const& tx = ctx.tx;
28
29 // Special case for Batch inner transactions
30 if (tx.isFlag(tfInnerBatchTxn) && ctx.rules.enabled(featureBatch) && !tx.isFieldPresent(sfCounterparty))
31 {
32 auto const parentBatchId = ctx.parentBatchId.value_or(uint256{0});
33 JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: "
34 << "no Counterparty for inner LoanSet transaction.";
35 return temBAD_SIGNER;
36 }
37
38 // These extra hoops are because STObjects cannot be Proxy'd from STObject.
39 auto const counterPartySig = [&tx]() -> std::optional<STObject const> {
40 if (tx.isFieldPresent(sfCounterpartySignature))
41 return tx.getFieldObject(sfCounterpartySignature);
42 return std::nullopt;
43 }();
44 if (!tx.isFlag(tfInnerBatchTxn) && !counterPartySig)
45 {
46 JLOG(ctx.j.warn()) << "LoanSet transaction must have a CounterpartySignature.";
47 return temBAD_SIGNER;
48 }
49
50 if (counterPartySig)
51 {
52 if (auto const ret = xrpl::detail::preflightCheckSigningKey(*counterPartySig, ctx.j))
53 return ret;
54 }
55
56 if (auto const data = tx[~sfData]; data && !data->empty() && !validDataLength(tx[~sfData], maxDataPayloadLength))
57 return temINVALID;
58 for (auto const& field : {&sfLoanServiceFee, &sfLatePaymentFee, &sfClosePaymentFee})
59 {
60 if (!validNumericMinimum(tx[~*field]))
61 return temINVALID;
62 }
63 // Principal Requested is required
64 if (auto const p = tx[sfPrincipalRequested]; p <= 0)
65 return temINVALID;
66 else if (!validNumericRange(tx[~sfLoanOriginationFee], p))
67 return temINVALID;
68 if (!validNumericRange(tx[~sfInterestRate], maxInterestRate))
69 return temINVALID;
70 if (!validNumericRange(tx[~sfOverpaymentFee], maxOverpaymentFee))
71 return temINVALID;
72 if (!validNumericRange(tx[~sfLateInterestRate], maxLateInterestRate))
73 return temINVALID;
74 if (!validNumericRange(tx[~sfCloseInterestRate], maxCloseInterestRate))
75 return temINVALID;
76 if (!validNumericRange(tx[~sfOverpaymentInterestRate], maxOverpaymentInterestRate))
77 return temINVALID;
78
79 if (auto const paymentTotal = tx[~sfPaymentTotal]; paymentTotal && *paymentTotal <= 0)
80 return temINVALID;
81
82 if (auto const paymentInterval = tx[~sfPaymentInterval];
84 return temINVALID;
85 // Grace period is between min default value and payment interval
86 else if (auto const gracePeriod = tx[~sfGracePeriod]; //
88 gracePeriod, paymentInterval.value_or(LoanSet::defaultPaymentInterval), defaultGracePeriod))
89 return temINVALID;
90
91 // Copied from preflight2
92 if (counterPartySig)
93 {
94 if (auto const ret = xrpl::detail::preflightCheckSimulateKeys(ctx.flags, *counterPartySig, ctx.j))
95 return *ret;
96 }
97
98 if (auto const brokerID = ctx.tx[~sfLoanBrokerID]; brokerID && *brokerID == beast::zero)
99 return temINVALID;
100
101 return tesSUCCESS;
102}
103
104NotTEC
106{
107 if (auto ret = Transactor::checkSign(ctx))
108 return ret;
109
110 // Counter signer is optional. If it's not specified, it's assumed to be
111 // `LoanBroker.Owner`. Note that we have not checked whether the
112 // loanbroker exists at this point.
113 auto const counterSigner = [&]() -> std::optional<AccountID> {
114 if (auto const c = ctx.tx.at(~sfCounterparty))
115 return c;
116
117 if (auto const broker = ctx.view.read(keylet::loanbroker(ctx.tx[sfLoanBrokerID])))
118 return broker->at(sfOwner);
119 return std::nullopt;
120 }();
121 if (!counterSigner)
122 return temBAD_SIGNER;
123
124 // Counterparty signature is optional. Presence is checked in preflight.
125 if (!ctx.tx.isFieldPresent(sfCounterpartySignature))
126 return tesSUCCESS;
127 auto const counterSig = ctx.tx.getFieldObject(sfCounterpartySignature);
128 return Transactor::checkSign(ctx.view, ctx.flags, ctx.parentBatchId, *counterSigner, counterSig, ctx.j);
129}
130
133{
134 auto const normalCost = Transactor::calculateBaseFee(view, tx);
135
136 // Compute the additional cost of each signature in the
137 // CounterpartySignature, whether a single signature or a multisignature
138 XRPAmount const baseFee = view.fees().base;
139
140 // Counterparty signature is optional, but getFieldObject will return an
141 // empty object if it's not present.
142 auto const counterSig = tx.getFieldObject(sfCounterpartySignature);
143 // Each signer adds one more baseFee to the minimum required fee
144 // for the transaction. Note that unlike the base class, the single signer
145 // is counted if present. It will only be absent in a batch inner
146 // transaction.
147 std::size_t const signerCount = [&counterSig]() {
148 // Compute defensively. Assure that "tx" cannot be accessed and cause
149 // confusion or miscalculations.
150 return counterSig.isFieldPresent(sfSigners) ? counterSig.getFieldArray(sfSigners).size()
151 : (counterSig.isFieldPresent(sfTxnSignature) ? 1 : 0);
152 }();
153
154 return normalCost + (signerCount * baseFee);
155}
156
159{
160 static std::vector<OptionaledField<STNumber>> const valueFields{
161 ~sfPrincipalRequested, ~sfLoanOriginationFee, ~sfLoanServiceFee, ~sfLatePaymentFee, ~sfClosePaymentFee
162 // Overpayment fee is really a rate. Don't check it here.
163 };
164
165 return valueFields;
166}
167
168static std::uint32_t
170{
171 return view.header().closeTime.time_since_epoch().count();
172}
173
174TER
176{
177 auto const& tx = ctx.tx;
178
179 {
180 // Check for numeric overflow of the schedule before we load any
181 // objects. The Grace Period for the last payment ends at:
182 // startDate + (paymentInterval * paymentTotal) + gracePeriod.
183 // If that value is larger than "maxTime", the value
184 // overflows, and we kill the transaction.
185 using timeType = decltype(sfNextPaymentDueDate)::type::value_type;
187 timeType constexpr maxTime = std::numeric_limits<timeType>::max();
188 static_assert(maxTime == 4'294'967'295);
189
190 auto const timeAvailable = maxTime - getStartDate(ctx.view);
191
192 auto const interval = ctx.tx.at(~sfPaymentInterval).value_or(defaultPaymentInterval);
193 auto const total = ctx.tx.at(~sfPaymentTotal).value_or(defaultPaymentTotal);
194 auto const grace = ctx.tx.at(~sfGracePeriod).value_or(defaultGracePeriod);
195
196 // The grace period can't be larger than the interval. Check it first,
197 // mostly so that unit tests can test that specific case.
198 if (grace > timeAvailable)
199 {
200 JLOG(ctx.j.warn()) << "Grace period exceeds protocol time limit.";
201 return tecKILLED;
202 }
203
204 if (interval > timeAvailable)
205 {
206 JLOG(ctx.j.warn()) << "Payment interval exceeds protocol time limit.";
207 return tecKILLED;
208 }
209
210 if (total > timeAvailable)
211 {
212 JLOG(ctx.j.warn()) << "Payment total exceeds protocol time limit.";
213 return tecKILLED;
214 }
215
216 auto const timeLastPayment = timeAvailable - grace;
217
218 if (timeLastPayment / interval < total)
219 {
220 JLOG(ctx.j.warn()) << "Last payment due date, or grace period for "
221 "last payment exceeds protocol time limit.";
222 return tecKILLED;
223 }
224 }
225
226 auto const account = tx[sfAccount];
227 auto const brokerID = tx[sfLoanBrokerID];
228
229 auto const brokerSle = ctx.view.read(keylet::loanbroker(brokerID));
230 if (!brokerSle)
231 {
232 // This can only be hit if there's a counterparty specified, otherwise
233 // it'll fail in the signature check
234 JLOG(ctx.j.warn()) << "LoanBroker does not exist.";
235 return tecNO_ENTRY;
236 }
237 auto const brokerOwner = brokerSle->at(sfOwner);
238 auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner);
239 if (account != brokerOwner && counterparty != brokerOwner)
240 {
241 JLOG(ctx.j.warn()) << "Neither Account nor Counterparty are the owner "
242 "of the LoanBroker.";
243 return tecNO_PERMISSION;
244 }
245 auto const brokerPseudo = brokerSle->at(sfAccount);
246
247 auto const borrower = counterparty == brokerOwner ? account : counterparty;
248 if (auto const borrowerSle = ctx.view.read(keylet::account(borrower)); !borrowerSle)
249 {
250 // It may not be possible to hit this case, because it'll fail the
251 // signature check with terNO_ACCOUNT.
252 JLOG(ctx.j.warn()) << "Borrower does not exist.";
253 return terNO_ACCOUNT;
254 }
255
256 auto const vault = ctx.view.read(keylet::vault(brokerSle->at(sfVaultID)));
257 if (!vault)
258 // Should be impossible
259 return tefBAD_LEDGER; // LCOV_EXCL_LINE
260
261 if (vault->at(sfAssetsMaximum) != 0 && vault->at(sfAssetsTotal) >= vault->at(sfAssetsMaximum))
262 {
263 JLOG(ctx.j.warn()) << "Vault at maximum assets limit. Can't add another loan.";
264 return tecLIMIT_EXCEEDED;
265 }
266
267 Asset const asset = vault->at(sfAsset);
268
269 auto const vaultPseudo = vault->at(sfAccount);
270
271 // Check that relevant values can be represented as the vault asset type.
272 // This check is almost duplicated in doApply, but that check is done after
273 // the overall loan scale is known. This is mostly only relevant for
274 // integral (non-IOU) types
275 for (auto const& field : getValueFields())
276 {
277 if (auto const value = tx[field]; value && STAmount{asset, *value} != *value)
278 {
279 JLOG(ctx.j.warn()) << field.f->getName() << " (" << *value << ") can not be represented as a(n) "
280 << to_string(asset) << ".";
281 return tecPRECISION_LOSS;
282 }
283 }
284
285 if (auto const ter = canAddHolding(ctx.view, asset))
286 return ter;
287
288 // vaultPseudo is going to send funds, so it can't be frozen.
289 if (auto const ret = checkFrozen(ctx.view, vaultPseudo, asset))
290 {
291 JLOG(ctx.j.warn()) << "Vault pseudo-account is frozen.";
292 return ret;
293 }
294
295 // brokerPseudo is the fallback account to receive LoanPay fees, even if the
296 // broker owner is unable to accept them. Don't create the loan if it is
297 // deep frozen.
298 if (auto const ret = checkDeepFrozen(ctx.view, brokerPseudo, asset))
299 {
300 JLOG(ctx.j.warn()) << "Broker pseudo-account is frozen.";
301 return ret;
302 }
303
304 // borrower is eventually going to have to pay back the loan, so it can't be
305 // frozen now. It is also going to receive funds, so it can't be deep
306 // frozen, but being frozen is a prerequisite for being deep frozen, so
307 // checking the one is sufficient.
308 if (auto const ret = checkFrozen(ctx.view, borrower, asset))
309 {
310 JLOG(ctx.j.warn()) << "Borrower account is frozen.";
311 return ret;
312 }
313 // brokerOwner is going to receive funds if there's an origination fee, so
314 // it can't be deep frozen
315 if (auto const ret = checkDeepFrozen(ctx.view, brokerOwner, asset))
316 {
317 JLOG(ctx.j.warn()) << "Broker owner account is frozen.";
318 return ret;
319 }
320
321 return tesSUCCESS;
322}
323
324TER
326{
327 auto const& tx = ctx_.tx;
328 auto& view = ctx_.view();
329
330 auto const brokerID = tx[sfLoanBrokerID];
331
332 auto const brokerSle = view.peek(keylet::loanbroker(brokerID));
333 if (!brokerSle)
334 return tefBAD_LEDGER; // LCOV_EXCL_LINE
335 auto const brokerOwner = brokerSle->at(sfOwner);
336 auto const brokerOwnerSle = view.peek(keylet::account(brokerOwner));
337 if (!brokerOwnerSle)
338 return tefBAD_LEDGER; // LCOV_EXCL_LINE
339
340 auto const vaultSle = view.peek(keylet ::vault(brokerSle->at(sfVaultID)));
341 if (!vaultSle)
342 return tefBAD_LEDGER; // LCOV_EXCL_LINE
343 auto const vaultPseudo = vaultSle->at(sfAccount);
344 Asset const vaultAsset = vaultSle->at(sfAsset);
345
346 auto const counterparty = tx[~sfCounterparty].value_or(brokerOwner);
347 auto const borrower = counterparty == brokerOwner ? account_ : counterparty;
348 auto const borrowerSle = view.peek(keylet::account(borrower));
349 if (!borrowerSle)
350 {
351 return tefBAD_LEDGER; // LCOV_EXCL_LINE
352 }
353
354 auto const brokerPseudo = brokerSle->at(sfAccount);
355 auto const brokerPseudoSle = view.peek(keylet::account(brokerPseudo));
356 if (!brokerPseudoSle)
357 {
358 return tefBAD_LEDGER; // LCOV_EXCL_LINE
359 }
360 auto const principalRequested = tx[sfPrincipalRequested];
361
362 auto vaultAvailableProxy = vaultSle->at(sfAssetsAvailable);
363 auto vaultTotalProxy = vaultSle->at(sfAssetsTotal);
364 auto const vaultScale = getAssetsTotalScale(vaultSle);
365 if (vaultAvailableProxy < principalRequested)
366 {
367 JLOG(j_.warn()) << "Insufficient assets available in the Vault to fund the loan.";
369 }
370
371 TenthBips32 const interestRate{tx[~sfInterestRate].value_or(0)};
372
373 auto const paymentInterval = tx[~sfPaymentInterval].value_or(defaultPaymentInterval);
374 auto const paymentTotal = tx[~sfPaymentTotal].value_or(defaultPaymentTotal);
375
376 auto const properties = computeLoanProperties(
377 vaultAsset,
378 principalRequested,
379 interestRate,
380 paymentInterval,
381 paymentTotal,
382 TenthBips16{brokerSle->at(sfManagementFeeRate)},
383 vaultScale);
384
385 LoanState const state = constructLoanState(
386 properties.loanState.valueOutstanding, principalRequested, properties.loanState.managementFeeDue);
387
388 auto const vaultMaximum = *vaultSle->at(sfAssetsMaximum);
389 XRPL_ASSERT_PARTS(
390 vaultMaximum == 0 || vaultMaximum > *vaultTotalProxy, "xrpl::LoanSet::doApply", "Vault is below maximum limit");
391 if (vaultMaximum != 0 && state.interestDue > vaultMaximum - vaultTotalProxy)
392 {
393 JLOG(j_.warn()) << "Loan would exceed the maximum assets of the vault";
394 return tecLIMIT_EXCEEDED;
395 }
396 // Check that relevant values won't lose precision. This is mostly only
397 // relevant for IOU assets.
398 for (auto const& field : getValueFields())
399 {
400 if (auto const value = tx[field]; value && !isRounded(vaultAsset, *value, properties.loanScale))
401 {
402 JLOG(j_.warn()) << field.f->getName() << " (" << *value << ") has too much precision. Total loan value is "
403 << properties.loanState.valueOutstanding << " with a scale of " << properties.loanScale;
404 return tecPRECISION_LOSS;
405 }
406 }
407
408 if (auto const ret =
409 checkLoanGuards(vaultAsset, principalRequested, interestRate != beast::zero, paymentTotal, properties, j_))
410 return ret;
411
412 // Check that the other computed values are valid
413 if (properties.loanState.managementFeeDue < 0 || properties.loanState.valueOutstanding <= 0 ||
414 properties.periodicPayment <= 0)
415 {
416 // LCOV_EXCL_START
417 JLOG(j_.warn()) << "Computed loan properties are invalid. Does not compute."
418 << " Management fee: " << properties.loanState.managementFeeDue
419 << ". Total Value: " << properties.loanState.valueOutstanding
420 << ". PeriodicPayment: " << properties.periodicPayment;
421 return tecINTERNAL;
422 // LCOV_EXCL_STOP
423 }
424
425 auto const originationFee = tx[~sfLoanOriginationFee].value_or(Number{});
426
427 auto const loanAssetsToBorrower = principalRequested - originationFee;
428
429 auto const newDebtDelta = principalRequested + state.interestDue;
430 auto const newDebtTotal = brokerSle->at(sfDebtTotal) + newDebtDelta;
431 if (auto const debtMaximum = brokerSle->at(sfDebtMaximum); debtMaximum != 0 && debtMaximum < newDebtTotal)
432 {
433 JLOG(j_.warn()) << "Loan would exceed the maximum debt limit of the LoanBroker.";
434 return tecLIMIT_EXCEEDED;
435 }
436 TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
437 {
438 // Round the minimum required cover up to be conservative. This ensures
439 // CoverAvailable never drops below the theoretical minimum, protecting
440 // the broker's solvency.
442 if (brokerSle->at(sfCoverAvailable) < tenthBipsOfValue(newDebtTotal, coverRateMinimum))
443 {
444 JLOG(j_.warn()) << "Insufficient first-loss capital to cover the loan.";
446 }
447 }
448
449 adjustOwnerCount(view, borrowerSle, 1, j_);
450 {
451 auto const ownerCount = borrowerSle->at(sfOwnerCount);
452 auto const balance = account_ == borrower ? mPriorBalance : borrowerSle->at(sfBalance).value().xrp();
453 if (balance < view.fees().accountReserve(ownerCount))
455 }
456
457 // Account for the origination fee using two payments
458 //
459 // 1. Transfer loanAssetsAvailable (principalRequested - originationFee)
460 // from vault pseudo-account to the borrower.
461 // Create a holding for the borrower if one does not already exist.
462
463 XRPL_ASSERT_PARTS(
464 borrower == account_ || borrower == counterparty, "xrpl::LoanSet::doApply", "borrower signed transaction");
465 if (auto const ter = addEmptyHolding(view, borrower, borrowerSle->at(sfBalance).value().xrp(), vaultAsset, j_);
466 ter && ter != tecDUPLICATE)
467 // ignore tecDUPLICATE. That means the holding already exists, and
468 // is fine here
469 return ter;
470
471 if (auto const ter = requireAuth(view, vaultAsset, borrower, AuthType::StrongAuth))
472 return ter;
473
474 // 2. Transfer originationFee, if any, from vault pseudo-account to
475 // LoanBroker owner.
476 if (originationFee != beast::zero)
477 {
478 // Create the holding if it doesn't already exist (necessary for MPTs).
479 // The owner may have deleted their MPT / line at some point.
480 XRPL_ASSERT_PARTS(
481 brokerOwner == account_ || brokerOwner == counterparty,
482 "xrpl::LoanSet::doApply",
483 "broker owner signed transaction");
484
485 if (auto const ter =
486 addEmptyHolding(view, brokerOwner, brokerOwnerSle->at(sfBalance).value().xrp(), vaultAsset, j_);
487 ter && ter != tecDUPLICATE)
488 // ignore tecDUPLICATE. That means the holding already exists,
489 // and is fine here
490 return ter;
491 }
492
493 if (auto const ter = requireAuth(view, vaultAsset, brokerOwner, AuthType::StrongAuth))
494 return ter;
495
496 if (auto const ter = accountSendMulti(
497 view,
498 vaultPseudo,
499 vaultAsset,
500 {{borrower, loanAssetsToBorrower}, {brokerOwner, originationFee}},
501 j_,
503 return ter;
504
505 // Get shortcuts to the loan property values
506 auto const startDate = getStartDate(view);
507 auto loanSequenceProxy = brokerSle->at(sfLoanSequence);
508
509 // Create the loan
510 auto loan = std::make_shared<SLE>(keylet::loan(brokerID, *loanSequenceProxy));
511
512 // Prevent copy/paste errors
513 auto setLoanField = [&loan, &tx](auto const& field, std::uint32_t const defValue = 0) {
514 // at() is smart enough to unseat a default field set to the default
515 // value
516 loan->at(field) = tx[field].value_or(defValue);
517 };
518
519 // Set required and fixed tx fields
520 loan->at(sfLoanScale) = properties.loanScale;
521 loan->at(sfStartDate) = startDate;
522 loan->at(sfPaymentInterval) = paymentInterval;
523 loan->at(sfLoanSequence) = *loanSequenceProxy;
524 loan->at(sfLoanBrokerID) = brokerID;
525 loan->at(sfBorrower) = borrower;
526 // Set all other transaction fields directly from the transaction
527 if (tx.isFlag(tfLoanOverpayment))
528 loan->setFlag(lsfLoanOverpayment);
529 setLoanField(~sfLoanOriginationFee);
530 setLoanField(~sfLoanServiceFee);
531 setLoanField(~sfLatePaymentFee);
532 setLoanField(~sfClosePaymentFee);
533 setLoanField(~sfOverpaymentFee);
534 setLoanField(~sfInterestRate);
535 setLoanField(~sfLateInterestRate);
536 setLoanField(~sfCloseInterestRate);
537 setLoanField(~sfOverpaymentInterestRate);
538 setLoanField(~sfGracePeriod, defaultGracePeriod);
539 // Set dynamic / computed fields to their initial values
540 loan->at(sfPrincipalOutstanding) = principalRequested;
541 loan->at(sfPeriodicPayment) = properties.periodicPayment;
542 loan->at(sfTotalValueOutstanding) = properties.loanState.valueOutstanding;
543 loan->at(sfManagementFeeOutstanding) = properties.loanState.managementFeeDue;
544 loan->at(sfPreviousPaymentDueDate) = 0;
545 loan->at(sfNextPaymentDueDate) = startDate + paymentInterval;
546 loan->at(sfPaymentRemaining) = paymentTotal;
547 view.insert(loan);
548
549 // Update the balances in the vault
550 vaultAvailableProxy -= principalRequested;
551 vaultTotalProxy += state.interestDue;
552 XRPL_ASSERT_PARTS(
553 *vaultAvailableProxy <= *vaultTotalProxy,
554 "xrpl::LoanSet::doApply",
555 "assets available must not be greater than assets outstanding");
556 view.update(vaultSle);
557
558 // Update the balances in the loan broker
559 adjustImpreciseNumber(brokerSle->at(sfDebtTotal), newDebtDelta, vaultAsset, vaultScale);
560 // The broker's owner count is solely for the number of outstanding loans,
561 // and is distinct from the broker's pseudo-account's owner count
562 adjustOwnerCount(view, brokerSle, 1, j_);
563 loanSequenceProxy += 1;
564 // The sequence should be extremely unlikely to roll over, but fail if it
565 // does
566 if (loanSequenceProxy == 0)
568 view.update(brokerSle);
569
570 // Put the loan into the pseudo-account's directory
571 if (auto const ter = dirLink(view, brokerPseudo, loan, sfLoanBrokerNode))
572 return ter;
573 // Borrower is the owner of the loan
574 if (auto const ter = dirLink(view, borrower, loan, sfOwnerNode))
575 return ter;
576
577 associateAsset(*vaultSle, vaultAsset);
578 associateAsset(*brokerSle, vaultAsset);
579 associateAsset(*loan, vaultAsset);
580
581 return tesSUCCESS;
582}
583
584//------------------------------------------------------------------------------
585
586} // namespace xrpl
Stream debug() const
Definition Journal.h:300
Stream warn() const
Definition Journal.h:312
STTx const & tx
ApplyView & view()
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition LoanSet.cpp:17
static TER preclaim(PreclaimContext const &ctx)
Definition LoanSet.cpp:175
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition LoanSet.cpp:132
static std::uint32_t constexpr minPaymentInterval
Definition LoanSet.h:46
static std::vector< OptionaledField< STNumber > > const & getValueFields()
Definition LoanSet.cpp:158
static NotTEC preflight(PreflightContext const &ctx)
Definition LoanSet.cpp:23
static std::uint32_t constexpr defaultPaymentInterval
Definition LoanSet.h:47
static NotTEC checkSign(PreclaimContext const &ctx)
Definition LoanSet.cpp:105
static std::uint32_t constexpr defaultPaymentTotal
Definition LoanSet.h:43
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition LoanSet.cpp:11
TER doApply() override
Definition LoanSet.cpp:325
static std::uint32_t constexpr defaultGracePeriod
Definition LoanSet.h:50
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
A view into a ledger.
Definition ReadView.h:31
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual LedgerHeader const & header() const =0
Returns information about the ledger.
virtual std::shared_ptr< SLE const > 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.
Definition Rules.cpp:118
T::value_type at(TypedField< T > const &f) const
Get the value of a field.
Definition STObject.h:1041
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
STObject getFieldObject(SField const &field) const
Definition STObject.cpp:653
AccountID const account_
Definition Transactor.h:112
static NotTEC checkSign(PreclaimContext const &ctx)
static bool validNumericMinimum(std::optional< T > value, T min=T{})
Minimum will usually be zero.
Definition Transactor.h:406
beast::Journal const j_
Definition Transactor.h:110
ApplyView & view()
Definition Transactor.h:128
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
XRPAmount mPriorBalance
Definition Transactor.h:113
static bool validDataLength(std::optional< Slice > const &slice, std::size_t maxLength)
ApplyContext & ctx_
Definition Transactor.h:108
static bool validNumericRange(std::optional< T > value, T max, T min=T{})
Definition Transactor.h:390
constexpr value_type value() const
Returns the underlying value.
Definition XRPAmount.h:217
T is_same_v
T max(T... args)
NotTEC preflightCheckSigningKey(STObject const &sigObject, beast::Journal j)
Checks the validity of the transactor signing key.
std::optional< NotTEC > preflightCheckSimulateKeys(ApplyFlags flags, STObject const &sigObject, beast::Journal j)
Checks the special signing key state needed for simulation.
Keylet loanbroker(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:498
Keylet loan(uint256 const &loanBrokerID, std::uint32_t loanSeq) noexcept
Definition Indexes.cpp:504
Keylet vault(AccountID const &owner, std::uint32_t seq) noexcept
Definition Indexes.cpp:492
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
TER checkDeepFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
Definition View.h:198
@ terNO_ACCOUNT
Definition TER.h:197
TER addEmptyHolding(ApplyView &view, AccountID const &accountID, XRPAmount priorBalance, Issue const &issue, beast::Journal journal)
Any transactors that call addEmptyHolding() in doApply must call canAddHolding() in preflight with th...
Definition View.cpp:1250
TER canAddHolding(ReadView const &view, Asset const &asset)
Definition View.cpp:1101
constexpr std::uint32_t tfInnerBatchTxn
Definition TxFlags.h:41
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:597
constexpr T tenthBipsOfValue(T value, TenthBips< TBips > bips)
Definition Protocol.h:107
int getAssetsTotalScale(SLE::const_ref vaultSle)
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
Definition View.h:121
void adjustImpreciseNumber(NumberProxy value, Number const &adjustment, Asset const &asset, int vaultScale)
@ tefBAD_LEDGER
Definition TER.h:150
bool checkLendingProtocolDependencies(PreflightContext const &ctx)
constexpr std::uint32_t const tfLoanOverpayment
Definition TxFlags.h:272
std::size_t constexpr maxDataPayloadLength
The maximum length of Data payload.
Definition Protocol.h:237
TER checkLoanGuards(Asset const &vaultAsset, Number const &principalRequested, bool expectInterest, std::uint32_t paymentTotal, LoanProperties const &properties, beast::Journal j)
TERSubset< CanCvtToTER > TER
Definition TER.h:620
void adjustOwnerCount(ApplyView &view, std::shared_ptr< SLE > const &sle, std::int32_t amount, beast::Journal j)
Adjust the owner count up or down.
Definition View.cpp:941
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2710
constexpr std::uint32_t const tfLoanSetMask
Definition TxFlags.h:283
static std::uint32_t getStartDate(ReadView const &view)
Definition LoanSet.cpp:169
TER dirLink(ApplyView &view, AccountID const &owner, std::shared_ptr< SLE > &object, SF_UINT64 const &node=sfOwnerNode)
Definition View.cpp:961
@ temINVALID
Definition TER.h:90
@ temBAD_SIGNER
Definition TER.h:95
@ tecNO_ENTRY
Definition TER.h:287
@ tecINTERNAL
Definition TER.h:291
@ tecINSUFFICIENT_FUNDS
Definition TER.h:306
@ tecPRECISION_LOSS
Definition TER.h:344
@ tecINSUFFICIENT_RESERVE
Definition TER.h:288
@ tecKILLED
Definition TER.h:297
@ tecMAX_SEQUENCE_REACHED
Definition TER.h:301
@ tecLIMIT_EXCEEDED
Definition TER.h:342
@ tecNO_PERMISSION
Definition TER.h:286
@ tecDUPLICATE
Definition TER.h:296
@ lsfLoanOverpayment
void associateAsset(STLedgerEntry &sle, Asset const &asset)
Associate an Asset with all sMD_NeedsAsset fields in a ledger entry.
TER accountSendMulti(ApplyView &view, AccountID const &senderID, Asset const &asset, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Like accountSend, except one account is sending multiple payments (with the same asset!...
Definition View.cpp:2465
LoanProperties computeLoanProperties(Asset const &asset, Number const &principalOutstanding, TenthBips32 interestRate, std::uint32_t paymentInterval, std::uint32_t paymentsRemaining, TenthBips32 managementFeeRate, std::int32_t minimumScale)
LoanState constructLoanState(Number const &totalValueOutstanding, Number const &principalOutstanding, Number const &managementFeeOutstanding)
@ tesSUCCESS
Definition TER.h:225
bool isRounded(Asset const &asset, Number const &value, std::int32_t scale)
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
XRPAmount base
NetClock::time_point closeTime
This structure captures the parts of a loan state.
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
ReadView const & view
Definition Transactor.h:56
beast::Journal const j
Definition Transactor.h:61
std::optional< uint256 const > const parentBatchId
Definition Transactor.h:60
State information when preflighting a tx.
Definition Transactor.h:15
beast::Journal const j
Definition Transactor.h:22
std::optional< uint256 const > parentBatchId
Definition Transactor.h:21
T time_since_epoch(T... args)