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