Skip to content

Commit

Permalink
Add IsDeposit enum. Update tests. Cnange swap error allowance back to…
Browse files Browse the repository at this point in the history
… 10e-7.
  • Loading branch information
gregtatcam committed Nov 14, 2024
1 parent e528062 commit 1ea0727
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 142 deletions.
99 changes: 49 additions & 50 deletions src/test/app/AMM_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2138,14 +2138,22 @@ struct AMM_test : public jtx::AMMTest

// Withdraw close to one side of the pool. Account's LP tokens
// are rounded to all LP tokens.
testAMM([&](AMM& ammAlice, Env&) {
ammAlice.withdraw(
alice,
STAmount{USD, UINT64_C(9'999'999999999999), -12},
std::nullopt,
std::nullopt,
ter(tecAMM_BALANCE));
});
testAMM(
[&](AMM& ammAlice, Env& env) {
auto const err = env.enabled(fixAMMv1_3)
? ter(tecINVARIANT_FAILED)
: ter(tecAMM_BALANCE);
ammAlice.withdraw(
alice,
STAmount{USD, UINT64_C(9'999'999999999999), -12},
std::nullopt,
std::nullopt,
err);
},
std::nullopt,
0,
std::nullopt,
{all, all - fixAMMv1_3});

// Tiny withdraw
testAMM([&](AMM& ammAlice, Env&) {
Expand Down Expand Up @@ -3155,30 +3163,15 @@ struct AMM_test : public jtx::AMMTest
}
else
{
if (!features[fixAMMv1_3])
BEAST_EXPECT(
env.balance(carol, USD) ==
STAmount(USD, UINT64_C(29'499'00572620544), -11));
else
BEAST_EXPECT(
env.balance(carol, USD) ==
STAmount(USD, UINT64_C(29'499'00572620543), -11));
if (!features[fixAMMv1_3])
BEAST_EXPECT(
env.balance(bob, USD) ==
STAmount(USD, UINT64_C(18'999'00572616194), -11));
else
BEAST_EXPECT(
env.balance(bob, USD) ==
STAmount(USD, UINT64_C(18'999'00572616191), -11));
if (!features[fixAMMv1_3])
BEAST_EXPECT(
env.balance(ed, USD) ==
STAmount(USD, UINT64_C(18'999'0057261184), -10));
else
BEAST_EXPECT(
env.balance(ed, USD) ==
STAmount(USD, UINT64_C(18'999'00572611839), -11));
BEAST_EXPECT(
env.balance(carol, USD) ==
STAmount(USD, UINT64_C(29'499'00572620544), -11));
BEAST_EXPECT(
env.balance(bob, USD) ==
STAmount(USD, UINT64_C(18'999'00572616194), -11));
BEAST_EXPECT(
env.balance(ed, USD) ==
STAmount(USD, UINT64_C(18'999'0057261184), -10));
// USD pool is slightly higher because of the fees.
if (!features[fixAMMv1_3])
BEAST_EXPECT(ammAlice.expectBalances(
Expand All @@ -3188,7 +3181,7 @@ struct AMM_test : public jtx::AMMTest
else
BEAST_EXPECT(ammAlice.expectBalances(
XRPAmount{13'000'000'003},
STAmount(USD, UINT64_C(13'002'98282151427), -11),
STAmount(USD, UINT64_C(13'002'98282151422), -11),
ammTokens));
}
ammTokens = ammAlice.getLPTokensBalance();
Expand Down Expand Up @@ -3238,7 +3231,7 @@ struct AMM_test : public jtx::AMMTest
else
BEAST_EXPECT(
env.balance(dan, USD) ==
STAmount(USD, UINT64_C(19'490'05672274393), -11));
STAmount(USD, UINT64_C(19'490'05672274398), -11));
// USD pool gains more in dan's fees.
if (!features[fixAMMv1_3])
BEAST_EXPECT(ammAlice.expectBalances(
Expand All @@ -3248,7 +3241,7 @@ struct AMM_test : public jtx::AMMTest
else
BEAST_EXPECT(ammAlice.expectBalances(
XRPAmount{13'000'000'003},
STAmount{USD, UINT64_C(13'012'92609877034), -11},
STAmount{USD, UINT64_C(13'012'92609877024), -11},
ammTokens));
// Discounted fee payment
ammAlice.deposit(carol, USD(100));
Expand All @@ -3261,7 +3254,7 @@ struct AMM_test : public jtx::AMMTest
else
BEAST_EXPECT(ammAlice.expectBalances(
XRPAmount{13'000'000'003},
STAmount{USD, UINT64_C(13'112'92609877034), -11},
STAmount{USD, UINT64_C(13'112'92609877024), -11},
ammTokens));
env(pay(carol, bob, USD(100)),
path(~USD),
Expand All @@ -3277,7 +3270,7 @@ struct AMM_test : public jtx::AMMTest
else
BEAST_EXPECT(ammAlice.expectBalances(
XRPAmount{13'100'000'671},
STAmount{USD, UINT64_C(13'012'92609877034), -11},
STAmount{USD, UINT64_C(13'012'92609877024), -11},
ammTokens));
}
// Payment with the trading fee
Expand All @@ -3304,7 +3297,7 @@ struct AMM_test : public jtx::AMMTest
{
BEAST_EXPECT(ammAlice.expectBalances(
XRPAmount{13'000'000'671},
STAmount{USD, UINT64_C(13'114'03663044947), -11},
STAmount{USD, UINT64_C(13'114'03663044937), -11},
ammTokens));
}
// Auction slot expired, no discounted fee
Expand All @@ -3319,10 +3312,6 @@ struct AMM_test : public jtx::AMMTest
BEAST_EXPECT(
env.balance(carol, USD) ==
STAmount(USD, UINT64_C(29'399'00572620544), -11));
else
BEAST_EXPECT(
env.balance(carol, USD) ==
STAmount(USD, UINT64_C(29'399'00572620543), -11));
ammTokens = ammAlice.getLPTokensBalance();
for (int i = 0; i < 10; ++i)
{
Expand Down Expand Up @@ -3355,10 +3344,10 @@ struct AMM_test : public jtx::AMMTest
{
BEAST_EXPECT(
env.balance(carol, USD) ==
STAmount(USD, UINT64_C(29'389'06197177119), -11));
STAmount(USD, UINT64_C(29'389'06197177129), -11));
BEAST_EXPECT(ammAlice.expectBalances(
XRPAmount{13'000'000'671},
STAmount{USD, UINT64_C(13'123'98038488371), -11},
STAmount{USD, UINT64_C(13'123'98038488352), -11},
ammTokens));
}
env(pay(carol, bob, USD(100)), path(~USD), sendmax(XRP(110)));
Expand All @@ -3384,7 +3373,7 @@ struct AMM_test : public jtx::AMMTest
{
BEAST_EXPECT(ammAlice.expectBalances(
XRPAmount(13'100'824'793),
STAmount{USD, UINT64_C(13'023'98038488371), -11},
STAmount{USD, UINT64_C(13'023'98038488352), -11},
ammTokens));
}
},
Expand Down Expand Up @@ -5394,12 +5383,17 @@ struct AMM_test : public jtx::AMMTest
// Due to round off some accounts have a tiny gain, while
// other have a tiny loss. The last account to withdraw
// gets everything in the pool.
if (!features[fixAMMv1_1] || features[fixAMMv1_3])
if (!features[fixAMMv1_1] && !features[fixAMMv1_3])
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000),
STAmount{USD, UINT64_C(10'000'0000000013), -10},
IOUAmount{10'000'000}));
else if (features[fixAMMv1_1] && !features[fixAMMv1_3])
else if (features[fixAMMv1_3])
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000),
STAmount{USD, UINT64_C(10'000'0000000003), -10},
IOUAmount{10'000'000}));
else
BEAST_EXPECT(ammAlice.expectBalances(
XRP(10'000), USD(10'000), IOUAmount{10'000'000}));
BEAST_EXPECT(expectLine(env, ben, USD(1'500'000)));
Expand Down Expand Up @@ -5431,11 +5425,16 @@ struct AMM_test : public jtx::AMMTest
BEAST_EXPECT(expectLine(env, nataly, USD(1'500'000)));
ammAlice.withdrawAll(alice);
BEAST_EXPECT(!ammAlice.ammExists());
if (!features[fixAMMv1_1] || features[fixAMMv1_3])
if (!features[fixAMMv1_1])
BEAST_EXPECT(expectLine(
env,
alice,
STAmount{USD, UINT64_C(30'000'0000000013), -10}));
else if (features[fixAMMv1_3])
BEAST_EXPECT(expectLine(
env,
alice,
STAmount{USD, UINT64_C(30'000'0000000003), -10}));
else
BEAST_EXPECT(expectLine(env, alice, USD(30'000)));
// alice XRP balance is 30,000initial - 50 ammcreate fee -
Expand Down Expand Up @@ -5523,8 +5522,8 @@ struct AMM_test : public jtx::AMMTest
BEAST_EXPECT(accountBalance(env, chris) == "1999999999790");
BEAST_EXPECT(accountBalance(env, dan) == "1999999999790");
BEAST_EXPECT(accountBalance(env, carol) == "29999999790");
BEAST_EXPECT(accountBalance(env, ed) == "1999999999791");
BEAST_EXPECT(accountBalance(env, paul) == "1999999999794");
BEAST_EXPECT(accountBalance(env, ed) == "1999999999792");
BEAST_EXPECT(accountBalance(env, paul) == "1999999999793");
BEAST_EXPECT(
accountBalance(env, nataly) == "1999999999795");
BEAST_EXPECT(accountBalance(env, alice) == "29950000070");
Expand Down
78 changes: 61 additions & 17 deletions src/xrpld/app/misc/AMMHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ reduceOffer(auto const& amount)

} // namespace detail

enum class IsDeposit : bool { Yes, No };

/** Calculate LP Tokens given AMM pool reserves.
* @param asset1 AMM one side of the pool reserve
* @param asset2 AMM another side of the pool reserve
Expand Down Expand Up @@ -611,13 +613,13 @@ square(Number const& n);
* withdraw to cancel out the precision loss.
* @param lptAMMBalance LPT AMM Balance
* @param lpTokens LP tokens to deposit or withdraw
* @param isDeposit true if deposit, false if withdraw
* @param isDeposit Yes if deposit, No if withdraw
*/
STAmount
adjustLPTokens(
STAmount const& lptAMMBalance,
STAmount const& lpTokens,
bool isDeposit);
IsDeposit isDeposit);

/** Calls adjustLPTokens() and adjusts deposit or withdraw amounts if
* the adjusted LP tokens are less than the provided LP tokens.
Expand All @@ -627,7 +629,7 @@ adjustLPTokens(
* @param lptAMMBalance LPT AMM Balance
* @param lpTokens LP tokens to deposit or withdraw
* @param tfee trading fee in basis points
* @param isDeposit true if deposit, false if withdraw
* @param isDeposit Yes if deposit, No if withdraw
* @return
*/
std::tuple<STAmount, std::optional<STAmount>, STAmount>
Expand All @@ -638,7 +640,7 @@ adjustAmountsByLPTokens(
STAmount const& lptAMMBalance,
STAmount const& lpTokens,
std::uint16_t tfee,
bool isDeposit);
IsDeposit isDeposit);

/** Positive solution for quadratic equation:
* x = (-b + sqrt(b**2 + 4*a*c))/(2*a)
Expand All @@ -652,30 +654,35 @@ multiply(STAmount const& amount, Number const& frac, Number::rounding_mode rm);
namespace detail {

inline Number::rounding_mode
getLPTokenRounding(bool isDeposit)
getLPTokenRounding(IsDeposit isDeposit)
{
// Minimize on deposit, maximize on withdraw to ensure
// AMM invariant sqrt(poolAsset1 * poolAsset2) >= LPTokensBalance
return isDeposit ? Number::downward : Number::upward;
return isDeposit == IsDeposit::Yes ? Number::downward : Number::upward;
}

inline Number::rounding_mode
getAssetRounding(bool isDeposit)
getAssetRounding(IsDeposit isDeposit)
{
// Maximize on deposit, minimize on withdraw to ensure
// AMM invariant sqrt(poolAsset1 * poolAsset2) >= LPTokensBalance
return isDeposit ? Number::upward : Number::downward;
return isDeposit == IsDeposit::Yes ? Number::upward : Number::downward;
}

} // namespace detail

/** Round AMM equal deposit/withdrawal amount. Deposit/withdrawal formulas
* calculate the amount as a fractional value of the pool balance. The rounding
* takes place on the last step of multiplying the balance by the fraction if
* AMMv1_3 is enabled.
*/
template <typename A>
STAmount
getRoundedAsset(
Rules const& rules,
STAmount const& balance,
A const& frac,
bool isDeposit)
IsDeposit isDeposit)
{
if (!rules.enabled(fixAMMv1_3))
{
Expand All @@ -688,31 +695,66 @@ getRoundedAsset(
return multiply(balance, frac, rm);
}

/** Round AMM single deposit/withdrawal amount. In this case
* there is no a shared frac value, which is calculated the same way pre/post
* AMMv1_3. The lambda is used to delay evaluation until the function
* is executed so that the calculation is not done twice. noRoundCb() is
* called if AMMv1_3 is disabled. If productOnly is true then the requested
* rounding is set and the result of productCb() is returned. Otherwise
* the balance is multiplied by productCb() with the requested rounding.
*/
STAmount
getRoundedAsset(
Rules const& rules,
std::function<Number()>&& noRoundCb,
STAmount const& balance,
std::function<Number()>&& productCb,
bool productOnly,
bool isDeposit);

IsDeposit isDeposit);

/** Round AMM deposit/withdrawal LPToken amount. Deposit/withdrawal formulas
* calculate the lptokens as a fractional value of the AMM total lptokens.
* The rounding takes place on the last step of multiplying the balance by
* the fraction if AMMv1_3 is enabled. The tokens are then
* adjusted to factor in the loss in precision (we only keep 16 significant
* digits) when adding the lptokens to the balance.
*/
STAmount
getRoundedLPTokens(
Rules const& rules,
STAmount const& balance,
Number const& frac,
bool isDeposit);

IsDeposit isDeposit);

/** Round AMM single deposit/withdrawal LPToken amount.
* The lambda are used to delay evaluation until the function is executed
* so that the calculations are not done twice.
* noRoundCb() is called if AMMv1_3 is disabled. If productOnly is true then
* the requested rounding is set and the result of productCb() is returned.
* Otherwise the balance is multiplied by productCb() with the requested
* rounding. The tokens are then adjusted to factor in the loss in precision
* (we only keep 16 significant digits) when adding the tokens to the balance.
*/
STAmount
getRoundedLPTokens(
Rules const& rules,
std::function<Number()>&& noRoundCb,
STAmount const& lptAMMBalance,
std::function<Number()>&& productCb,
bool productOnly,
bool isDeposit);

IsDeposit isDeposit);

/* Next two functions adjust asset in/out amount to factor in the adjusted
* lptokens. The lptokens are calculated from the asset in/out. The lptokens are
* then adjusted to factor in the loss in precision. The adjusted lptokens might
* be less than the initially calculated tokens. Therefore, the asset in/out
* must be adjusted. The rounding might result in the adjusted amount being
* greater than the original asset in/out amount. If this happens,
* then the original amount is reduced by the difference in the adjusted amount
* and the original amount. The actual tokens and the actual adjusted amount
* are then recalculated. The minimum of the original and the actual
* adjusted amount is returned.
*/
std::pair<STAmount, STAmount>
adjustAssetInByTokens(
Rules const& rules,
Expand All @@ -721,8 +763,7 @@ adjustAssetInByTokens(
STAmount const& lptAMMBalance,
STAmount const& tokens,
std::uint16_t tfee);

STAmount
std::pair<STAmount, STAmount>
adjustAssetOutByTokens(
Rules const& rules,
STAmount const& balance,
Expand All @@ -731,6 +772,9 @@ adjustAssetOutByTokens(
STAmount const& tokens,
std::uint16_t tfee);

/** Find a fraction of tokens after the tokens are adjusted. The fraction
* is used to adjust equal deposit/withdraw amount.
*/
Number
adjustFracByTokens(
Rules const& rules,
Expand Down
Loading

0 comments on commit 1ea0727

Please sign in to comment.