20 #include <ripple/app/tx/impl/AMMWithdraw.h>
22 #include <ripple/app/misc/AMMHelpers.h>
23 #include <ripple/app/misc/AMMUtils.h>
24 #include <ripple/basics/Number.h>
25 #include <ripple/ledger/Sandbox.h>
26 #include <ripple/ledger/View.h>
27 #include <ripple/protocol/AMMCore.h>
28 #include <ripple/protocol/STAccount.h>
29 #include <ripple/protocol/TxFlags.h>
47 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid flags.";
65 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid flags.";
70 if (!lpTokens || amount || amount2 || ePrice)
75 if (lpTokens || amount || amount2 || ePrice)
80 if (!amount || lpTokens || amount2 || ePrice)
85 if (!amount || lpTokens || amount2 || ePrice)
90 if (!amount || !amount2 || lpTokens || ePrice)
95 if (!amount || !lpTokens || amount2 || ePrice)
100 if (!amount || !ePrice || lpTokens || amount2)
108 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: Invalid asset pair.";
112 if (amount && amount2 && amount->issue() == amount2->issue())
114 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens, same issue."
115 << amount->issue() <<
" " << amount2->issue();
119 if (lpTokens && *lpTokens <= beast::zero)
121 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens.";
133 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid Asset1Out";
143 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid Asset2OutAmount";
152 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid EPrice";
180 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: Invalid asset pair.";
192 FreezeHandling::fhIGNORE_FREEZE,
195 return expected.error();
196 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
197 if (lptAMMBalance == beast::zero)
199 if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
200 lptAMMBalance < beast::zero)
203 <<
"AMM Withdraw: reserves or tokens balance is zero.";
210 auto const& balance) ->
TER {
213 if (amount > balance)
216 <<
"AMM Withdraw: withdrawing more than the balance, "
224 <<
"AMM Withdraw: account is not authorized, "
232 <<
"AMM Withdraw: AMM account or currency is frozen, "
239 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: account is frozen, "
248 if (
auto const ter = checkAmount(amount, amountBalance))
251 if (
auto const ter = checkAmount(amount2, amount2Balance))
254 auto const lpTokens =
256 auto const lpTokensWithdraw =
259 if (lpTokens <= beast::zero)
261 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: tokens balance is zero.";
265 if (lpTokensWithdraw && lpTokensWithdraw->issue() != lpTokens.issue())
267 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid LPTokens.";
271 if (lpTokensWithdraw && *lpTokensWithdraw > lpTokens)
273 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid tokens.";
278 ePrice && ePrice->issue() != lpTokens.issue())
280 JLOG(ctx.
j.
debug()) <<
"AMM Withdraw: invalid EPrice.";
286 if (
auto const ter = checkAmount(amountBalance, amountBalance))
288 if (
auto const ter = checkAmount(amount2Balance, amount2Balance))
308 auto const lpTokens =
310 auto const lpTokensWithdraw =
320 FreezeHandling::fhZERO_IF_FROZEN,
323 return {expected.error(),
false};
324 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
328 auto const [result, newLPTokenBalance] =
330 &amountBalance = amountBalance,
331 &amount2Balance = amount2Balance,
363 sb,
ammAccountID, amountBalance, lptAMMBalance, *amount, tfee);
375 JLOG(
j_.
error()) <<
"AMM Withdraw: invalid options.";
380 return {result,
false};
382 bool updateBalance =
true;
383 if (newLPTokenBalance == beast::zero)
400 <<
"AMM Withdraw: tokens " <<
to_string(newLPTokenBalance.iou()) <<
" "
439 amountWithdraw.
issue(),
441 FreezeHandling::fhZERO_IF_FROZEN,
444 return {expected.error(),
STAmount{}};
445 auto const [curBalance, curBalance2, _] = *expected;
449 [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
461 amountWithdraw, amount2Withdraw, lpTokensWithdraw);
464 if (lpTokensWithdrawActual <= beast::zero ||
465 lpTokensWithdrawActual > lpTokens)
468 <<
"AMM Withdraw: failed to withdraw, invalid LP tokens "
469 <<
" tokens: " << lpTokensWithdrawActual <<
" " << lpTokens <<
" "
470 << lpTokensAMMBalance;
475 if ((amountWithdrawActual == curBalance &&
476 amount2WithdrawActual != curBalance2) ||
477 (amount2WithdrawActual == curBalance2 &&
478 amountWithdrawActual != curBalance))
481 <<
"AMM Withdraw: failed to withdraw one side of the pool "
482 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
483 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
484 << lpTokensAMMBalance;
489 if (lpTokensWithdrawActual == lpTokensAMMBalance &&
490 (amountWithdrawActual != curBalance ||
491 amount2WithdrawActual != curBalance2))
494 <<
"AMM Withdraw: failed to withdraw all tokens "
495 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
496 <<
" curBalance2: " << amount2WithdrawActual.value_or(
STAmount{0})
497 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
498 << lpTokensAMMBalance;
503 if (amountWithdrawActual > curBalance ||
504 amount2WithdrawActual > curBalance2)
507 <<
"AMM Withdraw: withdrawing more than the pool's balance "
508 <<
" curBalance: " << curBalance <<
" " << amountWithdrawActual
509 <<
" curBalance2: " << curBalance2 <<
" "
510 << (amount2WithdrawActual ? *amount2WithdrawActual :
STAmount{})
511 <<
" lpTokensBalance: " << lpTokensWithdraw <<
" lptBalance "
512 << lpTokensAMMBalance;
521 amountWithdrawActual,
527 <<
"AMM Withdraw: failed to withdraw " << amountWithdrawActual;
532 if (amount2WithdrawActual)
538 *amount2WithdrawActual,
544 << *amount2WithdrawActual;
553 lpTokensWithdrawActual,
554 lpTokensWithdrawActual.issue(),
559 <<
"AMM Withdraw: failed to withdraw LPTokens";
563 return {
tesSUCCESS, lpTokensAMMBalance - lpTokensWithdrawActual};
582 if (lpTokensWithdraw == lptAMMBalance)
593 auto const frac =
divide(lpTokensWithdraw, lptAMMBalance,
noIssue());
594 auto const withdrawAmount =
596 auto const withdraw2Amount =
602 if (withdrawAmount == beast::zero || withdraw2Amount == beast::zero)
617 JLOG(
j_.
error()) <<
"AMMWithdraw::equalWithdrawTokens exception "
659 auto frac =
Number{amount} / amountBalance;
660 auto const amount2Withdraw = amount2Balance * frac;
661 if (amount2Withdraw <= amount2)
671 frac =
Number{amount2} / amount2Balance;
672 auto const amountWithdraw = amountBalance * frac;
673 assert(amountWithdraw <= amount);
699 auto const tokens =
lpTokensOut(amountBalance, amount, lptAMMBalance, tfee);
700 if (tokens == beast::zero)
733 auto const amountWithdraw =
735 if (amount == beast::zero || amountWithdraw >= amount)
787 Number const ae = amountBalance * ePrice;
788 auto const f =
getFee(tfee);
789 auto const tokens = lptAMMBalance * (lptAMMBalance + ae * (f - 2)) /
790 (lptAMMBalance * f - ae);
793 auto const amountWithdraw =
toSTAmount(amount.
issue(), tokens / ePrice);
794 if (amount == beast::zero || amountWithdraw >= amount)
STAmount lpTokensOut(STAmount const &asset1Balance, STAmount const &asset1Withdraw, STAmount const &lptAMMBalance, std::uint16_t tfee)
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Stream trace() const
Severity stream access functions.
Issue const & issue() const
const SF_AMOUNT sfAmount2
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.
const SF_AMOUNT sfLPTokenBalance
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Keylet amm(Issue const &issue1, Issue const &issue2) noexcept
AMM entry.
T make_optional(T... args)
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
Number getFee(std::uint16_t tfee)
Convert to the fee from the basis points.
std::pair< TER, bool > applyGuts(Sandbox &view)
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)
const beast::Journal journal
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
AccountID ammAccountID(std::uint16_t prefix, uint256 const &parentHash, uint256 const &ammID)
Calculate AMM account ID.
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account)
Check if the account requires authorization.
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Issue, Issue >> const &pair=std::nullopt, bool validZero=false)
Validate the amount.
STAmount withdrawByTokens(STAmount const &assetBalance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
constexpr std::uint32_t tfOneAssetWithdrawAll
STAmount divide(STAmount const &amount, Rate const &rate)
constexpr std::uint32_t tfWithdrawMask
constexpr std::uint32_t tfWithdrawAll
std::pair< TER, STAmount > singleWithdrawEPrice(Sandbox &view, 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.
static NotTEC preflight(PreflightContext const &ctx)
static std::optional< STAmount > tokensWithdraw(STAmount const &lpTokens, std::optional< STAmount > const &tokensIn, std::uint32_t flags)
Keylet account(AccountID const &id) noexcept
AccountID root.
std::pair< TER, STAmount > equalWithdrawTokens(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, STAmount const &lpTokensWithdraw, std::uint16_t tfee)
Equal-asset withdrawal (LPTokens) of some AMM instance pools shares represented by the number of LPTo...
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
std::pair< TER, STAmount > singleWithdraw(Sandbox &view, 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.
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 tfWithdrawSubTx
Discardable, editable view to a ledger.
TERSubset< CanCvtToTER > TER
std::uint32_t getFlags() const
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue >> const &pair=std::nullopt)
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
constexpr std::uint32_t tfSingleAsset
STAmount multiply(STAmount const &amount, Rate const &rate)
State information when determining if a tx is likely to claim a fee.
std::pair< TER, STAmount > singleWithdrawTokens(Sandbox &view, 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.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t tfLPToken
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
constexpr std::uint32_t tfTwoAsset
TER deleteAMMAccount(Sandbox &view, Issue const &asset, Issue const &asset2, beast::Journal j)
Delete trustlines to AMM.
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::pair< TER, STAmount > withdraw(Sandbox &view, AccountID const &ammAccount, STAmount const &amountWithdraw, STAmount const &amountBalance, std::optional< STAmount > const &amount2Withdraw, STAmount const &lpTokensAMMBalance, STAmount const &lpTokensWithdraw, std::uint16_t tfee)
Withdraw requested assets and token from AMM into LP account.
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
const SF_ACCOUNT sfAccount
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
const SF_AMOUNT sfLPTokenIn
static TER preclaim(PreclaimContext const &ctx)
State information when preflighting a tx.
constexpr std::uint32_t tfOneAssetLPToken
std::pair< TER, STAmount > equalWithdrawLimit(Sandbox &view, 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 ...
bool isFrozen(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer)
TERSubset< CanCvtToNotTEC > NotTEC
constexpr std::uint32_t tfLimitLPToken