rippled
Loading...
Searching...
No Matches
TokenHelpers.cpp
1#include <xrpl/ledger/helpers/TokenHelpers.h>
2//
3#include <xrpl/basics/Log.h>
4#include <xrpl/beast/utility/instrumentation.h>
5#include <xrpl/ledger/helpers/AccountRootHelpers.h>
6#include <xrpl/ledger/helpers/MPTokenHelpers.h>
7#include <xrpl/ledger/helpers/RippleStateHelpers.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/Indexes.h>
10#include <xrpl/protocol/LedgerFormats.h>
11#include <xrpl/protocol/Protocol.h>
12#include <xrpl/protocol/Quality.h>
13#include <xrpl/protocol/TER.h>
14#include <xrpl/protocol/st.h>
15
16#include <type_traits>
17#include <variant>
18
19namespace xrpl {
20
21// Forward declaration for function that remains in View.h/cpp
22bool
24 ReadView const& view,
25 AccountID const& account,
26 Issue const& asset,
27 Issue const& asset2);
28
29//------------------------------------------------------------------------------
30//
31// Freeze checking (Asset-based)
32//
33//------------------------------------------------------------------------------
34
35bool
36isGlobalFrozen(ReadView const& view, Asset const& asset)
37{
38 return std::visit(
39 [&]<ValidIssueType TIss>(TIss const& issue) {
40 if constexpr (std::is_same_v<TIss, Issue>)
41 {
42 return isGlobalFrozen(view, issue.getIssuer());
43 }
44 else
45 {
46 return isGlobalFrozen(view, issue);
47 }
48 },
49 asset.value());
50}
51
52bool
53isIndividualFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
54{
55 return std::visit(
56 [&](auto const& issue) { return isIndividualFrozen(view, account, issue); }, asset.value());
57}
58
59bool
60isFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth)
61{
62 return std::visit(
63 [&](auto const& issue) { return isFrozen(view, account, issue, depth); }, asset.value());
64}
65
66TER
67checkFrozen(ReadView const& view, AccountID const& account, Issue const& issue)
68{
69 return isFrozen(view, account, issue) ? (TER)tecFROZEN : (TER)tesSUCCESS;
70}
71
72TER
73checkFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
74{
75 return isFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS;
76}
77
78TER
79checkFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
80{
81 return std::visit(
82 [&](auto const& issue) { return checkFrozen(view, account, issue); }, asset.value());
83}
84
85bool
87 ReadView const& view,
89 Issue const& issue)
90{
91 for (auto const& account : accounts)
92 {
93 if (isFrozen(view, account, issue.currency, issue.account))
94 return true;
95 }
96 return false;
97}
98
99bool
101 ReadView const& view,
102 std::initializer_list<AccountID> const& accounts,
103 Asset const& asset,
104 int depth)
105{
106 return std::visit(
107 [&]<ValidIssueType TIss>(TIss const& issue) {
108 if constexpr (std::is_same_v<TIss, Issue>)
109 {
110 return isAnyFrozen(view, accounts, issue);
111 }
112 else
113 {
114 return isAnyFrozen(view, accounts, issue, depth);
115 }
116 },
117 asset.value());
118}
119
120bool
121isDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue, int depth)
122{
123 // Unlike IOUs, frozen / locked MPTs are not allowed to send or receive
124 // funds, so checking "deep frozen" is the same as checking "frozen".
125 return isFrozen(view, account, mptIssue, depth);
126}
127
128bool
129isDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset, int depth)
130{
131 return std::visit(
132 [&](auto const& issue) { return isDeepFrozen(view, account, issue, depth); },
133 asset.value());
134}
135
136TER
137checkDeepFrozen(ReadView const& view, AccountID const& account, MPTIssue const& mptIssue)
138{
139 return isDeepFrozen(view, account, mptIssue) ? (TER)tecLOCKED : (TER)tesSUCCESS;
140}
141
142TER
143checkDeepFrozen(ReadView const& view, AccountID const& account, Asset const& asset)
144{
145 return std::visit(
146 [&](auto const& issue) { return checkDeepFrozen(view, account, issue); }, asset.value());
147}
148
149//------------------------------------------------------------------------------
150//
151// Account balance functions
152//
153//------------------------------------------------------------------------------
154
157 ReadView const& view,
158 AccountID const& account,
159 Currency const& currency,
160 AccountID const& issuer,
161 FreezeHandling zeroIfFrozen,
163{
164 auto sle = view.read(keylet::line(account, issuer, currency));
165
166 if (!sle)
167 {
168 return nullptr;
169 }
170
171 if (zeroIfFrozen == fhZERO_IF_FROZEN)
172 {
173 if (isFrozen(view, account, currency, issuer) ||
174 isDeepFrozen(view, account, currency, issuer))
175 {
176 return nullptr;
177 }
178
179 // when fixFrozenLPTokenTransfer is enabled, if currency is lptoken,
180 // we need to check if the associated assets have been frozen
181 if (view.rules().enabled(fixFrozenLPTokenTransfer))
182 {
183 auto const sleIssuer = view.read(keylet::account(issuer));
184 if (!sleIssuer)
185 {
186 return nullptr; // LCOV_EXCL_LINE
187 }
188 if (sleIssuer->isFieldPresent(sfAMMID))
189 {
190 auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID]));
191
192 if (!sleAmm ||
194 view,
195 account,
196 (*sleAmm)[sfAsset].get<Issue>(),
197 (*sleAmm)[sfAsset2].get<Issue>()))
198 {
199 return nullptr;
200 }
201 }
202 }
203 }
204
205 return sle;
206}
207
208static STAmount
210 ReadView const& view,
211 SLE::const_ref sle,
212 AccountID const& account,
213 Currency const& currency,
214 AccountID const& issuer,
215 bool includeOppositeLimit,
217{
218 STAmount amount;
219 if (sle)
220 {
221 amount = sle->getFieldAmount(sfBalance);
222 bool const accountHigh = account > issuer;
223 auto const& oppositeField = accountHigh ? sfLowLimit : sfHighLimit;
224 if (accountHigh)
225 {
226 // Put balance in account terms.
227 amount.negate();
228 }
229 if (includeOppositeLimit)
230 {
231 amount += sle->getFieldAmount(oppositeField);
232 }
233 amount.setIssuer(issuer);
234 }
235 else
236 {
237 amount.clear(Issue{currency, issuer});
238 }
239
240 JLOG(j.trace()) << "getTrustLineBalance:" << " account=" << to_string(account)
241 << " amount=" << amount.getFullText();
242
243 return view.balanceHook(account, issuer, amount);
244}
245
246STAmount
248 ReadView const& view,
249 AccountID const& account,
250 Currency const& currency,
251 AccountID const& issuer,
252 FreezeHandling zeroIfFrozen,
254 SpendableHandling includeFullBalance)
255{
256 STAmount const amount;
257 if (isXRP(currency))
258 {
259 return {xrpLiquid(view, account, 0, j)};
260 }
261
262 bool const returnSpendable = (includeFullBalance == shFULL_BALANCE);
263 if (returnSpendable && account == issuer)
264 {
265 // If the account is the issuer, then their limit is effectively
266 // infinite
267 return STAmount{Issue{currency, issuer}, STAmount::cMaxValue, STAmount::cMaxOffset};
268 }
269
270 // IOU: Return balance on trust line modulo freeze
271 SLE::const_pointer const sle =
272 getLineIfUsable(view, account, currency, issuer, zeroIfFrozen, j);
273
274 return getTrustLineBalance(view, sle, account, currency, issuer, returnSpendable, j);
275}
276
277STAmount
279 ReadView const& view,
280 AccountID const& account,
281 Issue const& issue,
282 FreezeHandling zeroIfFrozen,
284 SpendableHandling includeFullBalance)
285{
286 return accountHolds(
287 view, account, issue.currency, issue.account, zeroIfFrozen, j, includeFullBalance);
288}
289
290STAmount
292 ReadView const& view,
293 AccountID const& account,
294 MPTIssue const& mptIssue,
295 FreezeHandling zeroIfFrozen,
296 AuthHandling zeroIfUnauthorized,
298 SpendableHandling includeFullBalance)
299{
300 bool const returnSpendable = (includeFullBalance == shFULL_BALANCE);
301
302 if (returnSpendable && account == mptIssue.getIssuer())
303 {
304 // if the account is the issuer, and the issuance exists, their limit is
305 // the issuance limit minus the outstanding value
306 auto const issuance = view.read(keylet::mptIssuance(mptIssue.getMptID()));
307
308 if (!issuance)
309 {
310 return STAmount{mptIssue};
311 }
312 return STAmount{
313 mptIssue,
314 issuance->at(~sfMaximumAmount).value_or(maxMPTokenAmount) -
315 issuance->at(sfOutstandingAmount)};
316 }
317
318 STAmount amount;
319
320 auto const sleMpt = view.read(keylet::mptoken(mptIssue.getMptID(), account));
321
322 if (!sleMpt)
323 {
324 amount.clear(mptIssue);
325 }
326 else if (zeroIfFrozen == fhZERO_IF_FROZEN && isFrozen(view, account, mptIssue))
327 {
328 amount.clear(mptIssue);
329 }
330 else
331 {
332 amount = STAmount{mptIssue, sleMpt->getFieldU64(sfMPTAmount)};
333
334 // Only if auth check is needed, as it needs to do an additional read
335 // operation. Note featureSingleAssetVault will affect error codes.
336 if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
337 view.rules().enabled(featureSingleAssetVault))
338 {
339 if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth);
340 !isTesSuccess(err))
341 amount.clear(mptIssue);
342 }
343 else if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED)
344 {
345 auto const sleIssuance = view.read(keylet::mptIssuance(mptIssue.getMptID()));
346
347 // if auth is enabled on the issuance and mpt is not authorized,
348 // clear amount
349 if (sleIssuance && sleIssuance->isFlag(lsfMPTRequireAuth) &&
350 !sleMpt->isFlag(lsfMPTAuthorized))
351 amount.clear(mptIssue);
352 }
353 }
354
355 return amount;
356}
357
358[[nodiscard]] STAmount
360 ReadView const& view,
361 AccountID const& account,
362 Asset const& asset,
363 FreezeHandling zeroIfFrozen,
364 AuthHandling zeroIfUnauthorized,
366 SpendableHandling includeFullBalance)
367{
368 return std::visit(
369 [&]<ValidIssueType TIss>(TIss const& value) {
370 if constexpr (std::is_same_v<TIss, Issue>)
371 {
372 return accountHolds(view, account, value, zeroIfFrozen, j, includeFullBalance);
373 }
374 else if constexpr (std::is_same_v<TIss, MPTIssue>)
375 {
376 return accountHolds(
377 view, account, value, zeroIfFrozen, zeroIfUnauthorized, j, includeFullBalance);
378 }
379 },
380 asset.value());
381}
382
383STAmount
385 ReadView const& view,
386 AccountID const& id,
387 STAmount const& saDefault,
388 FreezeHandling freezeHandling,
390{
391 if (!saDefault.native() && saDefault.getIssuer() == id)
392 return saDefault;
393
394 return accountHolds(
395 view, id, saDefault.getCurrency(), saDefault.getIssuer(), freezeHandling, j);
396}
397
398Rate
399transferRate(ReadView const& view, STAmount const& amount)
400{
401 return std::visit(
402 [&]<ValidIssueType TIss>(TIss const& issue) {
403 if constexpr (std::is_same_v<TIss, Issue>)
404 {
405 return transferRate(view, issue.getIssuer());
406 }
407 else
408 {
409 return transferRate(view, issue.getMptID());
410 }
411 },
412 amount.asset().value());
413}
414
415//------------------------------------------------------------------------------
416//
417// Holding operations
418//
419//------------------------------------------------------------------------------
420
421[[nodiscard]] TER
422canAddHolding(ReadView const& view, Issue const& issue)
423{
424 if (issue.native())
425 {
426 return tesSUCCESS; // No special checks for XRP
427 }
428
429 auto const issuer = view.read(keylet::account(issue.getIssuer()));
430 if (!issuer)
431 {
432 return terNO_ACCOUNT;
433 }
434 if (!issuer->isFlag(lsfDefaultRipple))
435 {
436 return terNO_RIPPLE;
437 }
438
439 return tesSUCCESS;
440}
441
442[[nodiscard]] TER
443canAddHolding(ReadView const& view, Asset const& asset)
444{
445 return std::visit(
446 [&]<ValidIssueType TIss>(TIss const& issue) -> TER { return canAddHolding(view, issue); },
447 asset.value());
448}
449
450TER
452 ApplyView& view,
453 AccountID const& accountID,
454 XRPAmount priorBalance,
455 Asset const& asset,
456 beast::Journal journal)
457{
458 return std::visit(
459 [&]<ValidIssueType TIss>(TIss const& issue) -> TER {
460 return addEmptyHolding(view, accountID, priorBalance, issue, journal);
461 },
462 asset.value());
463}
464
465TER
467 ApplyView& view,
468 AccountID const& accountID,
469 Asset const& asset,
470 beast::Journal journal)
471{
472 return std::visit(
473 [&]<ValidIssueType TIss>(TIss const& issue) -> TER {
474 return removeEmptyHolding(view, accountID, issue, journal);
475 },
476 asset.value());
477}
478
479//------------------------------------------------------------------------------
480//
481// Authorization and transfer checks
482//
483//------------------------------------------------------------------------------
484
485TER
486requireAuth(ReadView const& view, Asset const& asset, AccountID const& account, AuthType authType)
487{
488 return std::visit(
489 [&]<ValidIssueType TIss>(TIss const& issue_) {
490 return requireAuth(view, issue_, account, authType);
491 },
492 asset.value());
493}
494
495TER
496canTransfer(ReadView const& view, Asset const& asset, AccountID const& from, AccountID const& to)
497{
498 return std::visit(
499 [&]<ValidIssueType TIss>(TIss const& issue) -> TER {
500 return canTransfer(view, issue, from, to);
501 },
502 asset.value());
503}
504
505//------------------------------------------------------------------------------
506//
507// Money Transfers
508//
509//------------------------------------------------------------------------------
510
511// Direct send w/o fees:
512// - Redeeming IOUs and/or sending sender's own IOUs.
513// - Create trust line if needed.
514// --> bCheckIssuer : normally require issuer to be involved.
515static TER
517 ApplyView& view,
518 AccountID const& uSenderID,
519 AccountID const& uReceiverID,
520 STAmount const& saAmount,
521 bool bCheckIssuer,
523{
524 AccountID const& issuer = saAmount.getIssuer();
525 Currency const& currency = saAmount.getCurrency();
526
527 // Make sure issuer is involved.
528 XRPL_ASSERT(
529 !bCheckIssuer || uSenderID == issuer || uReceiverID == issuer,
530 "xrpl::rippleCreditIOU : matching issuer or don't care");
531 (void)issuer;
532
533 // Disallow sending to self.
534 XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleCreditIOU : sender is not receiver");
535
536 bool const bSenderHigh = uSenderID > uReceiverID;
537 auto const index = keylet::line(uSenderID, uReceiverID, currency);
538
539 XRPL_ASSERT(
540 !isXRP(uSenderID) && uSenderID != noAccount(), "xrpl::rippleCreditIOU : sender is not XRP");
541 XRPL_ASSERT(
542 !isXRP(uReceiverID) && uReceiverID != noAccount(),
543 "xrpl::rippleCreditIOU : receiver is not XRP");
544
545 // If the line exists, modify it accordingly.
546 if (auto const sleRippleState = view.peek(index))
547 {
548 STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
549
550 if (bSenderHigh)
551 saBalance.negate(); // Put balance in sender terms.
552
553 view.creditHook(uSenderID, uReceiverID, saAmount, saBalance);
554
555 STAmount const saBefore = saBalance;
556
557 saBalance -= saAmount;
558
559 JLOG(j.trace()) << "rippleCreditIOU: " << to_string(uSenderID) << " -> "
560 << to_string(uReceiverID) << " : before=" << saBefore.getFullText()
561 << " amount=" << saAmount.getFullText()
562 << " after=" << saBalance.getFullText();
563
564 std::uint32_t const uFlags(sleRippleState->getFieldU32(sfFlags));
565 bool bDelete = false;
566
567 // FIXME This NEEDS to be cleaned up and simplified. It's impossible
568 // for anyone to understand.
569 if (saBefore > beast::zero
570 // Sender balance was positive.
571 && saBalance <= beast::zero
572 // Sender is zero or negative.
573 && ((uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) != 0u)
574 // Sender reserve is set.
575 && static_cast<bool>(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) !=
576 static_cast<bool>(
577 view.read(keylet::account(uSenderID))->getFlags() & lsfDefaultRipple) &&
578 ((uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) == 0u) &&
579 !sleRippleState->getFieldAmount(!bSenderHigh ? sfLowLimit : sfHighLimit)
580 // Sender trust limit is 0.
581 && (sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityIn : sfHighQualityIn) == 0u)
582 // Sender quality in is 0.
583 &&
584 (sleRippleState->getFieldU32(!bSenderHigh ? sfLowQualityOut : sfHighQualityOut) == 0u))
585 // Sender quality out is 0.
586 {
587 // Clear the reserve of the sender, possibly delete the line!
588 adjustOwnerCount(view, view.peek(keylet::account(uSenderID)), -1, j);
589
590 // Clear reserve flag.
591 sleRippleState->setFieldU32(
592 sfFlags, uFlags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve));
593
594 // Balance is zero, receiver reserve is clear.
595 bDelete = !saBalance // Balance is zero.
596 && ((uFlags & (bSenderHigh ? lsfLowReserve : lsfHighReserve)) == 0u);
597 // Receiver reserve is clear.
598 }
599
600 if (bSenderHigh)
601 saBalance.negate();
602
603 // Want to reflect balance to zero even if we are deleting line.
604 sleRippleState->setFieldAmount(sfBalance, saBalance);
605 // ONLY: Adjust ripple balance.
606
607 if (bDelete)
608 {
609 return trustDelete(
610 view,
611 sleRippleState,
612 bSenderHigh ? uReceiverID : uSenderID,
613 !bSenderHigh ? uReceiverID : uSenderID,
614 j);
615 }
616
617 view.update(sleRippleState);
618 return tesSUCCESS;
619 }
620
621 STAmount const saReceiverLimit(Issue{currency, uReceiverID});
622 STAmount saBalance{saAmount};
623
624 saBalance.setIssuer(noAccount());
625
626 JLOG(j.debug()) << "rippleCreditIOU: "
627 "create line: "
628 << to_string(uSenderID) << " -> " << to_string(uReceiverID) << " : "
629 << saAmount.getFullText();
630
631 auto const sleAccount = view.peek(keylet::account(uReceiverID));
632 if (!sleAccount)
633 return tefINTERNAL; // LCOV_EXCL_LINE
634
635 bool const noRipple = (sleAccount->getFlags() & lsfDefaultRipple) == 0;
636
637 return trustCreate(
638 view,
639 bSenderHigh,
640 uSenderID,
641 uReceiverID,
642 index.key,
643 sleAccount,
644 false,
645 noRipple,
646 false,
647 false,
648 saBalance,
649 saReceiverLimit,
650 0,
651 0,
652 j);
653}
654
655// Send regardless of limits.
656// --> saAmount: Amount/currency/issuer to deliver to receiver.
657// <-- saActual: Amount actually cost. Sender pays fees.
658static TER
660 ApplyView& view,
661 AccountID const& uSenderID,
662 AccountID const& uReceiverID,
663 STAmount const& saAmount,
664 STAmount& saActual,
666 WaiveTransferFee waiveFee)
667{
668 auto const& issuer = saAmount.getIssuer();
669
670 XRPL_ASSERT(
671 !isXRP(uSenderID) && !isXRP(uReceiverID),
672 "xrpl::rippleSendIOU : neither sender nor receiver is XRP");
673 XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleSendIOU : sender is not receiver");
674
675 if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount())
676 {
677 // Direct send: redeeming IOUs and/or sending own IOUs.
678 auto const ter = rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, false, j);
679 if (!isTesSuccess(ter))
680 return ter;
681 saActual = saAmount;
682 return tesSUCCESS;
683 }
684
685 // Sending 3rd party IOUs: transit.
686
687 // Calculate the amount to transfer accounting
688 // for any transfer fees if the fee is not waived:
689 saActual = (waiveFee == WaiveTransferFee::Yes) ? saAmount
690 : multiply(saAmount, transferRate(view, issuer));
691
692 JLOG(j.debug()) << "rippleSendIOU> " << to_string(uSenderID) << " - > "
693 << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText()
694 << " cost=" << saActual.getFullText();
695
696 TER terResult = rippleCreditIOU(view, issuer, uReceiverID, saAmount, true, j);
697
698 if (tesSUCCESS == terResult)
699 terResult = rippleCreditIOU(view, uSenderID, issuer, saActual, true, j);
700
701 return terResult;
702}
703
704// Send regardless of limits.
705// --> receivers: Amount/currency/issuer to deliver to receivers.
706// <-- saActual: Amount actually cost to sender. Sender pays fees.
707static TER
709 ApplyView& view,
710 AccountID const& senderID,
711 Issue const& issue,
712 MultiplePaymentDestinations const& receivers,
713 STAmount& actual,
715 WaiveTransferFee waiveFee)
716{
717 auto const& issuer = issue.getIssuer();
718
719 XRPL_ASSERT(!isXRP(senderID), "xrpl::rippleSendMultiIOU : sender is not XRP");
720
721 // These may diverge
722 STAmount takeFromSender{issue};
723 actual = takeFromSender;
724
725 // Failures return immediately.
726 for (auto const& r : receivers)
727 {
728 auto const& receiverID = r.first;
729 STAmount const amount{issue, r.second};
730
731 /* If we aren't sending anything or if the sender is the same as the
732 * receiver then we don't need to do anything.
733 */
734 if (!amount || (senderID == receiverID))
735 continue;
736
737 XRPL_ASSERT(!isXRP(receiverID), "xrpl::rippleSendMultiIOU : receiver is not XRP");
738
739 if (senderID == issuer || receiverID == issuer || issuer == noAccount())
740 {
741 // Direct send: redeeming IOUs and/or sending own IOUs.
742 if (auto const ter = rippleCreditIOU(view, senderID, receiverID, amount, false, j))
743 return ter;
744 actual += amount;
745 // Do not add amount to takeFromSender, because rippleCreditIOU took
746 // it.
747
748 continue;
749 }
750
751 // Sending 3rd party IOUs: transit.
752
753 // Calculate the amount to transfer accounting
754 // for any transfer fees if the fee is not waived:
755 STAmount const actualSend = (waiveFee == WaiveTransferFee::Yes)
756 ? amount
757 : multiply(amount, transferRate(view, issuer));
758 actual += actualSend;
759 takeFromSender += actualSend;
760
761 JLOG(j.debug()) << "rippleSendMultiIOU> " << to_string(senderID) << " - > "
762 << to_string(receiverID) << " : deliver=" << amount.getFullText()
763 << " cost=" << actual.getFullText();
764
765 if (TER const terResult = rippleCreditIOU(view, issuer, receiverID, amount, true, j))
766 return terResult;
767 }
768
769 if (senderID != issuer && takeFromSender)
770 {
771 if (TER const terResult = rippleCreditIOU(view, senderID, issuer, takeFromSender, true, j))
772 return terResult;
773 }
774
775 return tesSUCCESS;
776}
777
778static TER
780 ApplyView& view,
781 AccountID const& uSenderID,
782 AccountID const& uReceiverID,
783 STAmount const& saAmount,
785 WaiveTransferFee waiveFee)
786{
787 if (view.rules().enabled(fixAMMv1_1))
788 {
789 if (saAmount < beast::zero || saAmount.holds<MPTIssue>())
790 {
791 return tecINTERNAL; // LCOV_EXCL_LINE
792 }
793 }
794 else
795 {
796 // LCOV_EXCL_START
797 XRPL_ASSERT(
798 saAmount >= beast::zero && !saAmount.holds<MPTIssue>(),
799 "xrpl::accountSendIOU : minimum amount and not MPT");
800 // LCOV_EXCL_STOP
801 }
802
803 /* If we aren't sending anything or if the sender is the same as the
804 * receiver then we don't need to do anything.
805 */
806 if (!saAmount || (uSenderID == uReceiverID))
807 return tesSUCCESS;
808
809 if (!saAmount.native())
810 {
811 STAmount saActual;
812
813 JLOG(j.trace()) << "accountSendIOU: " << to_string(uSenderID) << " -> "
814 << to_string(uReceiverID) << " : " << saAmount.getFullText();
815
816 return rippleSendIOU(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
817 }
818
819 /* XRP send which does not check reserve and can do pure adjustment.
820 * Note that sender or receiver may be null and this not a mistake; this
821 * setup is used during pathfinding and it is carefully controlled to
822 * ensure that transfers are balanced.
823 */
824 TER terResult(tesSUCCESS);
825
826 SLE::pointer const sender =
827 uSenderID != beast::zero ? view.peek(keylet::account(uSenderID)) : SLE::pointer();
828 SLE::pointer const receiver =
829 uReceiverID != beast::zero ? view.peek(keylet::account(uReceiverID)) : SLE::pointer();
830
831 if (auto stream = j.trace())
832 {
833 std::string sender_bal("-");
834 std::string receiver_bal("-");
835
836 if (sender)
837 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
838
839 if (receiver)
840 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
841
842 stream << "accountSendIOU> " << to_string(uSenderID) << " (" << sender_bal << ") -> "
843 << to_string(uReceiverID) << " (" << receiver_bal
844 << ") : " << saAmount.getFullText();
845 }
846
847 if (sender)
848 {
849 if (sender->getFieldAmount(sfBalance) < saAmount)
850 {
851 // VFALCO Its laborious to have to mutate the
852 // TER based on params everywhere
853 // LCOV_EXCL_START
854 terResult = view.open() ? TER{telFAILED_PROCESSING} : TER{tecFAILED_PROCESSING};
855 // LCOV_EXCL_STOP
856 }
857 else
858 {
859 auto const sndBal = sender->getFieldAmount(sfBalance);
860 view.creditHook(uSenderID, xrpAccount(), saAmount, sndBal);
861
862 // Decrement XRP balance.
863 sender->setFieldAmount(sfBalance, sndBal - saAmount);
864 view.update(sender);
865 }
866 }
867
868 if (tesSUCCESS == terResult && receiver)
869 {
870 // Increment XRP balance.
871 auto const rcvBal = receiver->getFieldAmount(sfBalance);
872 receiver->setFieldAmount(sfBalance, rcvBal + saAmount);
873 view.creditHook(xrpAccount(), uReceiverID, saAmount, -rcvBal);
874
875 view.update(receiver);
876 }
877
878 if (auto stream = j.trace())
879 {
880 std::string sender_bal("-");
881 std::string receiver_bal("-");
882
883 if (sender)
884 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
885
886 if (receiver)
887 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
888
889 stream << "accountSendIOU< " << to_string(uSenderID) << " (" << sender_bal << ") -> "
890 << to_string(uReceiverID) << " (" << receiver_bal
891 << ") : " << saAmount.getFullText();
892 }
893
894 return terResult;
895}
896
897static TER
899 ApplyView& view,
900 AccountID const& senderID,
901 Issue const& issue,
902 MultiplePaymentDestinations const& receivers,
904 WaiveTransferFee waiveFee)
905{
906 XRPL_ASSERT_PARTS(
907 receivers.size() > 1, "xrpl::accountSendMultiIOU", "multiple recipients provided");
908
909 if (!issue.native())
910 {
911 STAmount actual;
912 JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID) << " sending "
913 << receivers.size() << " IOUs";
914
915 return rippleSendMultiIOU(view, senderID, issue, receivers, actual, j, waiveFee);
916 }
917
918 /* XRP send which does not check reserve and can do pure adjustment.
919 * Note that sender or receiver may be null and this not a mistake; this
920 * setup could be used during pathfinding and it is carefully controlled to
921 * ensure that transfers are balanced.
922 */
923
924 SLE::pointer const sender =
925 senderID != beast::zero ? view.peek(keylet::account(senderID)) : SLE::pointer();
926
927 if (auto stream = j.trace())
928 {
929 std::string sender_bal("-");
930
931 if (sender)
932 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
933
934 stream << "accountSendMultiIOU> " << to_string(senderID) << " (" << sender_bal << ") -> "
935 << receivers.size() << " receivers.";
936 }
937
938 // Failures return immediately.
939 STAmount takeFromSender{issue};
940 for (auto const& r : receivers)
941 {
942 auto const& receiverID = r.first;
943 STAmount const amount{issue, r.second};
944
945 if (amount < beast::zero)
946 {
947 return tecINTERNAL; // LCOV_EXCL_LINE
948 }
949
950 /* If we aren't sending anything or if the sender is the same as the
951 * receiver then we don't need to do anything.
952 */
953 if (!amount || (senderID == receiverID))
954 continue;
955
956 SLE::pointer const receiver =
957 receiverID != beast::zero ? view.peek(keylet::account(receiverID)) : SLE::pointer();
958
959 if (auto stream = j.trace())
960 {
961 std::string receiver_bal("-");
962
963 if (receiver)
964 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
965
966 stream << "accountSendMultiIOU> " << to_string(senderID) << " -> "
967 << to_string(receiverID) << " (" << receiver_bal
968 << ") : " << amount.getFullText();
969 }
970
971 if (receiver)
972 {
973 // Increment XRP balance.
974 auto const rcvBal = receiver->getFieldAmount(sfBalance);
975 receiver->setFieldAmount(sfBalance, rcvBal + amount);
976 view.creditHook(xrpAccount(), receiverID, amount, -rcvBal);
977
978 view.update(receiver);
979
980 // Take what is actually sent
981 takeFromSender += amount;
982 }
983
984 if (auto stream = j.trace())
985 {
986 std::string receiver_bal("-");
987
988 if (receiver)
989 receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
990
991 stream << "accountSendMultiIOU< " << to_string(senderID) << " -> "
992 << to_string(receiverID) << " (" << receiver_bal
993 << ") : " << amount.getFullText();
994 }
995 }
996
997 if (sender)
998 {
999 if (sender->getFieldAmount(sfBalance) < takeFromSender)
1000 {
1001 return TER{tecFAILED_PROCESSING};
1002 }
1003 auto const sndBal = sender->getFieldAmount(sfBalance);
1004 view.creditHook(senderID, xrpAccount(), takeFromSender, sndBal);
1005
1006 // Decrement XRP balance.
1007 sender->setFieldAmount(sfBalance, sndBal - takeFromSender);
1008 view.update(sender);
1009 }
1010
1011 if (auto stream = j.trace())
1012 {
1013 std::string sender_bal("-");
1014
1015 if (sender)
1016 sender_bal = sender->getFieldAmount(sfBalance).getFullText();
1017
1018 stream << "accountSendMultiIOU< " << to_string(senderID) << " (" << sender_bal << ") -> "
1019 << receivers.size() << " receivers.";
1020 }
1021 return tesSUCCESS;
1022}
1023
1024static TER
1026 ApplyView& view,
1027 AccountID const& uSenderID,
1028 AccountID const& uReceiverID,
1029 STAmount const& saAmount,
1031{
1032 // Do not check MPT authorization here - it must have been checked earlier
1033 auto const mptID = keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID());
1034 auto const& issuer = saAmount.getIssuer();
1035 auto sleIssuance = view.peek(mptID);
1036 if (!sleIssuance)
1037 return tecOBJECT_NOT_FOUND;
1038 if (uSenderID == issuer)
1039 {
1040 (*sleIssuance)[sfOutstandingAmount] += saAmount.mpt().value();
1041 view.update(sleIssuance);
1042 }
1043 else
1044 {
1045 auto const mptokenID = keylet::mptoken(mptID.key, uSenderID);
1046 if (auto sle = view.peek(mptokenID))
1047 {
1048 auto const amt = sle->getFieldU64(sfMPTAmount);
1049 auto const pay = saAmount.mpt().value();
1050 if (amt < pay)
1051 return tecINSUFFICIENT_FUNDS;
1052 (*sle)[sfMPTAmount] = amt - pay;
1053 view.update(sle);
1054 }
1055 else
1056 {
1057 return tecNO_AUTH;
1058 }
1059 }
1060
1061 if (uReceiverID == issuer)
1062 {
1063 auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount);
1064 auto const redeem = saAmount.mpt().value();
1065 if (outstanding >= redeem)
1066 {
1067 sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - redeem);
1068 view.update(sleIssuance);
1069 }
1070 else
1071 {
1072 return tecINTERNAL; // LCOV_EXCL_LINE
1073 }
1074 }
1075 else
1076 {
1077 auto const mptokenID = keylet::mptoken(mptID.key, uReceiverID);
1078 if (auto sle = view.peek(mptokenID))
1079 {
1080 (*sle)[sfMPTAmount] += saAmount.mpt().value();
1081 view.update(sle);
1082 }
1083 else
1084 {
1085 return tecNO_AUTH;
1086 }
1087 }
1088
1089 return tesSUCCESS;
1090}
1091
1092static TER
1094 ApplyView& view,
1095 AccountID const& uSenderID,
1096 AccountID const& uReceiverID,
1097 STAmount const& saAmount,
1098 STAmount& saActual,
1100 WaiveTransferFee waiveFee)
1101{
1102 XRPL_ASSERT(uSenderID != uReceiverID, "xrpl::rippleSendMPT : sender is not receiver");
1103
1104 // Safe to get MPT since rippleSendMPT is only called by accountSendMPT
1105 auto const& issuer = saAmount.getIssuer();
1106
1107 auto const sle = view.read(keylet::mptIssuance(saAmount.get<MPTIssue>().getMptID()));
1108 if (!sle)
1109 return tecOBJECT_NOT_FOUND;
1110
1111 if (uSenderID == issuer || uReceiverID == issuer)
1112 {
1113 // if sender is issuer, check that the new OutstandingAmount will not
1114 // exceed MaximumAmount
1115 if (uSenderID == issuer)
1116 {
1117 auto const sendAmount = saAmount.mpt().value();
1118 auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
1119 if (sendAmount > maximumAmount ||
1120 sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
1121 return tecPATH_DRY;
1122 }
1123
1124 // Direct send: redeeming MPTs and/or sending own MPTs.
1125 auto const ter = rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
1126 if (!isTesSuccess(ter))
1127 return ter;
1128 saActual = saAmount;
1129 return tesSUCCESS;
1130 }
1131
1132 // Sending 3rd party MPTs: transit.
1133 saActual = (waiveFee == WaiveTransferFee::Yes)
1134 ? saAmount
1135 : multiply(saAmount, transferRate(view, saAmount.get<MPTIssue>().getMptID()));
1136
1137 JLOG(j.debug()) << "rippleSendMPT> " << to_string(uSenderID) << " - > "
1138 << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText()
1139 << " cost=" << saActual.getFullText();
1140
1141 if (auto const terResult = rippleCreditMPT(view, issuer, uReceiverID, saAmount, j);
1142 !isTesSuccess(terResult))
1143 return terResult;
1144
1145 return rippleCreditMPT(view, uSenderID, issuer, saActual, j);
1146}
1147
1148static TER
1150 ApplyView& view,
1151 AccountID const& senderID,
1152 MPTIssue const& mptIssue,
1153 MultiplePaymentDestinations const& receivers,
1154 STAmount& actual,
1156 WaiveTransferFee waiveFee)
1157{
1158 auto const& issuer = mptIssue.getIssuer();
1159
1160 auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()));
1161 if (!sle)
1162 return tecOBJECT_NOT_FOUND;
1163
1164 // For the issuer-as-sender case, track the running total to validate
1165 // against MaximumAmount. The read-only SLE (view.read) is not updated
1166 // by rippleCreditMPT, so a per-iteration SLE read would be stale.
1167 // Use uint64_t, not STAmount, to keep MaximumAmount comparisons in exact
1168 // integer arithmetic. STAmount implicitly converts to Number, whose
1169 // small-scale mantissa (~16 digits) can lose precision for values near
1170 // maxMPTokenAmount (19 digits).
1171 std::uint64_t totalSendAmount{0};
1172 std::uint64_t const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
1173 std::uint64_t const outstandingAmount = sle->getFieldU64(sfOutstandingAmount);
1174
1175 // actual accumulates the total cost to the sender (includes transfer
1176 // fees for third-party transit sends). takeFromSender accumulates only
1177 // the transit portion that is debited to the issuer in bulk after the
1178 // loop. They diverge when there are transfer fees.
1179 STAmount takeFromSender{mptIssue};
1180 actual = takeFromSender;
1181
1182 for (auto const& [receiverID, amt] : receivers)
1183 {
1184 STAmount const amount{mptIssue, amt};
1185
1186 if (amount < beast::zero)
1187 return tecINTERNAL; // LCOV_EXCL_LINE
1188
1189 if (!amount || senderID == receiverID)
1190 continue;
1191
1192 if (senderID == issuer || receiverID == issuer)
1193 {
1194 if (senderID == issuer)
1195 {
1196 XRPL_ASSERT_PARTS(
1197 takeFromSender == beast::zero,
1198 "xrpl::rippleSendMultiMPT",
1199 "sender == issuer, takeFromSender == zero");
1200
1201 std::uint64_t const sendAmount = amount.mpt().value();
1202
1203 if (view.rules().enabled(fixSecurity3_1_3))
1204 {
1205 // Post-fixSecurity3_1_3: aggregate MaximumAmount
1206 // check. WARNING: the order of conditions is
1207 // critical — each guards the subtraction in the
1208 // next against unsigned underflow. Do not reorder.
1209 bool const exceedsMaximumAmount =
1210 // This send alone exceeds the max cap
1211 sendAmount > maximumAmount ||
1212 // The aggregate of all sends exceeds the max cap
1213 totalSendAmount > maximumAmount - sendAmount ||
1214 // Outstanding + aggregate exceeds the max cap
1215 outstandingAmount > maximumAmount - sendAmount - totalSendAmount;
1216
1217 if (exceedsMaximumAmount)
1218 return tecPATH_DRY;
1219 totalSendAmount += sendAmount;
1220 }
1221 else
1222 {
1223 // Pre-fixSecurity3_1_3: per-iteration MaximumAmount
1224 // check. Reads sfOutstandingAmount from a stale
1225 // view.read() snapshot — incorrect for multi-destination
1226 // sends but retained for ledger replay compatibility.
1227 if (sendAmount > maximumAmount ||
1228 outstandingAmount > maximumAmount - sendAmount)
1229 return tecPATH_DRY;
1230 }
1231 }
1232
1233 // Direct send: redeeming MPTs and/or sending own MPTs.
1234 if (auto const ter = rippleCreditMPT(view, senderID, receiverID, amount, j))
1235 return ter;
1236 actual += amount;
1237 // Do not add amount to takeFromSender, because rippleCreditMPT
1238 // took it.
1239
1240 continue;
1241 }
1242
1243 // Sending 3rd party MPTs: transit.
1244 STAmount const actualSend = (waiveFee == WaiveTransferFee::Yes)
1245 ? amount
1246 : multiply(amount, transferRate(view, amount.get<MPTIssue>().getMptID()));
1247 actual += actualSend;
1248 takeFromSender += actualSend;
1249
1250 JLOG(j.debug()) << "rippleSendMultiMPT> " << to_string(senderID) << " - > "
1251 << to_string(receiverID) << " : deliver=" << amount.getFullText()
1252 << " cost=" << actualSend.getFullText();
1253
1254 if (auto const terResult = rippleCreditMPT(view, issuer, receiverID, amount, j))
1255 return terResult;
1256 }
1257 if (senderID != issuer && takeFromSender)
1258 {
1259 if (TER const terResult = rippleCreditMPT(view, senderID, issuer, takeFromSender, j))
1260 return terResult;
1261 }
1262
1263 return tesSUCCESS;
1264}
1265
1266static TER
1268 ApplyView& view,
1269 AccountID const& uSenderID,
1270 AccountID const& uReceiverID,
1271 STAmount const& saAmount,
1273 WaiveTransferFee waiveFee)
1274{
1275 XRPL_ASSERT(
1276 saAmount >= beast::zero && saAmount.holds<MPTIssue>(),
1277 "xrpl::accountSendMPT : minimum amount and MPT");
1278
1279 /* If we aren't sending anything or if the sender is the same as the
1280 * receiver then we don't need to do anything.
1281 */
1282 if (!saAmount || (uSenderID == uReceiverID))
1283 return tesSUCCESS;
1284
1285 STAmount saActual{saAmount.asset()};
1286
1287 return rippleSendMPT(view, uSenderID, uReceiverID, saAmount, saActual, j, waiveFee);
1288}
1289
1290static TER
1292 ApplyView& view,
1293 AccountID const& senderID,
1294 MPTIssue const& mptIssue,
1295 MultiplePaymentDestinations const& receivers,
1297 WaiveTransferFee waiveFee)
1298{
1299 STAmount actual;
1300
1301 return rippleSendMultiMPT(view, senderID, mptIssue, receivers, actual, j, waiveFee);
1302}
1303
1304//------------------------------------------------------------------------------
1305//
1306// Public Dispatcher Functions
1307//
1308//------------------------------------------------------------------------------
1309
1310TER
1312 ApplyView& view,
1313 AccountID const& uSenderID,
1314 AccountID const& uReceiverID,
1315 STAmount const& saAmount,
1316 bool bCheckIssuer,
1318{
1319 return std::visit(
1320 [&]<ValidIssueType TIss>(TIss const& issue) {
1321 if constexpr (std::is_same_v<TIss, Issue>)
1322 {
1323 return rippleCreditIOU(view, uSenderID, uReceiverID, saAmount, bCheckIssuer, j);
1324 }
1325 else
1326 {
1327 XRPL_ASSERT(!bCheckIssuer, "xrpl::rippleCredit : not checking issuer");
1328 return rippleCreditMPT(view, uSenderID, uReceiverID, saAmount, j);
1329 }
1330 },
1331 saAmount.asset().value());
1332}
1333
1334TER
1336 ApplyView& view,
1337 AccountID const& uSenderID,
1338 AccountID const& uReceiverID,
1339 STAmount const& saAmount,
1341 WaiveTransferFee waiveFee)
1342{
1343 return std::visit(
1344 [&]<ValidIssueType TIss>(TIss const& issue) {
1345 if constexpr (std::is_same_v<TIss, Issue>)
1346 {
1347 return accountSendIOU(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
1348 }
1349 else
1350 {
1351 return accountSendMPT(view, uSenderID, uReceiverID, saAmount, j, waiveFee);
1352 }
1353 },
1354 saAmount.asset().value());
1355}
1356
1357TER
1359 ApplyView& view,
1360 AccountID const& senderID,
1361 Asset const& asset,
1362 MultiplePaymentDestinations const& receivers,
1364 WaiveTransferFee waiveFee)
1365{
1366 XRPL_ASSERT_PARTS(
1367 receivers.size() > 1, "xrpl::accountSendMulti", "multiple recipients provided");
1368 return std::visit(
1369 [&]<ValidIssueType TIss>(TIss const& issue) {
1370 if constexpr (std::is_same_v<TIss, Issue>)
1371 {
1372 return accountSendMultiIOU(view, senderID, issue, receivers, j, waiveFee);
1373 }
1374 else
1375 {
1376 return accountSendMultiMPT(view, senderID, issue, receivers, j, waiveFee);
1377 }
1378 },
1379 asset.value());
1380}
1381
1382TER
1384 ApplyView& view,
1385 AccountID const& from,
1386 AccountID const& to,
1387 STAmount const& amount,
1389{
1390 XRPL_ASSERT(from != beast::zero, "xrpl::transferXRP : nonzero from account");
1391 XRPL_ASSERT(to != beast::zero, "xrpl::transferXRP : nonzero to account");
1392 XRPL_ASSERT(from != to, "xrpl::transferXRP : sender is not receiver");
1393 XRPL_ASSERT(amount.native(), "xrpl::transferXRP : amount is XRP");
1394
1395 SLE::pointer const sender = view.peek(keylet::account(from));
1396 SLE::pointer const receiver = view.peek(keylet::account(to));
1397 if (!sender || !receiver)
1398 return tefINTERNAL; // LCOV_EXCL_LINE
1399
1400 JLOG(j.trace()) << "transferXRP: " << to_string(from) << " -> " << to_string(to)
1401 << ") : " << amount.getFullText();
1402
1403 if (sender->getFieldAmount(sfBalance) < amount)
1404 {
1405 // VFALCO Its unfortunate we have to keep
1406 // mutating these TER everywhere
1407 // FIXME: this logic should be moved to callers maybe?
1408 // LCOV_EXCL_START
1410 // LCOV_EXCL_STOP
1411 }
1412
1413 // Decrement XRP balance.
1414 sender->setFieldAmount(sfBalance, sender->getFieldAmount(sfBalance) - amount);
1415 view.update(sender);
1416
1417 receiver->setFieldAmount(sfBalance, receiver->getFieldAmount(sfBalance) + amount);
1418 view.update(receiver);
1419
1420 return tesSUCCESS;
1421}
1422
1423} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:116
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual void creditHook(AccountID const &from, AccountID const &to, STAmount const &amount, STAmount const &preCreditBalance)
Definition ApplyView.h:216
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
constexpr value_type const & value() const
Definition Asset.h:154
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
AccountID account
Definition Issue.h:16
bool native() const
Definition Issue.cpp:53
AccountID const & getIssuer() const
Definition Issue.h:25
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:114
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:26
AccountID const & getIssuer() const
Definition MPTIssue.cpp:21
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
Definition ReadView.h:155
virtual bool open() const =0
Returns true if this reflects an open 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
constexpr bool holds() const noexcept
Definition STAmount.h:439
constexpr TIss const & get() const
std::string getFullText() const override
Definition STAmount.cpp:646
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:572
void negate()
Definition STAmount.h:548
static int const cMaxOffset
Definition STAmount.h:46
Currency const & getCurrency() const
Definition STAmount.h:476
bool native() const noexcept
Definition STAmount.h:432
Asset const & asset() const
Definition STAmount.h:457
MPTAmount mpt() const
Definition STAmount.cpp:291
AccountID const & getIssuer() const
Definition STAmount.h:482
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const_pointer
void setFieldAmount(SField const &field, STAmount const &)
Definition STObject.cpp:789
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:649
T is_same_v
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:474
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:404
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:220
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:486
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)
@ telFAILED_PROCESSING
Definition TER.h:36
@ terNO_RIPPLE
Definition TER.h:204
@ terNO_ACCOUNT
Definition TER.h:197
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
FreezeHandling
Controls the treatment of frozen account balances.
@ fhZERO_IF_FROZEN
SpendableHandling
Controls whether to include the account's full spendable balance.
@ shFULL_BALANCE
bool isXRP(AccountID const &c)
Definition AccountID.h:70
bool isIndividualFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
TER trustCreate(ApplyView &view, bool const bSrcHigh, AccountID const &uSrcAccountID, AccountID const &uDstAccountID, uint256 const &uIndex, SLE::ref sleAccount, bool const bAuth, bool const bNoRipple, bool const bFreeze, bool bDeepFreeze, STAmount const &saBalance, STAmount const &saLimit, std::uint32_t uQualityIn, std::uint32_t uQualityOut, beast::Journal j)
Create a trust line.
static TER accountSendMultiIOU(ApplyView &view, AccountID const &senderID, Issue const &issue, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee)
static TER rippleSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
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)
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
Definition Protocol.h:234
@ tefINTERNAL
Definition TER.h:153
bool isAnyFrozen(ReadView const &view, std::initializer_list< AccountID > const &accounts, MPTIssue const &mptIssue, int depth=0)
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=shSIMPLE_BALANCE)
WaiveTransferFee
static TER accountSendMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
static TER rippleCreditMPT(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j)
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
static TER rippleCreditIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
bool isDeepFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
static TER rippleSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, STAmount &saActual, beast::Journal j, WaiveTransferFee waiveFee)
static TER rippleSendMultiMPT(ApplyView &view, AccountID const &senderID, MPTIssue const &mptIssue, MultiplePaymentDestinations const &receivers, STAmount &actual, beast::Journal j, WaiveTransferFee waiveFee)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
bool isGlobalFrozen(ReadView const &view, AccountID const &issuer)
Check if the issuer has the global freeze flag set.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, int depth=0)
static TER rippleSendMultiIOU(ApplyView &view, AccountID const &senderID, Issue const &issue, MultiplePaymentDestinations const &receivers, STAmount &actual, beast::Journal j, WaiveTransferFee waiveFee)
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 STAmount getTrustLineBalance(ReadView const &view, SLE::const_ref sle, AccountID const &account, Currency const &currency, AccountID const &issuer, bool includeOppositeLimit, beast::Journal j)
AuthHandling
Controls the treatment of unauthorized MPT balances.
@ ahZERO_IF_UNAUTHORIZED
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
static SLE::const_pointer getLineIfUsable(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
TER canTransfer(ReadView const &view, MPTIssue const &mptIssue, AccountID const &from, AccountID const &to)
Check if the destination account is allowed to receive MPT.
TER transferXRP(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &amount, beast::Journal j)
AccountID const & noAccount()
A placeholder for empty accounts.
static TER accountSendIOU(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecLOCKED
Definition TER.h:339
@ tecPATH_DRY
Definition TER.h:275
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecNO_AUTH
Definition TER.h:281
@ tecINTERNAL
Definition TER.h:291
@ tecFAILED_PROCESSING
Definition TER.h:267
@ tecFROZEN
Definition TER.h:284
@ tecINSUFFICIENT_FUNDS
Definition TER.h:306
static TER accountSendMultiMPT(ApplyView &view, AccountID const &senderID, MPTIssue const &mptIssue, MultiplePaymentDestinations const &receivers, beast::Journal j, WaiveTransferFee waiveFee)
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!...
TER trustDelete(ApplyView &view, std::shared_ptr< SLE > const &sleRippleState, AccountID const &uLowAccountID, AccountID const &uHighAccountID, beast::Journal j)
TER removeEmptyHolding(ApplyView &view, AccountID const &accountID, MPTIssue const &mptIssue, beast::Journal journal)
bool isLPTokenFrozen(ReadView const &view, AccountID const &account, Issue const &asset, Issue const &asset2)
Definition View.cpp:84
@ 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.
TER rippleCredit(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, bool bCheckIssuer, beast::Journal j)
Calls static rippleCreditIOU if saAmount represents Issue.
T size(T... args)
T visit(T... args)