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)
 
  201            << 
"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];
 
  303    auto const ammAccountID = (*ammSle)[sfAccount];
 
  307    auto const lpTokens =
 
  309    auto const lpTokensWithdraw =
 
  319            return {res.error(), 
false};
 
  332        return {expected.error(), 
false};
 
  333    auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
 
  337    auto const [result, newLPTokenBalance] =
 
  339         &amountBalance = amountBalance,
 
  340         &amount2Balance = amount2Balance,
 
  397        JLOG(
j_.
error()) << 
"AMM Withdraw: invalid options.";
 
  403        return {result, 
false};
 
  414        return {res.first, 
false};
 
  418        << 
"AMM Withdraw: tokens " << 
to_string(newLPTokenBalance.iou()) << 
" " 
 
  467    return {ter, newLPTokenBalance};
 
 
  491        amountWithdraw.
issue(),
 
  499    auto const [curBalance, curBalance2, _] = *expected;
 
  503        [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
 
  515            amountWithdraw, amount2Withdraw, lpTokensWithdraw);
 
  518    if (lpTokensWithdrawActual <= beast::zero ||
 
  519        lpTokensWithdrawActual > lpTokens)
 
  521        JLOG(journal.
debug())
 
  522            << 
"AMM Withdraw: failed to withdraw, invalid LP tokens: " 
  523            << lpTokensWithdrawActual << 
" " << lpTokens << 
" " 
  524            << lpTokensAMMBalance;
 
  531        lpTokensWithdrawActual > lpTokensAMMBalance)
 
  534        JLOG(journal.
debug())
 
  535            << 
"AMM Withdraw: failed to withdraw, unexpected LP tokens: " 
  536            << lpTokensWithdrawActual << 
" " << lpTokens << 
" " 
  537            << lpTokensAMMBalance;
 
  543    if ((amountWithdrawActual == curBalance &&
 
  544         amount2WithdrawActual != curBalance2) ||
 
  545        (amount2WithdrawActual == curBalance2 &&
 
  546         amountWithdrawActual != curBalance))
 
  548        JLOG(journal.
debug())
 
  549            << 
"AMM Withdraw: failed to withdraw one side of the pool " 
  550            << 
" curBalance: " << curBalance << 
" " << amountWithdrawActual
 
  551            << 
" lpTokensBalance: " << lpTokensWithdraw << 
" lptBalance " 
  552            << lpTokensAMMBalance;
 
  557    if (lpTokensWithdrawActual == lpTokensAMMBalance &&
 
  558        (amountWithdrawActual != curBalance ||
 
  559         amount2WithdrawActual != curBalance2))
 
  561        JLOG(journal.
debug())
 
  562            << 
"AMM Withdraw: failed to withdraw all tokens " 
  563            << 
" curBalance: " << curBalance << 
" " << amountWithdrawActual
 
  564            << 
" curBalance2: " << amount2WithdrawActual.value_or(
STAmount{0})
 
  565            << 
" lpTokensBalance: " << lpTokensWithdraw << 
" lptBalance " 
  566            << lpTokensAMMBalance;
 
  571    if (amountWithdrawActual > curBalance ||
 
  572        amount2WithdrawActual > curBalance2)
 
  574        JLOG(journal.
debug())
 
  575            << 
"AMM Withdraw: withdrawing more than the pool's balance " 
  576            << 
" curBalance: " << curBalance << 
" " << amountWithdrawActual
 
  577            << 
" curBalance2: " << curBalance2 << 
" " 
  578            << (amount2WithdrawActual ? *amount2WithdrawActual : 
STAmount{})
 
  579            << 
" lpTokensBalance: " << lpTokensWithdraw << 
" lptBalance " 
  580            << lpTokensAMMBalance;
 
  586    auto sufficientReserve = [&](
Issue const& issue) -> 
TER {
 
  587        if (!enabledFixAMMv1_2 || 
isXRP(issue))
 
  594            auto const balance = (*sleAccount)[sfBalance].xrp();
 
  595            std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);
 
  599                (ownerCount < 2) ? 
XRPAmount(beast::zero)
 
  602            if (
std::max(priorBalance, balance) < reserve)
 
  608    if (
auto const err = sufficientReserve(amountWithdrawActual.issue()))
 
  616        amountWithdrawActual,
 
  622        JLOG(journal.
debug())
 
  623            << 
"AMM Withdraw: failed to withdraw " << amountWithdrawActual;
 
  629    if (amount2WithdrawActual)
 
  631        if (
auto const err = sufficientReserve(amount2WithdrawActual->issue());
 
  639            *amount2WithdrawActual,
 
  645            JLOG(journal.
debug()) << 
"AMM Withdraw: failed to withdraw " 
  646                                  << *amount2WithdrawActual;
 
  656        lpTokensWithdrawActual,
 
  657        lpTokensWithdrawActual.issue(),
 
  662        JLOG(journal.
debug()) << 
"AMM Withdraw: failed to withdraw LPTokens";
 
  669        lpTokensAMMBalance - lpTokensWithdrawActual,
 
  670        amountWithdrawActual,
 
  671        amount2WithdrawActual);
 
 
  682        return lpTokensWithdraw;
 
 
  702    std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) =
 
  718    return {ter, newLPTokenBalance};
 
 
  731    bool updateBalance = 
true;
 
  732    if (lpTokenBalance == beast::zero)
 
  743        ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
 
 
  772        if (lpTokensWithdraw == lptAMMBalance)
 
  792            view.
rules(), lptAMMBalance, lpTokensWithdraw, withdrawAll);
 
  797        auto const frac = 
divide(tokensAdj, lptAMMBalance, 
noIssue());
 
  798        auto const amountWithdraw =
 
  800        auto const amount2Withdraw =
 
  806        if (amountWithdraw == beast::zero || amount2Withdraw == beast::zero)
 
  828        JLOG(journal.
error())
 
  829            << 
"AMMWithdraw::equalWithdrawTokens exception " << e.
what();
 
 
  872    auto frac = 
Number{amount} / amountBalance;
 
  873    auto amount2Withdraw =
 
  883    if (amount2Withdraw <= amount2)
 
  897    frac = 
Number{amount2} / amount2Balance;
 
  898    auto amountWithdraw =
 
  912            amountWithdraw <= amount,
 
  913            "ripple::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw");
 
  916    else if (amountWithdraw > amount)
 
 
  948        lpTokensIn(amountBalance, amount, lptAMMBalance, tfee),
 
  950    if (tokens == beast::zero)
 
  959        view.
rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
 
 
 1000    auto const amountWithdraw =
 
 1001        ammAssetOut(amountBalance, lptAMMBalance, tokensAdj, tfee);
 
 1002    if (amount == beast::zero || amountWithdraw >= amount)
 
 
 1059    Number const ae = amountBalance * ePrice;
 
 1060    auto const f = 
getFee(tfee);
 
 1061    auto tokNoRoundCb = [&] {
 
 1062        return lptAMMBalance * (lptAMMBalance + ae * (f - 2)) /
 
 1063            (lptAMMBalance * f - ae);
 
 1065    auto tokProdCb = [&] {
 
 1066        return (lptAMMBalance + ae * (f - 2)) / (lptAMMBalance * f - ae);
 
 1070    if (tokensAdj <= beast::zero)
 
 1077    auto amtNoRoundCb = [&] { 
return tokensAdj / ePrice; };
 
 1078    auto amtProdCb = [&] { 
return tokensAdj / ePrice; };
 
 1082    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.