20#include <xrpld/app/tx/detail/AMMWithdraw.h>
22#include <xrpld/app/misc/AMMHelpers.h>
23#include <xrpld/app/misc/AMMUtils.h>
24#include <xrpld/ledger/Sandbox.h>
25#include <xrpl/basics/Number.h>
26#include <xrpl/protocol/AMMCore.h>
27#include <xrpl/protocol/STAccount.h>
28#include <xrpl/protocol/TxFlags.h>
46 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid flags.";
50 auto const amount = ctx.
tx[~sfAmount];
51 auto const amount2 = ctx.
tx[~sfAmount2];
52 auto const ePrice = ctx.
tx[~sfEPrice];
53 auto const lpTokens = ctx.
tx[~sfLPTokenIn];
64 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid flags.";
69 if (!lpTokens || amount || amount2 || ePrice)
74 if (lpTokens || amount || amount2 || ePrice)
79 if (!amount || lpTokens || amount2 || ePrice)
84 if (!amount || lpTokens || amount2 || ePrice)
89 if (!amount || !amount2 || lpTokens || ePrice)
94 if (!amount || !lpTokens || amount2 || ePrice)
99 if (!amount || !ePrice || lpTokens || amount2)
103 auto const asset = ctx.
tx[sfAsset].get<
Issue>();
104 auto const asset2 = ctx.
tx[sfAsset2].get<
Issue>();
107 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: Invalid asset pair.";
111 if (amount && amount2 && amount->issue() == amount2->issue())
113 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens, same issue."
114 << amount->issue() <<
" " << amount2->issue();
118 if (lpTokens && *lpTokens <= beast::zero)
120 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens.";
132 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid Asset1Out";
142 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid Asset2OutAmount";
151 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid EPrice";
173 auto const accountID = ctx.
tx[sfAccount];
179 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: Invalid asset pair.";
183 auto const amount = ctx.
tx[~sfAmount];
184 auto const amount2 = ctx.
tx[~sfAmount2];
194 return expected.error();
195 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
196 if (lptAMMBalance == beast::zero)
198 if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
199 lptAMMBalance < beast::zero)
202 <<
"AMM Withdraw: reserves or tokens balance is zero.";
206 auto const ammAccountID = ammSle->getAccountID(sfAccount);
209 auto const& balance) ->
TER {
212 if (amount > balance)
215 <<
"AMM Withdraw: withdrawing more than the balance, "
223 <<
"AMM Withdraw: account is not authorized, "
231 <<
"AMM Withdraw: AMM account or currency is frozen, "
238 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: account is frozen, "
247 if (
auto const ter = checkAmount(amount, amountBalance))
250 if (
auto const ter = checkAmount(amount2, amount2Balance))
253 auto const lpTokens =
255 auto const lpTokensWithdraw =
258 if (lpTokens <= beast::zero)
260 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: tokens balance is zero.";
264 if (lpTokensWithdraw && lpTokensWithdraw->issue() != lpTokens.issue())
266 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid LPTokens.";
270 if (lpTokensWithdraw && *lpTokensWithdraw > lpTokens)
272 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens.";
276 if (
auto const ePrice = ctx.
tx[~sfEPrice];
277 ePrice && ePrice->issue() != lpTokens.issue())
279 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid EPrice.";
285 if (
auto const ter = checkAmount(amountBalance, amountBalance))
287 if (
auto const ter = checkAmount(amount2Balance, amount2Balance))
297 auto const amount =
ctx_.
tx[~sfAmount];
298 auto const amount2 =
ctx_.
tx[~sfAmount2];
299 auto const ePrice =
ctx_.
tx[~sfEPrice];
307 auto const lpTokens =
309 auto const lpTokensWithdraw =
319 return {res.error(),
false};
320 else if (res.value())
324 ammSle->getFieldAmount(sfLPTokenBalance),
327 ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
347 return {expected.error(),
false};
348 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
352 auto const [result, newLPTokenBalance] =
354 &amountBalance = amountBalance,
355 &amount2Balance = amount2Balance,
412 JLOG(
j_.
error()) <<
"AMM Withdraw: invalid options.";
418 return {result,
false};
429 return {res.first,
false};
433 <<
"AMM Withdraw: tokens " <<
to_string(newLPTokenBalance.iou()) <<
" "
482 return {ter, newLPTokenBalance};
506 amountWithdraw.
issue(),
514 auto const [curBalance, curBalance2, _] = *expected;
518 [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
530 amountWithdraw, amount2Withdraw, lpTokensWithdraw);
533 if (lpTokensWithdrawActual <= beast::zero ||
534 lpTokensWithdrawActual > lpTokens)
536 JLOG(journal.
debug())
537 <<
"AMM Withdraw: failed to withdraw, invalid LP tokens: "
538 << lpTokensWithdrawActual <<
" " << lpTokens <<
" "
539 << lpTokensAMMBalance;
546 lpTokensWithdrawActual > lpTokensAMMBalance)
549 JLOG(journal.
debug())
550 <<
"AMM Withdraw: failed to withdraw, unexpected LP tokens: "
551 << lpTokensWithdrawActual <<
" " << lpTokens <<
" "
552 << lpTokensAMMBalance;
558 if ((amountWithdrawActual == curBalance &&
559 amount2WithdrawActual != curBalance2) ||
560 (amount2WithdrawActual == curBalance2 &&
561 amountWithdrawActual != curBalance))
563 JLOG(journal.
debug())
564 <<
"AMM Withdraw: failed to withdraw one side of the pool "
565 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
566 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
567 << lpTokensAMMBalance;
572 if (lpTokensWithdrawActual == lpTokensAMMBalance &&
573 (amountWithdrawActual != curBalance ||
574 amount2WithdrawActual != curBalance2))
576 JLOG(journal.
debug())
577 <<
"AMM Withdraw: failed to withdraw all tokens "
578 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
579 <<
" curBalance2: " << amount2WithdrawActual.value_or(
STAmount{0})
580 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
581 << lpTokensAMMBalance;
586 if (amountWithdrawActual > curBalance ||
587 amount2WithdrawActual > curBalance2)
589 JLOG(journal.
debug())
590 <<
"AMM Withdraw: withdrawing more than the pool's balance "
591 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
592 <<
" curBalance2: " << curBalance2 <<
" "
593 << (amount2WithdrawActual ? *amount2WithdrawActual :
STAmount{})
594 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
595 << lpTokensAMMBalance;
601 auto sufficientReserve = [&](
Issue const& issue) ->
TER {
602 if (!enabledFixAMMv1_2 ||
isXRP(issue))
609 auto const balance = (*sleAccount)[sfBalance].xrp();
610 std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);
614 (ownerCount < 2) ?
XRPAmount(beast::zero)
617 if (
std::max(priorBalance, balance) < reserve)
623 if (
auto const err = sufficientReserve(amountWithdrawActual.issue()))
631 amountWithdrawActual,
637 JLOG(journal.
debug())
638 <<
"AMM Withdraw: failed to withdraw " << amountWithdrawActual;
644 if (amount2WithdrawActual)
646 if (
auto const err = sufficientReserve(amount2WithdrawActual->issue());
654 *amount2WithdrawActual,
660 JLOG(journal.
debug()) <<
"AMM Withdraw: failed to withdraw "
661 << *amount2WithdrawActual;
671 lpTokensWithdrawActual,
672 lpTokensWithdrawActual.issue(),
677 JLOG(journal.
debug()) <<
"AMM Withdraw: failed to withdraw LPTokens";
684 lpTokensAMMBalance - lpTokensWithdrawActual,
685 amountWithdrawActual,
686 amount2WithdrawActual);
703 std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) =
719 return {ter, newLPTokenBalance};
732 bool updateBalance =
true;
733 if (lpTokenBalance == beast::zero)
744 ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
773 if (lpTokensWithdraw == lptAMMBalance)
792 auto const frac =
divide(lpTokensWithdraw, lptAMMBalance,
noIssue());
793 auto const withdrawAmount =
795 auto const withdraw2Amount =
801 if (withdrawAmount == beast::zero || withdraw2Amount == beast::zero)
823 JLOG(journal.
error())
824 <<
"AMMWithdraw::equalWithdrawTokens exception " << e.
what();
867 auto frac =
Number{amount} / amountBalance;
868 auto const amount2Withdraw = amount2Balance * frac;
869 if (amount2Withdraw <= amount2)
883 frac =
Number{amount2} / amount2Balance;
884 auto const amountWithdraw = amountBalance * frac;
886 amountWithdraw <= amount,
887 "ripple::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw");
915 auto const tokens =
lpTokensOut(amountBalance, amount, lptAMMBalance, tfee);
916 if (tokens == beast::zero)
952 auto const amountWithdraw =
954 if (amount == beast::zero || amountWithdraw >= amount)
1011 Number const ae = amountBalance * ePrice;
1012 auto const f =
getFee(tfee);
1013 auto const tokens = lptAMMBalance * (lptAMMBalance + ae * (f - 2)) /
1014 (lptAMMBalance * f - ae);
1017 auto const amountWithdraw =
toSTAmount(amount.
issue(), tokens / ePrice);
1018 if (amount == beast::zero || amountWithdraw >= amount)
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
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.
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.
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
STAmount lpTokensOut(STAmount const &asset1Balance, STAmount const &asset1Withdraw, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's withdraw amount.
constexpr std::uint32_t tfLimitLPToken
STAmount multiply(STAmount const &amount, Rate const &rate)
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.
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, bool isDeposit)
Calls adjustLPTokens() and adjusts deposit or withdraw amounts if the adjusted LP tokens are less tha...
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.
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)
AccountID ammAccountID(std::uint16_t prefix, uint256 const &parentHash, uint256 const &ammID)
Calculate AMM account ID.
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.
constexpr std::uint32_t tfWithdrawAll
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
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)
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)
TERSubset< CanCvtToTER > TER
STAmount withdrawByTokens(STAmount const &assetBalance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset withdrawal by tokens.
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
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.