rippled
AMMWithdraw.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2023 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/tx/impl/AMMWithdraw.h>
21 
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>
30 
31 #include <bit>
32 
33 namespace ripple {
34 
35 NotTEC
37 {
38  if (!ammEnabled(ctx.rules))
39  return temDISABLED;
40 
41  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
42  return ret;
43 
44  auto const flags = ctx.tx.getFlags();
45  if (flags & tfWithdrawMask)
46  {
47  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid flags.";
48  return temINVALID_FLAG;
49  }
50 
51  auto const amount = ctx.tx[~sfAmount];
52  auto const amount2 = ctx.tx[~sfAmount2];
53  auto const ePrice = ctx.tx[~sfEPrice];
54  auto const lpTokens = ctx.tx[~sfLPTokenIn];
55  // Valid combinations are:
56  // LPTokens
57  // tfWithdrawAll
58  // Amount
59  // tfOneAssetWithdrawAll & Amount
60  // Amount and Amount2
61  // Amount and LPTokens
62  // Amount and EPrice
63  if (std::popcount(flags & tfWithdrawSubTx) != 1)
64  {
65  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid flags.";
66  return temMALFORMED;
67  }
68  if (flags & tfLPToken)
69  {
70  if (!lpTokens || amount || amount2 || ePrice)
71  return temMALFORMED;
72  }
73  else if (flags & tfWithdrawAll)
74  {
75  if (lpTokens || amount || amount2 || ePrice)
76  return temMALFORMED;
77  }
78  else if (flags & tfOneAssetWithdrawAll)
79  {
80  if (!amount || lpTokens || amount2 || ePrice)
81  return temMALFORMED;
82  }
83  else if (flags & tfSingleAsset)
84  {
85  if (!amount || lpTokens || amount2 || ePrice)
86  return temMALFORMED;
87  }
88  else if (flags & tfTwoAsset)
89  {
90  if (!amount || !amount2 || lpTokens || ePrice)
91  return temMALFORMED;
92  }
93  else if (flags & tfOneAssetLPToken)
94  {
95  if (!amount || !lpTokens || amount2 || ePrice)
96  return temMALFORMED;
97  }
98  else if (flags & tfLimitLPToken)
99  {
100  if (!amount || !ePrice || lpTokens || amount2)
101  return temMALFORMED;
102  }
103 
104  auto const asset = ctx.tx[sfAsset];
105  auto const asset2 = ctx.tx[sfAsset2];
106  if (auto const res = invalidAMMAssetPair(asset, asset2))
107  {
108  JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
109  return res;
110  }
111 
112  if (amount && amount2 && amount->issue() == amount2->issue())
113  {
114  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens, same issue."
115  << amount->issue() << " " << amount2->issue();
116  return temBAD_AMM_TOKENS;
117  }
118 
119  if (lpTokens && *lpTokens <= beast::zero)
120  {
121  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
122  return temBAD_AMM_TOKENS;
123  }
124 
125  if (amount)
126  {
127  if (auto const res = invalidAMMAmount(
128  *amount,
129  std::make_optional(std::make_pair(asset, asset2)),
131  ePrice))
132  {
133  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset1Out";
134  return res;
135  }
136  }
137 
138  if (amount2)
139  {
140  if (auto const res = invalidAMMAmount(
141  *amount2, std::make_optional(std::make_pair(asset, asset2))))
142  {
143  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset2OutAmount";
144  return res;
145  }
146  }
147 
148  if (ePrice)
149  {
150  if (auto const res = invalidAMMAmount(*ePrice))
151  {
152  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice";
153  return res;
154  }
155  }
156 
157  return preflight2(ctx);
158 }
159 
162  STAmount const& lpTokens,
163  std::optional<STAmount> const& tokensIn,
164  std::uint32_t flags)
165 {
166  if (flags & (tfWithdrawAll | tfOneAssetWithdrawAll))
167  return lpTokens;
168  return tokensIn;
169 }
170 
171 TER
173 {
174  auto const accountID = ctx.tx[sfAccount];
175 
176  auto const ammSle =
177  ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
178  if (!ammSle)
179  {
180  JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
181  return terNO_AMM;
182  }
183 
184  auto const amount = ctx.tx[~sfAmount];
185  auto const amount2 = ctx.tx[~sfAmount2];
186 
187  auto const expected = ammHolds(
188  ctx.view,
189  *ammSle,
190  amount ? amount->issue() : std::optional<Issue>{},
191  amount2 ? amount2->issue() : std::optional<Issue>{},
192  FreezeHandling::fhIGNORE_FREEZE,
193  ctx.j);
194  if (!expected)
195  return expected.error();
196  auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
197  if (lptAMMBalance == beast::zero)
198  return tecAMM_EMPTY;
199  if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
200  lptAMMBalance < beast::zero)
201  {
202  JLOG(ctx.j.debug())
203  << "AMM Withdraw: reserves or tokens balance is zero.";
204  return tecINTERNAL;
205  }
206 
207  auto const ammAccountID = ammSle->getAccountID(sfAccount);
208 
209  auto checkAmount = [&](std::optional<STAmount> const& amount,
210  auto const& balance) -> TER {
211  if (amount)
212  {
213  if (amount > balance)
214  {
215  JLOG(ctx.j.debug())
216  << "AMM Withdraw: withdrawing more than the balance, "
217  << *amount;
218  return tecAMM_BALANCE;
219  }
220  if (auto const ter =
221  requireAuth(ctx.view, amount->issue(), accountID))
222  {
223  JLOG(ctx.j.debug())
224  << "AMM Withdraw: account is not authorized, "
225  << amount->issue();
226  return ter;
227  }
228  // AMM account or currency frozen
229  if (isFrozen(ctx.view, ammAccountID, amount->issue()))
230  {
231  JLOG(ctx.j.debug())
232  << "AMM Withdraw: AMM account or currency is frozen, "
233  << to_string(accountID);
234  return tecFROZEN;
235  }
236  // Account frozen
237  if (isIndividualFrozen(ctx.view, accountID, amount->issue()))
238  {
239  JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen, "
240  << to_string(accountID) << " "
241  << to_string(amount->issue().currency);
242  return tecFROZEN;
243  }
244  }
245  return tesSUCCESS;
246  };
247 
248  if (auto const ter = checkAmount(amount, amountBalance))
249  return ter;
250 
251  if (auto const ter = checkAmount(amount2, amount2Balance))
252  return ter;
253 
254  auto const lpTokens =
255  ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
256  auto const lpTokensWithdraw =
257  tokensWithdraw(lpTokens, ctx.tx[~sfLPTokenIn], ctx.tx.getFlags());
258 
259  if (lpTokens <= beast::zero)
260  {
261  JLOG(ctx.j.debug()) << "AMM Withdraw: tokens balance is zero.";
262  return tecAMM_BALANCE;
263  }
264 
265  if (lpTokensWithdraw && lpTokensWithdraw->issue() != lpTokens.issue())
266  {
267  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid LPTokens.";
268  return temBAD_AMM_TOKENS;
269  }
270 
271  if (lpTokensWithdraw && *lpTokensWithdraw > lpTokens)
272  {
273  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
274  return tecAMM_INVALID_TOKENS;
275  }
276 
277  if (auto const ePrice = ctx.tx[~sfEPrice];
278  ePrice && ePrice->issue() != lpTokens.issue())
279  {
280  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice.";
281  return temBAD_AMM_TOKENS;
282  }
283 
284  if (ctx.tx.getFlags() & (tfLPToken | tfWithdrawAll))
285  {
286  if (auto const ter = checkAmount(amountBalance, amountBalance))
287  return ter;
288  if (auto const ter = checkAmount(amount2Balance, amount2Balance))
289  return ter;
290  }
291 
292  return tesSUCCESS;
293 }
294 
297 {
298  auto const amount = ctx_.tx[~sfAmount];
299  auto const amount2 = ctx_.tx[~sfAmount2];
300  auto const ePrice = ctx_.tx[~sfEPrice];
301  auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
302  if (!ammSle)
303  return {tecINTERNAL, false};
304  auto const ammAccountID = (*ammSle)[sfAccount];
305  auto const accountSle = sb.read(keylet::account(ammAccountID));
306  if (!accountSle)
307  return {tecINTERNAL, false};
308  auto const lpTokens =
309  ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
310  auto const lpTokensWithdraw =
311  tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags());
312 
313  auto const tfee = getTradingFee(ctx_.view(), *ammSle, account_);
314 
315  auto const expected = ammHolds(
316  sb,
317  *ammSle,
318  amount ? amount->issue() : std::optional<Issue>{},
319  amount2 ? amount2->issue() : std::optional<Issue>{},
320  FreezeHandling::fhZERO_IF_FROZEN,
321  ctx_.journal);
322  if (!expected)
323  return {expected.error(), false};
324  auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
325 
326  auto const subTxType = ctx_.tx.getFlags() & tfWithdrawSubTx;
327 
328  auto const [result, newLPTokenBalance] =
329  [&,
330  &amountBalance = amountBalance,
331  &amount2Balance = amount2Balance,
332  &lptAMMBalance = lptAMMBalance]() -> std::pair<TER, STAmount> {
333  if (subTxType & tfTwoAsset)
334  return equalWithdrawLimit(
335  sb,
336  ammAccountID,
337  amountBalance,
338  amount2Balance,
339  lptAMMBalance,
340  *amount,
341  *amount2,
342  tfee);
343  if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll)
344  return singleWithdrawTokens(
345  sb,
346  ammAccountID,
347  amountBalance,
348  lptAMMBalance,
349  *amount,
350  *lpTokensWithdraw,
351  tfee);
352  if (subTxType & tfLimitLPToken)
353  return singleWithdrawEPrice(
354  sb,
355  ammAccountID,
356  amountBalance,
357  lptAMMBalance,
358  *amount,
359  *ePrice,
360  tfee);
361  if (subTxType & tfSingleAsset)
362  return singleWithdraw(
363  sb, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee);
364  if (subTxType & tfLPToken || subTxType & tfWithdrawAll)
365  return equalWithdrawTokens(
366  sb,
367  ammAccountID,
368  amountBalance,
369  amount2Balance,
370  lptAMMBalance,
371  lpTokens,
372  *lpTokensWithdraw,
373  tfee);
374  // should not happen.
375  JLOG(j_.error()) << "AMM Withdraw: invalid options.";
377  }();
378 
379  if (result != tesSUCCESS)
380  return {result, false};
381 
382  bool updateBalance = true;
383  if (newLPTokenBalance == beast::zero)
384  {
385  if (auto const ter =
387  ter != tesSUCCESS && ter != tecINCOMPLETE)
388  return {ter, false};
389  else
390  updateBalance = (ter == tecINCOMPLETE);
391  }
392 
393  if (updateBalance)
394  {
395  ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
396  sb.update(ammSle);
397  }
398 
399  JLOG(ctx_.journal.trace())
400  << "AMM Withdraw: tokens " << to_string(newLPTokenBalance.iou()) << " "
401  << to_string(lpTokens.iou()) << " " << to_string(lptAMMBalance.iou());
402 
403  return {tesSUCCESS, true};
404 }
405 
406 TER
408 {
409  // This is the ledger view that we work against. Transactions are applied
410  // as we go on processing transactions.
411  Sandbox sb(&ctx_.view());
412 
413  auto const result = applyGuts(sb);
414  if (result.second)
415  sb.apply(ctx_.rawView());
416 
417  return result.first;
418 }
419 
422  Sandbox& view,
423  AccountID const& ammAccount,
424  STAmount const& amountBalance,
425  STAmount const& amountWithdraw,
426  std::optional<STAmount> const& amount2Withdraw,
427  STAmount const& lpTokensAMMBalance,
428  STAmount const& lpTokensWithdraw,
429  std::uint16_t tfee)
430 {
431  auto const ammSle =
433  if (!ammSle)
434  return {tecINTERNAL, STAmount{}};
435  auto const lpTokens = ammLPHolds(view, *ammSle, account_, ctx_.journal);
436  auto const expected = ammHolds(
437  view,
438  *ammSle,
439  amountWithdraw.issue(),
440  std::nullopt,
441  FreezeHandling::fhZERO_IF_FROZEN,
442  j_);
443  if (!expected)
444  return {expected.error(), STAmount{}};
445  auto const [curBalance, curBalance2, _] = *expected;
446  (void)_;
447 
448  auto const
449  [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
453  amountBalance,
454  amountWithdraw,
455  amount2Withdraw,
456  lpTokensAMMBalance,
457  lpTokensWithdraw,
458  tfee,
459  false);
460  return std::make_tuple(
461  amountWithdraw, amount2Withdraw, lpTokensWithdraw);
462  }();
463 
464  if (lpTokensWithdrawActual <= beast::zero ||
465  lpTokensWithdrawActual > lpTokens)
466  {
467  JLOG(ctx_.journal.debug())
468  << "AMM Withdraw: failed to withdraw, invalid LP tokens "
469  << " tokens: " << lpTokensWithdrawActual << " " << lpTokens << " "
470  << lpTokensAMMBalance;
471  return {tecAMM_INVALID_TOKENS, STAmount{}};
472  }
473 
474  // Withdrawing one side of the pool
475  if ((amountWithdrawActual == curBalance &&
476  amount2WithdrawActual != curBalance2) ||
477  (amount2WithdrawActual == curBalance2 &&
478  amountWithdrawActual != curBalance))
479  {
480  JLOG(ctx_.journal.debug())
481  << "AMM Withdraw: failed to withdraw one side of the pool "
482  << " curBalance: " << curBalance << " " << amountWithdrawActual
483  << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
484  << lpTokensAMMBalance;
485  return {tecAMM_BALANCE, STAmount{}};
486  }
487 
488  // May happen if withdrawing an amount close to one side of the pool
489  if (lpTokensWithdrawActual == lpTokensAMMBalance &&
490  (amountWithdrawActual != curBalance ||
491  amount2WithdrawActual != curBalance2))
492  {
493  JLOG(ctx_.journal.debug())
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;
499  return {tecAMM_BALANCE, STAmount{}};
500  }
501 
502  // Withdrawing more than the pool's balance
503  if (amountWithdrawActual > curBalance ||
504  amount2WithdrawActual > curBalance2)
505  {
506  JLOG(ctx_.journal.debug())
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;
513  return {tecAMM_BALANCE, STAmount{}};
514  }
515 
516  // Withdraw amountWithdraw
517  auto res = accountSend(
518  view,
519  ammAccount,
520  account_,
521  amountWithdrawActual,
522  ctx_.journal,
524  if (res != tesSUCCESS)
525  {
526  JLOG(ctx_.journal.debug())
527  << "AMM Withdraw: failed to withdraw " << amountWithdrawActual;
528  return {res, STAmount{}};
529  }
530 
531  // Withdraw amount2Withdraw
532  if (amount2WithdrawActual)
533  {
534  res = accountSend(
535  view,
536  ammAccount,
537  account_,
538  *amount2WithdrawActual,
539  ctx_.journal,
541  if (res != tesSUCCESS)
542  {
543  JLOG(ctx_.journal.debug()) << "AMM Withdraw: failed to withdraw "
544  << *amount2WithdrawActual;
545  return {res, STAmount{}};
546  }
547  }
548 
549  // Withdraw LP tokens
550  res = redeemIOU(
551  view,
552  account_,
553  lpTokensWithdrawActual,
554  lpTokensWithdrawActual.issue(),
555  ctx_.journal);
556  if (res != tesSUCCESS)
557  {
558  JLOG(ctx_.journal.debug())
559  << "AMM Withdraw: failed to withdraw LPTokens";
560  return {res, STAmount{}};
561  }
562 
563  return {tesSUCCESS, lpTokensAMMBalance - lpTokensWithdrawActual};
564 }
565 
570  Sandbox& view,
571  AccountID const& ammAccount,
572  STAmount const& amountBalance,
573  STAmount const& amount2Balance,
574  STAmount const& lptAMMBalance,
575  STAmount const& lpTokens,
576  STAmount const& lpTokensWithdraw,
577  std::uint16_t tfee)
578 {
579  try
580  {
581  // Withdrawing all tokens in the pool
582  if (lpTokensWithdraw == lptAMMBalance)
583  return withdraw(
584  view,
585  ammAccount,
586  amountBalance,
587  amountBalance,
588  amount2Balance,
589  lptAMMBalance,
590  lpTokensWithdraw,
591  tfee);
592 
593  auto const frac = divide(lpTokensWithdraw, lptAMMBalance, noIssue());
594  auto const withdrawAmount =
595  multiply(amountBalance, frac, amountBalance.issue());
596  auto const withdraw2Amount =
597  multiply(amount2Balance, frac, amount2Balance.issue());
598  // LP is making equal withdrawal by tokens but the requested amount
599  // of LP tokens is likely too small and results in one-sided pool
600  // withdrawal due to round off. Fail so the user withdraws
601  // more tokens.
602  if (withdrawAmount == beast::zero || withdraw2Amount == beast::zero)
603  return {tecAMM_FAILED, STAmount{}};
604 
605  return withdraw(
606  view,
607  ammAccount,
608  amountBalance,
609  withdrawAmount,
610  withdraw2Amount,
611  lptAMMBalance,
612  lpTokensWithdraw,
613  tfee);
614  }
615  catch (std::exception const& e)
616  {
617  JLOG(j_.error()) << "AMMWithdraw::equalWithdrawTokens exception "
618  << e.what();
619  }
620  return {tecINTERNAL, STAmount{}};
621 }
622 
650  Sandbox& view,
651  AccountID const& ammAccount,
652  STAmount const& amountBalance,
653  STAmount const& amount2Balance,
654  STAmount const& lptAMMBalance,
655  STAmount const& amount,
656  STAmount const& amount2,
657  std::uint16_t tfee)
658 {
659  auto frac = Number{amount} / amountBalance;
660  auto const amount2Withdraw = amount2Balance * frac;
661  if (amount2Withdraw <= amount2)
662  return withdraw(
663  view,
664  ammAccount,
665  amountBalance,
666  amount,
667  toSTAmount(amount2.issue(), amount2Withdraw),
668  lptAMMBalance,
669  toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
670  tfee);
671  frac = Number{amount2} / amount2Balance;
672  auto const amountWithdraw = amountBalance * frac;
673  assert(amountWithdraw <= amount);
674  return withdraw(
675  view,
676  ammAccount,
677  amountBalance,
678  toSTAmount(amount.issue(), amountWithdraw),
679  amount2,
680  lptAMMBalance,
681  toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
682  tfee);
683 }
684 
692  Sandbox& view,
693  AccountID const& ammAccount,
694  STAmount const& amountBalance,
695  STAmount const& lptAMMBalance,
696  STAmount const& amount,
697  std::uint16_t tfee)
698 {
699  auto const tokens = lpTokensOut(amountBalance, amount, lptAMMBalance, tfee);
700  if (tokens == beast::zero)
701  return {tecAMM_FAILED, STAmount{}};
702  return withdraw(
703  view,
704  ammAccount,
705  amountBalance,
706  amount,
707  std::nullopt,
708  lptAMMBalance,
709  tokens,
710  tfee);
711 }
712 
725  Sandbox& view,
726  AccountID const& ammAccount,
727  STAmount const& amountBalance,
728  STAmount const& lptAMMBalance,
729  STAmount const& amount,
730  STAmount const& lpTokensWithdraw,
731  std::uint16_t tfee)
732 {
733  auto const amountWithdraw =
734  withdrawByTokens(amountBalance, lptAMMBalance, lpTokensWithdraw, tfee);
735  if (amount == beast::zero || amountWithdraw >= amount)
736  return withdraw(
737  view,
738  ammAccount,
739  amountBalance,
740  amountWithdraw,
741  std::nullopt,
742  lptAMMBalance,
743  lpTokensWithdraw,
744  tfee);
745  return {tecAMM_FAILED, STAmount{}};
746 }
747 
769  Sandbox& view,
770  AccountID const& ammAccount,
771  STAmount const& amountBalance,
772  STAmount const& lptAMMBalance,
773  STAmount const& amount,
774  STAmount const& ePrice,
775  std::uint16_t tfee)
776 {
777  // LPTokens is asset in => E = t / a and formula (8) is:
778  // a = A*(t1**2 + t1*(f - 2))/(t1*f - 1)
779  // substitute a as t/E =>
780  // t/E = A*(t1**2 + t1*(f - 2))/(t1*f - 1), t1=t/T => t = t1*T
781  // t1*T/E = A*((t/T)**2 + t*(f - 2)/T)/(t*f/T - 1) =>
782  // T/E = A*(t1 + f-2)/(t1*f - 1) =>
783  // T*(t1*f - 1) = A*E*(t1 + f - 2) =>
784  // t1*T*f - T = t1*A*E + A*E*(f - 2) =>
785  // t1*(T*f - A*E) = T + A*E*(f - 2) =>
786  // t = T*(T + A*E*(f - 2))/(T*f - A*E)
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);
791  if (tokens <= 0)
792  return {tecAMM_FAILED, STAmount{}};
793  auto const amountWithdraw = toSTAmount(amount.issue(), tokens / ePrice);
794  if (amount == beast::zero || amountWithdraw >= amount)
795  return withdraw(
796  view,
797  ammAccount,
798  amountBalance,
799  amountWithdraw,
800  std::nullopt,
801  lptAMMBalance,
802  toSTAmount(lptAMMBalance.issue(), tokens),
803  tfee);
804 
805  return {tecAMM_FAILED, STAmount{}};
806 }
807 
808 } // namespace ripple
ripple::tecFROZEN
@ tecFROZEN
Definition: TER.h:283
ripple::lpTokensOut
STAmount lpTokensOut(STAmount const &asset1Balance, STAmount const &asset1Withdraw, STAmount const &lptAMMBalance, std::uint16_t tfee)
Definition: AMMHelpers.cpp:90
ripple::preflight2
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:133
std::make_tuple
T make_tuple(T... args)
ripple::sfAsset
const SF_ISSUE sfAsset
ripple::PreclaimContext::view
ReadView const & view
Definition: Transactor.h:56
std::exception
STL class.
ripple::accountSend
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Definition: View.cpp:1142
ripple::PreclaimContext::j
const beast::Journal j
Definition: Transactor.h:60
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:308
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:350
ripple::sfAmount2
const SF_AMOUNT sfAmount2
ripple::sfAmount
const SF_AMOUNT sfAmount
ripple::Transactor::j_
const beast::Journal j_
Definition: Transactor.h:89
ripple::isTesSuccess
bool isTesSuccess(TER x)
Definition: TER.h:636
ripple::WaiveTransferFee::Yes
@ Yes
ripple::Sandbox::apply
void apply(RawView &to)
Definition: Sandbox.h:55
ripple::ammHolds
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.
Definition: AMMUtils.cpp:45
std::pair
ripple::sfLPTokenBalance
const SF_AMOUNT sfLPTokenBalance
ripple::ammEnabled
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition: AMMCore.cpp:126
ripple::keylet::amm
Keylet amm(Issue const &issue1, Issue const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:383
ripple::sfEPrice
const SF_AMOUNT sfEPrice
std::make_optional
T make_optional(T... args)
ripple::AMMWithdraw::doApply
TER doApply() override
Definition: AMMWithdraw.cpp:407
ripple::detail::ApplyViewBase::update
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
Definition: ApplyViewBase.cpp:146
ripple::getFee
Number getFee(std::uint16_t tfee)
Convert to the fee from the basis points.
Definition: AMMCore.h:109
ripple::tecAMM_EMPTY
@ tecAMM_EMPTY
Definition: TER.h:312
ripple::AMMWithdraw::applyGuts
std::pair< TER, bool > applyGuts(Sandbox &view)
Definition: AMMWithdraw.cpp:296
ripple::adjustAmountsByLPTokens
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)
Definition: AMMHelpers.cpp:147
ripple::ApplyContext::journal
const beast::Journal journal
Definition: ApplyContext.h:51
ripple::noIssue
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition: Issue.h:113
std::tuple
ripple::ApplyContext::rawView
RawView & rawView()
Definition: ApplyContext.h:67
ripple::PreflightContext::j
const beast::Journal j
Definition: Transactor.h:38
ripple::tecINCOMPLETE
@ tecINCOMPLETE
Definition: TER.h:315
ripple::ammAccountID
AccountID ammAccountID(std::uint16_t prefix, uint256 const &parentHash, uint256 const &ammID)
Calculate AMM account ID.
Definition: AMMCore.cpp:30
ripple::preflight1
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:81
ripple::tecAMM_FAILED
@ tecAMM_FAILED
Definition: TER.h:310
ripple::requireAuth
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account)
Check if the account requires authorization.
Definition: View.cpp:1511
ripple::invalidAMMAmount
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Issue, Issue >> const &pair=std::nullopt, bool validZero=false)
Validate the amount.
Definition: AMMCore.cpp:94
ripple::withdrawByTokens
STAmount withdrawByTokens(STAmount const &assetBalance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Definition: AMMHelpers.cpp:114
ripple::tfOneAssetWithdrawAll
constexpr std::uint32_t tfOneAssetWithdrawAll
Definition: TxFlags.h:169
ripple::divide
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:86
ripple::base_uint< 160, detail::AccountIDTag >
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:110
ripple::tfWithdrawMask
constexpr std::uint32_t tfWithdrawMask
Definition: TxFlags.h:181
ripple::tfWithdrawAll
constexpr std::uint32_t tfWithdrawAll
Definition: TxFlags.h:168
ripple::temBAD_AMM_TOKENS
@ temBAD_AMM_TOKENS
Definition: TER.h:128
ripple::AMMWithdraw::singleWithdrawEPrice
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.
Definition: AMMWithdraw.cpp:768
ripple::sfAsset2
const SF_ISSUE sfAsset2
ripple::Number
Definition: Number.h:36
ripple::AMMWithdraw::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: AMMWithdraw.cpp:36
ripple::tokensWithdraw
static std::optional< STAmount > tokensWithdraw(STAmount const &lpTokens, std::optional< STAmount > const &tokensIn, std::uint32_t flags)
Definition: AMMWithdraw.cpp:161
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:142
ripple::AMMWithdraw::equalWithdrawTokens
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...
Definition: AMMWithdraw.cpp:569
ripple::toSTAmount
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
Definition: AmountConversions.h:30
ripple::tecAMM_INVALID_TOKENS
@ tecAMM_INVALID_TOKENS
Definition: TER.h:311
ripple::TERSubset< CanCvtToTER >
ripple::AMMWithdraw::singleWithdraw
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.
Definition: AMMWithdraw.cpp:691
ripple::getTradingFee
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
Definition: AMMUtils.cpp:138
ripple::tfWithdrawSubTx
constexpr std::uint32_t tfWithdrawSubTx
Definition: TxFlags.h:175
ripple::Sandbox
Discardable, editable view to a ledger.
Definition: Sandbox.h:34
ripple::tecAMM_BALANCE
@ tecAMM_BALANCE
Definition: TER.h:309
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:607
ripple::STAmount
Definition: STAmount.h:46
beast::Journal::error
Stream error() const
Definition: Journal.h:332
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:290
ripple::STObject::getFlags
std::uint32_t getFlags() const
Definition: STObject.cpp:481
std::uint32_t
ripple::invalidAMMAssetPair
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue >> const &pair=std::nullopt)
Definition: AMMCore.cpp:79
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::tfSingleAsset
constexpr std::uint32_t tfSingleAsset
Definition: TxFlags.h:170
ripple::ApplyContext::view
ApplyView & view()
Definition: ApplyContext.h:54
ripple::PreclaimContext::tx
STTx const & tx
Definition: Transactor.h:58
bit
ripple::multiply
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:47
ripple::PreclaimContext
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:52
ripple::AMMWithdraw::singleWithdrawTokens
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.
Definition: AMMWithdraw.cpp:724
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::tfLPToken
constexpr std::uint32_t tfLPToken
Definition: TxFlags.h:167
ripple::isIndividualFrozen
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:181
ripple::Transactor::view
ApplyView & view()
Definition: Transactor.h:107
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:113
ripple::redeemIOU
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:1399
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::terNO_AMM
@ terNO_AMM
Definition: TER.h:220
ripple::tfTwoAsset
constexpr std::uint32_t tfTwoAsset
Definition: TxFlags.h:171
ripple::deleteAMMAccount
TER deleteAMMAccount(Sandbox &view, Issue const &asset, Issue const &asset2, beast::Journal j)
Delete trustlines to AMM.
Definition: AMMUtils.cpp:234
ripple::ammLPHolds
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.
Definition: AMMUtils.cpp:104
ripple::AMMWithdraw::withdraw
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.
Definition: AMMWithdraw.cpp:421
ripple::Transactor::ctx_
ApplyContext & ctx_
Definition: Transactor.h:88
std::optional< STAmount >
beast::Journal::debug
Stream debug() const
Definition: Journal.h:314
ripple::detail::ApplyViewBase::read
std::shared_ptr< SLE const > read(Keylet const &k) const override
Return the state item associated with a key.
Definition: ApplyViewBase.cpp:71
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
std::make_pair
T make_pair(T... args)
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::detail::ApplyViewBase::peek
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Definition: ApplyViewBase.cpp:128
ripple::temMALFORMED
@ temMALFORMED
Definition: TER.h:86
ripple::sfLPTokenIn
const SF_AMOUNT sfLPTokenIn
ripple::AMMWithdraw::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: AMMWithdraw.cpp:172
ripple::PreflightContext::tx
STTx const & tx
Definition: Transactor.h:35
ripple::PreflightContext
State information when preflighting a tx.
Definition: Transactor.h:31
ripple::tfOneAssetLPToken
constexpr std::uint32_t tfOneAssetLPToken
Definition: TxFlags.h:172
ripple::AMMWithdraw::equalWithdrawLimit
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 ...
Definition: AMMWithdraw.cpp:649
ripple::PreflightContext::rules
const Rules rules
Definition: Transactor.h:36
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:235
ripple::Transactor::account_
const AccountID account_
Definition: Transactor.h:91
ripple::isFrozen
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:203
ripple::ApplyContext::tx
STTx const & tx
Definition: ApplyContext.h:48
std::exception::what
T what(T... args)
ripple::NotTEC
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:567
ripple::tfLimitLPToken
constexpr std::uint32_t tfLimitLPToken
Definition: TxFlags.h:173