rippled
Loading...
Searching...
No Matches
LoanPay.cpp
1#include <xrpl/tx/transactors/lending/LoanPay.h>
2//
3#include <xrpl/json/to_string.h>
4#include <xrpl/ledger/helpers/TokenHelpers.h>
5#include <xrpl/protocol/Protocol.h>
6#include <xrpl/protocol/STTakesAsset.h>
7#include <xrpl/protocol/TxFlags.h>
8#include <xrpl/tx/transactors/lending/LendingHelpers.h>
9#include <xrpl/tx/transactors/lending/LoanManage.h>
10
11#include <bit>
12
13namespace xrpl {
14
15bool
20
23{
24 return tfLoanPayMask;
25}
26
29{
30 if (ctx.tx[sfLoanID] == beast::zero)
31 return temINVALID;
32
33 if (ctx.tx[sfAmount] <= beast::zero)
34 return temBAD_AMOUNT;
35
36 // The loan payment flags are all mutually exclusive. If more than one is
37 // set, the tx is malformed.
38 static_assert(
39 (tfLoanLatePayment | tfLoanFullPayment | tfLoanOverpayment) ==
40 ~(tfLoanPayMask | tfUniversal));
41 auto const flagsSet = ctx.tx.getFlags() & ~(tfLoanPayMask | tfUniversal);
42 if (std::popcount(flagsSet) > 1)
43 {
44 JLOG(ctx.j.warn()) << "Only one LoanPay flag can be set per tx. " << flagsSet
45 << " is too many.";
46 return temINVALID_FLAG;
47 }
48
49 return tesSUCCESS;
50}
51
54{
55 using namespace Lending;
56
57 auto const normalCost = Transactor::calculateBaseFee(view, tx);
58
59 if (tx.isFlag(tfLoanFullPayment) || tx.isFlag(tfLoanLatePayment))
60 {
61 // The loan will be making one set of calculations for one full or late
62 // payment
63 return normalCost;
64 }
65
66 // The fee is based on the potential number of payments, unless the loan is
67 // being fully paid off.
68 auto const amount = tx[sfAmount];
69 auto const loanID = tx[sfLoanID];
70
71 auto const loanSle = view.read(keylet::loan(loanID));
72 if (!loanSle)
73 {
74 // Let preclaim worry about the error for this
75 return normalCost;
76 }
77
78 if (loanSle->at(sfPaymentRemaining) <= loanPaymentsPerFeeIncrement)
79 {
80 // If there are fewer than loanPaymentsPerFeeIncrement payments left to
81 // pay, we can skip the computations.
82 return normalCost;
83 }
84
85 if (hasExpired(view, loanSle->at(sfNextPaymentDueDate)))
86 {
87 // If the payment is late, and the late payment flag is not set, it'll
88 // fail
89 return normalCost;
90 }
91
92 auto const brokerSle = view.read(keylet::loanbroker(loanSle->at(sfLoanBrokerID)));
93 if (!brokerSle)
94 {
95 // Let preclaim worry about the error for this
96 return normalCost;
97 }
98 auto const vaultSle = view.read(keylet::vault(brokerSle->at(sfVaultID)));
99 if (!vaultSle)
100 {
101 // Let preclaim worry about the error for this
102 return normalCost;
103 }
104
105 auto const asset = vaultSle->at(sfAsset);
106
107 if (asset != amount.asset())
108 {
109 // Let preclaim worry about the error for this
110 return normalCost;
111 }
112
113 auto const scale = loanSle->at(sfLoanScale);
114
115 auto const regularPayment = roundPeriodicPayment(asset, loanSle->at(sfPeriodicPayment), scale) +
116 loanSle->at(sfLoanServiceFee);
117
118 // If making an overpayment, count it as a full payment because it will do
119 // about the same amount of work, if not more.
120 NumberRoundModeGuard const mg(tx.isFlag(tfLoanOverpayment) ? Number::upward : Number::downward);
121 // Estimate how many payments will be made
122 Number const numPaymentEstimate = static_cast<std::int64_t>(amount / regularPayment);
123
124 // Charge one base fee per paymentsPerFeeIncrement payments, rounding up.
126 auto const feeIncrements = std::max(
127 std::int64_t(1),
128 static_cast<std::int64_t>(numPaymentEstimate / loanPaymentsPerFeeIncrement));
129
130 return feeIncrements * normalCost;
131}
132
133TER
135{
136 auto const& tx = ctx.tx;
137
138 auto const account = tx[sfAccount];
139 auto const loanID = tx[sfLoanID];
140 auto const amount = tx[sfAmount];
141
142 auto const loanSle = ctx.view.read(keylet::loan(loanID));
143 if (!loanSle)
144 {
145 JLOG(ctx.j.warn()) << "Loan does not exist.";
146 return tecNO_ENTRY;
147 }
148
149 if (loanSle->at(sfBorrower) != account)
150 {
151 JLOG(ctx.j.warn()) << "Loan does not belong to the account.";
152 return tecNO_PERMISSION;
153 }
154
155 if (tx.isFlag(tfLoanOverpayment) && !loanSle->isFlag(lsfLoanOverpayment))
156 {
157 JLOG(ctx.j.warn()) << "Requested overpayment on a loan that doesn't allow it";
158 return ctx.view.rules().enabled(fixSecurity3_1_3) ? TER{tecNO_PERMISSION} : temINVALID_FLAG;
159 }
160
161 auto const principalOutstanding = loanSle->at(sfPrincipalOutstanding);
162 auto const paymentRemaining = loanSle->at(sfPaymentRemaining);
163
164 if (paymentRemaining == 0 || principalOutstanding == 0)
165 {
166 JLOG(ctx.j.warn()) << "Loan is already paid off.";
167 return tecKILLED;
168 }
169
170 auto const loanBrokerID = loanSle->at(sfLoanBrokerID);
171 auto const loanBrokerSle = ctx.view.read(keylet::loanbroker(loanBrokerID));
172 if (!loanBrokerSle)
173 {
174 // This should be impossible
175 // LCOV_EXCL_START
176 JLOG(ctx.j.fatal()) << "LoanBroker does not exist.";
177 return tefBAD_LEDGER;
178 // LCOV_EXCL_STOP
179 }
180 auto const vaultID = loanBrokerSle->at(sfVaultID);
181 auto const vaultSle = ctx.view.read(keylet::vault(vaultID));
182 if (!vaultSle)
183 {
184 // This should be impossible
185 // LCOV_EXCL_START
186 JLOG(ctx.j.fatal()) << "Vault does not exist.";
187 return tefBAD_LEDGER;
188 // LCOV_EXCL_STOP
189 }
190 auto const asset = vaultSle->at(sfAsset);
191 auto const vaultPseudoAccount = vaultSle->at(sfAccount);
192
193 if (amount.asset() != asset)
194 {
195 JLOG(ctx.j.warn()) << "Loan amount does not match the Vault asset.";
196 return tecWRONG_ASSET;
197 }
198
199 if (auto const ret = checkFrozen(ctx.view, account, asset))
200 {
201 JLOG(ctx.j.warn()) << "Borrower account is frozen.";
202 return ret;
203 }
204 if (auto const ret = checkDeepFrozen(ctx.view, vaultPseudoAccount, asset))
205 {
206 JLOG(ctx.j.warn()) << "Vault pseudo-account can not receive funds (deep frozen).";
207 return ret;
208 }
209 if (auto const ret = requireAuth(ctx.view, asset, account))
210 {
211 JLOG(ctx.j.warn()) << "Borrower account is not authorized.";
212 return ret;
213 }
214 // Make sure the borrower has enough funds to make the payment!
215 // Do not support "partial payments" - if the transaction says to pay X,
216 // then the account must have X available, even if the loan payment takes
217 // less.
218 if (auto const balance = accountHolds(
219 ctx.view,
220 account,
221 asset,
224 ctx.j,
226 balance < amount)
227 {
228 JLOG(ctx.j.warn()) << "Payment amount too large. Amount: " << to_string(amount.getJson())
229 << ". Balance: " << to_string(balance.getJson());
231 }
232
233 return tesSUCCESS;
234}
235
236TER
238{
239 auto const& tx = ctx_.tx;
240 auto& view = ctx_.view();
241
242 auto const amount = tx[sfAmount];
243
244 auto const loanID = tx[sfLoanID];
245 auto const loanSle = view.peek(keylet::loan(loanID));
246 if (!loanSle)
247 return tefBAD_LEDGER; // LCOV_EXCL_LINE
248 std::int32_t const loanScale = loanSle->at(sfLoanScale);
249
250 auto const brokerID = loanSle->at(sfLoanBrokerID);
251 auto const brokerSle = view.peek(keylet::loanbroker(brokerID));
252 if (!brokerSle)
253 return tefBAD_LEDGER; // LCOV_EXCL_LINE
254 auto const brokerOwner = brokerSle->at(sfOwner);
255 auto const brokerPseudoAccount = brokerSle->at(sfAccount);
256 auto const vaultID = brokerSle->at(sfVaultID);
257 auto const vaultSle = view.peek(keylet::vault(vaultID));
258 if (!vaultSle)
259 return tefBAD_LEDGER; // LCOV_EXCL_LINE
260 auto const vaultPseudoAccount = vaultSle->at(sfAccount);
261 auto const asset = *vaultSle->at(sfAsset);
262
263 // Determine where to send the broker's fee
264 auto coverAvailableProxy = brokerSle->at(sfCoverAvailable);
265 TenthBips32 const coverRateMinimum{brokerSle->at(sfCoverRateMinimum)};
266 auto debtTotalProxy = brokerSle->at(sfDebtTotal);
267
268 // Send the broker fee to the owner if they have sufficient cover available,
269 // _and_ if the owner can receive funds
270 // _and_ if the broker is authorized to hold funds. If not, so as not to
271 // block the payment, add it to the cover balance (send it to the broker
272 // pseudo account).
273 //
274 // Normally freeze status is checked in preclaim, but we do it here to
275 // avoid duplicating the check. It'll claim a fee either way.
276 bool const sendBrokerFeeToOwner = [&]() {
277 // Round the minimum required cover up to be conservative. This ensures
278 // CoverAvailable never drops below the theoretical minimum, protecting
279 // the broker's solvency.
281 return coverAvailableProxy >=
283 asset, tenthBipsOfValue(debtTotalProxy.value(), coverRateMinimum), loanScale) &&
284 !isDeepFrozen(view, brokerOwner, asset) &&
285 !requireAuth(view, asset, brokerOwner, AuthType::StrongAuth);
286 }();
287
288 auto const brokerPayee = sendBrokerFeeToOwner ? brokerOwner : brokerPseudoAccount;
289 auto const brokerPayeeSle = view.peek(keylet::account(brokerPayee));
290 if (!sendBrokerFeeToOwner)
291 {
292 // If we can't send the fee to the owner, and the pseudo-account is
293 // frozen, then we have to fail the payment.
294 if (auto const ret = checkDeepFrozen(view, brokerPayee, asset))
295 {
296 JLOG(j_.warn()) << "Both Loan Broker and Loan Broker pseudo-account "
297 "can not receive funds (deep frozen).";
298 return ret;
299 }
300 }
301
302 //------------------------------------------------------
303 // Loan object state changes
304
305 // Unimpair the loan if it was impaired. Do this before the payment is
306 // attempted, so the original values can be used. If the payment fails, this
307 // change will be discarded.
308 if (loanSle->isFlag(lsfLoanImpaired))
309 {
310 if (auto const ret = LoanManage::unimpairLoan(view, loanSle, vaultSle, asset, j_))
311 {
312 JLOG(j_.fatal()) << "Failed to unimpair loan before payment.";
313 return ret; // LCOV_EXCL_LINE
314 }
315 }
316
317 LoanPaymentType const paymentType = [&tx]() {
318 // preflight already checked that at most one flag is set.
319 if (tx.isFlag(tfLoanLatePayment))
321 if (tx.isFlag(tfLoanFullPayment))
323 if (tx.isFlag(tfLoanOverpayment))
326 }();
327
328 Expected<LoanPaymentParts, TER> const paymentParts =
329 loanMakePayment(asset, view, loanSle, brokerSle, amount, paymentType, j_);
330
331 if (!paymentParts)
332 {
333 XRPL_ASSERT_PARTS(
334 paymentParts.error(), "xrpl::LoanPay::doApply", "payment error is an error");
335 return paymentParts.error();
336 }
337
338 // If the payment computation completed without error, the loanSle object
339 // has been modified.
340 view.update(loanSle);
341
342 XRPL_ASSERT_PARTS(
343 // It is possible to pay 0 principal
344 paymentParts->principalPaid >= 0,
345 "xrpl::LoanPay::doApply",
346 "valid principal paid");
347 XRPL_ASSERT_PARTS(
348 // It is possible to pay 0 interest
349 paymentParts->interestPaid >= 0,
350 "xrpl::LoanPay::doApply",
351 "valid interest paid");
352 XRPL_ASSERT_PARTS(
353 // It should not be possible to pay 0 total
354 paymentParts->principalPaid + paymentParts->interestPaid > 0,
355 "xrpl::LoanPay::doApply",
356 "valid total paid");
357 XRPL_ASSERT_PARTS(paymentParts->feePaid >= 0, "xrpl::LoanPay::doApply", "valid fee paid");
358
359 if (paymentParts->principalPaid < 0 || paymentParts->interestPaid < 0 ||
360 paymentParts->feePaid < 0)
361 {
362 // LCOV_EXCL_START
363 JLOG(j_.fatal()) << "Loan payment computation returned invalid values.";
364 return tecLIMIT_EXCEEDED;
365 // LCOV_EXCL_STOP
366 }
367
368 JLOG(j_.debug()) << "Loan Pay: principal paid: " << paymentParts->principalPaid
369 << ", interest paid: " << paymentParts->interestPaid
370 << ", fee paid: " << paymentParts->feePaid
371 << ", value change: " << paymentParts->valueChange;
372
373 //------------------------------------------------------
374 // LoanBroker object state changes
375 view.update(brokerSle);
376
377 auto assetsAvailableProxy = vaultSle->at(sfAssetsAvailable);
378 auto assetsTotalProxy = vaultSle->at(sfAssetsTotal);
379
380 // The vault may be at a different scale than the loan. Reduce rounding
381 // errors during the payment by rounding some of the values to that scale.
382 auto const vaultScale = getAssetsTotalScale(vaultSle);
383
384 auto const totalPaidToVaultRaw = paymentParts->principalPaid + paymentParts->interestPaid;
385 auto const totalPaidToVaultRounded =
386 roundToAsset(asset, totalPaidToVaultRaw, vaultScale, Number::downward);
387 XRPL_ASSERT_PARTS(
388 !asset.integral() || totalPaidToVaultRaw == totalPaidToVaultRounded,
389 "xrpl::LoanPay::doApply",
390 "rounding does nothing for integral asset");
391 // Account for value changes when reducing the broker's debt:
392 // - Positive value change (from full/late/overpayments): Subtract from the
393 // amount credited toward debt to avoid over-reducing the debt.
394 // - Negative value change (from full/overpayments): Add to the amount
395 // credited toward debt,effectively increasing the debt reduction.
396 auto const totalPaidToVaultForDebt = totalPaidToVaultRaw - paymentParts->valueChange;
397
398 auto const totalPaidToBroker = paymentParts->feePaid;
399
400 XRPL_ASSERT_PARTS(
401 (totalPaidToVaultRaw + totalPaidToBroker) ==
402 (paymentParts->principalPaid + paymentParts->interestPaid + paymentParts->feePaid),
403 "xrpl::LoanPay::doApply",
404 "payments add up");
405
406 // Decrease LoanBroker Debt by the amount paid, add the Loan value change
407 // (which might be negative). totalPaidToVaultForDebt may be negative,
408 // increasing the debt
409 XRPL_ASSERT_PARTS(
410 isRounded(asset, totalPaidToVaultForDebt, loanScale),
411 "xrpl::LoanPay::doApply",
412 "totalPaidToVaultForDebt rounding good");
413 // Despite our best efforts, it's possible for rounding errors to accumulate
414 // in the loan broker's debt total. This is because the broker may have more
415 // than one loan with significantly different scales.
416 adjustImpreciseNumber(debtTotalProxy, -totalPaidToVaultForDebt, asset, vaultScale);
417
418 //------------------------------------------------------
419 // Vault object state changes
420 view.update(vaultSle);
421
422#if !NDEBUG
423 {
424 Number const assetsAvailableBefore = *assetsAvailableProxy;
425 Number const pseudoAccountBalanceBefore = accountHolds(
426 view,
427 vaultPseudoAccount,
428 asset,
431 j_);
432
433 XRPL_ASSERT_PARTS(
434 assetsAvailableBefore == pseudoAccountBalanceBefore,
435 "xrpl::LoanPay::doApply",
436 "vault pseudo balance agrees before");
437 }
438#endif
439
440 assetsAvailableProxy += totalPaidToVaultRounded;
441 assetsTotalProxy += paymentParts->valueChange;
442
443 XRPL_ASSERT_PARTS(
444 *assetsAvailableProxy <= *assetsTotalProxy,
445 "xrpl::LoanPay::doApply",
446 "assets available must not be greater than assets outstanding");
447
448 if (*assetsAvailableProxy > *assetsTotalProxy)
449 {
450 // LCOV_EXCL_START
451 JLOG(j_.fatal()) << "Vault assets available must not be greater "
452 "than assets outstanding. Available: "
453 << *assetsAvailableProxy << ", Total: " << *assetsTotalProxy;
454 return tecINTERNAL;
455 // LCOV_EXCL_STOP
456 }
457
458 JLOG(j_.debug()) << "total paid to vault raw: " << totalPaidToVaultRaw
459 << ", total paid to vault rounded: " << totalPaidToVaultRounded
460 << ", total paid to broker: " << totalPaidToBroker
461 << ", amount from transaction: " << amount;
462
463 // Move funds
464 XRPL_ASSERT_PARTS(
465 totalPaidToVaultRounded + totalPaidToBroker <= amount,
466 "xrpl::LoanPay::doApply",
467 "amount is sufficient");
468
469 if (!sendBrokerFeeToOwner)
470 {
471 // If there is not enough first-loss capital, add the fee to First Loss
472 // Cover Pool. Note that this moves the entire fee - it does not attempt
473 // to split it. The broker can Withdraw it later if they want, or leave
474 // it for future needs.
475 coverAvailableProxy += totalPaidToBroker;
476 }
477
478 associateAsset(*loanSle, asset);
479 associateAsset(*brokerSle, asset);
480 associateAsset(*vaultSle, asset);
481
482 // Duplicate some checks after rounding
483 XRPL_ASSERT_PARTS(
484 *assetsAvailableProxy <= *assetsTotalProxy,
485 "xrpl::LoanPay::doApply",
486 "assets available must not be greater than assets outstanding");
487
488#if !NDEBUG
489 auto const accountBalanceBefore = accountHolds(
490 view,
491 account_,
492 asset,
495 j_,
497 auto const vaultBalanceBefore = account_ == vaultPseudoAccount
498 ? STAmount{asset, 0}
499 : accountHolds(
500 view,
501 vaultPseudoAccount,
502 asset,
505 j_,
507 auto const brokerBalanceBefore = account_ == brokerPayee
508 ? STAmount{asset, 0}
509 : accountHolds(
510 view,
511 brokerPayee,
512 asset,
515 j_,
517#endif
518
519 if (totalPaidToVaultRounded != beast::zero)
520 {
521 if (auto const ter = requireAuth(view, asset, vaultPseudoAccount, AuthType::StrongAuth))
522 return ter;
523 }
524
525 if (totalPaidToBroker != beast::zero)
526 {
527 if (brokerPayee == account_)
528 {
529 // The broker may have deleted their holding. Recreate it if needed
530 if (auto const ter = addEmptyHolding(
531 view, brokerPayee, brokerPayeeSle->at(sfBalance).value().xrp(), asset, j_);
532 ter && ter != tecDUPLICATE)
533 {
534 // ignore tecDUPLICATE. That means the holding already exists,
535 // and is fine here
536 return ter;
537 }
538 }
539 if (auto const ter = requireAuth(view, asset, brokerPayee, AuthType::StrongAuth))
540 return ter;
541 }
542
543 if (auto const ter = accountSendMulti(
544 view,
545 account_,
546 asset,
547 {{vaultPseudoAccount, totalPaidToVaultRounded}, {brokerPayee, totalPaidToBroker}},
548 j_,
550 return ter;
551
552#if !NDEBUG
553 Number const assetsAvailableAfter = *assetsAvailableProxy;
554 Number const pseudoAccountBalanceAfter = accountHolds(
555 view,
556 vaultPseudoAccount,
557 asset,
560 j_);
561 XRPL_ASSERT_PARTS(
562 assetsAvailableAfter == pseudoAccountBalanceAfter,
563 "xrpl::LoanPay::doApply",
564 "vault pseudo balance agrees after");
565
566 auto const accountBalanceAfter = accountHolds(
567 view,
568 account_,
569 asset,
572 j_,
574 auto const vaultBalanceAfter = account_ == vaultPseudoAccount
575 ? STAmount{asset, 0}
576 : accountHolds(
577 view,
578 vaultPseudoAccount,
579 asset,
582 j_,
584 auto const brokerBalanceAfter = account_ == brokerPayee
585 ? STAmount{asset, 0}
586 : accountHolds(
587 view,
588 brokerPayee,
589 asset,
592 j_,
594
595 XRPL_ASSERT_PARTS(
596 accountBalanceBefore + vaultBalanceBefore + brokerBalanceBefore ==
597 accountBalanceAfter + vaultBalanceAfter + brokerBalanceAfter,
598 "xrpl::LoanPay::doApply",
599 "funds are conserved (with rounding)");
600 XRPL_ASSERT_PARTS(
601 accountBalanceAfter >= beast::zero, "xrpl::LoanPay::doApply", "positive account balance");
602 XRPL_ASSERT_PARTS(
603 accountBalanceAfter < accountBalanceBefore || account_ == asset.getIssuer(),
604 "xrpl::LoanPay::doApply",
605 "account balance decreased");
606 XRPL_ASSERT_PARTS(
607 vaultBalanceAfter >= beast::zero && brokerBalanceAfter >= beast::zero,
608 "xrpl::LoanPay::doApply",
609 "positive vault and broker balances");
610 XRPL_ASSERT_PARTS(
611 vaultBalanceAfter >= vaultBalanceBefore,
612 "xrpl::LoanPay::doApply",
613 "vault balance did not decrease");
614 XRPL_ASSERT_PARTS(
615 brokerBalanceAfter >= brokerBalanceBefore,
616 "xrpl::LoanPay::doApply",
617 "broker balance did not decrease");
618 XRPL_ASSERT_PARTS(
619 vaultBalanceAfter > vaultBalanceBefore || brokerBalanceAfter > brokerBalanceBefore,
620 "xrpl::LoanPay::doApply",
621 "vault and/or broker balance increased");
622#endif
623
624 return tesSUCCESS;
625}
626
627//------------------------------------------------------------------------------
628
629} // namespace xrpl
Stream fatal() const
Definition Journal.h:325
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 std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
constexpr E const & error() const
Definition Expected.h:147
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 preclaim(PreclaimContext const &ctx)
Definition LoanPay.cpp:134
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
Definition LoanPay.cpp:22
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
Definition LoanPay.cpp:53
static bool checkExtraFeatures(PreflightContext const &ctx)
Definition LoanPay.cpp:16
TER doApply() override
Definition LoanPay.cpp:237
static NotTEC preflight(PreflightContext const &ctx)
Definition LoanPay.cpp:28
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
static rounding_mode setround(rounding_mode mode)
Definition Number.cpp:39
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
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
bool isFlag(std::uint32_t) const
Definition STObject.cpp:503
std::uint32_t getFlags() const
Definition STObject.cpp:509
AccountID const account_
Definition Transactor.h:116
beast::Journal const j_
Definition Transactor.h:114
ApplyView & view()
Definition Transactor.h:132
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
ApplyContext & ctx_
Definition Transactor.h:112
T max(T... args)
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)
@ fhZERO_IF_FROZEN
@ fhIGNORE_FREEZE
@ shFULL_BALANCE
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
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:34
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)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
constexpr FlagValue tfUniversal
Definition TxFlags.h:42
@ ahIGNORE_AUTH
@ ahZERO_IF_UNAUTHORIZED
Expected< LoanPaymentParts, TER > loanMakePayment(Asset const &asset, ApplyView &view, SLE::ref loan, SLE::const_ref brokerSle, STAmount const &amount, LoanPaymentType const paymentType, beast::Journal j)
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:695
Number roundPeriodicPayment(Asset const &asset, Number const &periodicPayment, std::int32_t scale)
Ensure the periodic payment is always rounded consistently.
@ temINVALID
Definition TER.h:90
@ temINVALID_FLAG
Definition TER.h:91
@ temBAD_AMOUNT
Definition TER.h:69
@ tecWRONG_ASSET
Definition TER.h:341
@ tecNO_ENTRY
Definition TER.h:287
@ tecINTERNAL
Definition TER.h:291
@ tecINSUFFICIENT_FUNDS
Definition TER.h:306
@ tecKILLED
Definition TER.h:297
@ 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!...
@ 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)
T popcount(T... args)
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
State information when preflighting a tx.
Definition Transactor.h:14
beast::Journal const j
Definition Transactor.h:21