fix: An incorrect available XRP balance check in AMM deposit allows reserve-locked funds to be used as adding liquidity

This commit is contained in:
tequ
2026-04-28 15:57:49 +09:00
parent 381094498d
commit 6ee071c5b0
2 changed files with 56 additions and 8 deletions

View File

@@ -239,6 +239,15 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
auto const sponsorSle = getTxReserveSponsor(ctx.view, ctx.tx);
auto const accountSle = ctx.view.read(keylet::account(accountID));
auto const reserveAdj = (sponsorSle || sle) ? 0 : 1;
if (xrpLiquid(ctx.view, accountID, reserveAdj, ctx.j) < deposit)
{
if (sle)
return tecUNFUNDED_AMM;
return tecINSUF_RESERVE_LINE;
}
if (auto const ret = checkInsufficientReserve(
ctx.view,
ctx.tx,
@@ -247,11 +256,10 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
sponsorSle,
1,
!sle);
isTesSuccess(ret))
return TER(tesSUCCESS);
if (sle)
return tecUNFUNDED_AMM;
return tecINSUF_RESERVE_LINE;
sponsorSle && !isTesSuccess(ret))
return tecINSUF_RESERVE_LINE;
return tesSUCCESS;
}
return accountFunds(
ctx.view,
@@ -556,9 +564,8 @@ AMMDeposit::deposit(
// Adjust the reserve if LP doesn't have LPToken trustline
auto const trustlineExists =
view.exists(keylet::line(account_, lpIssue.account, lpIssue.currency));
auto const ownerCountAdj = trustlineExists ? 0 : 1;
if (xrpLiquid(view, sponsor.value_or(account_), sponsor ? ownerCountAdj : 0, j_) >=
depositAmount)
auto const reserveAdj = (sponsor || trustlineExists) ? 0 : 1;
if (xrpLiquid(view, account_, reserveAdj, j_) >= depositAmount)
return tesSUCCESS;
}
else if (

View File

@@ -2182,6 +2182,47 @@ public:
submit(ammDeposit(env, bob, USD(100), EUR(100)));
});
}
{
// AMMDeposit single-asset XRP: reserve sponsor covers LP trustline reserve
// but depositor's own liquid XRP is insufficient for the deposit → tecUNFUNDED_AMM
Env env{*this, testable_amendments()};
env.fund(XRP(10000), alice, bob, gw, sponsor);
env.close();
env(trust(bob, USD(10000)));
env(trust(alice, USD(10000)));
env.close();
env(pay(gw, bob, USD(1000)));
env.close();
AMM amm(env, bob, XRP(1000), USD(100));
// alice has 1 owner object (USD trust line); give her reserve + 5 XRP liquid
adjustAccountXRPBalance(env, alice, reserve(env, ownerCount(env, alice)) + XRP(5));
auto const jv = AMM::depositJv(
{.account = alice,
.asset1In = XRP(10),
.assets = std::make_pair(Asset{xrpIssue()}, Asset{USD.issue()})});
if (cosigning)
{
env(jv,
sponsor::as(sponsor, spfSponsorReserve),
sig(sfSponsorSignature, sponsor),
ter(tecINSUF_RESERVE_LINE));
}
else
{
env(sponsor::set_reserve(sponsor, 0, 1), sponsor::sponseeAcc(alice));
env.close();
env(jv, sponsor::as(sponsor, spfSponsorReserve), ter(tecINSUF_RESERVE_LINE));
env(sponsor::del(sponsor), sponsor::sponseeAcc(alice));
}
env.close();
BEAST_EXPECT(ownerCount(env, alice) == 1); // no LP token was created
}
{
// AMMWithdraw
{