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