rippled
AMMDeposit.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/AMMDeposit.h>
21 
22 #include <ripple/app/misc/AMMHelpers.h>
23 #include <ripple/app/misc/AMMUtils.h>
24 #include <ripple/ledger/Sandbox.h>
25 #include <ripple/ledger/View.h>
26 #include <ripple/protocol/AMMCore.h>
27 #include <ripple/protocol/Feature.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 & tfDepositMask)
46  {
47  JLOG(ctx.j.debug()) << "AMM Deposit: 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[~sfLPTokenOut];
55  auto const tradingFee = ctx.tx[~sfTradingFee];
56  // Valid options for the flags are:
57  // tfLPTokens: LPTokenOut, [Amount, Amount2]
58  // tfSingleAsset: Amount, [LPTokenOut]
59  // tfTwoAsset: Amount, Amount2, [LPTokenOut]
60  // tfTwoAssetIfEmpty: Amount, Amount2, [sfTradingFee]
61  // tfOnAssetLPToken: Amount and LPTokenOut
62  // tfLimitLPToken: Amount and EPrice
63  if (std::popcount(flags & tfDepositSubTx) != 1)
64  {
65  JLOG(ctx.j.debug()) << "AMM Deposit: invalid flags.";
66  return temMALFORMED;
67  }
68  if (flags & tfLPToken)
69  {
70  // if included then both amount and amount2 are deposit min
71  if (!lpTokens || ePrice || (amount && !amount2) ||
72  (!amount && amount2) || tradingFee)
73  return temMALFORMED;
74  }
75  else if (flags & tfSingleAsset)
76  {
77  // if included then lpTokens is deposit min
78  if (!amount || amount2 || ePrice || tradingFee)
79  return temMALFORMED;
80  }
81  else if (flags & tfTwoAsset)
82  {
83  // if included then lpTokens is deposit min
84  if (!amount || !amount2 || ePrice || tradingFee)
85  return temMALFORMED;
86  }
87  else if (flags & tfOneAssetLPToken)
88  {
89  if (!amount || !lpTokens || amount2 || ePrice || tradingFee)
90  return temMALFORMED;
91  }
92  else if (flags & tfLimitLPToken)
93  {
94  if (!amount || !ePrice || lpTokens || amount2 || tradingFee)
95  return temMALFORMED;
96  }
97  else if (flags & tfTwoAssetIfEmpty)
98  {
99  if (!amount || !amount2 || ePrice || lpTokens)
100  return temMALFORMED;
101  }
102 
103  auto const asset = ctx.tx[sfAsset];
104  auto const asset2 = ctx.tx[sfAsset2];
105  if (auto const res = invalidAMMAssetPair(asset, asset2))
106  {
107  JLOG(ctx.j.debug()) << "AMM Deposit: invalid asset pair.";
108  return res;
109  }
110 
111  if (amount && amount2 && amount->issue() == amount2->issue())
112  {
113  JLOG(ctx.j.debug()) << "AMM Deposit: invalid tokens, same issue."
114  << amount->issue() << " " << amount2->issue();
115  return temBAD_AMM_TOKENS;
116  }
117 
118  if (lpTokens && *lpTokens <= beast::zero)
119  {
120  JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens";
121  return temBAD_AMM_TOKENS;
122  }
123 
124  if (amount)
125  {
126  if (auto const res = invalidAMMAmount(
127  *amount,
128  std::make_optional(std::make_pair(asset, asset2)),
129  ePrice.has_value()))
130  {
131  JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount";
132  return res;
133  }
134  }
135 
136  if (amount2)
137  {
138  if (auto const res = invalidAMMAmount(
139  *amount2, std::make_optional(std::make_pair(asset, asset2))))
140  {
141  JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount2";
142  return res;
143  }
144  }
145 
146  // must be amount issue
147  if (amount && ePrice)
148  {
149  if (auto const res = invalidAMMAmount(
150  *ePrice,
152  std::make_pair(amount->issue(), amount->issue()))))
153  {
154  JLOG(ctx.j.debug()) << "AMM Deposit: invalid EPrice";
155  return res;
156  }
157  }
158 
159  if (tradingFee > TRADING_FEE_THRESHOLD)
160  {
161  JLOG(ctx.j.debug()) << "AMM Deposit: invalid trading fee.";
162  return temBAD_FEE;
163  }
164 
165  return preflight2(ctx);
166 }
167 
168 TER
170 {
171  auto const accountID = ctx.tx[sfAccount];
172 
173  auto const ammSle =
174  ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
175  if (!ammSle)
176  {
177  JLOG(ctx.j.debug()) << "AMM Deposit: Invalid asset pair.";
178  return terNO_AMM;
179  }
180 
181  auto const expected = ammHolds(
182  ctx.view,
183  *ammSle,
184  std::nullopt,
185  std::nullopt,
186  FreezeHandling::fhIGNORE_FREEZE,
187  ctx.j);
188  if (!expected)
189  return expected.error();
190  auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
191  if (ctx.tx.getFlags() & tfTwoAssetIfEmpty)
192  {
193  if (lptAMMBalance != beast::zero)
194  return tecAMM_NOT_EMPTY;
195  if (amountBalance != beast::zero || amount2Balance != beast::zero)
196  {
197  JLOG(ctx.j.debug()) << "AMM Deposit: tokens balance is not zero.";
198  return tecINTERNAL;
199  }
200  }
201  else
202  {
203  if (lptAMMBalance == beast::zero)
204  return tecAMM_EMPTY;
205  if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
206  lptAMMBalance < beast::zero)
207  {
208  JLOG(ctx.j.debug())
209  << "AMM Deposit: reserves or tokens balance is zero.";
210  return tecINTERNAL;
211  }
212  }
213 
214  // Check account has sufficient funds.
215  // Return tesSUCCESS if it does, error otherwise.
216  // Have to check again in deposit() because
217  // amounts might be derived based on tokens or
218  // limits.
219  auto balance = [&](auto const& deposit) -> TER {
220  if (isXRP(deposit))
221  {
222  auto const lpIssue = (*ammSle)[sfLPTokenBalance].issue();
223  // Adjust the reserve if LP doesn't have LPToken trustline
224  auto const sle = ctx.view.read(
225  keylet::line(accountID, lpIssue.account, lpIssue.currency));
226  if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit)
227  return TER(tesSUCCESS);
228  if (sle)
229  return tecUNFUNDED_AMM;
230  return tecINSUF_RESERVE_LINE;
231  }
232  return (accountID == deposit.issue().account ||
233  accountHolds(
234  ctx.view,
235  accountID,
236  deposit.issue(),
237  FreezeHandling::fhIGNORE_FREEZE,
238  ctx.j) >= deposit)
239  ? TER(tesSUCCESS)
240  : tecUNFUNDED_AMM;
241  };
242 
243  auto const amount = ctx.tx[~sfAmount];
244  auto const amount2 = ctx.tx[~sfAmount2];
245  auto const ammAccountID = ammSle->getAccountID(sfAccount);
246 
247  auto checkAmount = [&](std::optional<STAmount> const& amount,
248  bool checkBalance) -> TER {
249  if (amount)
250  {
251  // This normally should not happen.
252  // Account is not authorized to hold the assets it's depositing,
253  // or it doesn't even have a trust line for them
254  if (auto const ter =
255  requireAuth(ctx.view, amount->issue(), accountID))
256  {
257  JLOG(ctx.j.debug())
258  << "AMM Deposit: account is not authorized, "
259  << amount->issue();
260  return ter;
261  }
262  // AMM account or currency frozen
263  if (isFrozen(ctx.view, ammAccountID, amount->issue()))
264  {
265  JLOG(ctx.j.debug())
266  << "AMM Deposit: AMM account or currency is frozen, "
267  << to_string(accountID);
268  return tecFROZEN;
269  }
270  // Account frozen
271  if (isIndividualFrozen(ctx.view, accountID, amount->issue()))
272  {
273  JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen, "
274  << to_string(accountID) << " "
275  << to_string(amount->issue().currency);
276  return tecFROZEN;
277  }
278  if (checkBalance)
279  {
280  if (auto const ter = balance(*amount))
281  {
282  JLOG(ctx.j.debug())
283  << "AMM Deposit: account has insufficient funds, "
284  << *amount;
285  return ter;
286  }
287  }
288  }
289  return tesSUCCESS;
290  };
291 
292  // amount and amount2 are deposit min in case of tfLPToken
293  if (!(ctx.tx.getFlags() & tfLPToken))
294  {
295  if (auto const ter = checkAmount(amount, true))
296  return ter;
297 
298  if (auto const ter = checkAmount(amount2, true))
299  return ter;
300  }
301  else
302  {
303  if (auto const ter = checkAmount(amountBalance, false))
304  return ter;
305  if (auto const ter = checkAmount(amount2Balance, false))
306  return ter;
307  }
308 
309  // Equal deposit lp tokens
310  if (auto const lpTokens = ctx.tx[~sfLPTokenOut];
311  lpTokens && lpTokens->issue() != lptAMMBalance.issue())
312  {
313  JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens.";
314  return temBAD_AMM_TOKENS;
315  }
316 
317  // Check the reserve for LPToken trustline if not LP.
318  // We checked above but need to check again if depositing IOU only.
319  if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::zero)
320  {
321  STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
322  // Insufficient reserve
323  if (xrpBalance <= beast::zero)
324  {
325  JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves";
326  return tecINSUF_RESERVE_LINE;
327  }
328  }
329 
330  return tesSUCCESS;
331 }
332 
335 {
336  auto const amount = ctx_.tx[~sfAmount];
337  auto const amount2 = ctx_.tx[~sfAmount2];
338  auto const ePrice = ctx_.tx[~sfEPrice];
339  auto const lpTokensDeposit = ctx_.tx[~sfLPTokenOut];
340  auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
341  if (!ammSle)
342  return {tecINTERNAL, false};
343  auto const ammAccountID = (*ammSle)[sfAccount];
344 
345  auto const expected = ammHolds(
346  sb,
347  *ammSle,
348  amount ? amount->issue() : std::optional<Issue>{},
349  amount2 ? amount2->issue() : std::optional<Issue>{},
350  FreezeHandling::fhZERO_IF_FROZEN,
351  ctx_.journal);
352  if (!expected)
353  return {expected.error(), false};
354  auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
355  auto const tfee = (lptAMMBalance == beast::zero)
356  ? ctx_.tx[~sfTradingFee].value_or(0)
357  : getTradingFee(ctx_.view(), *ammSle, account_);
358 
359  auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx;
360 
361  auto const [result, newLPTokenBalance] =
362  [&,
363  &amountBalance = amountBalance,
364  &amount2Balance = amount2Balance,
365  &lptAMMBalance = lptAMMBalance]() -> std::pair<TER, STAmount> {
366  if (subTxType & tfTwoAsset)
367  return equalDepositLimit(
368  sb,
369  ammAccountID,
370  amountBalance,
371  amount2Balance,
372  lptAMMBalance,
373  *amount,
374  *amount2,
375  lpTokensDeposit,
376  tfee);
377  if (subTxType & tfOneAssetLPToken)
378  return singleDepositTokens(
379  sb,
380  ammAccountID,
381  amountBalance,
382  *amount,
383  lptAMMBalance,
384  *lpTokensDeposit,
385  tfee);
386  if (subTxType & tfLimitLPToken)
387  return singleDepositEPrice(
388  sb,
389  ammAccountID,
390  amountBalance,
391  *amount,
392  lptAMMBalance,
393  *ePrice,
394  tfee);
395  if (subTxType & tfSingleAsset)
396  return singleDeposit(
397  sb,
398  ammAccountID,
399  amountBalance,
400  lptAMMBalance,
401  *amount,
402  lpTokensDeposit,
403  tfee);
404  if (subTxType & tfLPToken)
405  return equalDepositTokens(
406  sb,
407  ammAccountID,
408  amountBalance,
409  amount2Balance,
410  lptAMMBalance,
411  *lpTokensDeposit,
412  amount,
413  amount2,
414  tfee);
415  if (subTxType & tfTwoAssetIfEmpty)
417  sb,
418  ammAccountID,
419  *amount,
420  *amount2,
421  lptAMMBalance.issue(),
422  tfee);
423  // should not happen.
424  JLOG(j_.error()) << "AMM Deposit: invalid options.";
426  }();
427 
428  if (result == tesSUCCESS)
429  {
430  assert(newLPTokenBalance > beast::zero);
431  ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
432  // LP depositing into AMM empty state gets the auction slot
433  // and the voting
434  if (lptAMMBalance == beast::zero)
436  sb, ammSle, account_, lptAMMBalance.issue(), tfee);
437 
438  sb.update(ammSle);
439  }
440 
441  return {result, result == tesSUCCESS};
442 }
443 
444 TER
446 {
447  // This is the ledger view that we work against. Transactions are applied
448  // as we go on processing transactions.
449  Sandbox sb(&ctx_.view());
450 
451  auto const result = applyGuts(sb);
452  if (result.second)
453  sb.apply(ctx_.rawView());
454 
455  return result.first;
456 }
457 
460  Sandbox& view,
461  AccountID const& ammAccount,
462  STAmount const& amountBalance,
463  STAmount const& amountDeposit,
464  std::optional<STAmount> const& amount2Deposit,
465  STAmount const& lptAMMBalance,
466  STAmount const& lpTokensDeposit,
467  std::optional<STAmount> const& depositMin,
468  std::optional<STAmount> const& deposit2Min,
469  std::optional<STAmount> const& lpTokensDepositMin,
470  std::uint16_t tfee)
471 {
472  // Check account has sufficient funds.
473  // Return true if it does, false otherwise.
474  auto checkBalance = [&](auto const& depositAmount) -> TER {
475  if (depositAmount <= beast::zero)
476  return temBAD_AMOUNT;
477  if (isXRP(depositAmount))
478  {
479  auto const& lpIssue = lpTokensDeposit.issue();
480  // Adjust the reserve if LP doesn't have LPToken trustline
481  auto const sle = view.read(
482  keylet::line(account_, lpIssue.account, lpIssue.currency));
483  if (xrpLiquid(view, account_, !sle, j_) >= depositAmount)
484  return tesSUCCESS;
485  }
486  else if (
487  account_ == depositAmount.issue().account ||
488  accountHolds(
489  view,
490  account_,
491  depositAmount.issue(),
492  FreezeHandling::fhIGNORE_FREEZE,
493  ctx_.journal) >= depositAmount)
494  return tesSUCCESS;
495  return tecUNFUNDED_AMM;
496  };
497 
498  auto const
499  [amountDepositActual, amount2DepositActual, lpTokensDepositActual] =
501  amountBalance,
502  amountDeposit,
503  amount2Deposit,
504  lptAMMBalance,
505  lpTokensDeposit,
506  tfee,
507  true);
508 
509  if (lpTokensDepositActual <= beast::zero)
510  {
511  JLOG(ctx_.journal.debug()) << "AMM Deposit: adjusted tokens zero";
512  return {tecAMM_INVALID_TOKENS, STAmount{}};
513  }
514 
515  if (amountDepositActual < depositMin ||
516  amount2DepositActual < deposit2Min ||
517  lpTokensDepositActual < lpTokensDepositMin)
518  {
519  JLOG(ctx_.journal.debug())
520  << "AMM Deposit: min deposit fails " << amountDepositActual << " "
521  << depositMin.value_or(STAmount{}) << " "
522  << amount2DepositActual.value_or(STAmount{}) << " "
523  << deposit2Min.value_or(STAmount{}) << " " << lpTokensDepositActual
524  << " " << lpTokensDepositMin.value_or(STAmount{});
525  return {tecAMM_FAILED, STAmount{}};
526  }
527 
528  // Deposit amountDeposit
529  if (auto const ter = checkBalance(amountDepositActual))
530  {
531  JLOG(ctx_.journal.debug()) << "AMM Deposit: account has insufficient "
532  "checkBalance to deposit or is 0"
533  << amountDepositActual;
534  return {ter, STAmount{}};
535  }
536 
537  auto res = accountSend(
538  view,
539  account_,
540  ammAccount,
541  amountDepositActual,
542  ctx_.journal,
544  if (res != tesSUCCESS)
545  {
546  JLOG(ctx_.journal.debug())
547  << "AMM Deposit: failed to deposit " << amountDepositActual;
548  return {res, STAmount{}};
549  }
550 
551  // Deposit amount2Deposit
552  if (amount2DepositActual)
553  {
554  if (auto const ter = checkBalance(*amount2DepositActual))
555  {
556  JLOG(ctx_.journal.debug())
557  << "AMM Deposit: account has insufficient checkBalance to "
558  "deposit or is 0 "
559  << *amount2DepositActual;
560  return {ter, STAmount{}};
561  }
562 
563  res = accountSend(
564  view,
565  account_,
566  ammAccount,
567  *amount2DepositActual,
568  ctx_.journal,
570  if (res != tesSUCCESS)
571  {
572  JLOG(ctx_.journal.debug())
573  << "AMM Deposit: failed to deposit " << *amount2DepositActual;
574  return {res, STAmount{}};
575  }
576  }
577 
578  // Deposit LP tokens
579  res = accountSend(
580  view, ammAccount, account_, lpTokensDepositActual, ctx_.journal);
581  if (res != tesSUCCESS)
582  {
583  JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens";
584  return {res, STAmount{}};
585  }
586 
587  return {tesSUCCESS, lptAMMBalance + lpTokensDepositActual};
588 }
589 
595  Sandbox& view,
596  AccountID const& ammAccount,
597  STAmount const& amountBalance,
598  STAmount const& amount2Balance,
599  STAmount const& lptAMMBalance,
600  STAmount const& lpTokensDeposit,
601  std::optional<STAmount> const& depositMin,
602  std::optional<STAmount> const& deposit2Min,
603  std::uint16_t tfee)
604 {
605  try
606  {
607  auto const frac =
608  divide(lpTokensDeposit, lptAMMBalance, lptAMMBalance.issue());
609  return deposit(
610  view,
611  ammAccount,
612  amountBalance,
613  multiply(amountBalance, frac, amountBalance.issue()),
614  multiply(amount2Balance, frac, amount2Balance.issue()),
615  lptAMMBalance,
616  lpTokensDeposit,
617  depositMin,
618  deposit2Min,
619  std::nullopt,
620  tfee);
621  }
622  catch (std::exception const& e)
623  {
624  JLOG(j_.error()) << "AMMDeposit::equalDepositTokens exception "
625  << e.what();
626  }
627  return {tecINTERNAL, STAmount{}};
628 }
629 
660  Sandbox& view,
661  AccountID const& ammAccount,
662  STAmount const& amountBalance,
663  STAmount const& amount2Balance,
664  STAmount const& lptAMMBalance,
665  STAmount const& amount,
666  STAmount const& amount2,
667  std::optional<STAmount> const& lpTokensDepositMin,
668  std::uint16_t tfee)
669 {
670  auto frac = Number{amount} / amountBalance;
671  auto tokens = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
672  if (tokens == beast::zero)
673  return {tecAMM_FAILED, STAmount{}};
674  auto const amount2Deposit = amount2Balance * frac;
675  if (amount2Deposit <= amount2)
676  return deposit(
677  view,
678  ammAccount,
679  amountBalance,
680  amount,
681  toSTAmount(amount2Balance.issue(), amount2Deposit),
682  lptAMMBalance,
683  tokens,
684  std::nullopt,
685  std::nullopt,
686  lpTokensDepositMin,
687  tfee);
688  frac = Number{amount2} / amount2Balance;
689  tokens = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
690  if (tokens == beast::zero)
691  return {tecAMM_FAILED, STAmount{}};
692  auto const amountDeposit = amountBalance * frac;
693  if (amountDeposit <= amount)
694  return deposit(
695  view,
696  ammAccount,
697  amountBalance,
698  toSTAmount(amountBalance.issue(), amountDeposit),
699  amount2,
700  lptAMMBalance,
701  tokens,
702  std::nullopt,
703  std::nullopt,
704  lpTokensDepositMin,
705  tfee);
706  return {tecAMM_FAILED, STAmount{}};
707 }
708 
719  Sandbox& view,
720  AccountID const& ammAccount,
721  STAmount const& amountBalance,
722  STAmount const& lptAMMBalance,
723  STAmount const& amount,
724  std::optional<STAmount> const& lpTokensDepositMin,
725  std::uint16_t tfee)
726 {
727  auto const tokens = lpTokensIn(amountBalance, amount, lptAMMBalance, tfee);
728  if (tokens == beast::zero)
729  return {tecAMM_FAILED, STAmount{}};
730  return deposit(
731  view,
732  ammAccount,
733  amountBalance,
734  amount,
735  std::nullopt,
736  lptAMMBalance,
737  tokens,
738  std::nullopt,
739  std::nullopt,
740  lpTokensDepositMin,
741  tfee);
742 }
743 
753  Sandbox& view,
754  AccountID const& ammAccount,
755  STAmount const& amountBalance,
756  STAmount const& amount,
757  STAmount const& lptAMMBalance,
758  STAmount const& lpTokensDeposit,
759  std::uint16_t tfee)
760 {
761  auto const amountDeposit =
762  ammAssetIn(amountBalance, lptAMMBalance, lpTokensDeposit, tfee);
763  if (amountDeposit > amount)
764  return {tecAMM_FAILED, STAmount{}};
765  return deposit(
766  view,
767  ammAccount,
768  amountBalance,
769  amountDeposit,
770  std::nullopt,
771  lptAMMBalance,
772  lpTokensDeposit,
773  std::nullopt,
774  std::nullopt,
775  std::nullopt,
776  tfee);
777 }
778 
806  Sandbox& view,
807  AccountID const& ammAccount,
808  STAmount const& amountBalance,
809  STAmount const& amount,
810  STAmount const& lptAMMBalance,
811  STAmount const& ePrice,
812  std::uint16_t tfee)
813 {
814  if (amount != beast::zero)
815  {
816  auto const tokens =
817  lpTokensIn(amountBalance, amount, lptAMMBalance, tfee);
818  if (tokens <= beast::zero)
819  return {tecAMM_FAILED, STAmount{}};
820  auto const ep = Number{amount} / tokens;
821  if (ep <= ePrice)
822  return deposit(
823  view,
824  ammAccount,
825  amountBalance,
826  amount,
827  std::nullopt,
828  lptAMMBalance,
829  tokens,
830  std::nullopt,
831  std::nullopt,
832  std::nullopt,
833  tfee);
834  }
835 
836  // LPTokens is asset out => E = b / t
837  // substituting t in formula (3) as b/E:
838  // b/E = T * [b/B - sqrt(t2**2 + b/(f1*B)) + t2]/
839  // [1 + sqrt(t2**2 + b/(f1*B)) -t2] (A)
840  // where f1 = 1 - fee, f2 = (1 - fee/2)/f1
841  // Let R = b/(f1*B), then b/B = f1*R and b = R*f1*B
842  // Then (A) is
843  // R*f1*B = E*T*[R*f1 -sqrt(f2**2 + R) + f2]/[1 + sqrt(f2**2 + R) - f2] =>
844  // Let c = f1*B/(E*T) =>
845  // R*c*(1 + sqrt(f2**2 + R) + f2) = R*f1 - sqrt(f2**2 + R) - f2 =>
846  // (R*c + 1)*sqrt(f2**2 + R) = R*(f1 + c*f2 - c) + f2 =>
847  // Let d = f1 + c*f2 - c =>
848  // (R*c + 1)*sqrt(f2**2 + R) = R*d + f2 =>
849  // (R*c + 1)**2 * (f2**2 + R) = (R*d + f2)**2 =>
850  // (R*c)**2 + R*((c*f2)**2 + 2*c - d**2) + 2*c*f2**2 + 1 -2*d*f2 = 0 =>
851  // a1 = c**2, b1 = (c*f2)**2 + 2*c - d**2, c1 = 2*c*f2**2 + 1 - 2*d*f2
852  // R = (-b1 + sqrt(b1**2 + 4*a1*c1))/(2*a1)
853  auto const f1 = feeMult(tfee);
854  auto const f2 = feeMultHalf(tfee) / f1;
855  auto const c = f1 * amountBalance / (ePrice * lptAMMBalance);
856  auto const d = f1 + c * f2 - c;
857  auto const a1 = c * c;
858  auto const b1 = c * c * f2 * f2 + 2 * c - d * d;
859  auto const c1 = 2 * c * f2 * f2 + 1 - 2 * d * f2;
860  auto const amountDeposit = toSTAmount(
861  amountBalance.issue(),
862  f1 * amountBalance * solveQuadraticEq(a1, b1, c1));
863  if (amountDeposit <= beast::zero)
864  return {tecAMM_FAILED, STAmount{}};
865  auto const tokens =
866  toSTAmount(lptAMMBalance.issue(), amountDeposit / ePrice);
867  return deposit(
868  view,
869  ammAccount,
870  amountBalance,
871  amountDeposit,
872  std::nullopt,
873  lptAMMBalance,
874  tokens,
875  std::nullopt,
876  std::nullopt,
877  std::nullopt,
878  tfee);
879 }
880 
883  Sandbox& view,
884  AccountID const& ammAccount,
885  STAmount const& amount,
886  STAmount const& amount2,
887  Issue const& lptIssue,
888  std::uint16_t tfee)
889 {
890  return deposit(
891  view,
892  ammAccount,
893  amount,
894  amount,
895  amount2,
896  STAmount{lptIssue, 0},
897  ammLPTokens(amount, amount2, lptIssue),
898  std::nullopt,
899  std::nullopt,
900  std::nullopt,
901  tfee);
902 }
903 
904 } // namespace ripple
ripple::AMMDeposit::singleDepositTokens
std::pair< TER, STAmount > singleDepositTokens(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit, std::uint16_t tfee)
Single asset deposit (Asset1In, LPTokens) by the tokens.
Definition: AMMDeposit.cpp:752
ripple::initializeFeeAuctionVote
void initializeFeeAuctionVote(ApplyView &view, std::shared_ptr< SLE > &ammSle, AccountID const &account, Issue const &lptIssue, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
Definition: AMMUtils.cpp:283
ripple::tecFROZEN
@ tecFROZEN
Definition: TER.h:284
ripple::preflight2
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:130
ripple::tecUNFUNDED_AMM
@ tecUNFUNDED_AMM
Definition: TER.h:309
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
ripple::tecINSUF_RESERVE_LINE
@ tecINSUF_RESERVE_LINE
Definition: TER.h:269
ripple::sfAsset
const SF_ISSUE sfAsset
ripple::solveQuadraticEq
Number solveQuadraticEq(Number const &a, Number const &b, Number const &c)
Definition: AMMHelpers.cpp:201
ripple::PreclaimContext::view
ReadView const & view
Definition: Transactor.h:56
std::exception
STL class.
ripple::AMMDeposit::doApply
TER doApply() override
Definition: AMMDeposit.cpp:445
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
ripple::lpTokensIn
STAmount lpTokensIn(STAmount const &asset1Balance, STAmount const &asset1Deposit, STAmount const &lptAMMBalance, std::uint16_t tfee)
Definition: AMMHelpers.cpp:41
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:637
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::feeMult
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition: AMMCore.h:118
ripple::accountHolds
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:226
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::optional::value_or
T value_or(T... args)
std::make_optional
T make_optional(T... args)
ripple::detail::ApplyViewBase::update
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
Definition: ApplyViewBase.cpp:146
ripple::tecAMM_EMPTY
@ tecAMM_EMPTY
Definition: TER.h:313
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::ApplyContext::rawView
RawView & rawView()
Definition: ApplyContext.h:67
ripple::AMMDeposit::preclaim
static TER preclaim(PreclaimContext const &ctx)
Definition: AMMDeposit.cpp:169
ripple::sfLPTokenOut
const SF_AMOUNT sfLPTokenOut
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::sfTradingFee
const SF_UINT16 sfTradingFee
ripple::tecAMM_FAILED
@ tecAMM_FAILED
Definition: TER.h:311
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::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::temBAD_AMM_TOKENS
@ temBAD_AMM_TOKENS
Definition: TER.h:128
ripple::tecAMM_NOT_EMPTY
@ tecAMM_NOT_EMPTY
Definition: TER.h:314
ripple::sfAsset2
const SF_ISSUE sfAsset2
ripple::Number
Definition: Number.h:36
ripple::toSTAmount
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
Definition: AmountConversions.h:30
ripple::tecAMM_INVALID_TOKENS
@ tecAMM_INVALID_TOKENS
Definition: TER.h:312
ripple::TERSubset< CanCvtToTER >
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::Sandbox
Discardable, editable view to a ledger.
Definition: Sandbox.h:34
ripple::ammAssetIn
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Definition: AMMHelpers.cpp:67
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:608
ripple::STAmount
Definition: STAmount.h:46
beast::Journal::error
Stream error() const
Definition: Journal.h:332
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:291
ripple::xrpLiquid
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition: View.cpp:346
ripple::STObject::getFlags
std::uint32_t getFlags() const
Definition: STObject.cpp:481
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
ripple::temBAD_AMOUNT
@ temBAD_AMOUNT
Definition: TER.h:88
std::uint16_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::keylet::line
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition: Indexes.cpp:202
ripple::temBAD_FEE
@ temBAD_FEE
Definition: TER.h:91
ripple::AMMDeposit::singleDepositEPrice
std::pair< TER, STAmount > singleDepositEPrice(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &ePrice, std::uint16_t tfee)
Single asset deposit (Asset1In, EPrice) with two constraints.
Definition: AMMDeposit.cpp:805
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::AMMDeposit::equalDepositLimit
std::pair< TER, STAmount > equalDepositLimit(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &amount, STAmount const &amount2, std::optional< STAmount > const &lpTokensDepositMin, std::uint16_t tfee)
Equal asset deposit (Asset1In, Asset2In) with the constraint on the maximum amount of both assets tha...
Definition: AMMDeposit.cpp:659
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
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::TRADING_FEE_THRESHOLD
constexpr std::uint16_t TRADING_FEE_THRESHOLD
Definition: AMMCore.h:31
ripple::tfDepositMask
constexpr std::uint32_t tfDepositMask
Definition: TxFlags.h:182
ripple::AMMDeposit::applyGuts
std::pair< TER, bool > applyGuts(Sandbox &view)
Definition: AMMDeposit.cpp:334
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::AMMDeposit::equalDepositTokens
std::pair< TER, STAmount > equalDepositTokens(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit, std::optional< STAmount > const &depositMin, std::optional< STAmount > const &deposit2Min, std::uint16_t tfee)
Equal asset deposit (LPTokens) for the specified share of the AMM instance pools.
Definition: AMMDeposit.cpp:594
ripple::tfDepositSubTx
constexpr std::uint32_t tfDepositSubTx
Definition: TxFlags.h:178
ripple::Transactor::view
ApplyView & view()
Definition: Transactor.h:107
ripple::temDISABLED
@ temDISABLED
Definition: TER.h:113
ripple::terNO_AMM
@ terNO_AMM
Definition: TER.h:220
ripple::tfTwoAsset
constexpr std::uint32_t tfTwoAsset
Definition: TxFlags.h:171
ripple::ammLPTokens
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Issue const &lptIssue)
Definition: AMMHelpers.cpp:25
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::AMMDeposit::preflight
static NotTEC preflight(PreflightContext const &ctx)
Definition: AMMDeposit.cpp:36
ripple::AMMDeposit::singleDeposit
std::pair< TER, STAmount > singleDeposit(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &lptAMMBalance, STAmount const &amount, std::optional< STAmount > const &lpTokensDepositMin, std::uint16_t tfee)
Single asset deposit (Asset1In) by the amount.
Definition: AMMDeposit.cpp:718
ripple::Transactor::ctx_
ApplyContext & ctx_
Definition: Transactor.h:88
std::optional< STAmount >
beast::Journal::debug
Stream debug() const
Definition: Journal.h:314
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::PreflightContext::tx
STTx const & tx
Definition: Transactor.h:35
ripple::PreflightContext
State information when preflighting a tx.
Definition: Transactor.h:31
ripple::AMMDeposit::deposit
std::pair< TER, STAmount > deposit(Sandbox &view, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amountDeposit, std::optional< STAmount > const &amount2Deposit, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit, std::optional< STAmount > const &depositMin, std::optional< STAmount > const &deposit2Min, std::optional< STAmount > const &lpTokensDepositMin, std::uint16_t tfee)
Deposit requested assets and token amount into LP account.
Definition: AMMDeposit.cpp:459
ripple::tfOneAssetLPToken
constexpr std::uint32_t tfOneAssetLPToken
Definition: TxFlags.h:172
ripple::PreflightContext::rules
const Rules rules
Definition: Transactor.h:36
ripple::tfTwoAssetIfEmpty
constexpr std::uint32_t tfTwoAssetIfEmpty
Definition: TxFlags.h:174
ripple::feeMultHalf
Number feeMultHalf(std::uint16_t tfee)
Get fee multiplier (1 - tfee / 2) @tfee trading fee in basis points.
Definition: AMMCore.h:127
ripple::AMMDeposit::equalDepositInEmptyState
std::pair< TER, STAmount > equalDepositInEmptyState(Sandbox &view, AccountID const &ammAccount, STAmount const &amount, STAmount const &amount2, Issue const &lptIssue, std::uint16_t tfee)
Equal deposit in empty AMM state (LP tokens balance is 0)
Definition: AMMDeposit.cpp:882
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:236
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:568
ripple::tfLimitLPToken
constexpr std::uint32_t tfLimitLPToken
Definition: TxFlags.h:173