From baf4b8381f32d00e05d4e5bc0598d14ab80c00ac Mon Sep 17 00:00:00 2001 From: Shawn Xie <35279399+shawnxie999@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:02:33 -0400 Subject: [PATCH] fix `DeliveredAmount` and `delivered_amount` in transaction metadata for direct MPT transfer (#5569) The Payment transaction metadata is missing the `DeliveredAmount` field that displays the actual amount delivered to the destination excluding transfer fees. This amendment fixes this problem. --- include/xrpl/protocol/detail/features.macro | 1 + src/test/rpc/DeliveredAmount_test.cpp | 84 +++++++++++++++++++++ src/xrpld/app/tx/detail/Payment.cpp | 9 +++ 3 files changed, 94 insertions(+) diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index c83dacfa73..e36a466971 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -32,6 +32,7 @@ // If you add an amendment here, then do not forget to increment `numFeatures` // in include/xrpl/protocol/Feature.h. +XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (AMMClawbackRounding, Supported::no, VoteBehavior::DefaultNo) XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo) diff --git a/src/test/rpc/DeliveredAmount_test.cpp b/src/test/rpc/DeliveredAmount_test.cpp index 17763790e8..d084f92f25 100644 --- a/src/test/rpc/DeliveredAmount_test.cpp +++ b/src/test/rpc/DeliveredAmount_test.cpp @@ -21,6 +21,7 @@ #include #include +#include #include namespace ripple { @@ -329,12 +330,95 @@ class DeliveredAmount_test : public beast::unit_test::suite } } + void + testMPTDeliveredAmountRPC(FeatureBitset features) + { + testcase("MPT DeliveredAmount"); + + using namespace jtx; + Account const alice("alice"); + Account const carol("carol"); + Account const bob("bob"); + Env env{*this, features}; + + MPTTester mptAlice( + env, alice, {.holders = {bob, carol}, .close = false}); + + mptAlice.create( + {.transferFee = 25000, + .ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanTransfer}); + auto const MPT = mptAlice["MPT"]; + + mptAlice.authorize({.account = bob}); + mptAlice.authorize({.account = carol}); + + // issuer to holder + mptAlice.pay(alice, bob, 10000); + + // holder to holder + env(pay(bob, carol, mptAlice.mpt(1000)), txflags(tfPartialPayment)); + env.close(); + + // Get the hash for the most recent transaction. + std::string txHash{ + env.tx()->getJson(JsonOptions::none)[jss::hash].asString()}; + Json::Value meta = env.rpc("tx", txHash)[jss::result][jss::meta]; + + if (features[fixMPTDeliveredAmount]) + { + BEAST_EXPECT( + meta[sfDeliveredAmount.jsonName] == + STAmount{MPT(800)}.getJson(JsonOptions::none)); + BEAST_EXPECT( + meta[jss::delivered_amount] == + STAmount{MPT(800)}.getJson(JsonOptions::none)); + } + else + { + BEAST_EXPECT(!meta.isMember(sfDeliveredAmount.jsonName)); + BEAST_EXPECT( + meta[jss::delivered_amount] = Json::Value("unavailable")); + } + + env(pay(bob, carol, MPT(1000)), + sendmax(MPT(1200)), + txflags(tfPartialPayment)); + env.close(); + + txHash = env.tx()->getJson(JsonOptions::none)[jss::hash].asString(); + meta = env.rpc("tx", txHash)[jss::result][jss::meta]; + + if (features[fixMPTDeliveredAmount]) + { + BEAST_EXPECT( + meta[sfDeliveredAmount.jsonName] == + STAmount{MPT(960)}.getJson(JsonOptions::none)); + BEAST_EXPECT( + meta[jss::delivered_amount] == + STAmount{MPT(960)}.getJson(JsonOptions::none)); + } + else + { + BEAST_EXPECT(!meta.isMember(sfDeliveredAmount.jsonName)); + BEAST_EXPECT( + meta[jss::delivered_amount] = Json::Value("unavailable")); + } + } + public: void run() override { + using namespace test::jtx; + FeatureBitset const all{testable_amendments()}; + testTxDeliveredAmountRPC(); testAccountDeliveredAmountSubscribe(); + + testMPTDeliveredAmountRPC(all - fixMPTDeliveredAmount); + testMPTDeliveredAmountRPC(all); } }; diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 692e03109e..386b170ed1 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -580,7 +580,16 @@ Payment::doApply() auto res = accountSend( pv, account_, dstAccountID, amountDeliver, ctx_.journal); if (res == tesSUCCESS) + { pv.apply(ctx_.rawView()); + + // If the actual amount delivered is different from the original + // amount due to partial payment or transfer fee, we need to update + // DelieveredAmount using the actual delivered amount + if (view().rules().enabled(fixMPTDeliveredAmount) && + amountDeliver != dstAmount) + ctx_.deliver(amountDeliver); + } else if (res == tecINSUFFICIENT_FUNDS || res == tecPATH_DRY) res = tecPATH_PARTIAL;