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