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/Feature.h>
29 #include <ripple/protocol/STAccount.h>
30 #include <ripple/protocol/TxFlags.h>
31 
32 #include <bit>
33 
34 namespace ripple {
35 
36 NotTEC
38 {
39  if (!ammEnabled(ctx.rules))
40  return temDISABLED;
41 
42  if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
43  return ret;
44 
45  auto const flags = ctx.tx.getFlags();
46  if (flags & tfWithdrawMask)
47  {
48  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid flags.";
49  return temINVALID_FLAG;
50  }
51 
52  auto const amount = ctx.tx[~sfAmount];
53  auto const amount2 = ctx.tx[~sfAmount2];
54  auto const ePrice = ctx.tx[~sfEPrice];
55  auto const lpTokens = ctx.tx[~sfLPTokenIn];
56  // Valid combinations are:
57  // LPTokens
58  // tfWithdrawAll
59  // Amount
60  // tfOneAssetWithdrawAll & Amount
61  // Amount and Amount2
62  // Amount and LPTokens
63  // Amount and EPrice
64  if (std::popcount(flags & tfWithdrawSubTx) != 1)
65  {
66  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid flags.";
67  return temMALFORMED;
68  }
69  if (flags & tfLPToken)
70  {
71  if (!lpTokens || amount || amount2 || ePrice)
72  return temMALFORMED;
73  }
74  else if (flags & tfWithdrawAll)
75  {
76  if (lpTokens || amount || amount2 || ePrice)
77  return temMALFORMED;
78  }
79  else if (flags & tfOneAssetWithdrawAll)
80  {
81  if (!amount || lpTokens || amount2 || ePrice)
82  return temMALFORMED;
83  }
84  else if (flags & tfSingleAsset)
85  {
86  if (!amount || lpTokens || amount2 || ePrice)
87  return temMALFORMED;
88  }
89  else if (flags & tfTwoAsset)
90  {
91  if (!amount || !amount2 || lpTokens || ePrice)
92  return temMALFORMED;
93  }
94  else if (flags & tfOneAssetLPToken)
95  {
96  if (!amount || !lpTokens || amount2 || ePrice)
97  return temMALFORMED;
98  }
99  else if (flags & tfLimitLPToken)
100  {
101  if (!amount || !ePrice || lpTokens || amount2)
102  return temMALFORMED;
103  }
104 
105  auto const asset = ctx.tx[sfAsset];
106  auto const asset2 = ctx.tx[sfAsset2];
107  if (auto const res = invalidAMMAssetPair(asset, asset2))
108  {
109  JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
110  return res;
111  }
112 
113  if (amount && amount2 && amount->issue() == amount2->issue())
114  {
115  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens, same issue."
116  << amount->issue() << " " << amount2->issue();
117  return temBAD_AMM_TOKENS;
118  }
119 
120  if (lpTokens && *lpTokens <= beast::zero)
121  {
122  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
123  return temBAD_AMM_TOKENS;
124  }
125 
126  if (amount)
127  {
128  if (auto const res = invalidAMMAmount(
129  *amount,
130  std::make_optional(std::make_pair(asset, asset2)),
132  ePrice))
133  {
134  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset1Out";
135  return res;
136  }
137  }
138 
139  if (amount2)
140  {
141  if (auto const res = invalidAMMAmount(
142  *amount2, std::make_optional(std::make_pair(asset, asset2))))
143  {
144  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset2OutAmount";
145  return res;
146  }
147  }
148 
149  if (ePrice)
150  {
151  if (auto const res = invalidAMMAmount(*ePrice))
152  {
153  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice";
154  return res;
155  }
156  }
157 
158  return preflight2(ctx);
159 }
160 
163  STAmount const& lpTokens,
164  std::optional<STAmount> const& tokensIn,
165  std::uint32_t flags)
166 {
167  if (flags & (tfWithdrawAll | tfOneAssetWithdrawAll))
168  return lpTokens;
169  return tokensIn;
170 }
171 
172 TER
174 {
175  auto const accountID = ctx.tx[sfAccount];
176 
177  auto const ammSle =
178  ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
179  if (!ammSle)
180  {
181  JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
182  return terNO_AMM;
183  }
184 
185  auto const amount = ctx.tx[~sfAmount];
186  auto const amount2 = ctx.tx[~sfAmount2];
187 
188  auto const expected = ammHolds(
189  ctx.view,
190  *ammSle,
191  amount ? amount->issue() : std::optional<Issue>{},
192  amount2 ? amount2->issue() : std::optional<Issue>{},
193  FreezeHandling::fhIGNORE_FREEZE,
194  ctx.j);
195  if (!expected)
196  return expected.error();
197  auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
198  if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
199  lptAMMBalance <= beast::zero)
200  {
201  JLOG(ctx.j.debug())
202  << "AMM Withdraw: reserves or tokens balance is zero.";
203  return tecINTERNAL;
204  }
205 
206  auto const ammAccountID = ammSle->getAccountID(sfAccount);
207 
208  auto checkAmount = [&](std::optional<STAmount> const& amount,
209  auto const& balance) -> TER {
210  if (amount)
211  {
212  if (amount > balance)
213  {
214  JLOG(ctx.j.debug())
215  << "AMM Withdraw: withdrawing more than the balance, "
216  << *amount;
217  return tecAMM_BALANCE;
218  }
219  if (auto const ter =
220  requireAuth(ctx.view, amount->issue(), accountID))
221  {
222  JLOG(ctx.j.debug())
223  << "AMM Withdraw: account is not authorized, "
224  << amount->issue();
225  return ter;
226  }
227  // AMM account or currency frozen
228  if (isFrozen(ctx.view, ammAccountID, amount->issue()))
229  {
230  JLOG(ctx.j.debug())
231  << "AMM Withdraw: AMM account or currency is frozen, "
232  << to_string(accountID);
233  return tecFROZEN;
234  }
235  // Account frozen
236  if (isIndividualFrozen(ctx.view, accountID, amount->issue()))
237  {
238  JLOG(ctx.j.debug()) << "AMM Withdraw: account is frozen, "
239  << to_string(accountID) << " "
240  << to_string(amount->issue().currency);
241  return tecFROZEN;
242  }
243  }
244  return tesSUCCESS;
245  };
246 
247  if (auto const ter = checkAmount(amount, amountBalance))
248  return ter;
249 
250  if (auto const ter = checkAmount(amount2, amount2Balance))
251  return ter;
252 
253  auto const lpTokens =
254  ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
255  auto const lpTokensWithdraw =
256  tokensWithdraw(lpTokens, ctx.tx[~sfLPTokenIn], ctx.tx.getFlags());
257 
258  if (lpTokens <= beast::zero)
259  {
260  JLOG(ctx.j.debug()) << "AMM Withdraw: tokens balance is zero.";
261  return tecAMM_BALANCE;
262  }
263 
264  if (lpTokensWithdraw && lpTokensWithdraw->issue() != lpTokens.issue())
265  {
266  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid LPTokens.";
267  return temBAD_AMM_TOKENS;
268  }
269 
270  if (lpTokensWithdraw && *lpTokensWithdraw > lpTokens)
271  {
272  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
273  return tecAMM_INVALID_TOKENS;
274  }
275 
276  if (auto const ePrice = ctx.tx[~sfEPrice];
277  ePrice && ePrice->issue() != lpTokens.issue())
278  {
279  JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice.";
280  return temBAD_AMM_TOKENS;
281  }
282 
283  if (ctx.tx.getFlags() & (tfLPToken | tfWithdrawAll))
284  {
285  if (auto const ter = checkAmount(amountBalance, amountBalance))
286  return ter;
287  if (auto const ter = checkAmount(amount2Balance, amount2Balance))
288  return ter;
289  }
290 
291  return tesSUCCESS;
292 }
293 
296 {
297  auto const amount = ctx_.tx[~sfAmount];
298  auto const amount2 = ctx_.tx[~sfAmount2];
299  auto const ePrice = ctx_.tx[~sfEPrice];
300  auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
301  if (!ammSle)
302  return {tecINTERNAL, false};
303  auto const ammAccountID = (*ammSle)[sfAccount];
304  auto const lpTokens =
305  ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
306  auto const lpTokensWithdraw =
307  tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags());
308 
309  auto const tfee = getTradingFee(ctx_.view(), *ammSle, account_);
310 
311  auto const expected = ammHolds(
312  sb,
313  *ammSle,
314  amount ? amount->issue() : std::optional<Issue>{},
315  amount2 ? amount2->issue() : std::optional<Issue>{},
316  FreezeHandling::fhZERO_IF_FROZEN,
317  ctx_.journal);
318  if (!expected)
319  return {expected.error(), false};
320  auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
321 
322  auto const subTxType = ctx_.tx.getFlags() & tfWithdrawSubTx;
323 
324  auto const [result, newLPTokenBalance] =
325  [&,
326  &amountBalance = amountBalance,
327  &amount2Balance = amount2Balance,
328  &lptAMMBalance = lptAMMBalance]() -> std::pair<TER, STAmount> {
329  if (subTxType & tfTwoAsset)
330  return equalWithdrawLimit(
331  sb,
332  ammAccountID,
333  amountBalance,
334  amount2Balance,
335  lptAMMBalance,
336  *amount,
337  *amount2,
338  tfee);
339  if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll)
340  return singleWithdrawTokens(
341  sb,
342  ammAccountID,
343  amountBalance,
344  lptAMMBalance,
345  *amount,
346  *lpTokensWithdraw,
347  tfee);
348  if (subTxType & tfLimitLPToken)
349  return singleWithdrawEPrice(
350  sb,
351  ammAccountID,
352  amountBalance,
353  lptAMMBalance,
354  *amount,
355  *ePrice,
356  tfee);
357  if (subTxType & tfSingleAsset)
358  return singleWithdraw(
359  sb, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee);
360  if (subTxType & tfLPToken || subTxType & tfWithdrawAll)
361  return equalWithdrawTokens(
362  sb,
363  ammAccountID,
364  amountBalance,
365  amount2Balance,
366  lptAMMBalance,
367  lpTokens,
368  *lpTokensWithdraw,
369  tfee);
370  // should not happen.
371  JLOG(j_.error()) << "AMM Withdraw: invalid options.";
373  }();
374 
375  // AMM is deleted if zero tokens balance
376  if (result == tesSUCCESS && newLPTokenBalance != beast::zero)
377  {
378  ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
379  sb.update(ammSle);
380 
381  JLOG(ctx_.journal.trace())
382  << "AMM Withdraw: tokens " << to_string(newLPTokenBalance.iou())
383  << " " << to_string(lpTokens.iou()) << " "
384  << to_string(lptAMMBalance.iou());
385  }
386 
387  return {result, result == tesSUCCESS};
388 }
389 
390 TER
392 {
393  // This is the ledger view that we work against. Transactions are applied
394  // as we go on processing transactions.
395  Sandbox sb(&ctx_.view());
396 
397  auto const result = applyGuts(sb);
398  if (result.second)
399  sb.apply(ctx_.rawView());
400 
401  return result.first;
402 }
403 
404 TER
406 {
407  auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
408  auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
409 
410  if (!sleAMMRoot || !ammSle)
411  return tecINTERNAL;
412 
413  // Note, the AMM trust lines are deleted since the balance
414  // goes to 0. It also means there are no linked
415  // ledger objects.
416  sb.erase(ammSle);
417  sb.erase(sleAMMRoot);
418 
419  return tesSUCCESS;
420 }
421 
424  Sandbox& view,
425  AccountID const& ammAccount,
426  STAmount const& amountBalance,
427  STAmount const& amountWithdraw,
428  std::optional<STAmount> const& amount2Withdraw,
429  STAmount const& lpTokensAMMBalance,
430  STAmount const& lpTokensWithdraw,
431  std::uint16_t tfee)
432 {
433  auto const ammSle =
435  if (!ammSle)
436  return {tecINTERNAL, STAmount{}};
437  auto const lpTokens = ammLPHolds(view, *ammSle, account_, ctx_.journal);
438  auto const expected = ammHolds(
439  view,
440  *ammSle,
441  amountWithdraw.issue(),
442  std::nullopt,
443  FreezeHandling::fhZERO_IF_FROZEN,
444  j_);
445  if (!expected)
446  return {expected.error(), STAmount{}};
447  auto const [curBalance, curBalance2, _] = *expected;
448  (void)_;
449 
450  auto const
451  [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
455  amountBalance,
456  amountWithdraw,
457  amount2Withdraw,
458  lpTokensAMMBalance,
459  lpTokensWithdraw,
460  tfee,
461  false);
462  return std::make_tuple(
463  amountWithdraw, amount2Withdraw, lpTokensWithdraw);
464  }();
465 
466  if (lpTokensWithdrawActual <= beast::zero ||
467  lpTokensWithdrawActual > lpTokens)
468  {
469  JLOG(ctx_.journal.debug())
470  << "AMM Withdraw: failed to withdraw, invalid LP tokens "
471  << " tokens: " << lpTokensWithdrawActual << " " << lpTokens << " "
472  << lpTokensAMMBalance;
473  return {tecAMM_INVALID_TOKENS, STAmount{}};
474  }
475 
476  // Withdrawing one side of the pool
477  if ((amountWithdrawActual == curBalance &&
478  amount2WithdrawActual != curBalance2) ||
479  (amount2WithdrawActual == curBalance2 &&
480  amountWithdrawActual != curBalance))
481  {
482  JLOG(ctx_.journal.debug())
483  << "AMM Withdraw: failed to withdraw one side of the pool "
484  << " curBalance: " << curBalance << " " << amountWithdrawActual
485  << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
486  << lpTokensAMMBalance;
487  return {tecAMM_BALANCE, STAmount{}};
488  }
489 
490  // May happen if withdrawing an amount close to one side of the pool
491  if (lpTokensWithdrawActual == lpTokensAMMBalance &&
492  (amountWithdrawActual != curBalance ||
493  amount2WithdrawActual != curBalance2))
494  {
495  JLOG(ctx_.journal.debug())
496  << "AMM Withdraw: failed to withdraw all tokens "
497  << " curBalance: " << curBalance << " " << amountWithdrawActual
498  << " curBalance2: " << amount2WithdrawActual.value_or(STAmount{0})
499  << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
500  << lpTokensAMMBalance;
501  return {tecAMM_BALANCE, STAmount{}};
502  }
503 
504  // Withdrawing more than the pool's balance
505  if (amountWithdrawActual > curBalance ||
506  amount2WithdrawActual > curBalance2)
507  {
508  JLOG(ctx_.journal.debug())
509  << "AMM Withdraw: withdrawing more than the pool's balance "
510  << " curBalance: " << curBalance << " " << amountWithdrawActual
511  << " curBalance2: " << curBalance2 << " "
512  << (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
513  << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
514  << lpTokensAMMBalance;
515  return {tecAMM_BALANCE, STAmount{}};
516  }
517 
518  // Withdraw amountWithdraw
519  auto res = accountSend(
520  view,
521  ammAccount,
522  account_,
523  amountWithdrawActual,
524  ctx_.journal,
526  if (res != tesSUCCESS)
527  {
528  JLOG(ctx_.journal.debug())
529  << "AMM Withdraw: failed to withdraw " << amountWithdrawActual;
530  return {res, STAmount{}};
531  }
532 
533  // Withdraw amount2Withdraw
534  if (amount2WithdrawActual)
535  {
536  res = accountSend(
537  view,
538  ammAccount,
539  account_,
540  *amount2WithdrawActual,
541  ctx_.journal,
543  if (res != tesSUCCESS)
544  {
545  JLOG(ctx_.journal.debug()) << "AMM Withdraw: failed to withdraw "
546  << *amount2WithdrawActual;
547  return {res, STAmount{}};
548  }
549  }
550 
551  // Withdraw LP tokens
552  res = redeemIOU(
553  view,
554  account_,
555  lpTokensWithdrawActual,
556  lpTokensWithdrawActual.issue(),
557  ctx_.journal);
558  if (res != tesSUCCESS)
559  {
560  JLOG(ctx_.journal.debug())
561  << "AMM Withdraw: failed to withdraw LPTokens";
562  return {res, STAmount{}};
563  }
564 
565  if (lpTokensWithdrawActual == lpTokensAMMBalance)
566  return {deleteAccount(view, ammAccount), STAmount{}};
567 
568  return {tesSUCCESS, lpTokensAMMBalance - lpTokensWithdrawActual};
569 }
570 
575  Sandbox& view,
576  AccountID const& ammAccount,
577  STAmount const& amountBalance,
578  STAmount const& amount2Balance,
579  STAmount const& lptAMMBalance,
580  STAmount const& lpTokens,
581  STAmount const& lpTokensWithdraw,
582  std::uint16_t tfee)
583 {
584  try
585  {
586  // Withdrawing all tokens in the pool
587  if (lpTokensWithdraw == lptAMMBalance)
588  return withdraw(
589  view,
590  ammAccount,
591  amountBalance,
592  amountBalance,
593  amount2Balance,
594  lptAMMBalance,
595  lpTokensWithdraw,
596  tfee);
597 
598  auto const frac = divide(lpTokensWithdraw, lptAMMBalance, noIssue());
599  auto const withdrawAmount =
600  multiply(amountBalance, frac, amountBalance.issue());
601  auto const withdraw2Amount =
602  multiply(amount2Balance, frac, amount2Balance.issue());
603  // LP is making equal withdrawal by tokens but the requested amount
604  // of LP tokens is likely too small and results in one-sided pool
605  // withdrawal due to round off. Fail so the user withdraws
606  // more tokens.
607  if (withdrawAmount == beast::zero || withdraw2Amount == beast::zero)
608  return {tecAMM_FAILED, STAmount{}};
609 
610  return withdraw(
611  view,
612  ammAccount,
613  amountBalance,
614  withdrawAmount,
615  withdraw2Amount,
616  lptAMMBalance,
617  lpTokensWithdraw,
618  tfee);
619  }
620  catch (std::exception const& e)
621  {
622  JLOG(j_.error()) << "AMMWithdraw::equalWithdrawTokens exception "
623  << e.what();
624  }
625  return {tecINTERNAL, STAmount{}};
626 }
627 
655  Sandbox& view,
656  AccountID const& ammAccount,
657  STAmount const& amountBalance,
658  STAmount const& amount2Balance,
659  STAmount const& lptAMMBalance,
660  STAmount const& amount,
661  STAmount const& amount2,
662  std::uint16_t tfee)
663 {
664  auto frac = Number{amount} / amountBalance;
665  auto const amount2Withdraw = amount2Balance * frac;
666  if (amount2Withdraw <= amount2)
667  return withdraw(
668  view,
669  ammAccount,
670  amountBalance,
671  amount,
672  toSTAmount(amount2.issue(), amount2Withdraw),
673  lptAMMBalance,
674  toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
675  tfee);
676  frac = Number{amount2} / amount2Balance;
677  auto const amountWithdraw = amountBalance * frac;
678  assert(amountWithdraw <= amount);
679  return withdraw(
680  view,
681  ammAccount,
682  amountBalance,
683  toSTAmount(amount.issue(), amountWithdraw),
684  amount2,
685  lptAMMBalance,
686  toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac),
687  tfee);
688 }
689 
697  Sandbox& view,
698  AccountID const& ammAccount,
699  STAmount const& amountBalance,
700  STAmount const& lptAMMBalance,
701  STAmount const& amount,
702  std::uint16_t tfee)
703 {
704  auto const tokens = lpTokensOut(amountBalance, amount, lptAMMBalance, tfee);
705  if (tokens == beast::zero)
706  return {tecAMM_FAILED, STAmount{}};
707  return withdraw(
708  view,
709  ammAccount,
710  amountBalance,
711  amount,
712  std::nullopt,
713  lptAMMBalance,
714  tokens,
715  tfee);
716 }
717 
730  Sandbox& view,
731  AccountID const& ammAccount,
732  STAmount const& amountBalance,
733  STAmount const& lptAMMBalance,
734  STAmount const& amount,
735  STAmount const& lpTokensWithdraw,
736  std::uint16_t tfee)
737 {
738  auto const amountWithdraw =
739  withdrawByTokens(amountBalance, lptAMMBalance, lpTokensWithdraw, tfee);
740  if (amount == beast::zero || amountWithdraw >= amount)
741  return withdraw(
742  view,
743  ammAccount,
744  amountBalance,
745  amountWithdraw,
746  std::nullopt,
747  lptAMMBalance,
748  lpTokensWithdraw,
749  tfee);
750  return {tecAMM_FAILED, STAmount{}};
751 }
752 
774  Sandbox& view,
775  AccountID const& ammAccount,
776  STAmount const& amountBalance,
777  STAmount const& lptAMMBalance,
778  STAmount const& amount,
779  STAmount const& ePrice,
780  std::uint16_t tfee)
781 {
782  // LPTokens is asset in => E = t / a and formula (8) is:
783  // a = A*(t1**2 + t1*(f - 2))/(t1*f - 1)
784  // substitute a as t/E =>
785  // t/E = A*(t1**2 + t1*(f - 2))/(t1*f - 1), t1=t/T => t = t1*T
786  // t1*T/E = A*((t/T)**2 + t*(f - 2)/T)/(t*f/T - 1) =>
787  // T/E = A*(t1 + f-2)/(t1*f - 1) =>
788  // T*(t1*f - 1) = A*E*(t1 + f - 2) =>
789  // t1*T*f - T = t1*A*E + A*E*(f - 2) =>
790  // t1*(T*f - A*E) = T + A*E*(f - 2) =>
791  // t = T*(T + A*E*(f - 2))/(T*f - A*E)
792  Number const ae = amountBalance * ePrice;
793  auto const f = getFee(tfee);
794  auto const tokens = lptAMMBalance * (lptAMMBalance + ae * (f - 2)) /
795  (lptAMMBalance * f - ae);
796  if (tokens <= 0)
797  return {tecAMM_FAILED, STAmount{}};
798  auto const amountWithdraw = toSTAmount(amount.issue(), tokens / ePrice);
799  if (amount == beast::zero || amountWithdraw >= amount)
800  return withdraw(
801  view,
802  ammAccount,
803  amountBalance,
804  amountWithdraw,
805  std::nullopt,
806  lptAMMBalance,
807  toSTAmount(lptAMMBalance.issue(), tokens),
808  tfee);
809 
810  return {tecAMM_FAILED, STAmount{}};
811 }
812 
813 } // namespace ripple
ripple::tecFROZEN
@ tecFROZEN
Definition: TER.h:273
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:130
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:1159
ripple::PreclaimContext::j
const beast::Journal j
Definition: Transactor.h:60
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::STAmount::issue
Issue const & issue() const
Definition: STAmount.h:347
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:604
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:375
ripple::sfEPrice
const SF_AMOUNT sfEPrice
std::make_optional
T make_optional(T... args)
ripple::AMMWithdraw::doApply
TER doApply() override
Definition: AMMWithdraw.cpp:391
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::AMMWithdraw::applyGuts
std::pair< TER, bool > applyGuts(Sandbox &view)
Definition: AMMWithdraw.cpp:295
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::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:78
ripple::tecAMM_FAILED
@ tecAMM_FAILED
Definition: TER.h:300
ripple::requireAuth
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account)
Check if the account requires authorization.
Definition: View.cpp:1528
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:109
ripple::tfWithdrawMask
constexpr std::uint32_t tfWithdrawMask
Definition: TxFlags.h:180
ripple::tfWithdrawAll
constexpr std::uint32_t tfWithdrawAll
Definition: TxFlags.h:168
ripple::temBAD_AMM_TOKENS
@ temBAD_AMM_TOKENS
Definition: TER.h:127
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:773
ripple::sfAsset2
const SF_ISSUE sfAsset2
ripple::Number
Definition: Number.h:36
ripple::AMMWithdraw::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: AMMWithdraw.cpp:37
ripple::tokensWithdraw
static std::optional< STAmount > tokensWithdraw(STAmount const &lpTokens, std::optional< STAmount > const &tokensIn, std::uint32_t flags)
Definition: AMMWithdraw.cpp:162
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:134
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:574
ripple::toSTAmount
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
Definition: AmountConversions.h:30
ripple::tecAMM_INVALID_TOKENS
@ tecAMM_INVALID_TOKENS
Definition: TER.h:301
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:696
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:174
ripple::Sandbox
Discardable, editable view to a ledger.
Definition: Sandbox.h:34
ripple::tecAMM_BALANCE
@ tecAMM_BALANCE
Definition: TER.h:299
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:575
ripple::STAmount
Definition: STAmount.h:45
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:280
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::detail::ApplyViewBase::erase
void erase(std::shared_ptr< SLE > const &sle) override
Remove a peeked SLE.
Definition: ApplyViewBase.cpp:134
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:729
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:198
ripple::Transactor::view
ApplyView & view()
Definition: Transactor.h:107
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:112
ripple::redeemIOU
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
Definition: View.cpp:1416
ripple::sfFlags
const SF_UINT32 sfFlags
ripple::terNO_AMM
@ terNO_AMM
Definition: TER.h:210
ripple::tfTwoAsset
constexpr std::uint32_t tfTwoAsset
Definition: TxFlags.h:171
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:423
ripple::Transactor::ctx_
ApplyContext & ctx_
Definition: Transactor.h:88
std::optional< STAmount >
ripple::AMMWithdraw::deleteAccount
TER deleteAccount(Sandbox &view, AccountID const &ammAccountID)
Delete AMM account.
Definition: AMMWithdraw.cpp:405
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
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:85
ripple::sfLPTokenIn
const SF_AMOUNT sfLPTokenIn
ripple::AMMWithdraw::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: AMMWithdraw.cpp:173
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:654
ripple::PreflightContext::rules
const Rules rules
Definition: Transactor.h:36
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:225
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:220
ripple::ApplyContext::tx
STTx const & tx
Definition: ApplyContext.h:48
std::exception::what
T what(T... args)
ripple::WaiveTransferFee::Yes
@ Yes
ripple::NotTEC
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:535
ripple::tfLimitLPToken
constexpr std::uint32_t tfLimitLPToken
Definition: TxFlags.h:173