mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-27 15:28:03 +00:00
fix: AMMClawback exact LP token boundary (#7373)
This commit is contained in:
@@ -324,11 +324,13 @@ AMMClawback::equalWithdrawMatchingOneAmount(
|
||||
auto amount2Withdraw = amount2Balance * frac;
|
||||
|
||||
auto const lpTokensWithdraw = toSTAmount(lptAMMBalance.asset(), lptAMMBalance * frac);
|
||||
if (lpTokensWithdraw > holdLPtokens)
|
||||
auto const& rules = sb.rules();
|
||||
// Pre-fixCleanup3_4_0 only a strictly greater computed LP amount takes
|
||||
// the withdraw-all path. Equality left the last holder unable to be
|
||||
// fully clawed. The amendment treats equality as withdraw-all.
|
||||
if (rules.enabled(fixCleanup3_4_0) ? lpTokensWithdraw >= holdLPtokens
|
||||
: lpTokensWithdraw > holdLPtokens)
|
||||
{
|
||||
// if lptoken balance less than what the issuer intended to clawback,
|
||||
// clawback all the tokens. Because we are doing a two-asset withdrawal,
|
||||
// tfee is actually not used, so pass tfee as 0.
|
||||
return AMMWithdraw::equalWithdrawTokens(
|
||||
sb,
|
||||
ammSle,
|
||||
@@ -348,7 +350,6 @@ AMMClawback::equalWithdrawMatchingOneAmount(
|
||||
ctx_.journal);
|
||||
}
|
||||
|
||||
auto const& rules = sb.rules();
|
||||
if (rules.enabled(fixAMMClawbackRounding))
|
||||
{
|
||||
auto tokensAdj = getRoundedLPTokens(rules, lptAMMBalance, frac, IsDeposit::No);
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/ledger/helpers/AMMHelpers.h>
|
||||
#include <xrpl/protocol/AmountConversions.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFlags.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
@@ -2713,6 +2715,67 @@ class AMMClawback_test : public beast::unit_test::Suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testExactLPTokenEquality(FeatureBitset features)
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
if (!features[fixAMMv1_3] || !features[fixAMMClawbackRounding])
|
||||
return;
|
||||
|
||||
testcase("test exact LP token equality boundary");
|
||||
|
||||
Env env(*this, features);
|
||||
Account const gw{"gateway"}, alice{"alice"}, bob{"bob"};
|
||||
env.fund(XRP(100000), gw, alice, bob);
|
||||
env.close();
|
||||
env(fset(gw, asfAllowTrustLineClawback));
|
||||
env.close();
|
||||
|
||||
auto const usd = gw["USD"];
|
||||
env.trust(usd(100000), alice);
|
||||
env(pay(gw, alice, usd(50000)));
|
||||
env.trust(usd(100000), bob);
|
||||
env(pay(gw, bob, usd(40000)));
|
||||
env.close();
|
||||
|
||||
// bob keeps alice from being the sole LP, otherwise the clawback
|
||||
// first rewrites the AMM's LP balance to alice's tokens and the
|
||||
// boundary is no longer distinguishable.
|
||||
AMM amm(env, alice, XRP(2), usd(1));
|
||||
amm.deposit(alice, IOUAmount{1'876123487565916, -15});
|
||||
amm.deposit(bob, IOUAmount{1'000'000});
|
||||
|
||||
auto const [amountBalance, amount2Balance, lptAMMBalance] = amm.balances(usd, XRP);
|
||||
auto const aliceLP = amm.getLPTokensBalance(alice);
|
||||
auto const holderLPTokens = STAmount{aliceLP, amm.lptIssue()};
|
||||
BEAST_EXPECT(lptAMMBalance > holderLPTokens);
|
||||
|
||||
// Clawing alice's pro-rata share lands the transactor's computed LP
|
||||
// amount exactly on her balance.
|
||||
auto const amount = toSTAmount(usd, Number{amountBalance} * holderLPTokens / lptAMMBalance);
|
||||
BEAST_EXPECT(
|
||||
toSTAmount(lptAMMBalance.asset(), lptAMMBalance * (Number{amount} / amountBalance)) ==
|
||||
holderLPTokens);
|
||||
|
||||
env(amm::ammClawback(gw, alice, usd, XRP, amount));
|
||||
env.close();
|
||||
|
||||
auto const aliceLPAfter = amm.getLPTokensBalance(alice);
|
||||
if (features[fixCleanup3_4_0])
|
||||
{
|
||||
// Equality takes the withdraw-all path, redeeming alice's tokens
|
||||
// exactly.
|
||||
BEAST_EXPECT(aliceLPAfter == IOUAmount(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// The fall-through re-rounds the LP amount against the much
|
||||
// larger pool balance, leaving alice with dust.
|
||||
BEAST_EXPECT(aliceLPAfter != IOUAmount(0) && aliceLPAfter < aliceLP);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -2746,6 +2809,7 @@ class AMMClawback_test : public beast::unit_test::Suite
|
||||
testAssetFrozen(features);
|
||||
testSingleDepositAndClawback(features);
|
||||
testLastHolderLPTokenBalance(features);
|
||||
testExactLPTokenEquality(features);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user