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