20#include <xrpld/app/misc/AMMHelpers.h>
21#include <xrpld/app/misc/AMMUtils.h>
22#include <xrpld/app/tx/detail/AMMWithdraw.h>
24#include <xrpl/basics/Number.h>
25#include <xrpl/ledger/Sandbox.h>
26#include <xrpl/protocol/AMMCore.h>
27#include <xrpl/protocol/TxFlags.h>
48 auto const amount = ctx.
tx[~sfAmount];
49 auto const amount2 = ctx.
tx[~sfAmount2];
50 auto const ePrice = ctx.
tx[~sfEPrice];
51 auto const lpTokens = ctx.
tx[~sfLPTokenIn];
62 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid flags.";
67 if (!lpTokens || amount || amount2 || ePrice)
72 if (lpTokens || amount || amount2 || ePrice)
77 if (!amount || lpTokens || amount2 || ePrice)
82 if (!amount || lpTokens || amount2 || ePrice)
87 if (!amount || !amount2 || lpTokens || ePrice)
92 if (!amount || !lpTokens || amount2 || ePrice)
97 if (!amount || !ePrice || lpTokens || amount2)
101 auto const asset = ctx.
tx[sfAsset].get<
Issue>();
102 auto const asset2 = ctx.
tx[sfAsset2].get<
Issue>();
105 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: Invalid asset pair.";
109 if (amount && amount2 && amount->issue() == amount2->issue())
111 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens, same issue."
112 << amount->issue() <<
" " << amount2->issue();
116 if (lpTokens && *lpTokens <= beast::zero)
118 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens.";
130 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid Asset1Out";
140 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid Asset2OutAmount";
149 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid EPrice";
171 auto const accountID = ctx.
tx[sfAccount];
177 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: Invalid asset pair.";
181 auto const amount = ctx.
tx[~sfAmount];
182 auto const amount2 = ctx.
tx[~sfAmount2];
192 return expected.error();
193 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
194 if (lptAMMBalance == beast::zero)
196 if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
197 lptAMMBalance < beast::zero)
200 <<
"AMM Withdraw: reserves or tokens balance is zero.";
204 auto const ammAccountID = ammSle->getAccountID(sfAccount);
207 auto const& balance) ->
TER {
210 if (amount > balance)
213 <<
"AMM Withdraw: withdrawing more than the balance, "
221 <<
"AMM Withdraw: account is not authorized, "
229 <<
"AMM Withdraw: AMM account or currency is frozen, "
236 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: account is frozen, "
245 if (
auto const ter = checkAmount(amount, amountBalance))
248 if (
auto const ter = checkAmount(amount2, amount2Balance))
251 auto const lpTokens =
253 auto const lpTokensWithdraw =
256 if (lpTokens <= beast::zero)
258 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: tokens balance is zero.";
262 if (lpTokensWithdraw && lpTokensWithdraw->issue() != lpTokens.issue())
264 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid LPTokens.";
268 if (lpTokensWithdraw && *lpTokensWithdraw > lpTokens)
270 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens.";
274 if (
auto const ePrice = ctx.
tx[~sfEPrice];
275 ePrice && ePrice->issue() != lpTokens.issue())
277 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid EPrice.";
283 if (
auto const ter = checkAmount(amountBalance, amountBalance))
285 if (
auto const ter = checkAmount(amount2Balance, amount2Balance))
295 auto const amount =
ctx_.
tx[~sfAmount];
296 auto const amount2 =
ctx_.
tx[~sfAmount2];
297 auto const ePrice =
ctx_.
tx[~sfEPrice];
301 auto const ammAccountID = (*ammSle)[sfAccount];
305 auto const lpTokens =
307 auto const lpTokensWithdraw =
317 return {res.error(),
false};
330 return {expected.error(),
false};
331 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
335 auto const [result, newLPTokenBalance] =
337 &amountBalance = amountBalance,
338 &amount2Balance = amount2Balance,
395 JLOG(
j_.
error()) <<
"AMM Withdraw: invalid options.";
401 return {result,
false};
412 return {res.first,
false};
416 <<
"AMM Withdraw: tokens " <<
to_string(newLPTokenBalance.iou()) <<
" "
465 return {ter, newLPTokenBalance};
489 amountWithdraw.
issue(),
497 auto const [curBalance, curBalance2, _] = *expected;
501 [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
513 amountWithdraw, amount2Withdraw, lpTokensWithdraw);
516 if (lpTokensWithdrawActual <= beast::zero ||
517 lpTokensWithdrawActual > lpTokens)
519 JLOG(journal.
debug())
520 <<
"AMM Withdraw: failed to withdraw, invalid LP tokens: "
521 << lpTokensWithdrawActual <<
" " << lpTokens <<
" "
522 << lpTokensAMMBalance;
529 lpTokensWithdrawActual > lpTokensAMMBalance)
532 JLOG(journal.
debug())
533 <<
"AMM Withdraw: failed to withdraw, unexpected LP tokens: "
534 << lpTokensWithdrawActual <<
" " << lpTokens <<
" "
535 << lpTokensAMMBalance;
541 if ((amountWithdrawActual == curBalance &&
542 amount2WithdrawActual != curBalance2) ||
543 (amount2WithdrawActual == curBalance2 &&
544 amountWithdrawActual != curBalance))
546 JLOG(journal.
debug())
547 <<
"AMM Withdraw: failed to withdraw one side of the pool "
548 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
549 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
550 << lpTokensAMMBalance;
555 if (lpTokensWithdrawActual == lpTokensAMMBalance &&
556 (amountWithdrawActual != curBalance ||
557 amount2WithdrawActual != curBalance2))
559 JLOG(journal.
debug())
560 <<
"AMM Withdraw: failed to withdraw all tokens "
561 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
562 <<
" curBalance2: " << amount2WithdrawActual.value_or(
STAmount{0})
563 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
564 << lpTokensAMMBalance;
569 if (amountWithdrawActual > curBalance ||
570 amount2WithdrawActual > curBalance2)
572 JLOG(journal.
debug())
573 <<
"AMM Withdraw: withdrawing more than the pool's balance "
574 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
575 <<
" curBalance2: " << curBalance2 <<
" "
576 << (amount2WithdrawActual ? *amount2WithdrawActual :
STAmount{})
577 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
578 << lpTokensAMMBalance;
584 auto sufficientReserve = [&](
Issue const& issue) ->
TER {
585 if (!enabledFixAMMv1_2 ||
isXRP(issue))
592 auto const balance = (*sleAccount)[sfBalance].xrp();
593 std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);
597 (ownerCount < 2) ?
XRPAmount(beast::zero)
600 if (
std::max(priorBalance, balance) < reserve)
606 if (
auto const err = sufficientReserve(amountWithdrawActual.issue()))
614 amountWithdrawActual,
620 JLOG(journal.
debug())
621 <<
"AMM Withdraw: failed to withdraw " << amountWithdrawActual;
627 if (amount2WithdrawActual)
629 if (
auto const err = sufficientReserve(amount2WithdrawActual->issue());
637 *amount2WithdrawActual,
643 JLOG(journal.
debug()) <<
"AMM Withdraw: failed to withdraw "
644 << *amount2WithdrawActual;
654 lpTokensWithdrawActual,
655 lpTokensWithdrawActual.issue(),
660 JLOG(journal.
debug()) <<
"AMM Withdraw: failed to withdraw LPTokens";
667 lpTokensAMMBalance - lpTokensWithdrawActual,
668 amountWithdrawActual,
669 amount2WithdrawActual);
680 return lpTokensWithdraw;
700 std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) =
716 return {ter, newLPTokenBalance};
729 bool updateBalance =
true;
730 if (lpTokenBalance == beast::zero)
741 ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
770 if (lpTokensWithdraw == lptAMMBalance)
790 view.
rules(), lptAMMBalance, lpTokensWithdraw, withdrawAll);
795 auto const frac =
divide(tokensAdj, lptAMMBalance,
noIssue());
796 auto const amountWithdraw =
798 auto const amount2Withdraw =
804 if (amountWithdraw == beast::zero || amount2Withdraw == beast::zero)
826 JLOG(journal.
error())
827 <<
"AMMWithdraw::equalWithdrawTokens exception " << e.
what();
870 auto frac =
Number{amount} / amountBalance;
871 auto amount2Withdraw =
881 if (amount2Withdraw <= amount2)
895 frac =
Number{amount2} / amount2Balance;
896 auto amountWithdraw =
910 amountWithdraw <= amount,
911 "ripple::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw");
914 else if (amountWithdraw > amount)
946 lpTokensIn(amountBalance, amount, lptAMMBalance, tfee),
948 if (tokens == beast::zero)
957 view.
rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
998 auto const amountWithdraw =
999 ammAssetOut(amountBalance, lptAMMBalance, tokensAdj, tfee);
1000 if (amount == beast::zero || amountWithdraw >= amount)
1057 Number const ae = amountBalance * ePrice;
1058 auto const f =
getFee(tfee);
1059 auto tokNoRoundCb = [&] {
1060 return lptAMMBalance * (lptAMMBalance + ae * (f - 2)) /
1061 (lptAMMBalance * f - ae);
1063 auto tokProdCb = [&] {
1064 return (lptAMMBalance + ae * (f - 2)) / (lptAMMBalance * f - ae);
1068 if (tokensAdj <= beast::zero)
1075 auto amtNoRoundCb = [&] {
return tokensAdj / ePrice; };
1076 auto amtProdCb = [&] {
return tokensAdj / ePrice; };
1080 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.
static bool checkExtraFeatures(PreflightContext const &ctx)
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.
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
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.
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.
constexpr std::uint32_t tfOneAssetLPToken
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
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)
Expected< bool, TER > verifyAndAdjustLPTokenBalance(Sandbox &sb, STAmount const &lpTokens, std::shared_ptr< SLE > &ammSle, AccountID const &account)
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
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
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)
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.
Number adjustFracByTokens(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &tokens, Number const &frac)
Find a fraction of tokens after the tokens are adjusted.
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.