Skip to content

Commit

Permalink
Add fixAMMv1_2 amendment (#5176)
Browse files Browse the repository at this point in the history
* Add reserve check on AMM Withdraw
* Try AMM max offer if changeSpotPriceQuality() fails
  • Loading branch information
gregtatcam authored Nov 5, 2024
1 parent d57cced commit ec61f5e
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 1 deletion.
2 changes: 1 addition & 1 deletion include/xrpl/protocol/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 81;
static constexpr std::size_t numFeatures = 82;

/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/features.macro
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.

XRPL_FIX (AMMv1_2, Supported::yes, VoteBehavior::DefaultNo)
// InvariantsV1_1 will be changes to Supported::yes when all the
// invariants expected to be included under it are complete.
XRPL_FEATURE(MPTokensV1, Supported::yes, VoteBehavior::DefaultNo)
Expand Down
51 changes: 51 additions & 0 deletions src/test/app/AMM_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7064,6 +7064,55 @@ struct AMM_test : public jtx::AMMTest
}
}

void
testFixReserveCheckOnWithdrawal(FeatureBitset features)
{
testcase("Fix Reserve Check On Withdrawal");
using namespace jtx;

auto const err = features[fixAMMv1_2] ? ter(tecINSUFFICIENT_RESERVE)
: ter(tesSUCCESS);

auto test = [&](auto&& cb) {
Env env(*this, features);
auto const starting_xrp =
reserve(env, 2) + env.current()->fees().base * 5;
env.fund(starting_xrp, gw);
env.fund(starting_xrp, alice);
env.trust(USD(2'000), alice);
env.close();
env(pay(gw, alice, USD(2'000)));
env.close();
AMM amm(env, gw, EUR(1'000), USD(1'000));
amm.deposit(alice, USD(1));
cb(amm);
};

// Equal withdraw
test([&](AMM& amm) { amm.withdrawAll(alice, std::nullopt, err); });

// Equal withdraw with a limit
test([&](AMM& amm) {
amm.withdraw(WithdrawArg{
.account = alice,
.asset1Out = EUR(0.1),
.asset2Out = USD(0.1),
.err = err});
amm.withdraw(WithdrawArg{
.account = alice,
.asset1Out = USD(0.1),
.asset2Out = EUR(0.1),
.err = err});
});

// Single withdraw
test([&](AMM& amm) {
amm.withdraw(WithdrawArg{
.account = alice, .asset1Out = EUR(0.1), .err = err});
amm.withdraw(WithdrawArg{.account = alice, .asset1Out = USD(0.1)});
});
}

void
run() override
{
Expand Down Expand Up @@ -7117,6 +7166,8 @@ struct AMM_test : public jtx::AMMTest
testAMMDepositWithFrozenAssets(all);
testAMMDepositWithFrozenAssets(all - featureAMMClawback);
testAMMDepositWithFrozenAssets(all - fixAMMv1_1 - featureAMMClawback);
testFixReserveCheckOnWithdrawal(all);
testFixReserveCheckOnWithdrawal(all - fixAMMv1_2);
}
};

Expand Down
7 changes: 7 additions & 0 deletions src/xrpld/app/paths/detail/AMMLiquidity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,13 @@ AMMLiquidity<TIn, TOut>::getOffer(
return AMMOffer<TIn, TOut>(
*this, *amounts, balances, Quality{*amounts});
}
else if (view.rules().enabled(fixAMMv1_2))
{
if (auto const maxAMMOffer = maxOffer(balances, view.rules());
maxAMMOffer &&
Quality{maxAMMOffer->amount()} > *clobQuality)
return maxAMMOffer;
}
}
catch (std::overflow_error const& e)
{
Expand Down
3 changes: 3 additions & 0 deletions src/xrpld/app/tx/detail/AMMClawback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ AMMClawback::applyGuts(Sandbox& sb)
0,
FreezeHandling::fhIGNORE_FREEZE,
WithdrawAll::Yes,
mPriorBalance,
ctx_.journal);
else
std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
Expand Down Expand Up @@ -267,6 +268,7 @@ AMMClawback::equalWithdrawMatchingOneAmount(
0,
FreezeHandling::fhIGNORE_FREEZE,
WithdrawAll::Yes,
mPriorBalance,
ctx_.journal);

// Because we are doing a two-asset withdrawal,
Expand All @@ -284,6 +286,7 @@ AMMClawback::equalWithdrawMatchingOneAmount(
0,
FreezeHandling::fhIGNORE_FREEZE,
WithdrawAll::No,
mPriorBalance,
ctx_.journal);
}

Expand Down
37 changes: 37 additions & 0 deletions src/xrpld/app/tx/detail/AMMWithdraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ AMMWithdraw::withdraw(
tfee,
FreezeHandling::fhZERO_IF_FROZEN,
isWithdrawAll(ctx_.tx),
mPriorBalance,
j_);
return {ter, newLPTokenBalance};
}
Expand All @@ -490,6 +491,7 @@ AMMWithdraw::withdraw(
std::uint16_t tfee,
FreezeHandling freezeHandling,
WithdrawAll withdrawAll,
XRPAmount const& priorBalance,
beast::Journal const& journal)
{
auto const lpTokens = ammLPHolds(view, ammSle, account, journal);
Expand Down Expand Up @@ -589,6 +591,33 @@ AMMWithdraw::withdraw(
return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
}

// Check the reserve in case a trustline has to be created
bool const enabledFixAMMv1_2 = view.rules().enabled(fixAMMv1_2);
auto sufficientReserve = [&](Issue const& issue) -> TER {
if (!enabledFixAMMv1_2 || isXRP(issue))
return tesSUCCESS;
if (!view.exists(keylet::line(account, issue)))
{
auto const sleAccount = view.read(keylet::account(account));
if (!sleAccount)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const balance = (*sleAccount)[sfBalance].xrp();
std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);

// See also SetTrust::doApply()
XRPAmount const reserve(
(ownerCount < 2) ? XRPAmount(beast::zero)
: view.fees().accountReserve(ownerCount + 1));

if (std::max(priorBalance, balance) < reserve)
return tecINSUFFICIENT_RESERVE;
}
return tesSUCCESS;
};

if (auto const err = sufficientReserve(amountWithdrawActual.issue()))
return {err, STAmount{}, STAmount{}, STAmount{}};

// Withdraw amountWithdraw
auto res = accountSend(
view,
Expand All @@ -609,6 +638,10 @@ AMMWithdraw::withdraw(
// Withdraw amount2Withdraw
if (amount2WithdrawActual)
{
if (auto const err = sufficientReserve(amount2WithdrawActual->issue());
err != tesSUCCESS)
return {err, STAmount{}, STAmount{}, STAmount{}};

res = accountSend(
view,
ammAccount,
Expand Down Expand Up @@ -676,6 +709,7 @@ AMMWithdraw::equalWithdrawTokens(
tfee,
FreezeHandling::fhZERO_IF_FROZEN,
isWithdrawAll(ctx_.tx),
mPriorBalance,
ctx_.journal);
return {ter, newLPTokenBalance};
}
Expand Down Expand Up @@ -725,6 +759,7 @@ AMMWithdraw::equalWithdrawTokens(
std::uint16_t tfee,
FreezeHandling freezeHanding,
WithdrawAll withdrawAll,
XRPAmount const& priorBalance,
beast::Journal const& journal)
{
try
Expand All @@ -745,6 +780,7 @@ AMMWithdraw::equalWithdrawTokens(
tfee,
freezeHanding,
WithdrawAll::Yes,
priorBalance,
journal);
}

Expand Down Expand Up @@ -773,6 +809,7 @@ AMMWithdraw::equalWithdrawTokens(
tfee,
freezeHanding,
withdrawAll,
priorBalance,
journal);
}
// LCOV_EXCL_START
Expand Down
4 changes: 4 additions & 0 deletions src/xrpld/app/tx/detail/AMMWithdraw.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class AMMWithdraw : public Transactor
* @param lpTokensWithdraw amount of tokens to withdraw
* @param tfee trading fee in basis points
* @param withdrawAll if withdrawing all lptokens
* @param priorBalance balance before fees
* @return
*/
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
Expand All @@ -112,6 +113,7 @@ class AMMWithdraw : public Transactor
std::uint16_t tfee,
FreezeHandling freezeHanding,
WithdrawAll withdrawAll,
XRPAmount const& priorBalance,
beast::Journal const& journal);

/** Withdraw requested assets and token from AMM into LP account.
Expand All @@ -127,6 +129,7 @@ class AMMWithdraw : public Transactor
* @param lpTokensWithdraw amount of lptokens to withdraw
* @param tfee trading fee in basis points
* @param withdrawAll if withdraw all lptokens
* @param priorBalance balance before fees
* @return
*/
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
Expand All @@ -143,6 +146,7 @@ class AMMWithdraw : public Transactor
std::uint16_t tfee,
FreezeHandling freezeHandling,
WithdrawAll withdrawAll,
XRPAmount const& priorBalance,
beast::Journal const& journal);

static std::pair<TER, bool>
Expand Down

0 comments on commit ec61f5e

Please sign in to comment.