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