From 1fa15b1a76e1dbcbea1381c4353886ab52543342 Mon Sep 17 00:00:00 2001
From: Shawn Xie <shawnxie920@gmail.com>
Date: Mon, 9 Sep 2024 12:40:12 -0400
Subject: [PATCH] MPT amount base 10

---
 src/libxrpl/protocol/STInteger.cpp    | 35 ++++++++++++++++++++-------
 src/libxrpl/protocol/STParsedJSON.cpp |  6 ++++-
 src/test/app/MPToken_test.cpp         | 16 ++++++------
 src/test/jtx/impl/mpt.cpp             | 13 +---------
 src/test/jtx/mpt.h                    |  2 +-
 5 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/src/libxrpl/protocol/STInteger.cpp b/src/libxrpl/protocol/STInteger.cpp
index 7b7420006f9..df42443aadd 100644
--- a/src/libxrpl/protocol/STInteger.cpp
+++ b/src/libxrpl/protocol/STInteger.cpp
@@ -61,7 +61,8 @@ STUInt8::getText() const
 }
 
 template <>
-Json::Value STUInt8::getJson(JsonOptions) const
+Json::Value
+STUInt8::getJson(JsonOptions) const
 {
     if (getFName() == sfTransactionResult)
     {
@@ -118,7 +119,8 @@ STUInt16::getText() const
 }
 
 template <>
-Json::Value STUInt16::getJson(JsonOptions) const
+Json::Value
+STUInt16::getJson(JsonOptions) const
 {
     if (getFName() == sfLedgerEntryType)
     {
@@ -164,7 +166,8 @@ STUInt32::getText() const
 }
 
 template <>
-Json::Value STUInt32::getJson(JsonOptions) const
+Json::Value
+STUInt32::getJson(JsonOptions) const
 {
     return value_;
 }
@@ -192,13 +195,27 @@ STUInt64::getText() const
 }
 
 template <>
-Json::Value STUInt64::getJson(JsonOptions) const
+Json::Value
+STUInt64::getJson(JsonOptions) const
 {
-    std::string str(16, 0);
-    auto ret = std::to_chars(str.data(), str.data() + str.size(), value_, 16);
-    assert(ret.ec == std::errc());
-    str.resize(std::distance(str.data(), ret.ptr));
-    return str;
+    auto convertToString = [](uint64_t const value, int const base) {
+        std::string str(
+            base == 10 ? 20 : 16, 0);  // Allocate space depending on base
+        auto ret =
+            std::to_chars(str.data(), str.data() + str.size(), value, base);
+        assert(ret.ec == std::errc());
+        str.resize(std::distance(str.data(), ret.ptr));
+        return str;
+    };
+
+    if (auto const& fName = getFName(); fName == sfMaximumAmount ||
+        fName == sfOutstandingAmount || fName == sfLockedAmount ||
+        fName == sfMPTAmount)
+    {
+        return convertToString(value_, 10);  // Convert to base 10
+    }
+
+    return convertToString(value_, 16);  // Convert to base 16
 }
 
 }  // namespace ripple
diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp
index 92044334431..3db15d1eb39 100644
--- a/src/libxrpl/protocol/STParsedJSON.cpp
+++ b/src/libxrpl/protocol/STParsedJSON.cpp
@@ -398,8 +398,12 @@ parseLeaf(
 
                     std::uint64_t val;
 
+                    // if the field is amount, serialize as base 10
                     auto [p, ec] = std::from_chars(
-                        str.data(), str.data() + str.size(), val, 16);
+                        str.data(),
+                        str.data() + str.size(),
+                        val,
+                        (field == sfMaximumAmount) ? 10 : 16);
 
                     if (ec != std::errc() || (p != str.data() + str.size()))
                         Throw<std::invalid_argument>("invalid data");
diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp
index 0cac8811366..b1789158987 100644
--- a/src/test/app/MPToken_test.cpp
+++ b/src/test/app/MPToken_test.cpp
@@ -53,7 +53,7 @@ class MPToken_test : public beast::unit_test::suite
 
             // tries to set a txfee while not enabling in the flag
             mptAlice.create(
-                {.maxAmt = 100,
+                {.maxAmt = "100",
                  .assetScale = 0,
                  .transferFee = 1,
                  .metadata = "test",
@@ -61,7 +61,7 @@ class MPToken_test : public beast::unit_test::suite
 
             // tries to set a txfee greater than max
             mptAlice.create(
-                {.maxAmt = 100,
+                {.maxAmt = "100",
                  .assetScale = 0,
                  .transferFee = maxTransferFee + 1,
                  .metadata = "test",
@@ -70,7 +70,7 @@ class MPToken_test : public beast::unit_test::suite
 
             // tries to set a txfee while not enabling transfer
             mptAlice.create(
-                {.maxAmt = 100,
+                {.maxAmt = "100",
                  .assetScale = 0,
                  .transferFee = maxTransferFee,
                  .metadata = "test",
@@ -78,7 +78,7 @@ class MPToken_test : public beast::unit_test::suite
 
             // empty metadata returns error
             mptAlice.create(
-                {.maxAmt = 100,
+                {.maxAmt = "100",
                  .assetScale = 0,
                  .transferFee = 0,
                  .metadata = "",
@@ -86,7 +86,7 @@ class MPToken_test : public beast::unit_test::suite
 
             // MaximumAmout of 0 returns error
             mptAlice.create(
-                {.maxAmt = 0,
+                {.maxAmt = "0",
                  .assetScale = 1,
                  .transferFee = 1,
                  .metadata = "test",
@@ -94,7 +94,7 @@ class MPToken_test : public beast::unit_test::suite
 
             // MaximumAmount larger than 63 bit returns error
             mptAlice.create(
-                {.maxAmt = 0xFFFFFFFFFFFFFFF0ull,
+                {.maxAmt = "18446744073709551600",
                  .assetScale = 0,
                  .transferFee = 0,
                  .metadata = "test",
@@ -116,7 +116,7 @@ class MPToken_test : public beast::unit_test::suite
             Env env{*this, features};
             MPTTester mptAlice(env, alice);
             mptAlice.create(
-                {.maxAmt = 0x7FFFFFFFFFFFFFFF,
+                {.maxAmt = "9223372036854775807",
                  .assetScale = 1,
                  .transferFee = 10,
                  .metadata = "123",
@@ -781,7 +781,7 @@ class MPToken_test : public beast::unit_test::suite
             MPTTester mptAlice(env, alice, {.holders = {&bob}});
 
             mptAlice.create(
-                {.maxAmt = 100,
+                {.maxAmt = "100",
                  .ownerCount = 1,
                  .holderCount = 0,
                  .flags = tfMPTCanTransfer});
diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp
index 88f206796bf..1e051a8092a 100644
--- a/src/test/jtx/impl/mpt.cpp
+++ b/src/test/jtx/impl/mpt.cpp
@@ -25,15 +25,6 @@ namespace ripple {
 namespace test {
 namespace jtx {
 
-static std::array<std::uint8_t, 8>
-uint64ToByteArray(std::uint64_t value)
-{
-    value = boost::endian::native_to_big(value);
-    std::array<std::uint8_t, 8> result;
-    std::memcpy(result.data(), &value, sizeof(value));
-    return result;
-}
-
 void
 mptflags::operator()(Env& env) const
 {
@@ -105,10 +96,8 @@ MPTTester::create(const MPTCreate& arg)
         jv[sfTransferFee.jsonName] = *arg.transferFee;
     if (arg.metadata)
         jv[sfMPTokenMetadata.jsonName] = strHex(*arg.metadata);
-
-    // convert maxAmt to hex string, since json doesn't accept 64-bit int
     if (arg.maxAmt)
-        jv[sfMaximumAmount.jsonName] = strHex(uint64ToByteArray(*arg.maxAmt));
+        jv[sfMaximumAmount.jsonName] = *arg.maxAmt;
     if (submit(arg, jv) != tesSUCCESS)
     {
         // Verify issuance doesn't exist
diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h
index e96b73f039a..78b09a03923 100644
--- a/src/test/jtx/mpt.h
+++ b/src/test/jtx/mpt.h
@@ -97,7 +97,7 @@ struct MPTConstr
 
 struct MPTCreate
 {
-    std::optional<std::int64_t> maxAmt = std::nullopt;
+    std::optional<std::string> maxAmt = std::nullopt;
     std::optional<std::uint8_t> assetScale = std::nullopt;
     std::optional<std::uint16_t> transferFee = std::nullopt;
     std::optional<std::string> metadata = std::nullopt;