Compare commits

...

3 Commits

Author SHA1 Message Date
tequ
d972ef1fb8 LCOV 2026-06-12 23:08:00 +09:00
tequ
6268a1cdbf fix build error and chore 2026-06-12 22:53:27 +09:00
tequ
ca2f855afb Fix ammLPHolds logic to include escrowed cases 2026-06-12 21:11:23 +09:00
5 changed files with 117 additions and 2 deletions

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <xrpld/app/tx/applySteps.h>
#include <xrpld/ledger/Dir.h>
#include <xrpl/protocol/Feature.h>
@@ -4316,6 +4317,93 @@ struct Escrow_test : public beast::unit_test::suite
env.close();
}
void
testIOUAMM(FeatureBitset features)
{
testcase("IOU AMM");
using namespace test::jtx;
using namespace std::chrono;
Account alice{"alice"};
Account bob{"bob"};
Account gw{"gw"};
auto const USD = gw["USD"];
// AMMCreate fails - insufficient balance
Env env(*this, features | featureAMM | featureAMMClawback);
env.fund(XRP(10000), alice, bob, gw);
env.close();
env.trust(USD(100000), alice);
env.close();
env(pay(gw, alice, USD(1000)));
env.close();
env(escrow(alice, bob, USD(1000)), finish_time(env.now() + 1s));
env.close();
// AMMCreate fails - insufficient balance
AMM ammFail(env, alice, XRP(1000), USD(1000), ter(tecUNFUNDED_AMM));
env(pay(gw, alice, USD(1000)));
env.close();
AMM ammAlice(env, alice, XRP(1000), USD(1000));
BEAST_EXPECT(ammAlice.ammExists());
// Single Asset Deposit fails - insufficient balance
ammAlice.deposit(
alice,
USD(1000),
std::nullopt,
std::nullopt,
std::nullopt,
ter(tecUNFUNDED_AMM));
// Double Asset Deposit fails - insufficient balance
ammAlice.deposit(
alice,
USD(1000),
XRP(1000),
std::nullopt,
std::nullopt,
ter(tecUNFUNDED_AMM));
auto const lptoken = ammAlice.getLPTokensBalance(alice);
// lock all LP tokens
env(escrow(alice, bob, STAmount{lptoken, ammAlice.lptIssue()}),
finish_time(env.now() + 1s));
env.close();
// Withdraw
ammAlice.withdraw(
alice, USD(1000), std::nullopt, std::nullopt, ter(tecAMM_BALANCE));
ammAlice.withdrawAll(alice, USD(1000), ter(tecAMM_BALANCE));
env(ammAlice.bid(BidArg{
.account = alice,
.bidMax = 100,
.assets = {{USD, XRP}},
}),
ter(tecAMM_INVALID_TOKENS));
ammAlice.vote(
alice,
1'000,
std::nullopt,
std::nullopt,
{{USD, XRP}},
ter(tecAMM_INVALID_TOKENS));
// Cannot escrow clawbackable tokens, so we cannot ammClawback escrowed
// tokens
// env(amm::ammClawback(gw, alice, USD, XRP, USD(100)),
// ter(tecAMM_BALANCE));
// env(amm::ammClawback(gw, alice, USD, XRP, std::nullopt),
// ter(tecAMM_BALANCE));
}
static uint256
getEscrowIndex(AccountID const& account, std::uint32_t uSequence)
{
@@ -4729,6 +4817,7 @@ struct Escrow_test : public beast::unit_test::suite
testIOUTLINSF(features);
testIOUPrecisionLoss(features);
testIOUClawback(features);
testIOUAMM(features);
}
public:

View File

@@ -31,7 +31,7 @@ namespace ripple {
namespace test {
namespace jtx {
static Number
[[maybe_unused]] static Number
number(STAmount const& a)
{
if (isXRP(a))

View File

@@ -148,6 +148,31 @@ ammLPHolds(
// Put balance in account terms.
amount.negate();
}
// If tokens can be escrowed then they can be locked in the trustline
// which means we must never spend them until the escrow is released.
if (view.rules().enabled(featurePaychanAndEscrowForTokens) &&
sle->isFieldPresent(sfLockedBalance))
{
STAmount const lockedBalance = sle->getFieldAmount(sfLockedBalance);
STAmount const spendableBalance = amount -
(lpAccount > ammAccount ? -lockedBalance : lockedBalance);
// RH NOTE: this is defensively programmed, it should never fire
// if something bad does happen the trustline acts as a frozen line.
if (spendableBalance < beast::zero || spendableBalance > amount)
{
// LCOV_EXCL_START
JLOG(j.error())
<< "SpendableBalance has illegal value in accountHolds "
<< spendableBalance;
amount.clear(Issue{currency, ammAccount});
// LCOV_EXCL_STOP
}
else
amount = spendableBalance;
}
amount.setIssuer(ammAccount);
JLOG(j.trace()) << "ammLPHolds:"

View File

@@ -259,7 +259,6 @@ AMMClawback::equalWithdrawMatchingOneAmount(
STAmount const& amount)
{
auto frac = Number{amount} / amountBalance;
auto amount2Withdraw = amount2Balance * frac;
auto const lpTokensWithdraw =
toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);

View File

@@ -375,10 +375,12 @@ accountHolds(
// if something bad does happen the trustline acts as a frozen line.
if (spendableBalance < beast::zero || spendableBalance > amount)
{
// LCOV_EXCL_START
JLOG(j.error())
<< "SpendableBalance has illegal value in accountHolds "
<< spendableBalance;
amount.clear(Issue{currency, issuer});
// LCOV_EXCL_STOP
}
else
amount = spendableBalance;