20#include <xrpld/app/misc/AMMHelpers.h>
21#include <xrpld/app/misc/AMMUtils.h>
22#include <xrpld/app/tx/detail/AMMWithdraw.h>
23#include <xrpld/ledger/Sandbox.h>
25#include <xrpl/basics/Number.h>
26#include <xrpl/protocol/AMMCore.h>
27#include <xrpl/protocol/TxFlags.h>
43 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid flags.";
47 auto const amount = ctx.
tx[~sfAmount];
48 auto const amount2 = ctx.
tx[~sfAmount2];
49 auto const ePrice = ctx.
tx[~sfEPrice];
50 auto const lpTokens = ctx.
tx[~sfLPTokenIn];
61 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid flags.";
66 if (!lpTokens || amount || amount2 || ePrice)
71 if (lpTokens || amount || amount2 || ePrice)
76 if (!amount || lpTokens || amount2 || ePrice)
81 if (!amount || lpTokens || amount2 || ePrice)
86 if (!amount || !amount2 || lpTokens || ePrice)
91 if (!amount || !lpTokens || amount2 || ePrice)
96 if (!amount || !ePrice || lpTokens || amount2)
100 auto const asset = ctx.
tx[sfAsset].get<
Issue>();
101 auto const asset2 = ctx.
tx[sfAsset2].get<
Issue>();
104 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: Invalid asset pair.";
108 if (amount && amount2 && amount->issue() == amount2->issue())
110 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens, same issue."
111 << amount->issue() <<
" " << amount2->issue();
115 if (lpTokens && *lpTokens <= beast::zero)
117 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens.";
129 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid Asset1Out";
139 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid Asset2OutAmount";
148 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid EPrice";
170 auto const accountID = ctx.
tx[sfAccount];
176 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: Invalid asset pair.";
180 auto const amount = ctx.
tx[~sfAmount];
181 auto const amount2 = ctx.
tx[~sfAmount2];
191 return expected.error();
192 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
193 if (lptAMMBalance == beast::zero)
195 if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
196 lptAMMBalance < beast::zero)
199 <<
"AMM Withdraw: reserves or tokens balance is zero.";
203 auto const ammAccountID = ammSle->getAccountID(sfAccount);
206 auto const& balance) ->
TER {
209 if (amount > balance)
212 <<
"AMM Withdraw: withdrawing more than the balance, "
220 <<
"AMM Withdraw: account is not authorized, "
228 <<
"AMM Withdraw: AMM account or currency is frozen, "
235 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: account is frozen, "
244 if (
auto const ter = checkAmount(amount, amountBalance))
247 if (
auto const ter = checkAmount(amount2, amount2Balance))
250 auto const lpTokens =
252 auto const lpTokensWithdraw =
255 if (lpTokens <= beast::zero)
257 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: tokens balance is zero.";
261 if (lpTokensWithdraw && lpTokensWithdraw->issue() != lpTokens.issue())
263 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid LPTokens.";
267 if (lpTokensWithdraw && *lpTokensWithdraw > lpTokens)
269 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens.";
273 if (
auto const ePrice = ctx.
tx[~sfEPrice];
274 ePrice && ePrice->issue() != lpTokens.issue())
276 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid EPrice.";
282 if (
auto const ter = checkAmount(amountBalance, amountBalance))
284 if (
auto const ter = checkAmount(amount2Balance, amount2Balance))
294 auto const amount =
ctx_.
tx[~sfAmount];
295 auto const amount2 =
ctx_.
tx[~sfAmount2];
296 auto const ePrice =
ctx_.
tx[~sfEPrice];
300 auto const ammAccountID = (*ammSle)[sfAccount];
304 auto const lpTokens =
306 auto const lpTokensWithdraw =
316 return {res.error(),
false};
317 else if (res.value())
321 ammSle->getFieldAmount(sfLPTokenBalance),
324 ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
344 return {expected.error(),
false};
345 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
349 auto const [result, newLPTokenBalance] =
351 &amountBalance = amountBalance,
352 &amount2Balance = amount2Balance,
409 JLOG(
j_.
error()) <<
"AMM Withdraw: invalid options.";
415 return {result,
false};
426 return {res.first,
false};
430 <<
"AMM Withdraw: tokens " <<
to_string(newLPTokenBalance.iou()) <<
" "
479 return {ter, newLPTokenBalance};
503 amountWithdraw.
issue(),
511 auto const [curBalance, curBalance2, _] = *expected;
515 [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
527 amountWithdraw, amount2Withdraw, lpTokensWithdraw);
530 if (lpTokensWithdrawActual <= beast::zero ||
531 lpTokensWithdrawActual > lpTokens)
533 JLOG(journal.
debug())
534 <<
"AMM Withdraw: failed to withdraw, invalid LP tokens: "
535 << lpTokensWithdrawActual <<
" " << lpTokens <<
" "
536 << lpTokensAMMBalance;
543 lpTokensWithdrawActual > lpTokensAMMBalance)
546 JLOG(journal.
debug())
547 <<
"AMM Withdraw: failed to withdraw, unexpected LP tokens: "
548 << lpTokensWithdrawActual <<
" " << lpTokens <<
" "
549 << lpTokensAMMBalance;
555 if ((amountWithdrawActual == curBalance &&
556 amount2WithdrawActual != curBalance2) ||
557 (amount2WithdrawActual == curBalance2 &&
558 amountWithdrawActual != curBalance))
560 JLOG(journal.
debug())
561 <<
"AMM Withdraw: failed to withdraw one side of the pool "
562 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
563 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
564 << lpTokensAMMBalance;
569 if (lpTokensWithdrawActual == lpTokensAMMBalance &&
570 (amountWithdrawActual != curBalance ||
571 amount2WithdrawActual != curBalance2))
573 JLOG(journal.
debug())
574 <<
"AMM Withdraw: failed to withdraw all tokens "
575 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
576 <<
" curBalance2: " << amount2WithdrawActual.value_or(
STAmount{0})
577 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
578 << lpTokensAMMBalance;
583 if (amountWithdrawActual > curBalance ||
584 amount2WithdrawActual > curBalance2)
586 JLOG(journal.
debug())
587 <<
"AMM Withdraw: withdrawing more than the pool's balance "
588 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
589 <<
" curBalance2: " << curBalance2 <<
" "
590 << (amount2WithdrawActual ? *amount2WithdrawActual :
STAmount{})
591 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
592 << lpTokensAMMBalance;
598 auto sufficientReserve = [&](
Issue const& issue) ->
TER {
599 if (!enabledFixAMMv1_2 ||
isXRP(issue))
606 auto const balance = (*sleAccount)[sfBalance].xrp();
607 std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);
611 (ownerCount < 2) ?
XRPAmount(beast::zero)
614 if (
std::max(priorBalance, balance) < reserve)
620 if (
auto const err = sufficientReserve(amountWithdrawActual.issue()))
628 amountWithdrawActual,
634 JLOG(journal.
debug())
635 <<
"AMM Withdraw: failed to withdraw " << amountWithdrawActual;
641 if (amount2WithdrawActual)
643 if (
auto const err = sufficientReserve(amount2WithdrawActual->issue());
651 *amount2WithdrawActual,
657 JLOG(journal.
debug()) <<
"AMM Withdraw: failed to withdraw "
658 << *amount2WithdrawActual;
668 lpTokensWithdrawActual,
669 lpTokensWithdrawActual.issue(),
674 JLOG(journal.
debug()) <<
"AMM Withdraw: failed to withdraw LPTokens";
681 lpTokensAMMBalance - lpTokensWithdrawActual,
682 amountWithdrawActual,
683 amount2WithdrawActual);
694 return lpTokensWithdraw;
714 std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) =
730 return {ter, newLPTokenBalance};
743 bool updateBalance =
true;
744 if (lpTokenBalance == beast::zero)
755 ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
784 if (lpTokensWithdraw == lptAMMBalance)
804 view.
rules(), lptAMMBalance, lpTokensWithdraw, withdrawAll);
809 auto const frac =
divide(tokensAdj, lptAMMBalance,
noIssue());
810 auto const amountWithdraw =
812 auto const amount2Withdraw =
818 if (amountWithdraw == beast::zero || amount2Withdraw == beast::zero)
840 JLOG(journal.
error())
841 <<
"AMMWithdraw::equalWithdrawTokens exception " << e.
what();
884 auto frac =
Number{amount} / amountBalance;
885 auto amount2Withdraw =
895 if (amount2Withdraw <= amount2)
909 frac =
Number{amount2} / amount2Balance;
910 auto amountWithdraw =
924 amountWithdraw <= amount,
925 "ripple::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw");
928 else if (amountWithdraw > amount)
960 lpTokensIn(amountBalance, amount, lptAMMBalance, tfee),
962 if (tokens == beast::zero)
971 view.
rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
1012 auto const amountWithdraw =
1013 ammAssetOut(amountBalance, lptAMMBalance, tokensAdj, tfee);
1014 if (amount == beast::zero || amountWithdraw >= amount)
1071 Number const ae = amountBalance * ePrice;
1072 auto const f =
getFee(tfee);
1073 auto tokNoRoundCb = [&] {
1074 return lptAMMBalance * (lptAMMBalance + ae * (f - 2)) /
1075 (lptAMMBalance * f - ae);
1077 auto tokProdCb = [&] {
1078 return (lptAMMBalance + ae * (f - 2)) / (lptAMMBalance * f - ae);
1082 if (tokensAdj <= beast::zero)
1089 auto amtNoRoundCb = [&] {
return tokensAdj / ePrice; };
1090 auto amtProdCb = [&] {
return tokensAdj / ePrice; };
1094 if (amount == beast::zero || amountWithdraw >= amount)
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
static WithdrawAll isWithdrawAll(STTx const &tx)
Check from the flags if it's withdraw all.
std::pair< TER, STAmount > singleWithdrawEPrice(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &lptAMMBalance, STAmount const &amount, STAmount const &ePrice, std::uint16_t tfee)
Withdraw single asset (Asset1Out, EPrice) with two constraints.
std::pair< TER, STAmount > equalWithdrawLimit(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &amount, STAmount const &amount2, std::uint16_t tfee)
Withdraw both assets (Asset1Out, Asset2Out) with the constraints on the maximum amount of each asset ...
static std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > equalWithdrawTokens(Sandbox &view, SLE const &ammSle, AccountID const account, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, STAmount const &lpTokensWithdraw, std::uint16_t tfee, FreezeHandling freezeHanding, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Equal-asset withdrawal (LPTokens) of some AMM instance pools shares represented by the number of LPTo...
std::pair< TER, bool > applyGuts(Sandbox &view)
std::pair< TER, STAmount > singleWithdraw(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &lptAMMBalance, STAmount const &amount, std::uint16_t tfee)
Single asset withdrawal (Asset1Out) equivalent to the amount specified in Asset1Out.
static std::pair< TER, bool > deleteAMMAccountIfEmpty(Sandbox &sb, std::shared_ptr< SLE > const ammSle, STAmount const &lpTokenBalance, Issue const &issue1, Issue const &issue2, beast::Journal const &journal)
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > withdraw(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, AccountID const &account, STAmount const &amountBalance, STAmount const &amountWithdraw, std::optional< STAmount > const &amount2Withdraw, STAmount const &lpTokensAMMBalance, STAmount const &lpTokensWithdraw, std::uint16_t tfee, FreezeHandling freezeHandling, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Withdraw requested assets and token from AMM into LP account.
std::pair< TER, STAmount > singleWithdrawTokens(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &lptAMMBalance, STAmount const &amount, STAmount const &lpTokensWithdraw, std::uint16_t tfee)
Single asset withdrawal (Asset1Out, LPTokens) proportional to the share specified by tokens.
beast::Journal const journal
A currency issued by an account.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual Fees const & fees() const =0
Returns the fees for the base ledger.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual Rules const & rules() const =0
Returns the tx processing rules.
Rules controlling protocol behavior.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Issue const & issue() const
std::uint32_t getFlags() const
Discardable, editable view to a ledger.
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Rules const & rules() const override
Returns the tx processing rules.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
T make_optional(T... args)
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t tfSingleAsset
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
STAmount divide(STAmount const &amount, Rate const &rate)
constexpr std::uint32_t tfOneAssetWithdrawAll
WithdrawAll
AMMWithdraw implements AMM withdraw Transactor.
FreezeHandling
Controls the treatment of frozen account balances.
bool isXRP(AccountID const &c)
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
constexpr std::uint32_t tfWithdrawMask
TER deleteAMMAccount(Sandbox &view, Issue const &asset, Issue const &asset2, beast::Journal j)
Delete trustlines to AMM.
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account)
Check if the account lacks required authorization.
std::pair< STAmount, STAmount > adjustAssetOutByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
constexpr std::uint32_t tfLimitLPToken
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
constexpr std::uint32_t tfOneAssetLPToken
Expected< bool, TER > isOnlyLiquidityProvider(ReadView const &view, Issue const &ammIssue, AccountID const &lpAccount)
Return true if the Liquidity Provider is the only AMM provider, false otherwise.
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
static std::optional< STAmount > tokensWithdraw(STAmount const &lpTokens, std::optional< STAmount > const &tokensIn, std::uint32_t flags)
bool isFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
constexpr std::uint32_t tfTwoAsset
STAmount ammLPHolds(ReadView const &view, Currency const &cur1, Currency const &cur2, AccountID const &ammAccount, AccountID const &lpAccount, beast::Journal const j)
Get the balance of LP tokens.
std::tuple< STAmount, std::optional< STAmount >, STAmount > adjustAmountsByLPTokens(STAmount const &amountBalance, STAmount const &amount, std::optional< STAmount > const &amount2, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee, IsDeposit isDeposit)
Calls adjustLPTokens() and adjusts deposit or withdraw amounts if the adjusted LP tokens are less tha...
constexpr std::uint32_t tfWithdrawAll
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
static STAmount adjustLPTokensIn(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &lpTokensWithdraw, WithdrawAll withdrawAll)
STAmount ammAssetOut(STAmount const &assetBalance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset withdrawal by tokens.
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
@ tecINSUFFICIENT_RESERVE
constexpr std::uint32_t tfLPToken
Number getFee(std::uint16_t tfee)
Convert to the fee from the basis points.
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
bool isTesSuccess(TER x) noexcept
Expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Issue > const &optIssue1, std::optional< Issue > const &optIssue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool and LP token balances.
constexpr std::uint32_t tfWithdrawSubTx
std::string to_string(base_uint< Bits, Tag > const &a)
STAmount lpTokensIn(STAmount const &asset1Balance, STAmount const &asset1Withdraw, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's withdraw amount.
TERSubset< CanCvtToTER > TER
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
Number adjustFracByTokens(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &tokens, Number const &frac)
Find a fraction of tokens after the tokens are adjusted.
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Calls static accountSendIOU if saAmount represents Issue.
TERSubset< CanCvtToNotTEC > NotTEC
XRPAmount accountReserve(std::size_t ownerCount) const
Returns the account reserve given the owner count, in drops.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.