From bcbaa4df07d1ef390c3be36d3df8082ca228e295 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Fri, 2 Oct 2026 21:42:14 +0000 Subject: [PATCH] fix: Enable the large Number mantissa with MPTokensV2 (#8330) --- include/xrpl/basics/Number.h | 24 ++++----- src/libxrpl/protocol/Rules.cpp | 8 +-- src/test/app/AMMClawbackMPT_test.cpp | 25 +++++---- src/test/app/AMMClawback_test.cpp | 4 +- src/test/app/AMMExtendedMPT_test.cpp | 2 +- src/test/app/AMMExtended_test.cpp | 51 ++++++------------ src/test/app/AMMMPT_test.cpp | 49 +++++++++--------- src/test/app/AMM_test.cpp | 77 +++------------------------- src/test/app/EscrowToken_test.cpp | 10 ++-- src/test/jtx/impl/AMMTest.cpp | 15 +++--- 10 files changed, 98 insertions(+), 167 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index d33c1ee71e..59f3e938d1 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -110,10 +110,10 @@ static_assert( * * However, it does not have sufficient precision to represent the full integer * range of int64_t values (-2^63 to 2^63-1), which are needed for XRP and MPT - * values. The implementation of SingleAssetVault, and LendingProtocol need to - * represent those integer values accurately and precisely, both for the - * STNumber field type, and for internal calculations. That necessitated the - * "large" scale. + * values. The implementation of SingleAssetVault, LendingProtocol, and + * MPTokensV2 need to represent those integer values accurately and precisely, + * both for the STNumber field type, and for internal calculations. That + * necessitated the "large" scale. * * The "Large" scales are intended to represent all values that can be represented * by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max @@ -134,8 +134,8 @@ struct MantissaRange final // NOLINTBEGIN(readability-enum-initial-value) // The values don't matter, except for Large enum class MantissaScale { - // Small can be removed when either featureSingleAssetVault or featureLendingProtocol are - // retired + // Small can be removed when any of featureSingleAssetVault, featureLendingProtocol, or + // featureMPTokensV2 are retired Small, // LargeLegacy can be removed when fixCleanup3_2_0 is retired LargeLegacy, @@ -311,10 +311,10 @@ concept Integral64 = std::is_same_v || std::is_same_v || std::is_same_v r) auto const range = [&r]() { // If any new conditions with new amendments are added to "enableLargeNumbers", those // amendments must also be added to useRulesGuards. - bool const enableLargeNumbers = - !r || (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol)); + bool const enableLargeNumbers = !r || + (r->enabled(featureSingleAssetVault) || r->enabled(featureLendingProtocol) || + r->enabled(featureMPTokensV2)); // If enableLargeNumbers is true, then useRulesGuards must also return true. // However, the reverse is not true. Other amendments can cause the rules guard to be used, // even though large numbers are _not_ used. @@ -84,7 +85,8 @@ useRulesGuards(Rules const& rules) // with createGuards, and any other callers, and the first set of guards can be created directly // at the call site, without using optional. return rules.enabled(featureSingleAssetVault) || rules.enabled(featureLendingProtocol) || - rules.enabled(fixCleanup3_2_0) || rules.enabled(fixCleanup3_3_0); + rules.enabled(fixCleanup3_2_0) || rules.enabled(fixCleanup3_3_0) || + rules.enabled(featureMPTokensV2); } void diff --git a/src/test/app/AMMClawbackMPT_test.cpp b/src/test/app/AMMClawbackMPT_test.cpp index 44eb61395a..7a018c3e56 100644 --- a/src/test/app/AMMClawbackMPT_test.cpp +++ b/src/test/app/AMMClawbackMPT_test.cpp @@ -31,6 +31,15 @@ namespace xrpl::test { class AMMClawbackMPT_test : public beast::unit_test::Suite { + // SingleAssetVault, LendingProtocol and MPTokensV2 enable the large + // Number mantissa. + static bool + largeMantissa(FeatureBitset const& features) + { + return features[featureSingleAssetVault] || features[featureLendingProtocol] || + features[featureMPTokensV2]; + } + void testInvalidRequest(FeatureBitset features) { @@ -1724,7 +1733,7 @@ class AMMClawbackMPT_test : public beast::unit_test::Suite .asString(); auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value].asString(); - if (features[featureSingleAssetVault] || features[featureLendingProtocol]) + if (largeMantissa(features)) { BEAST_EXPECT(lpToken == "1.414213562374011" && lpTokenBalance == "1.4142135623741"); } @@ -1741,16 +1750,14 @@ class AMMClawbackMPT_test : public beast::unit_test::Suite env(amm::ammClawback(gw, alice, usd, eur, std::nullopt)); BEAST_EXPECT(!amm.ammExists()); } - else if ( - features[fixAMMv1_3] && - (features[featureSingleAssetVault] || features[featureLendingProtocol])) + else if (features[fixAMMv1_3] && largeMantissa(features)) { env(amm::ammClawback(gw, alice, usd, eur, std::nullopt)); // Without the Rounding feature and with new Number a dust pool // amount remains BEAST_EXPECT(amm.ammExists()); } - else if (!features[featureSingleAssetVault] && !features[featureLendingProtocol]) + else if (!largeMantissa(features)) { env(amm::ammClawback(gw, alice, usd, eur, std::nullopt), Ter(tecINTERNAL)); BEAST_EXPECT(amm.ammExists()); @@ -1794,7 +1801,7 @@ class AMMClawbackMPT_test : public beast::unit_test::Suite .asString(); auto const lpTokenBalance = amm.ammRpcInfo()[jss::amm][jss::lp_token][jss::value].asString(); - if (!features[featureSingleAssetVault] && !features[featureLendingProtocol]) + if (!largeMantissa(features)) { BEAST_EXPECT(lpToken == "1.414213562374011" && lpTokenBalance == "1.414213562374"); } @@ -1811,16 +1818,14 @@ class AMMClawbackMPT_test : public beast::unit_test::Suite env(amm::ammClawback(gw, alice, usd, eur, std::nullopt)); BEAST_EXPECT(!amm.ammExists()); } - else if ( - features[fixAMMv1_3] && - (features[featureSingleAssetVault] || features[featureLendingProtocol])) + else if (features[fixAMMv1_3] && largeMantissa(features)) { // Without the Rounding feature and with new Number a dust pool // amount remains env(amm::ammClawback(gw, alice, usd, eur, std::nullopt)); BEAST_EXPECT(amm.ammExists()); } - else if (!features[featureSingleAssetVault] && !features[featureLendingProtocol]) + else if (!largeMantissa(features)) { env(amm::ammClawback(gw, alice, usd, eur, std::nullopt), Ter(tecINTERNAL)); BEAST_EXPECT(amm.ammExists()); diff --git a/src/test/app/AMMClawback_test.cpp b/src/test/app/AMMClawback_test.cpp index 4f025f08eb..a1b6626b25 100644 --- a/src/test/app/AMMClawback_test.cpp +++ b/src/test/app/AMMClawback_test.cpp @@ -2857,8 +2857,8 @@ class AMMClawback_test : public beast::unit_test::Suite { // For now, just disable SAV entirely, which locks in the small Number // mantissas - FeatureBitset const all = - jtx::testableAmendments() - featureSingleAssetVault - featureLendingProtocol; + FeatureBitset const all = jtx::testableAmendments() - featureSingleAssetVault - + featureLendingProtocol - featureMPTokensV2; testInvalidRequest(all); testInvalidRequest(all - featureMPTokensV2); diff --git a/src/test/app/AMMExtendedMPT_test.cpp b/src/test/app/AMMExtendedMPT_test.cpp index ae1339c41b..7818a21c72 100644 --- a/src/test/app/AMMExtendedMPT_test.cpp +++ b/src/test/app/AMMExtendedMPT_test.cpp @@ -116,7 +116,7 @@ private: Txflags(tfPartialPayment)); BEAST_EXPECT(ammCarol.expectBalances( - btc(1'001'000'000'374'816), eth(100'000'000'000'000'000), ammCarol.tokens())); + btc(1'001'000'000'374'814), eth(100'000'000'000'000'000), ammCarol.tokens())); env.require(Balance(bob_, eth(200'100'000'000'000'000))); BEAST_EXPECT(isOffer(env, carol_, btc(49'000'000'000'000), XRP(49))); diff --git a/src/test/app/AMMExtended_test.cpp b/src/test/app/AMMExtended_test.cpp index 04af29146f..5769a5300b 100644 --- a/src/test/app/AMMExtended_test.cpp +++ b/src/test/app/AMMExtended_test.cpp @@ -73,7 +73,8 @@ class AMMExtended_test : public jtx::AMMTest // For now, just disable SAV entirely, which locks in the small Number // mantissas FeatureBitset const all_{ - testableAmendments() - featureSingleAssetVault - featureLendingProtocol}; + testableAmendments() - featureSingleAssetVault - featureLendingProtocol - + featureMPTokensV2}; private: void @@ -267,39 +268,20 @@ private: {features}); // tfPassive -- place the offer without crossing it. - if (features[featureMPTokensV2]) - { - Env env{*this, features}; - fund(env, gw_, {alice_, carol_}, XRP(30'000'000), {USD(30'000'000)}); - - AMM const ammAlice(env, alice_, XRP(10'100'000), USD(10'000'000)); - - // Scale the exact-quality fixture up so the visual relationship - // stays clear: the passive CLOB offer has the same 1:1 quality as - // the generated AMM offer, so it should not cross. - env(offer(carol_, XRP(100'000), USD(100'000), tfPassive)); - env.close(); - BEAST_EXPECT( - ammAlice.expectBalances(XRP(10'100'000), USD(10'000'000), ammAlice.tokens())); - BEAST_EXPECT(expectOffers(env, carol_, 1, {{{XRP(100'000), USD(100'000)}}})); - } - else - { - testAMM( - [&](AMM& ammAlice, Env& env) { - // Carol creates a passive offer that could cross AMM. - // Carol's offer should stay in the ledger. - env(offer(carol_, XRP(100), USD(100), tfPassive)); - env.close(); - BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'100), STAmount{USD, 10'000}, ammAlice.tokens())); - BEAST_EXPECT(expectOffers(env, carol_, 1, {{{XRP(100), STAmount{USD, 100}}}})); - }, - {{XRP(10'100), USD(10'000)}}, - 0, - std::nullopt, - {features}); - } + testAMM( + [&](AMM& ammAlice, Env& env) { + // Carol creates a passive offer that could cross AMM. + // Carol's offer should stay in the ledger. + env(offer(carol_, XRP(100), USD(100), tfPassive)); + env.close(); + BEAST_EXPECT( + ammAlice.expectBalances(XRP(10'100), STAmount{USD, 10'000}, ammAlice.tokens())); + BEAST_EXPECT(expectOffers(env, carol_, 1, {{{XRP(100), STAmount{USD, 100}}}})); + }, + {{XRP(10'100), USD(10'000)}}, + 0, + std::nullopt, + {features}); // tfPassive -- cross only offers of better quality. testAMM( @@ -1450,7 +1432,6 @@ private: testRmFundedOffer(all_ - fixAMMv1_1 - fixAMMv1_3); testEnforceNoRipple(all_); testFillModes(all_); - testFillModes(all_ - featureMPTokensV2); testOfferCrossWithXRP(all_); testOfferCrossWithLimitOverride(all_); testCurrencyConversionEntire(all_); diff --git a/src/test/app/AMMMPT_test.cpp b/src/test/app/AMMMPT_test.cpp index 37e0ed585d..d194014827 100644 --- a/src/test/app/AMMMPT_test.cpp +++ b/src/test/app/AMMMPT_test.cpp @@ -1451,12 +1451,14 @@ private: auto carolXRP = env.balance(carol_, XRP); auto carolMPT = env.balance(carol_, MPT(ammAlice[1])); + // The exact deposit is 201; rounding in the pool's favor at + // the large mantissa takes one more unit. ammAlice.deposit(carol_, 100'000, MPT(ammAlice[1])(205)); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), MPT(ammAlice[1])(10'201), IOUAmount{10'100'000, 0})); + XRP(10'000), MPT(ammAlice[1])(10'202), IOUAmount{10'100'000, 0})); env.require(Balance(carol_, carolXRP - drops(baseFee))); - env.require(Balance(carol_, carolMPT - MPT(ammAlice[1])(201))); + env.require(Balance(carol_, carolMPT - MPT(ammAlice[1])(202))); }, {{XRP(10'000), gAmmmpt(10'000)}}); @@ -1467,11 +1469,13 @@ private: auto carolXRP = env.balance(carol_, XRP); auto carolMPT = env.balance(carol_, MPT(ammAlice[1])); + // The exact deposit is 201 XRP; rounding in the pool's favor + // at the large mantissa takes one more drop. ammAlice.deposit(carol_, 100'000, XRP(205)); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'201), MPT(ammAlice[1])(10'000), IOUAmount{10'100'000, 0})); + XRPAmount{10'201'000'001}, MPT(ammAlice[1])(10'000), IOUAmount{10'100'000, 0})); - env.require(Balance(carol_, carolXRP - XRP(201) - drops(baseFee))); + env.require(Balance(carol_, carolXRP - XRPAmount{201'000'001} - drops(baseFee))); env.require(Balance(carol_, carolMPT)); }, {{XRP(10'000), gAmmmpt(10'000)}}); @@ -1551,10 +1555,10 @@ private: STAmount{ammAlice.lptIssue(), 2004, -6}); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), MPT(ammAlice[1])(10'081), IOUAmount{10'039'920'31840891, -8})); + XRP(10'000), MPT(ammAlice[1])(10'080), IOUAmount{10'039'920'3184089, -7})); env.require(Balance(carol_, carolXRP - drops(baseFee))); - env.require(Balance(carol_, carolMPT - MPT(ammAlice[1])(81))); + env.require(Balance(carol_, carolMPT - MPT(ammAlice[1])(80))); }, {{XRP(10'000), gAmmmpt(10'000)}}); @@ -1573,10 +1577,10 @@ private: STAmount{ammAlice.lptIssue(), 2004, -6}); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10'000), MPT(ammAlice[1])(10'081), IOUAmount{10'039'920'31840891, -8})); + XRP(10'000), MPT(ammAlice[1])(10'080), IOUAmount{10'039'920'3184089, -7})); env.require(Balance(carol_, carolXRP - drops(baseFee))); - env.require(Balance(carol_, carolMPT - MPT(ammAlice[1])(81))); + env.require(Balance(carol_, carolMPT - MPT(ammAlice[1])(80))); }, {{XRP(10'000), gAmmmpt(10'000)}}); @@ -1797,9 +1801,9 @@ private: ammAlice.deposit(gw_, MPT(ammAlice[0])(1000)); BEAST_EXPECT(ammAlice.expectBalances( - MPT(ammAlice[0])(1'010'999), + MPT(ammAlice[0])(1'011'000), MPT(ammAlice[1])(1'010'000), - IOUAmount{1'010'499'376546071, -9})); + IOUAmount{1'010'499'876298854, -9})); }, {{gAmmmpt(10'000), gAmmmpt(10'000)}}); @@ -2212,7 +2216,7 @@ private: if (env.enabled(fixAMMv1_3)) { BEAST_EXPECT(ammAlice.expectBalances( - MPT(ammAlice[0])(1), STAmount{USD, 1, -11}, IOUAmount{1, -8})); + MPT(ammAlice[0])(1), STAmount{USD, 1, -12}, IOUAmount{1, -9})); } }, {{gAmmmpt(10'000'000'000), USD(10'000)}}, @@ -2541,11 +2545,12 @@ private: // XRP amount to withdraw is 0 ammAlice.withdraw( alice_, IOUAmount{1, -5}, std::nullopt, std::nullopt, Ter(tecAMM_FAILED)); - // Calculated tokens to withdraw are 0 + // Calculated tokens to withdraw are 0: 1e-16 of 10,000 USD is + // below the large Number mantissa's precision ammAlice.withdraw( alice_, std::nullopt, - STAmount{USD, 1, -11}, + STAmount{USD, 1, -16}, std::nullopt, Ter(tecAMM_INVALID_TOKENS)); ammAlice.deposit(carol_, STAmount{USD, 1, -10}); @@ -2707,7 +2712,7 @@ private: BEAST_EXPECT(ammAlice.expectBalances( XRPAmount(9000'000001), MPT(ammAlice[1])(10'000), - IOUAmount{9'486'832'98050514, -8})); + IOUAmount{9'486'832'980505138, -9})); }, {{XRP(10'000), gAmmmpt(10'000)}}); testAMM( @@ -2715,7 +2720,7 @@ private: // single withdraw MPT from XRP/MPT ammAlice.withdraw(alice_, MPT(ammAlice[1])(1'000)); BEAST_EXPECT(ammAlice.expectBalances( - XRP(10000), MPT(ammAlice[1])(9001), IOUAmount{9'486'832'98050514, -8})); + XRP(10000), MPT(ammAlice[1])(9001), IOUAmount{9'486'832'980505138, -9})); }, {{XRP(10'000), gAmmmpt(10'000)}}); testAMM( @@ -2723,9 +2728,7 @@ private: // single withdraw IOU from IOU/MPT ammAlice.withdraw(alice_, USD(1'000)); BEAST_EXPECT(ammAlice.expectBalances( - STAmount{USD, UINT64_C(9000'000000000004), -12}, - MPT(ammAlice[1])(10'000), - IOUAmount{9486'83298050514, -11})); + USD(9'000), MPT(ammAlice[1])(10'000), IOUAmount{9486'832980505138, -12})); }, {{USD(10'000), gAmmmpt(10'000)}}); testAMM( @@ -2733,7 +2736,7 @@ private: // single withdraw MPT from IOU/MPT ammAlice.withdraw(alice_, MPT(ammAlice[1])(1'000)); BEAST_EXPECT(ammAlice.expectBalances( - USD(10'000), MPT(ammAlice[1])(9001), IOUAmount{9486'83298050514, -11})); + USD(10'000), MPT(ammAlice[1])(9001), IOUAmount{9486'832980505138, -12})); }, {{USD(10'000), gAmmmpt(10'000)}}); testAMM( @@ -2743,7 +2746,7 @@ private: BEAST_EXPECT(ammAlice.expectBalances( MPT(ammAlice[0])(9001), MPT(ammAlice[1])(10'000), - IOUAmount{9486'83298050514, -11})); + IOUAmount{9486'832980505138, -12})); }, {{gAmmmpt(10'000), gAmmmpt(10'000)}}); @@ -3024,7 +3027,7 @@ private: ammAlice.deposit(carol_, 1'000); ammAlice.withdrawAll(carol_, USD(0)); BEAST_EXPECT(ammAlice.expectBalances( - STAmount{USD, UINT64_C(9'090'909090909092), -12}, + STAmount{USD, UINT64_C(9'090'909090909091), -12}, MPT(ammAlice[1])(11'000), IOUAmount{10'000})); }, @@ -3249,7 +3252,7 @@ private: BEAST_EXPECT(ammAlice.expectBalances( MPT(ammAlice[0])(10'000'000'000), STAmount{USD, 1, -11}, - IOUAmount{316227765, -9})); + IOUAmount{316227766, -9})); }, {{gAmmmpt(10'000'000'000), USD(10'000)}}); // XRP @@ -5960,7 +5963,7 @@ private: if (!features[fixAMMv1_3]) { BEAST_EXPECT(tokensFee == IOUAmount(98'019'80198019, -8)); - BEAST_EXPECT(tokensNoFee == IOUAmount(98'495'13933556, -8)); + BEAST_EXPECT(tokensNoFee == IOUAmount(98'495'13933557, -8)); } else { diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index 2645578707..ec39da2846 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -84,7 +84,8 @@ private: { // For now, just disable SAV entirely, which locks in the small Number // mantissas - return jtx::testableAmendments() - featureSingleAssetVault - featureLendingProtocol; + return jtx::testableAmendments() - featureSingleAssetVault - featureLendingProtocol - + featureMPTokensV2; } // Seed from the local testableAmendments() which strips SAV and Lending. @@ -3810,7 +3811,7 @@ private: BEAST_EXPECT(amm.expectBalances(XRP(1'000), USD(500), amm.tokens())); BEAST_EXPECT(expectOffers(env, carol_, 1, {{Amounts{XRP(100), USD(55)}}})); } - else if (!features[featureMPTokensV2]) + else { BEAST_EXPECT(amm.expectBalances( XRPAmount(909'090'909), @@ -3825,30 +3826,6 @@ private: env.balance(carol_, USD) == STAmount(USD, UINT64_C(29'949'94999999494), -11)); } - else - { - // Post-amendment the transfer fee is taken into account - // when calculating the limit out based on limitQuality. - // This increases the limitQuality and decreases - // the limit out. Consequently, AMM offer size is decreased, - // and the quality is increased, matching the overall - // quality. - // AMM offer ~50USD/91XRP - BEAST_EXPECT(amm.expectBalances( - XRPAmount(909'090'910), - STAmount{USD, UINT64_C(549'99999945), -8}, - amm.tokens())); - // Offer ~91XRP/50USD - BEAST_EXPECT(expectOffers( - env, - carol_, - 1, - {{Amounts{XRPAmount{9'090'910}, STAmount{USD, 5'0000005, -7}}}})); - // Carol pays 0.1% fee on ~50USD =~ 0.05USD - BEAST_EXPECT( - env.balance(carol_, USD) == - STAmount(USD, UINT64_C(29'949'95000060055), -11)); - } }, {{XRP(1'000), USD(500)}}, 0, @@ -6498,7 +6475,7 @@ private: BEAST_EXPECT(expectOffers(env, bob_, 1, {{Amounts{USD(1), XRPAmount(500)}}})); BEAST_EXPECT(expectOffers(env, carol_, 1, {{Amounts{XRP(100), USD(55)}}})); } - else if (!features[featureMPTokensV2]) + else { BEAST_EXPECT(amm.expectBalances( XRPAmount(909'090'909), @@ -6511,19 +6488,6 @@ private: {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); BEAST_EXPECT(expectOffers(env, bob_, 1, {{Amounts{USD(1), XRPAmount(500)}}})); } - else - { - BEAST_EXPECT(amm.expectBalances( - XRPAmount(909'090'910), - STAmount{USD, UINT64_C(549'99999945), -8}, - amm.tokens())); - BEAST_EXPECT(expectOffers( - env, - carol_, - 1, - {{Amounts{XRPAmount{9'090'910}, STAmount{USD, 5'0000005, -7}}}})); - BEAST_EXPECT(expectOffers(env, bob_, 1, {{Amounts{USD(1), XRPAmount(500)}}})); - } } // There is no blocking offer, the same AMM liquidity is consumed @@ -6535,30 +6499,10 @@ private: AMM const amm(env, alice_, XRP(1'000), USD(500)); env(offer(carol_, XRP(100), USD(55))); env.close(); - if (!features[featureMPTokensV2]) - { - BEAST_EXPECT(amm.expectBalances( - XRPAmount(909'090'909), - STAmount{USD, UINT64_C(550'000000055), -9}, - amm.tokens())); - BEAST_EXPECT(expectOffers( - env, - carol_, - 1, - {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); - } - else - { - BEAST_EXPECT(amm.expectBalances( - XRPAmount(909'090'910), - STAmount{USD, UINT64_C(549'99999945), -8}, - amm.tokens())); - BEAST_EXPECT(expectOffers( - env, - carol_, - 1, - {{Amounts{XRPAmount{9'090'910}, STAmount{USD, 5'0000005, -7}}}})); - } + BEAST_EXPECT(amm.expectBalances( + XRPAmount(909'090'909), STAmount{USD, UINT64_C(550'000000055), -9}, amm.tokens())); + BEAST_EXPECT(expectOffers( + env, carol_, 1, {{Amounts{XRPAmount{9'090'909}, STAmount{USD, 4'99999995, -8}}}})); } } @@ -7448,8 +7392,6 @@ private: .err = Ter(tecAMM_BALANCE)}); }; - // Bound holds regardless of the deposit-side fix amendment. - test(all - featureMPTokensV2); test(all); } @@ -7481,7 +7423,6 @@ private: testFlags(); testRippling(); testAMMAndCLOB(all); - testAMMAndCLOB(all - featureMPTokensV2); testAMMAndCLOB(all - fixAMMv1_1 - fixAMMv1_3); testTradingFee(all); testTradingFee(all - fixAMMv1_3); @@ -7501,10 +7442,8 @@ private: testOverflowOffer(all - fixAMMv1_1 - fixAMMv1_3); testSwapRounding(); testFixChangeSpotPriceQuality(all); - testFixChangeSpotPriceQuality(all - featureMPTokensV2); testFixChangeSpotPriceQuality(all - fixAMMv1_1 - fixAMMv1_3); testFixAMMOfferBlockedByLOB(all); - testFixAMMOfferBlockedByLOB(all - featureMPTokensV2); testFixAMMOfferBlockedByLOB(all - fixAMMv1_1 - fixAMMv1_3); testLPTokenBalance(all); testLPTokenBalance(all - fixAMMv1_3); diff --git a/src/test/app/EscrowToken_test.cpp b/src/test/app/EscrowToken_test.cpp index 3a2bc14183..730835bc96 100644 --- a/src/test/app/EscrowToken_test.cpp +++ b/src/test/app/EscrowToken_test.cpp @@ -573,8 +573,8 @@ struct EscrowToken_test : public beast::unit_test::Suite env(pay(gw, bob, usd(1))); env.close(); - bool const largeMantissa = - features[featureSingleAssetVault] || features[featureLendingProtocol]; + bool const largeMantissa = features[featureSingleAssetVault] || + features[featureLendingProtocol] || features[featureMPTokensV2]; // alice cannot create escrow for 1/10 iou - precision loss env(escrow::create(alice, bob, usd(1)), @@ -2294,8 +2294,8 @@ struct EscrowToken_test : public beast::unit_test::Suite env(pay(gw, bob, usd(1))); env.close(); - bool const largeMantissa = - features[featureSingleAssetVault] || features[featureLendingProtocol]; + bool const largeMantissa = features[featureSingleAssetVault] || + features[featureLendingProtocol] || features[featureMPTokensV2]; // alice cannot create escrow for 1/10 iou - precision loss env(escrow::create(alice, bob, usd(1)), @@ -4334,7 +4334,7 @@ public: using namespace test::jtx; FeatureBitset const all{testableAmendments()}; for (FeatureBitset const& feats : - {all - featureSingleAssetVault - featureLendingProtocol, all}) + {all - featureSingleAssetVault - featureLendingProtocol - featureMPTokensV2, all}) { testIOUWithFeats(feats); testIOUWithFeats(feats - fixCleanup3_2_0); diff --git a/src/test/jtx/impl/AMMTest.cpp b/src/test/jtx/impl/AMMTest.cpp index 3b4aae20e2..f3214e8d9c 100644 --- a/src/test/jtx/impl/AMMTest.cpp +++ b/src/test/jtx/impl/AMMTest.cpp @@ -139,14 +139,15 @@ AMMTestBase::testAMM(std::function const& cb, TestAM // Use small Number mantissas for the life of this test. NumberMantissaScaleGuard const sg{xrpl::MantissaRange::MantissaScale::Small}; - // For now, just disable SAV entirely, which locks in the small Number - // mantissas - Env env{ - *this, - features - featureSingleAssetVault - featureLendingProtocol, - arg.noLog ? std::make_unique(&logs) : nullptr}; - auto const [asset1, asset2] = arg.pool ? *arg.pool : std::make_pair(XRP(10000), USD(10000)); + + // For now, just disable SAV entirely, which locks in the small Number + // mantissas. MPTokensV2 enables them too, so keep it for MPT pools + // only, which require it. + auto envFeatures = features - featureSingleAssetVault - featureLendingProtocol; + if (!asset1.holds() && !asset2.holds()) + envFeatures = envFeatures - featureMPTokensV2; + Env env{*this, envFeatures, arg.noLog ? std::make_unique(&logs) : nullptr}; auto toFund = [&](STAmount const& a) -> STAmount { if (a.native()) {