xrpld
Loading...
Searching...
No Matches
STAmount.cpp
1#include <xrpl/protocol/STAmount.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/basics/contract.h>
6#include <xrpl/basics/safe_cast.h>
7#include <xrpl/beast/utility/Zero.h>
8#include <xrpl/beast/utility/instrumentation.h>
9#include <xrpl/json/json_forwards.h>
10#include <xrpl/json/json_value.h>
11#include <xrpl/protocol/AccountID.h>
12#include <xrpl/protocol/Asset.h>
13#include <xrpl/protocol/Concepts.h>
14#include <xrpl/protocol/Feature.h>
15#include <xrpl/protocol/IOUAmount.h>
16#include <xrpl/protocol/Issue.h>
17#include <xrpl/protocol/MPTAmount.h>
18#include <xrpl/protocol/MPTIssue.h>
19#include <xrpl/protocol/Protocol.h>
20#include <xrpl/protocol/Rules.h>
21#include <xrpl/protocol/SField.h>
22#include <xrpl/protocol/STArray.h>
23#include <xrpl/protocol/STBase.h>
24#include <xrpl/protocol/STNumber.h>
25#include <xrpl/protocol/STObject.h>
26#include <xrpl/protocol/Serializer.h>
27#include <xrpl/protocol/SystemParameters.h>
28#include <xrpl/protocol/UintTypes.h>
29#include <xrpl/protocol/XRPAmount.h>
30#include <xrpl/protocol/jss.h>
31
32#include <boost/algorithm/string/classification.hpp>
33#include <boost/algorithm/string/split.hpp>
34#include <boost/multiprecision/detail/default_ops.hpp>
35
36#include <algorithm>
37#include <cstddef>
38#include <cstdint>
39#include <exception>
40#include <iterator>
41#include <limits>
42#include <memory>
43#include <optional>
44#include <stdexcept>
45#include <string>
46#include <utility>
47#include <variant>
48#include <vector>
49
50namespace xrpl {
51
52static std::uint64_t const kTenTO14 = 100000000000000ull;
54static std::uint64_t const kTenTO17 = kTenTO14 * 1000;
55
56//------------------------------------------------------------------------------
57static std::int64_t
58getInt64Value(STAmount const& amount, bool valid, char const* error)
59{
60 if (!valid)
62 XRPL_ASSERT(amount.exponent() == 0, "xrpl::getInt64Value : exponent is zero");
63
64 auto ret = static_cast<std::int64_t>(amount.mantissa());
65
66 XRPL_ASSERT(
67 static_cast<std::uint64_t>(ret) == amount.mantissa(),
68 "xrpl::getInt64Value : mantissa must roundtrip");
69
70 if (amount.negative())
71 ret = -ret;
72
73 return ret;
74}
75
76static std::int64_t
77getSNValue(STAmount const& amount)
78{
79 return getInt64Value(amount, amount.native(), "amount is not native!");
80}
81
82static std::int64_t
83getMPTValue(STAmount const& amount)
84{
85 return getInt64Value(amount, amount.holds<MPTIssue>(), "amount is not MPT!");
86}
87
88static bool
89areComparable(STAmount const& v1, STAmount const& v2)
90{
91 return std::visit(
92 [&]<ValidIssueType TIss1, ValidIssueType TIss2>(TIss1 const& issue1, TIss2 const& issue2) {
93 if constexpr (kIsIssueV<TIss1> && kIsIssueV<TIss2>)
94 {
95 return v1.native() == v2.native() && issue1.currency == issue2.currency;
96 }
97 else if constexpr (kIsMptissueV<TIss1> && kIsMptissueV<TIss2>)
98 {
99 return issue1 == issue2;
100 }
101 else
102 {
103 return false;
104 }
105 },
106 v1.asset().value(),
107 v2.asset().value());
108}
109
110static_assert(kInitialXrp.drops() == STAmount::kMaxNativeN);
111
112STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
113{
114 std::uint64_t value = sit.get64();
115
116 // native or MPT
117 if ((value & kIssuedCurrency) == 0)
118 {
119 if ((value & kMpToken) != 0)
120 {
121 // is MPT
122 offset_ = 0;
123 isNegative_ = (value & kPositive) == 0;
124 value_ = (value << 8) | sit.get8();
125 asset_ = sit.get192();
126 return;
127 }
128 // else is XRP
129 asset_ = xrpIssue();
130 // positive
131 if ((value & kPositive) != 0)
132 {
134 offset_ = 0;
135 isNegative_ = false;
136 return;
137 }
138
139 // negative
140 if (value == 0)
141 Throw<std::runtime_error>("negative zero is not canonical");
142
144 offset_ = 0;
145 isNegative_ = true;
146 return;
147 }
148
149 Issue issue;
150 issue.currency = sit.get160();
151
152 if (isXRP(issue.currency))
153 Throw<std::runtime_error>("invalid native currency");
154
155 issue.account = sit.get160();
156
157 if (isXRP(issue.account))
158 Throw<std::runtime_error>("invalid native account");
159
160 // 10 bits for the offset, sign and "not native" flag
161 int offset = static_cast<int>(value >> (64 - 10));
162
163 value &= ~(1023ull << (64 - 10));
164
165 if (value != 0u)
166 {
167 bool const isNegative = (offset & 256) == 0;
168 offset = (offset & 255) - 97; // center the range
169
170 if (value < kMinValue || value > kMaxValue || offset < kMinOffset || offset > kMaxOffset)
171 {
172 Throw<std::runtime_error>("invalid currency value");
173 }
174
175 asset_ = issue;
176 value_ = value;
177 offset_ = offset;
178 isNegative_ = isNegative;
179 canonicalize();
180 return;
181 }
182
183 if (offset != 512)
184 Throw<std::runtime_error>("invalid currency value");
185
186 asset_ = issue;
187 value_ = 0;
188 offset_ = 0;
189 isNegative_ = false;
190 canonicalize();
191}
192
194 : STBase(name), asset_(xrpIssue()), offset_(0)
195{
196 set(mantissa);
197}
198
201{
202 XRPL_ASSERT(
204 "xrpl::STAmount::STAmount(SField, std::uint64_t, bool) : maximum "
205 "mantissa input");
206}
207
208STAmount::STAmount(SField const& name, STAmount const& from)
209 : STBase(name)
210 , asset_(from.asset_)
211 , value_(from.value_)
212 , offset_(from.offset_)
214{
215 XRPL_ASSERT(
217 "xrpl::STAmount::STAmount(SField, STAmount) : maximum input");
218 canonicalize();
219}
220
221//------------------------------------------------------------------------------
222
225{
226 XRPL_ASSERT(
228 "xrpl::STAmount::STAmount(std::uint64_t, bool) : maximum mantissa "
229 "input");
230}
231
233 : asset_(xrpIssue()), offset_(0), isNegative_(amount < beast::kZero)
234{
235 if (isNegative_)
236 {
238 }
239 else
240 {
242 }
243
244 canonicalize();
245}
246
249{
250 return std::make_unique<STAmount>(sit, name);
251}
252
253STBase*
254STAmount::copy(std::size_t n, void* buf) const
255{
256 return emplace(n, buf, *this);
257}
258
259STBase*
261{
262 return emplace(n, buf, std::move(*this));
263}
264
265//------------------------------------------------------------------------------
266//
267// Conversion
268//
269//------------------------------------------------------------------------------
272{
273 if (!native())
274 Throw<std::logic_error>("Cannot return non-native STAmount as XRPAmount");
275
276 auto drops = static_cast<XRPAmount::value_type>(value_);
277 XRPL_ASSERT(offset_ == 0, "xrpl::STAmount::xrp : amount is canonical");
278
279 if (isNegative_)
280 drops = -drops;
281
282 return XRPAmount{drops};
283}
284
287{
288 if (integral())
289 Throw<std::logic_error>("Cannot return non-IOU STAmount as IOUAmount");
290
291 auto mantissa = static_cast<std::int64_t>(value_);
292 auto exponent = offset_;
293
294 if (isNegative_)
296
297 return {mantissa, exponent};
298}
299
302{
303 if (!holds<MPTIssue>())
304 Throw<std::logic_error>("Cannot return STAmount as MPTAmount");
305
306 auto value = static_cast<MPTAmount::value_type>(value_);
307 XRPL_ASSERT(offset_ == 0, "xrpl::STAmount::mpt : amount is canonical");
308
309 if (isNegative_)
310 value = -value;
311
312 return MPTAmount{value};
313}
314
317{
318 XRPL_ASSERT(integral() == false, "xrpl::STAmount::operator=(IOUAmount) : is not integral");
319 offset_ = iou.exponent();
320 isNegative_ = iou < beast::kZero;
321 if (isNegative_)
322 {
323 value_ = static_cast<std::uint64_t>(-iou.mantissa());
324 }
325 else
326 {
327 value_ = static_cast<std::uint64_t>(iou.mantissa());
328 }
329 return *this;
330}
331
334{
335 if (!getCurrentTransactionRules() || isFeatureEnabled(featureSingleAssetVault) ||
336 isFeatureEnabled(featureLendingProtocol))
337 {
338 *this = fromNumber(asset_, number);
339 }
340 else
341 {
342 auto const originalMantissa = number.mantissa();
343 isNegative_ = originalMantissa < 0;
344 value_ = isNegative_ ? -originalMantissa : originalMantissa;
345 offset_ = number.exponent();
346 }
347 canonicalize();
348 return *this;
349}
350
351//------------------------------------------------------------------------------
352//
353// Operators
354//
355//------------------------------------------------------------------------------
356
359{
360 *this = *this + a;
361 return *this;
362}
363
366{
367 *this = *this - a;
368 return *this;
369}
370
372operator+(STAmount const& v1, STAmount const& v2)
373{
374 if (!areComparable(v1, v2))
375 Throw<std::runtime_error>("Can't add amounts that are't comparable!");
376
377 if (v2 == beast::kZero)
378 return v1;
379
380 if (v1 == beast::kZero)
381 {
382 // Result must be in terms of v1 currency and issuer.
383 return {v1.getFName(), v1.asset(), v2.mantissa(), v2.exponent(), v2.negative()};
384 }
385
386 if (v1.native())
387 return {v1.getFName(), getSNValue(v1) + getSNValue(v2)};
388 if (v1.holds<MPTIssue>())
389 return {v1.asset_, v1.mpt().value() + v2.mpt().value()};
390
391 auto x = v1;
392 x = v1.iou() + v2.iou();
393 return x;
394}
395
397operator-(STAmount const& v1, STAmount const& v2)
398{
399 return v1 + (-v2);
400}
401
402//------------------------------------------------------------------------------
403
404std::uint64_t const STAmount::kURateOne = getRate(STAmount(1), STAmount(1));
405
406void
408{
409 asset_ = asset;
410}
411
412// Convert an offer into an index amount so they sort by rate.
413// A taker will take the best, lowest, rate first.
414// (e.g. a taker will prefer pay 1 get 3 over pay 1 get 2.
415// --> offerOut: takerGets: How much the offerer is selling to the taker.
416// --> offerIn: takerPays: How much the offerer is receiving from the taker.
417// <-- uRate: normalize(offerIn/offerOut)
418// A lower rate is better for the person taking the order.
419// The taker gets more for less with a lower rate.
420// Zero is returned if the offer is worthless.
422getRate(STAmount const& offerOut, STAmount const& offerIn)
423{
424 if (offerOut == beast::kZero)
425 return 0;
426
427 try
428 {
429 STAmount const r = divide(offerIn, offerOut, noIssue());
430 if (r == beast::kZero) // offer is too good
431 return 0;
432 XRPL_ASSERT(
433 (r.exponent() >= -100) && (r.exponent() <= 155),
434 "xrpl::getRate : exponent inside range");
435 std::uint64_t const ret = r.exponent() + 100;
436 return (ret << (64 - 8)) | r.mantissa();
437 }
438 catch (...)
439 {
440 // overflow -- very bad offer
441 return 0;
442 }
443}
444
463bool
464canAdd(STAmount const& a, STAmount const& b)
465{
466 // cannot add different currencies
467 if (!areComparable(a, b))
468 return false;
469
470 // special case: adding anything to zero is always fine
471 if (a == beast::kZero || b == beast::kZero)
472 return true;
473
474 // XRP case (overflow & underflow check)
475 if (isXRP(a) && isXRP(b))
476 {
477 XRPAmount const aVal = a.xrp();
478 XRPAmount const bVal = b.xrp();
479
480 return !(
481 (bVal > XRPAmount{0} &&
483 (bVal < XRPAmount{0} &&
485 }
486
487 // IOU case (precision check)
488 auto const ret = std::visit(
489 [&]<ValidIssueType TIss1, ValidIssueType TIss2>(
490 TIss1 const&, TIss2 const&) -> std::optional<bool> {
491 if constexpr (kIsIssueV<TIss1> && kIsIssueV<TIss2>)
492 {
493 static STAmount const kOne{IOUAmount{1, 0}, noIssue()};
494 static STAmount const kMaxLoss{IOUAmount{1, -4}, noIssue()};
495 STAmount const lhs = divide((a - b) + b, a, noIssue()) - kOne;
496 STAmount const rhs = divide((b - a) + a, b, noIssue()) - kOne;
497 return ((rhs.negative() ? -rhs : rhs) + (lhs.negative() ? -lhs : lhs)) <= kMaxLoss;
498 }
499
500 // MPT (overflow & underflow check)
502 {
503 MPTAmount const aVal = a.mpt();
504 MPTAmount const bVal = b.mpt();
505 return !(
506 (bVal > MPTAmount{0} &&
508 (bVal < MPTAmount{0} &&
510 }
511 return std::nullopt;
512 },
513 a.asset().value(),
514 b.asset().value());
515 if (ret)
516 return *ret;
517 // LCOV_EXCL_START
518 UNREACHABLE("STAmount::canAdd : unexpected STAmount type");
519 return false;
520 // LCOV_EXCL_STOP
521}
522
540bool
541canSubtract(STAmount const& a, STAmount const& b)
542{
543 // Cannot subtract different currencies
544 if (!areComparable(a, b))
545 return false;
546
547 // Special case: subtracting zero is always fine
548 if (b == beast::kZero)
549 return true;
550
551 // XRP case (underflow & overflow check)
552 if (isXRP(a) && isXRP(b))
553 {
554 XRPAmount const aVal = a.xrp();
555 XRPAmount const bVal = b.xrp();
556 // Check for underflow
557 if (bVal > XRPAmount{0} && aVal < bVal)
558 return false;
559
560 // Check for overflow
561 if (bVal < XRPAmount{0} &&
563 return false;
564
565 return true;
566 }
567
568 // IOU case (no underflow)
569 auto const ret = std::visit(
570 [&]<ValidIssueType TIss1, ValidIssueType TIss2>(
571 TIss1 const&, TIss2 const&) -> std::optional<bool> {
572 if constexpr (kIsIssueV<TIss1> && kIsIssueV<TIss2>)
573 {
574 return true;
575 }
576
577 // MPT case (underflow & overflow check)
579 {
580 MPTAmount const aVal = a.mpt();
581 MPTAmount const bVal = b.mpt();
582
583 // Underflow check
584 if (bVal > MPTAmount{0} && aVal < bVal)
585 return false;
586
587 // Overflow check
588 if (bVal < MPTAmount{0} &&
590 return false;
591 return true;
592 }
593 return std::nullopt;
594 },
595 a.asset().value(),
596 b.asset().value());
597 if (ret)
598 return *ret;
599 // LCOV_EXCL_START
600 UNREACHABLE("STAmount::canSubtract : unexpected STAmount type");
601 return false;
602 // LCOV_EXCL_STOP
603}
604
605void
607{
609
610 if (!native())
611 {
612 // It is an error for currency or issuer not to be specified for valid
613 // json.
614 elem[jss::value] = getText();
615 asset_.setJson(elem);
616 }
617 else
618 {
619 elem = getText();
620 }
621}
622
623//------------------------------------------------------------------------------
624//
625// STBase
626//
627//------------------------------------------------------------------------------
628
631{
632 return STI_AMOUNT;
633}
634
637{
638 std::string ret;
639
640 ret.reserve(64);
641 ret = getText() + "/" + asset_.getText();
642 return ret;
643}
644
647{
648 // keep full internal accuracy, but make more human friendly if possible
649 if (*this == beast::kZero)
650 return "0";
651
652 std::string const rawValue(std::to_string(value_));
653 std::string ret;
654
655 if (isNegative_)
656 ret.append(1, '-');
657
658 bool const scientific((offset_ != 0) && ((offset_ < -25) || (offset_ > -5)));
659
660 if (native() || asset_.holds<MPTIssue>() || scientific)
661 {
662 ret.append(rawValue);
663
664 if (scientific)
665 {
666 ret.append(1, 'e');
668 }
669
670 return ret;
671 }
672
673 XRPL_ASSERT(offset_ + 43 > 0, "xrpl::STAmount::getText : minimum offset");
674
675 size_t const padPrefix = 27;
676 size_t const padSuffix = 23;
677
678 std::string val;
679 val.reserve(rawValue.length() + padPrefix + padSuffix);
680 val.append(padPrefix, '0');
681 val.append(rawValue);
682 val.append(padSuffix, '0');
683
684 size_t const offset(offset_ + 43);
685
686 auto preFrom(val.begin());
687 auto const preTo(val.begin() + offset);
688
689 auto const postFrom(val.begin() + offset);
690 auto postTo(val.end());
691
692 // Crop leading zeroes. Take advantage of the fact that there's always a
693 // fixed amount of leading zeroes and skip them.
694 if (std::distance(preFrom, preTo) > padPrefix)
695 preFrom += padPrefix;
696
697 XRPL_ASSERT(postTo >= postFrom, "xrpl::STAmount::getText : first distance check");
698
699 preFrom = std::find_if(preFrom, preTo, [](char c) { return c != '0'; });
700
701 // Crop trailing zeroes. Take advantage of the fact that there's always a
702 // fixed amount of trailing zeroes and skip them.
703 if (std::distance(postFrom, postTo) > padSuffix)
704 postTo -= padSuffix;
705
706 XRPL_ASSERT(postTo >= postFrom, "xrpl::STAmount::getText : second distance check");
707
708 postTo = std::find_if(
711 [](char c) { return c != '0'; })
712 .base();
713
714 // Assemble the output:
715 if (preFrom == preTo)
716 {
717 ret.append(1, '0');
718 }
719 else
720 {
721 ret.append(preFrom, preTo);
722 }
723
724 if (postTo != postFrom)
725 {
726 ret.append(1, '.');
727 ret.append(postFrom, postTo);
728 }
729
730 return ret;
731}
732
735{
736 json::Value elem;
737 setJson(elem);
738 return elem;
739}
740
741void
743{
744 asset_.visit(
745 [&](MPTIssue const& issue) {
746 auto u8 = static_cast<unsigned char>(kMpToken >> 56);
747 if (!isNegative_)
748 u8 |= static_cast<unsigned char>(kPositive >> 56);
749 s.add8(u8);
750 s.add64(value_);
751 s.addBitString(issue.getMptID());
752 },
753 [&](Issue const& issue) {
754 if (native())
755 {
756 XRPL_ASSERT(offset_ == 0, "xrpl::STAmount::add : zero offset");
757
758 if (!isNegative_)
759 {
761 }
762 else
763 {
764 s.add64(value_);
765 }
766 }
767 else
768 {
769 if (*this == beast::kZero)
770 {
772 }
773 else if (isNegative_) // 512 = not native
774 {
775 s.add64(value_ | (static_cast<std::uint64_t>(offset_ + 512 + 97) << (64 - 10)));
776 }
777 else // 256 = positive
778 {
779 s.add64(
780 value_ |
781 (static_cast<std::uint64_t>(offset_ + 512 + 256 + 97) << (64 - 10)));
782 }
783 s.addBitString(issue.currency);
784 s.addBitString(issue.account);
785 }
786 });
787}
788
789bool
791{
792 STAmount const* v = dynamic_cast<STAmount const*>(&t);
793 return (v != nullptr) && (*v == *this);
794}
795
796bool
798{
799 return (value_ == 0) && native();
800}
801
802//------------------------------------------------------------------------------
803
804// amount = value_ * [10 ^ offset_]
805// Representation range is 10^80 - 10^(-80).
806//
807// On the wire:
808// - high bit is 0 for XRP, 1 for issued currency
809// - next bit is 1 for positive, 0 for negative (except 0 issued currency, which
810// is a special case of 0x8000000000000000
811// - for issued currencies, the next 8 bits are (offset_+97).
812// The +97 is so that this value is always positive.
813// - The remaining bits are significant digits (mantissa)
814// That's 54 bits for issued currency and 62 bits for native
815// (but XRP only needs 57 bits for the max value of 10^17 drops)
816//
817// value_ is zero if the amount is zero, otherwise it's within the range
818// 10^15 to (10^16 - 1) inclusive.
819// offset_ is in the range -96 to +80.
820void
822{
823 if (integral())
824 {
825 // native and MPT currency amounts should always have an offset of zero
826 // log(2^64,10) ~ 19.2
827 if (value_ == 0 || offset_ <= -20)
828 {
829 value_ = 0;
830 offset_ = 0;
831 isNegative_ = false;
832 return;
833 }
834
835 // log(cMaxNativeN, 10) == 17
836 if (native() && offset_ > 17)
837 Throw<std::runtime_error>("Native currency amount out of range");
838 // log(maxMPTokenAmount, 10) ~ 18.96
839 if (asset_.holds<MPTIssue>() && offset_ > 18)
840 Throw<std::runtime_error>("MPT amount out of range");
841
843 auto set = [&](auto const& val) {
844 auto const value = val.value();
845 isNegative_ = value < 0;
847 };
848 if (native())
849 {
850 set(XRPAmount{num});
851 }
852 else if (asset_.holds<MPTIssue>())
853 {
854 set(MPTAmount{num});
855 }
856 else
857 {
858 Throw<std::runtime_error>("Unknown integral asset type"); // LCOV_EXCL_LINE
859 }
860 offset_ = 0;
861
862 if (native() && value_ > kMaxNativeN)
863 {
864 Throw<std::runtime_error>("Native currency amount out of range");
865 }
866 else if (!native() && value_ > kMaxMpTokenAmount)
867 {
868 Throw<std::runtime_error>("MPT amount out of range");
869 }
870
871 return;
872 }
873
874 *this = iou();
875}
876
877void
879{
880 if (v < 0)
881 {
882 isNegative_ = true;
883 value_ = static_cast<std::uint64_t>(-v);
884 }
885 else
886 {
887 isNegative_ = false;
888 value_ = static_cast<std::uint64_t>(v);
889 }
890}
891
892//------------------------------------------------------------------------------
893
896{
897 if (rate == 0)
898 return STAmount(noIssue());
899
900 std::uint64_t const mantissa = rate & ~(255ull << (64 - 8));
901 int const exponent = static_cast<int>(rate >> (64 - 8)) - 100;
902
903 return STAmount(noIssue(), mantissa, exponent);
904}
905
906STAmount
907amountFromString(Asset const& asset, std::string const& amount)
908{
909 auto const parts = partsFromString(amount);
910 if ((asset.native() || asset.holds<MPTIssue>()) && parts.exponent < 0)
911 Throw<std::runtime_error>("XRP and MPT must be specified as integral amount.");
912 return {asset, parts.mantissa, parts.exponent, parts.negative};
913}
914
915STAmount
916amountFromJson(SField const& name, json::Value const& v)
917{
918 Asset asset;
919
920 json::Value value;
921 json::Value currencyOrMPTID;
922 json::Value issuer;
923 bool isMPT = false;
924
925 if (v.isNull())
926 {
927 Throw<std::runtime_error>("XRP may not be specified with a null Json value");
928 }
929 else if (v.isObject())
930 {
931 if (!validJSONAsset(v))
932 Throw<std::runtime_error>("Invalid Asset's Json specification");
933
934 value = v[jss::value];
935 if (v.isMember(jss::mpt_issuance_id))
936 {
937 isMPT = true;
938 currencyOrMPTID = v[jss::mpt_issuance_id];
939 }
940 else
941 {
942 currencyOrMPTID = v[jss::currency];
943 issuer = v[jss::issuer];
944 }
945 }
946 else if (v.isArray())
947 {
948 value = v.get(json::UInt(0), 0);
949 currencyOrMPTID = v.get(json::UInt(1), json::ValueType::Null);
950 issuer = v.get(json::UInt(2), json::ValueType::Null);
951 }
952 else if (v.isString())
953 {
954 std::string val = v.asString();
956 boost::split(elements, val, boost::is_any_of("\t\n\r ,/"));
957
958 if (elements.size() > 3)
959 Throw<std::runtime_error>("invalid amount string");
960
961 value = elements[0];
962
963 if (elements.size() > 1)
964 currencyOrMPTID = elements[1];
965
966 if (elements.size() > 2)
967 issuer = elements[2];
968 }
969 else
970 {
971 value = v;
972 }
973
974 bool const native = !currencyOrMPTID.isString() || currencyOrMPTID.asString().empty() ||
975 (currencyOrMPTID.asString() == systemCurrencyCode());
976
977 if (native)
978 {
979 if (v.isObjectOrNull())
980 Throw<std::runtime_error>("XRP may not be specified as an object");
981 asset = xrpIssue();
982 }
983 else
984 {
985 if (isMPT)
986 {
987 // sequence (32 bits) + account (160 bits)
988 MPTID u;
989 if (!u.parseHex(currencyOrMPTID.asString()))
990 Throw<std::runtime_error>("invalid MPTokenIssuanceID");
991 asset = u;
992 }
993 else
994 {
995 Issue issue;
996 if (!toCurrency(issue.currency, currencyOrMPTID.asString()))
997 Throw<std::runtime_error>("invalid currency");
998 if (!issuer.isString() || !toIssuer(issue.account, issuer.asString()))
999 Throw<std::runtime_error>("invalid issuer");
1000 if (issue.native())
1001 Throw<std::runtime_error>("invalid issuer");
1002 asset = issue;
1003 }
1004 }
1005
1006 NumberParts parts;
1007
1008 if (value.isInt())
1009 {
1010 if (value.asInt() >= 0)
1011 {
1012 parts.mantissa = value.asInt();
1013 }
1014 else
1015 {
1016 parts.mantissa = value.asAbsUInt();
1017 parts.negative = true;
1018 }
1019 }
1020 else if (value.isUInt())
1021 {
1022 parts.mantissa = v.asUInt();
1023 }
1024 else if (value.isString())
1025 {
1026 parts = partsFromString(value.asString());
1027 // Can't specify XRP or MPT using fractional representation
1028 if ((asset.native() || asset.holds<MPTIssue>()) && parts.exponent < 0)
1029 Throw<std::runtime_error>("XRP and MPT must be specified as integral amount.");
1030 }
1031 else
1032 {
1033 Throw<std::runtime_error>("invalid amount type");
1034 }
1035
1036 return {name, asset, parts.mantissa, parts.exponent, parts.negative};
1037}
1038
1039bool
1041{
1042 try
1043 {
1044 result = amountFromJson(sfGeneric, jvSource);
1045 return true;
1046 }
1047 catch (std::exception const& e)
1048 {
1049 JLOG(debugLog().warn()) << "amountFromJsonNoThrow: caught: " << e.what();
1050 }
1051 return false;
1052}
1053
1054//------------------------------------------------------------------------------
1055//
1056// Operators
1057//
1058//------------------------------------------------------------------------------
1059
1060bool
1061operator==(STAmount const& lhs, STAmount const& rhs)
1062{
1063 return areComparable(lhs, rhs) && lhs.negative() == rhs.negative() &&
1064 lhs.exponent() == rhs.exponent() && lhs.mantissa() == rhs.mantissa();
1065}
1066
1067bool
1068operator<(STAmount const& lhs, STAmount const& rhs)
1069{
1070 if (!areComparable(lhs, rhs))
1071 Throw<std::runtime_error>("Can't compare amounts that are't comparable!");
1072
1073 if (lhs.negative() != rhs.negative())
1074 return lhs.negative();
1075
1076 if (lhs.mantissa() == 0)
1077 {
1078 if (rhs.negative())
1079 return false;
1080 return rhs.mantissa() != 0;
1081 }
1082
1083 // We know that lhs is non-zero and both sides have the same sign. Since
1084 // rhs is zero (and thus not negative), lhs must, therefore, be strictly
1085 // greater than zero. So if rhs is zero, the comparison must be false.
1086 if (rhs.mantissa() == 0)
1087 return false;
1088
1089 if (lhs.exponent() > rhs.exponent())
1090 return lhs.negative();
1091 if (lhs.exponent() < rhs.exponent())
1092 return !lhs.negative();
1093 if (lhs.mantissa() > rhs.mantissa())
1094 return lhs.negative();
1095 if (lhs.mantissa() < rhs.mantissa())
1096 return !lhs.negative();
1097
1098 return false;
1099}
1100
1101STAmount
1102operator-(STAmount const& value)
1103{
1104 if (value.mantissa() == 0)
1105 return value;
1106 return STAmount(
1107 value.getFName(),
1108 value.asset(),
1109 value.mantissa(),
1110 value.exponent(),
1111 !value.negative(),
1113}
1114
1115static bool
1116hasInvalidAmount(STBase const& field, int depth, beast::Journal j);
1117
1118static bool
1119hasInvalidAmount(STObject const& object, int depth, beast::Journal j)
1120{
1121 return std::ranges::any_of(
1122 object, [&](STBase const& field) { return hasInvalidAmount(field, depth, j); });
1123}
1124
1125static bool
1126hasInvalidAmount(STArray const& array, int depth, beast::Journal j)
1127{
1128 return std::ranges::any_of(
1129 array, [&](STObject const& object) { return hasInvalidAmount(object, depth, j); });
1130}
1131
1132static bool
1133hasInvalidAmount(STBase const& field, int depth, beast::Journal j)
1134{
1135 if (depth > 10)
1136 {
1137 JLOG(j.error()) << "hasInvalidAmount: depth exceeds 10";
1138 return true;
1139 }
1140
1141 // Dispatch on the serialized type tag rather than RTTI: this is on the invariant-checking path
1142 // and a dynamic_cast chain over every field of every modified entry is measurably expensive.
1143 // The object-like tags below all denote STObject subclasses (STLedgerEntry, STTx), so the
1144 // downcast is sound; nested fields are only ever plain STI_OBJECT / STI_ARRAY containers.
1145 // safeDowncast keeps a dynamic_cast validity assert in debug builds while compiling to
1146 // static_cast in release.
1147 switch (field.getSType())
1148 {
1149 case STI_AMOUNT: {
1150 auto const& amount = safeDowncast<STAmount const&>(field);
1151 return !isLegalMPT(amount) || !isLegalNet(amount);
1152 }
1153
1154 case STI_OBJECT:
1155 case STI_LEDGERENTRY:
1156 case STI_TRANSACTION:
1157 return hasInvalidAmount(safeDowncast<STObject const&>(field), depth + 1, j);
1158
1159 case STI_ARRAY:
1160 return hasInvalidAmount(safeDowncast<STArray const&>(field), depth + 1, j);
1161
1162 default: {
1163 XRPL_ASSERT(
1164 dynamic_cast<STObject const*>(&field) == nullptr,
1165 "xrpl::hasInvalidAmount : unhandled STObject type");
1166 return false;
1167 }
1168 }
1169}
1170
1171bool
1173{
1174 return hasInvalidAmount(field, 0, j);
1175}
1176
1177//------------------------------------------------------------------------------
1178//
1179// Arithmetic
1180//
1181//------------------------------------------------------------------------------
1182
1183// Calculate (a * b) / c when all three values are 64-bit
1184// without loss of precision:
1185static std::uint64_t
1186muldiv(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor)
1187{
1188 boost::multiprecision::uint128_t ret;
1189
1190 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1191 ret /= divisor;
1192
1194 {
1196 "overflow: (" + std::to_string(multiplier) + " * " + std::to_string(multiplicand) +
1197 ") / " + std::to_string(divisor));
1198 }
1199
1200 return static_cast<uint64_t>(ret);
1201}
1202
1203static std::uint64_t
1205 std::uint64_t multiplier,
1206 std::uint64_t multiplicand,
1207 std::uint64_t divisor,
1208 std::uint64_t rounding)
1209{
1210 boost::multiprecision::uint128_t ret;
1211
1212 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1213 ret += rounding;
1214 ret /= divisor;
1215
1217 {
1219 "overflow: ((" + std::to_string(multiplier) + " * " + std::to_string(multiplicand) +
1220 ") + " + std::to_string(rounding) + ") / " + std::to_string(divisor));
1221 }
1222
1223 return static_cast<uint64_t>(ret);
1224}
1225
1226STAmount
1227divide(STAmount const& num, STAmount const& den, Asset const& asset)
1228{
1229 if (den == beast::kZero)
1230 Throw<std::runtime_error>("division by zero");
1231
1232 if (num == beast::kZero)
1233 return {asset};
1234
1235 std::uint64_t numVal = num.mantissa();
1236 std::uint64_t denVal = den.mantissa();
1237 int numOffset = num.exponent();
1238 int denOffset = den.exponent();
1239
1240 if (num.integral())
1241 {
1242 while (numVal < STAmount::kMinValue)
1243 {
1244 // Need to bring into range
1245 numVal *= 10;
1246 --numOffset;
1247 }
1248 }
1249
1250 if (den.integral())
1251 {
1252 while (denVal < STAmount::kMinValue)
1253 {
1254 denVal *= 10;
1255 --denOffset;
1256 }
1257 }
1258
1259 // We divide the two mantissas (each is between 10^15
1260 // and 10^16). To maintain precision, we multiply the
1261 // numerator by 10^17 (the product is in the range of
1262 // 10^32 to 10^33) followed by a division, so the result
1263 // is in the range of 10^16 to 10^15.
1264 return STAmount(
1265 asset,
1266 muldiv(numVal, kTenTO17, denVal) + 5,
1267 numOffset - denOffset - 17,
1268 num.negative() != den.negative());
1269}
1270
1271STAmount
1272multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
1273{
1274 if (v1 == beast::kZero || v2 == beast::kZero)
1275 return STAmount(asset);
1276
1277 if (v1.native() && v2.native() && asset.native())
1278 {
1279 std::uint64_t const minV = std::min(getSNValue(v1), getSNValue(v2));
1280 std::uint64_t const maxV = std::max(getSNValue(v1), getSNValue(v2));
1281
1282 if (minV > 3000000000ull) // sqrt(cMaxNative)
1283 Throw<std::runtime_error>("Native value overflow");
1284
1285 if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32
1286 Throw<std::runtime_error>("Native value overflow");
1287
1288 return STAmount(v1.getFName(), minV * maxV);
1289 }
1290 if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>() && asset.holds<MPTIssue>())
1291 {
1292 std::uint64_t const minV = std::min(getMPTValue(v1), getMPTValue(v2));
1293 std::uint64_t const maxV = std::max(getMPTValue(v1), getMPTValue(v2));
1294
1295 if (minV > 3037000499ull) // sqrt(maxMPTokenAmount) ~ 3037000499.98
1296 Throw<std::runtime_error>("MPT value overflow");
1297
1298 if (((maxV >> 32) * minV) > 2147483648ull) // maxMPTokenAmount / 2^32
1299 Throw<std::runtime_error>("MPT value overflow");
1300
1301 return STAmount(asset, minV * maxV);
1302 }
1303
1304 auto const r = Number{v1} * Number{v2};
1305 return STAmount{asset, r};
1306}
1307
1308// This is the legacy version of canonicalizeRound. It's been in use
1309// for years, so it is deeply embedded in the behavior of cross-currency
1310// transactions.
1311//
1312// However, in 2022 it was noticed that the rounding characteristics were
1313// surprising. When the code converts from IOU-like to XRP-like there may
1314// be a fraction of the IOU-like representation that is too small to be
1315// represented in drops. `canonicalizeRound()` currently does some unusual
1316// rounding.
1317//
1318// 1. If the fractional part is greater than or equal to 0.1, then the
1319// number of drops is rounded up.
1320//
1321// 2. However, if the fractional part is less than 0.1 (for example,
1322// 0.099999), then the number of drops is rounded down.
1323//
1324// The XRP Ledger has this rounding behavior baked in. But there are
1325// situations where this rounding behavior led to undesirable outcomes.
1326// So an alternative rounding approach was introduced. You'll see that
1327// alternative below.
1328static void
1329canonicalizeRound(bool integral, std::uint64_t& value, int& offset, bool)
1330{
1331 if (integral)
1332 {
1333 if (offset < 0)
1334 {
1335 int loops = 0;
1336
1337 while (offset < -1)
1338 {
1339 value /= 10;
1340 ++offset;
1341 ++loops;
1342 }
1343
1344 value += (loops >= 2) ? 9 : 10; // add before last divide
1345 value /= 10;
1346 ++offset;
1347 }
1348 }
1349 else if (value > STAmount::kMaxValue)
1350 {
1351 while (value > (10 * STAmount::kMaxValue))
1352 {
1353 value /= 10;
1354 ++offset;
1355 }
1356
1357 value += 9; // add before last divide
1358 value /= 10;
1359 ++offset;
1360 }
1361}
1362
1363// The original canonicalizeRound did not allow the rounding direction to
1364// be specified. It also ignored some of the bits that could contribute to
1365// rounding decisions. canonicalizeRoundStrict() tracks all of the bits in
1366// the value being rounded.
1367static void
1368canonicalizeRoundStrict(bool integral, std::uint64_t& value, int& offset, bool roundUp)
1369{
1370 if (integral)
1371 {
1372 if (offset < 0)
1373 {
1374 bool hadRemainder = false;
1375
1376 while (offset < -1)
1377 {
1378 // It would be better to use std::lldiv than to separately
1379 // compute the remainder. But std::lldiv does not support
1380 // unsigned arguments.
1381 std::uint64_t const newValue = value / 10;
1382 hadRemainder |= (value != (newValue * 10));
1383 value = newValue;
1384 ++offset;
1385 }
1386 value += (hadRemainder && roundUp) ? 10 : 9; // Add before last divide
1387 value /= 10;
1388 ++offset;
1389 }
1390 }
1391 else if (value > STAmount::kMaxValue)
1392 {
1393 while (value > (10 * STAmount::kMaxValue))
1394 {
1395 value /= 10;
1396 ++offset;
1397 }
1398 value += 9; // add before last divide
1399 value /= 10;
1400 ++offset;
1401 }
1402}
1403
1404STAmount
1406{
1407 // Nothing to do for integral types.
1408 if (value.integral())
1409 return value;
1410
1411 // Nothing to do for zero.
1412 if (value == beast::kZero)
1413 return value;
1414
1415 // If the value's exponent is greater than or equal to the scale, then
1416 // rounding will do nothing, and might even lose precision, so just return
1417 // the value.
1418 if (value.exponent() >= scale)
1419 return value;
1420
1421 STAmount const referenceValue{value.asset(), STAmount::kMinValue, scale, value.negative()};
1422
1423 NumberRoundModeGuard const mg(rounding);
1424 // With an IOU, the result of addition will be truncated to the
1425 // precision of the larger value, which in this case is referenceValue. Then
1426 // remove the reference value via subtraction, and we're left with the
1427 // rounded value.
1428 return (value + referenceValue) - referenceValue;
1429}
1430
1431namespace {
1432
1433// We need a class that has an interface similar to NumberRoundModeGuard
1434// but does nothing.
1435class DontAffectNumberRoundMode
1436{
1437public:
1438 explicit DontAffectNumberRoundMode(Number::RoundingMode mode) noexcept
1439 {
1440 }
1441
1442 DontAffectNumberRoundMode(DontAffectNumberRoundMode const&) = delete;
1443
1444 DontAffectNumberRoundMode&
1445 operator=(DontAffectNumberRoundMode const&) = delete;
1446};
1447
1448} // anonymous namespace
1449
1450// Pass the canonicalizeRound function pointer as a template parameter.
1451//
1452// We might need to use NumberRoundModeGuard. Allow the caller
1453// to pass either that or a replacement as a template parameter.
1454template <void (*CanonicalizeFunc)(bool, std::uint64_t&, int&, bool), typename MightSaveRound>
1455static STAmount
1456mulRoundImpl(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp)
1457{
1458 if (v1 == beast::kZero || v2 == beast::kZero)
1459 return {asset};
1460
1461 if (v1.native() && v2.native() && asset.native())
1462 {
1463 std::uint64_t const minV = std::min(getSNValue(v1), getSNValue(v2));
1464 std::uint64_t const maxV = std::max(getSNValue(v1), getSNValue(v2));
1465
1466 if (minV > 3000000000ull) // sqrt(cMaxNative)
1467 Throw<std::runtime_error>("Native value overflow");
1468
1469 if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32
1470 Throw<std::runtime_error>("Native value overflow");
1471
1472 return STAmount(v1.getFName(), minV * maxV);
1473 }
1474
1475 if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>() && asset.holds<MPTIssue>())
1476 {
1477 std::uint64_t const minV = std::min(getMPTValue(v1), getMPTValue(v2));
1478 std::uint64_t const maxV = std::max(getMPTValue(v1), getMPTValue(v2));
1479
1480 if (minV > 3037000499ull) // sqrt(maxMPTokenAmount) ~ 3037000499.98
1481 Throw<std::runtime_error>("MPT value overflow");
1482
1483 if (((maxV >> 32) * minV) > 2147483648ull) // maxMPTokenAmount / 2^32
1484 Throw<std::runtime_error>("MPT value overflow");
1485
1486 return STAmount(asset, minV * maxV);
1487 }
1488
1489 std::uint64_t value1 = v1.mantissa(), value2 = v2.mantissa();
1490 int offset1 = v1.exponent(), offset2 = v2.exponent();
1491
1492 if (v1.integral())
1493 {
1494 while (value1 < STAmount::kMinValue)
1495 {
1496 value1 *= 10;
1497 --offset1;
1498 }
1499 }
1500
1501 if (v2.integral())
1502 {
1503 while (value2 < STAmount::kMinValue)
1504 {
1505 value2 *= 10;
1506 --offset2;
1507 }
1508 }
1509
1510 bool const resultNegative = v1.negative() != v2.negative();
1511
1512 // We multiply the two mantissas (each is between 10^15
1513 // and 10^16), so their product is in the 10^30 to 10^32
1514 // range. Dividing their product by 10^14 maintains the
1515 // precision, by scaling the result to 10^16 to 10^18.
1516 //
1517 // If we're rounding up, we want to round up away
1518 // from zero, and if we're rounding down, truncation
1519 // is implicit.
1520 std::uint64_t amount =
1521 muldivRound(value1, value2, kTenTO14, (resultNegative != roundUp) ? kTenTO14M1 : 0);
1522
1523 int offset = offset1 + offset2 + 14;
1524 if (resultNegative != roundUp)
1525 {
1526 CanonicalizeFunc(asset.integral(), amount, offset, roundUp);
1527 }
1528 STAmount result = [&]() {
1529 // If appropriate, tell Number to round down. This gives the desired
1530 // result from STAmount::canonicalize.
1531 MightSaveRound const savedRound(Number::RoundingMode::TowardsZero);
1532 return STAmount(asset, amount, offset, resultNegative);
1533 }();
1534
1535 if (roundUp && !resultNegative && !result)
1536 {
1537 if (asset.integral())
1538 {
1539 // return the smallest value above zero
1540 amount = 1;
1541 offset = 0;
1542 }
1543 else
1544 {
1545 // return the smallest value above zero
1546 amount = STAmount::kMinValue;
1547 offset = STAmount::kMinOffset;
1548 }
1549 return STAmount(asset, amount, offset, resultNegative);
1550 }
1551 return result;
1552}
1553
1554STAmount
1555mulRound(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp)
1556{
1558}
1559
1560STAmount
1561mulRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp)
1562{
1564}
1565
1566// We might need to use NumberRoundModeGuard. Allow the caller
1567// to pass either that or a replacement as a template parameter.
1568template <typename MightSaveRound>
1569static STAmount
1570divRoundImpl(STAmount const& num, STAmount const& den, Asset const& asset, bool roundUp)
1571{
1572 if (den == beast::kZero)
1573 Throw<std::runtime_error>("division by zero");
1574
1575 if (num == beast::kZero)
1576 return {asset};
1577
1578 std::uint64_t numVal = num.mantissa(), denVal = den.mantissa();
1579 int numOffset = num.exponent(), denOffset = den.exponent();
1580
1581 if (num.integral())
1582 {
1583 while (numVal < STAmount::kMinValue)
1584 {
1585 numVal *= 10;
1586 --numOffset;
1587 }
1588 }
1589
1590 if (den.integral())
1591 {
1592 while (denVal < STAmount::kMinValue)
1593 {
1594 denVal *= 10;
1595 --denOffset;
1596 }
1597 }
1598
1599 bool const resultNegative = (num.negative() != den.negative());
1600
1601 // We divide the two mantissas (each is between 10^15
1602 // and 10^16). To maintain precision, we multiply the
1603 // numerator by 10^17 (the product is in the range of
1604 // 10^32 to 10^33) followed by a division, so the result
1605 // is in the range of 10^16 to 10^15.
1606 //
1607 // We round away from zero if we're rounding up or
1608 // truncate if we're rounding down.
1609 std::uint64_t amount =
1610 muldivRound(numVal, kTenTO17, denVal, (resultNegative != roundUp) ? denVal - 1 : 0);
1611
1612 int offset = numOffset - denOffset - 17;
1613
1614 if (resultNegative != roundUp)
1615 canonicalizeRound(asset.integral(), amount, offset, roundUp);
1616
1617 STAmount result = [&]() {
1618 // If appropriate, tell Number the rounding mode we are using.
1619 // Note that "roundUp == true" actually means "round away from zero".
1620 // Otherwise, round toward zero.
1621 using enum Number::RoundingMode;
1622 MightSaveRound const savedRound(roundUp ^ resultNegative ? Upward : Downward);
1623 return STAmount(asset, amount, offset, resultNegative);
1624 }();
1625
1626 if (roundUp && !resultNegative && !result)
1627 {
1628 if (asset.integral())
1629 {
1630 // return the smallest value above zero
1631 amount = 1;
1632 offset = 0;
1633 }
1634 else
1635 {
1636 // return the smallest value above zero
1637 amount = STAmount::kMinValue;
1638 offset = STAmount::kMinOffset;
1639 }
1640 return STAmount(asset, amount, offset, resultNegative);
1641 }
1642 return result;
1643}
1644
1645STAmount
1646divRound(STAmount const& num, STAmount const& den, Asset const& asset, bool roundUp)
1647{
1648 return divRoundImpl<DontAffectNumberRoundMode>(num, den, asset, roundUp);
1649}
1650
1651STAmount
1652divRoundStrict(STAmount const& num, STAmount const& den, Asset const& asset, bool roundUp)
1653{
1654 return divRoundImpl<NumberRoundModeGuard>(num, den, asset, roundUp);
1655}
1656
1657[[nodiscard]] bool
1659{
1661}
1662} // namespace xrpl
T any_of(T... args)
T append(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:38
Stream error() const
Definition Journal.h:315
Represents a JSON value.
Definition json_value.h:130
bool isNull() const
isNull() tests to see if this field is null.
bool isObject() const
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
bool isArray() const
bool isString() const
UInt asUInt() const
std::string asString() const
Returns the unquoted string value.
bool isObjectOrNull() const
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr bool native() const
Definition Asset.h:115
bool integral() const
Definition Asset.h:123
constexpr bool holds() const
Definition Asset.h:166
constexpr value_type const & value() const
Definition Asset.h:190
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:507
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:24
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:54
std::int64_t value_type
Definition MPTAmount.h:22
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:122
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:33
Number is a floating point type that can represent a wide range of values.
Definition Number.h:306
constexpr rep mantissa() const noexcept
Returns the mantissa of the external view of the Number.
Definition Number.h:640
constexpr int exponent() const noexcept
Returns the exponent of the external view of the Number.
Definition Number.h:661
Identifies fields.
Definition SField.h:130
void set(std::int64_t v)
Definition STAmount.cpp:878
constexpr bool holds() const noexcept
Definition STAmount.h:460
void setIssue(Asset const &asset)
Set the Issue for this amount.
Definition STAmount.cpp:407
static constexpr std::uint64_t kIssuedCurrency
Definition STAmount.h:59
std::string getFullText() const override
Definition STAmount.cpp:636
exponent_type offset_
Definition STAmount.h:41
mantissa_type value_
Definition STAmount.h:40
static STAmount fromNumber(A const &asset, Number const &number)
Definition STAmount.h:551
void add(Serializer &s) const override
Definition STAmount.cpp:742
bool isNegative_
Definition STAmount.h:42
static std::uint64_t const kURateOne
Definition STAmount.h:64
void canonicalize()
Definition STAmount.cpp:821
std::uint64_t mantissa() const noexcept
Definition STAmount.h:472
static constexpr std::uint64_t kPositive
Definition STAmount.h:60
int signum() const noexcept
Definition STAmount.h:504
bool isEquivalent(STBase const &t) const override
Definition STAmount.cpp:790
STBase * copy(std::size_t n, void *buf) const override
Definition STAmount.cpp:254
std::string getText() const override
Definition STAmount.cpp:646
static constexpr int kMinOffset
Definition STAmount.h:47
SerializedTypeID getSType() const override
Definition STAmount.cpp:630
IOUAmount iou() const
Definition STAmount.cpp:286
bool negative() const noexcept
Definition STAmount.h:466
static constexpr std::uint64_t kMaxNativeN
Definition STAmount.h:58
bool isDefault() const override
Definition STAmount.cpp:797
bool integral() const noexcept
Definition STAmount.h:447
static constexpr std::uint64_t kValueMask
Definition STAmount.h:62
STAmount & operator=(beast::Zero)
Definition STAmount.h:536
static std::unique_ptr< STAmount > construct(SerialIter &, SField const &name)
Definition STAmount.cpp:248
bool native() const noexcept
Definition STAmount.h:453
Asset const & asset() const
Definition STAmount.h:478
STAmount & operator+=(STAmount const &)
Definition STAmount.cpp:358
void setJson(json::Value &) const
Definition STAmount.cpp:606
MPTAmount mpt() const
Definition STAmount.cpp:301
static constexpr std::uint64_t kMpToken
Definition STAmount.h:61
json::Value getJson(JsonOptions=JsonOptions::Values::None) const override
Definition STAmount.cpp:734
STAmount & operator-=(STAmount const &)
Definition STAmount.cpp:365
int exponent() const noexcept
Definition STAmount.h:441
bool isZeroAtScale(int scale) const
Checks if this amount evaluates to zero when constrained to a specific accounting scale.
static constexpr std::uint64_t kMinValue
Definition STAmount.h:51
static constexpr std::uint64_t kMaxValue
Definition STAmount.h:53
STBase * move(std::size_t n, void *buf) override
Definition STAmount.cpp:260
XRPAmount xrp() const
Definition STAmount.cpp:271
static constexpr int kMaxOffset
Definition STAmount.h:48
STAmount const & value() const noexcept
Definition STAmount.h:592
STAmount(SerialIter &sit, SField const &name)
Definition STAmount.cpp:112
A type which can be exported to a well known binary format.
Definition STBase.h:117
SField const & getFName() const
Definition STBase.cpp:126
static STBase * emplace(std::size_t n, void *buf, T &&val)
Definition STBase.h:215
uint160 get160()
Definition Serializer.h:382
std::uint64_t get64()
unsigned char get8()
uint192 get192()
Definition Serializer.h:388
int addBitString(BaseUInt< Bits, Tag > const &v)
Definition Serializer.h:105
int add8(unsigned char i)
constexpr value_type drops() const
Returns the number of drops.
Definition XRPAmount.h:159
std::int64_t value_type
Definition XRPAmount.h:26
T distance(T... args)
T empty(T... args)
T end(T... args)
T find_if(T... args)
T make_reverse_iterator(T... args)
T make_unique(T... args)
T max(T... args)
T min(T... args)
unsigned int UInt
@ Object
object value (collection of name/value pairs).
Definition json_value.h:26
@ Null
'null' value
Definition json_value.h:19
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr BaseUInt< Bits, Tag > operator+(BaseUInt< Bits, Tag > const &a, BaseUInt< Bits, Tag > const &b)
Definition base_uint.h:625
static std::int64_t getMPTValue(STAmount const &amount)
Definition STAmount.cpp:83
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
Definition Slice.h:199
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
static STAmount mulRoundImpl(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
bool isFeatureEnabled(uint256 const &feature, bool resultIfNoRules)
Check whether a feature is enabled in the current ledger rules.
Definition Rules.cpp:194
constexpr bool operator==(BaseUInt< Bits, Tag > const &lhs, BaseUInt< Bits, Tag > const &rhs)
Definition base_uint.h:588
bool isLegalMPT(STAmount const &value)
Definition STAmount.h:604
Dest safeDowncast(Src *s) noexcept
Definition safe_cast.h:78
bool isXRP(AccountID const &c)
Definition AccountID.h:70
beast::Journal debugLog()
Returns a debug journal.
Definition Log.cpp:399
NumberParts partsFromString(std::string const &number)
Definition STNumber.cpp:162
Number operator-(Number const &x, Number const &y)
Definition Number.h:736
STAmount mulRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
int scale(Number const &number, Asset const &asset)
Get the scale of a Number for a given asset.
Definition STAmount.h:779
bool isLegalNet(STAmount const &value)
Definition STAmount.h:598
bool validJSONAsset(json::Value const &jv)
Definition Asset.cpp:51
SField const sfGeneric
constexpr std::enable_if_t< std::is_integral_v< Dest > &&std::is_integral_v< Src >, Dest > unsafeCast(Src s) noexcept
Definition safe_cast.h:52
STAmount amountFromString(Asset const &asset, std::string const &amount)
Definition STAmount.cpp:907
std::optional< Rules > const & getCurrentTransactionRules()
Definition Rules.cpp:30
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:65
static std::uint64_t const kTenTO14
Definition STAmount.cpp:52
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount roundToScale(STAmount const &value, std::int32_t scale, Number::RoundingMode rounding=Number::getround())
Round an arbitrary precision Amount to the precision of an STAmount that has a given exponent.
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
Definition STAmount.cpp:464
bool amountFromJsonNoThrow(STAmount &result, json::Value const &jvSource)
STAmount amountFromQuality(std::uint64_t rate)
Definition STAmount.cpp:895
SerializedTypeID
Definition SField.h:93
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
Definition UintTypes.h:44
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:422
static std::int64_t getSNValue(STAmount const &amount)
Definition STAmount.cpp:77
static bool areComparable(STAmount const &v1, STAmount const &v2)
Definition STAmount.cpp:89
STAmount amountFromJson(SField const &name, json::Value const &v)
Definition STAmount.cpp:916
bool hasInvalidAmount(STBase const &field, beast::Journal j)
static std::uint64_t const kTenTO17
Definition STAmount.cpp:54
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
static std::int64_t getInt64Value(STAmount const &amount, bool valid, char const *error)
Definition STAmount.cpp:58
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:541
constexpr bool kIsIssueV
Definition Asset.h:151
static std::uint64_t muldivRound(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor, std::uint64_t rounding)
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:105
constexpr bool kIsMptissueV
Definition Asset.h:154
static std::uint64_t muldiv(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor)
static std::string const & systemCurrencyCode()
STAmount multiply(STAmount const &amount, Number const &frac, Number::RoundingMode rm)
static STAmount divRoundImpl(STAmount const &num, STAmount const &den, Asset const &asset, bool roundUp)
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
Definition Protocol.h:238
constexpr XRPAmount kInitialXrp
Configure the native currency.
static void canonicalizeRoundStrict(bool integral, std::uint64_t &value, int &offset, bool roundUp)
bool toIssuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
static std::uint64_t const kTenTO14M1
Definition STAmount.cpp:53
static void canonicalizeRound(bool integral, std::uint64_t &value, int &offset, bool)
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
T reserve(T... args)
T length(T... args)
Note, should be treated as flags that can be | and &.
Definition STBase.h:17
std::uint64_t mantissa
Definition STNumber.h:90
T to_string(T... args)
T visit(T... args)
T what(T... args)