diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index 78843991f..8d2365dd8 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -749,6 +749,7 @@ if (tests) src/test/app/Regression_test.cpp src/test/app/Remit_test.cpp src/test/app/SHAMapStore_test.cpp + src/test/app/ServiceFee_test.cpp src/test/app/SetAuth_test.cpp src/test/app/SetRegularKey_test.cpp src/test/app/SetTrust_test.cpp diff --git a/src/ripple/protocol/jss.h b/src/ripple/protocol/jss.h index 963434090..85b4be0ec 100644 --- a/src/ripple/protocol/jss.h +++ b/src/ripple/protocol/jss.h @@ -126,6 +126,7 @@ JSS(UNLReport); // transaction type. JSS(SettleDelay); // in: TransactionSign JSS(SendMax); // in: TransactionSign JSS(Sequence); // in/out: TransactionSign; field. +JSS(ServiceFee); // field. JSS(SetFlag); // field. JSS(SetRegularKey); // transaction type. JSS(SetHook); // transaction type. diff --git a/src/test/app/ServiceFee_test.cpp b/src/test/app/ServiceFee_test.cpp new file mode 100644 index 000000000..44bb05f11 --- /dev/null +++ b/src/test/app/ServiceFee_test.cpp @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include + +#include + +namespace ripple { +namespace test { +struct ServiceFee_test : public beast::unit_test::suite +{ + void + testEnabled(FeatureBitset features) + { + testcase("enabled"); + using namespace jtx; + using namespace std::literals::chrono_literals; + + auto const alice = Account("alice"); + auto const bob = Account("bob"); + auto const carol = Account("carol"); + for (bool const withSFee : {true, false}) + { + auto const amend = + withSFee ? features : features - featureServiceFee; + Env env{*this, amend}; + auto const feeDrops = env.current()->fees().base; + env.fund(XRP(1000), alice, bob, carol); + + auto const preAlice = env.balance(alice); + auto const preBob = env.balance(bob); + auto const preCarol = env.balance(carol); + + env(pay(alice, bob, XRP(10)), + fee(feeDrops), + sfee(XRP(1), carol), + ter(tesSUCCESS)); + env.close(); + + auto const postAlice = withSFee + ? preAlice - feeDrops - XRP(10) - XRP(1) + : preAlice - feeDrops - XRP(10); + auto const postBob = preBob + XRP(10); + auto const postCarol = withSFee ? preCarol + XRP(1) : preCarol; + BEAST_EXPECT(env.balance(alice) == postAlice); + BEAST_EXPECT(env.balance(bob) == postBob); + BEAST_EXPECT(env.balance(carol) == postCarol); + } + } + + void + testWithFeats(FeatureBitset features) + { + // testEnabled(); + } + +public: + void + run() override + { + using namespace test::jtx; + auto const sa = supported_amendments(); + testEnabled(sa); + // testWithFeats(sa - featureServiceFee); + // testWithFeats(sa); + } +}; + +BEAST_DEFINE_TESTSUITE(ServiceFee, app, ripple); +} // namespace test +} // namespace ripple diff --git a/src/test/jtx/fee.h b/src/test/jtx/fee.h index 58813409e..4a7b0aab9 100644 --- a/src/test/jtx/fee.h +++ b/src/test/jtx/fee.h @@ -61,6 +61,25 @@ public: operator()(Env&, JTx& jt) const; }; +/** Set the service fee on a JTx. */ +class sfee +{ +private: + STAmount amount_; + Account dest_; + +public: + explicit sfee( + STAmount const& amount, + Account const& destination) + : amount_(amount), dest_(destination) + { + } + + void + operator()(Env&, JTx& jtx) const; +}; + } // namespace jtx } // namespace test } // namespace ripple diff --git a/src/test/jtx/impl/fee.cpp b/src/test/jtx/impl/fee.cpp index dea666cd3..a877a518c 100644 --- a/src/test/jtx/impl/fee.cpp +++ b/src/test/jtx/impl/fee.cpp @@ -34,6 +34,14 @@ fee::operator()(Env&, JTx& jt) const jt[jss::Fee] = amount_->getJson(JsonOptions::none); } +void +sfee::operator()(Env&, JTx& jt) const +{ + jt.jv[jss::ServiceFee] = Json::objectValue; + jt.jv[jss::ServiceFee][jss::Amount] = amount_.getJson(JsonOptions::none); + jt.jv[jss::ServiceFee][jss::Destination] = dest_.human(); +} + } // namespace jtx } // namespace test } // namespace ripple