rippled
Loading...
Searching...
No Matches
AMMDeposit.cpp
1#include <xrpld/app/misc/AMMHelpers.h>
2#include <xrpld/app/misc/AMMUtils.h>
3#include <xrpld/app/tx/detail/AMMDeposit.h>
4
5#include <xrpl/ledger/Sandbox.h>
6#include <xrpl/ledger/View.h>
7#include <xrpl/protocol/AMMCore.h>
8#include <xrpl/protocol/Feature.h>
9#include <xrpl/protocol/TxFlags.h>
10
11namespace ripple {
12
13bool
18
25
28{
29 auto const flags = ctx.tx.getFlags();
30
31 auto const amount = ctx.tx[~sfAmount];
32 auto const amount2 = ctx.tx[~sfAmount2];
33 auto const ePrice = ctx.tx[~sfEPrice];
34 auto const lpTokens = ctx.tx[~sfLPTokenOut];
35 auto const tradingFee = ctx.tx[~sfTradingFee];
36 // Valid options for the flags are:
37 // tfLPTokens: LPTokenOut, [Amount, Amount2]
38 // tfSingleAsset: Amount, [LPTokenOut]
39 // tfTwoAsset: Amount, Amount2, [LPTokenOut]
40 // tfTwoAssetIfEmpty: Amount, Amount2, [sfTradingFee]
41 // tfOnAssetLPToken: Amount and LPTokenOut
42 // tfLimitLPToken: Amount and EPrice
43 if (std::popcount(flags & tfDepositSubTx) != 1)
44 {
45 JLOG(ctx.j.debug()) << "AMM Deposit: invalid flags.";
46 return temMALFORMED;
47 }
48 if (flags & tfLPToken)
49 {
50 // if included then both amount and amount2 are deposit min
51 if (!lpTokens || ePrice || (amount && !amount2) ||
52 (!amount && amount2) || tradingFee)
53 return temMALFORMED;
54 }
55 else if (flags & tfSingleAsset)
56 {
57 // if included then lpTokens is deposit min
58 if (!amount || amount2 || ePrice || tradingFee)
59 return temMALFORMED;
60 }
61 else if (flags & tfTwoAsset)
62 {
63 // if included then lpTokens is deposit min
64 if (!amount || !amount2 || ePrice || tradingFee)
65 return temMALFORMED;
66 }
67 else if (flags & tfOneAssetLPToken)
68 {
69 if (!amount || !lpTokens || amount2 || ePrice || tradingFee)
70 return temMALFORMED;
71 }
72 else if (flags & tfLimitLPToken)
73 {
74 if (!amount || !ePrice || lpTokens || amount2 || tradingFee)
75 return temMALFORMED;
76 }
77 else if (flags & tfTwoAssetIfEmpty)
78 {
79 if (!amount || !amount2 || ePrice || lpTokens)
80 return temMALFORMED;
81 }
82
83 auto const asset = ctx.tx[sfAsset].get<Issue>();
84 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
85 if (auto const res = invalidAMMAssetPair(asset, asset2))
86 {
87 JLOG(ctx.j.debug()) << "AMM Deposit: invalid asset pair.";
88 return res;
89 }
90
91 if (amount && amount2 && amount->issue() == amount2->issue())
92 {
93 JLOG(ctx.j.debug()) << "AMM Deposit: invalid tokens, same issue."
94 << amount->issue() << " " << amount2->issue();
95 return temBAD_AMM_TOKENS;
96 }
97
98 if (lpTokens && *lpTokens <= beast::zero)
99 {
100 JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens";
101 return temBAD_AMM_TOKENS;
102 }
103
104 if (amount)
105 {
106 if (auto const res = invalidAMMAmount(
107 *amount,
108 std::make_optional(std::make_pair(asset, asset2)),
109 ePrice.has_value()))
110 {
111 JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount";
112 return res;
113 }
114 }
115
116 if (amount2)
117 {
118 if (auto const res = invalidAMMAmount(
119 *amount2, std::make_optional(std::make_pair(asset, asset2))))
120 {
121 JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount2";
122 return res;
123 }
124 }
125
126 // must be amount issue
127 if (amount && ePrice)
128 {
129 if (auto const res = invalidAMMAmount(
130 *ePrice,
132 std::make_pair(amount->issue(), amount->issue()))))
133 {
134 JLOG(ctx.j.debug()) << "AMM Deposit: invalid EPrice";
135 return res;
136 }
137 }
138
139 if (tradingFee > TRADING_FEE_THRESHOLD)
140 {
141 JLOG(ctx.j.debug()) << "AMM Deposit: invalid trading fee.";
142 return temBAD_FEE;
143 }
144
145 return tesSUCCESS;
146}
147
148TER
150{
151 auto const accountID = ctx.tx[sfAccount];
152
153 auto const ammSle =
154 ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
155 if (!ammSle)
156 {
157 JLOG(ctx.j.debug()) << "AMM Deposit: Invalid asset pair.";
158 return terNO_AMM;
159 }
160
161 auto const expected = ammHolds(
162 ctx.view,
163 *ammSle,
167 ctx.j);
168 if (!expected)
169 return expected.error(); // LCOV_EXCL_LINE
170 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
171 if (ctx.tx.getFlags() & tfTwoAssetIfEmpty)
172 {
173 if (lptAMMBalance != beast::zero)
174 return tecAMM_NOT_EMPTY;
175 if (amountBalance != beast::zero || amount2Balance != beast::zero)
176 {
177 // LCOV_EXCL_START
178 JLOG(ctx.j.debug()) << "AMM Deposit: tokens balance is not zero.";
179 return tecINTERNAL;
180 // LCOV_EXCL_STOP
181 }
182 }
183 else
184 {
185 if (lptAMMBalance == beast::zero)
186 return tecAMM_EMPTY;
187 if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
188 lptAMMBalance < beast::zero)
189 {
190 // LCOV_EXCL_START
191 JLOG(ctx.j.debug())
192 << "AMM Deposit: reserves or tokens balance is zero.";
193 return tecINTERNAL;
194 // LCOV_EXCL_STOP
195 }
196 }
197
198 // Check account has sufficient funds.
199 // Return tesSUCCESS if it does, error otherwise.
200 // Have to check again in deposit() because
201 // amounts might be derived based on tokens or
202 // limits.
203 auto balance = [&](auto const& deposit) -> TER {
204 if (isXRP(deposit))
205 {
206 auto const lpIssue = (*ammSle)[sfLPTokenBalance].issue();
207 // Adjust the reserve if LP doesn't have LPToken trustline
208 auto const sle = ctx.view.read(
209 keylet::line(accountID, lpIssue.account, lpIssue.currency));
210 if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit)
211 return TER(tesSUCCESS);
212 if (sle)
213 return tecUNFUNDED_AMM;
215 }
216 return (accountID == deposit.issue().account ||
218 ctx.view,
219 accountID,
220 deposit.issue(),
222 ctx.j) >= deposit)
223 ? TER(tesSUCCESS)
225 };
226
227 if (ctx.view.rules().enabled(featureAMMClawback))
228 {
229 // Check if either of the assets is frozen, AMMDeposit is not allowed
230 // if either asset is frozen
231 auto checkAsset = [&](Issue const& asset) -> TER {
232 if (auto const ter = requireAuth(ctx.view, asset, accountID))
233 {
234 JLOG(ctx.j.debug())
235 << "AMM Deposit: account is not authorized, " << asset;
236 return ter;
237 }
238
239 if (isFrozen(ctx.view, accountID, asset))
240 {
241 JLOG(ctx.j.debug())
242 << "AMM Deposit: account or currency is frozen, "
243 << to_string(accountID) << " " << to_string(asset.currency);
244
245 return tecFROZEN;
246 }
247
248 return tesSUCCESS;
249 };
250
251 if (auto const ter = checkAsset(ctx.tx[sfAsset].get<Issue>()))
252 return ter;
253
254 if (auto const ter = checkAsset(ctx.tx[sfAsset2].get<Issue>()))
255 return ter;
256 }
257
258 auto const amount = ctx.tx[~sfAmount];
259 auto const amount2 = ctx.tx[~sfAmount2];
260 auto const ammAccountID = ammSle->getAccountID(sfAccount);
261
262 auto checkAmount = [&](std::optional<STAmount> const& amount,
263 bool checkBalance) -> TER {
264 if (amount)
265 {
266 // This normally should not happen.
267 // Account is not authorized to hold the assets it's depositing,
268 // or it doesn't even have a trust line for them
269 if (auto const ter =
270 requireAuth(ctx.view, amount->issue(), accountID))
271 {
272 // LCOV_EXCL_START
273 JLOG(ctx.j.debug())
274 << "AMM Deposit: account is not authorized, "
275 << amount->issue();
276 return ter;
277 // LCOV_EXCL_STOP
278 }
279 // AMM account or currency frozen
280 if (isFrozen(ctx.view, ammAccountID, amount->issue()))
281 {
282 JLOG(ctx.j.debug())
283 << "AMM Deposit: AMM account or currency is frozen, "
284 << to_string(accountID);
285 return tecFROZEN;
286 }
287 // Account frozen
288 if (isIndividualFrozen(ctx.view, accountID, amount->issue()))
289 {
290 JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen, "
291 << to_string(accountID) << " "
292 << to_string(amount->issue().currency);
293 return tecFROZEN;
294 }
295 if (checkBalance)
296 {
297 if (auto const ter = balance(*amount))
298 {
299 JLOG(ctx.j.debug())
300 << "AMM Deposit: account has insufficient funds, "
301 << *amount;
302 return ter;
303 }
304 }
305 }
306 return tesSUCCESS;
307 };
308
309 // amount and amount2 are deposit min in case of tfLPToken
310 if (!(ctx.tx.getFlags() & tfLPToken))
311 {
312 if (auto const ter = checkAmount(amount, true))
313 return ter;
314
315 if (auto const ter = checkAmount(amount2, true))
316 return ter;
317 }
318 else
319 {
320 if (auto const ter = checkAmount(amountBalance, false))
321 return ter;
322 if (auto const ter = checkAmount(amount2Balance, false))
323 return ter;
324 }
325
326 // Equal deposit lp tokens
327 if (auto const lpTokens = ctx.tx[~sfLPTokenOut];
328 lpTokens && lpTokens->issue() != lptAMMBalance.issue())
329 {
330 JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens.";
331 return temBAD_AMM_TOKENS;
332 }
333
334 // Check the reserve for LPToken trustline if not LP.
335 // We checked above but need to check again if depositing IOU only.
336 if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::zero)
337 {
338 STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
339 // Insufficient reserve
340 if (xrpBalance <= beast::zero)
341 {
342 JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves";
344 }
345 }
346
347 return tesSUCCESS;
348}
349
352{
353 auto const amount = ctx_.tx[~sfAmount];
354 auto const amount2 = ctx_.tx[~sfAmount2];
355 auto const ePrice = ctx_.tx[~sfEPrice];
356 auto const lpTokensDeposit = ctx_.tx[~sfLPTokenOut];
357 auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
358 if (!ammSle)
359 return {tecINTERNAL, false}; // LCOV_EXCL_LINE
360 auto const ammAccountID = (*ammSle)[sfAccount];
361
362 auto const expected = ammHolds(
363 sb,
364 *ammSle,
365 amount ? amount->issue() : std::optional<Issue>{},
366 amount2 ? amount2->issue() : std::optional<Issue>{},
368 ctx_.journal);
369 if (!expected)
370 return {expected.error(), false}; // LCOV_EXCL_LINE
371 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
372 auto const tfee = (lptAMMBalance == beast::zero)
373 ? ctx_.tx[~sfTradingFee].value_or(0)
374 : getTradingFee(ctx_.view(), *ammSle, account_);
375
376 auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx;
377
378 auto const [result, newLPTokenBalance] =
379 [&,
380 &amountBalance = amountBalance,
381 &amount2Balance = amount2Balance,
382 &lptAMMBalance = lptAMMBalance]() -> std::pair<TER, STAmount> {
383 if (subTxType & tfTwoAsset)
384 return equalDepositLimit(
385 sb,
386 ammAccountID,
387 amountBalance,
388 amount2Balance,
389 lptAMMBalance,
390 *amount,
391 *amount2,
392 lpTokensDeposit,
393 tfee);
394 if (subTxType & tfOneAssetLPToken)
395 return singleDepositTokens(
396 sb,
397 ammAccountID,
398 amountBalance,
399 *amount,
400 lptAMMBalance,
401 *lpTokensDeposit,
402 tfee);
403 if (subTxType & tfLimitLPToken)
404 return singleDepositEPrice(
405 sb,
406 ammAccountID,
407 amountBalance,
408 *amount,
409 lptAMMBalance,
410 *ePrice,
411 tfee);
412 if (subTxType & tfSingleAsset)
413 return singleDeposit(
414 sb,
415 ammAccountID,
416 amountBalance,
417 lptAMMBalance,
418 *amount,
419 lpTokensDeposit,
420 tfee);
421 if (subTxType & tfLPToken)
422 return equalDepositTokens(
423 sb,
424 ammAccountID,
425 amountBalance,
426 amount2Balance,
427 lptAMMBalance,
428 *lpTokensDeposit,
429 amount,
430 amount2,
431 tfee);
432 if (subTxType & tfTwoAssetIfEmpty)
434 sb,
435 ammAccountID,
436 *amount,
437 *amount2,
438 lptAMMBalance.issue(),
439 tfee);
440 // should not happen.
441 // LCOV_EXCL_START
442 JLOG(j_.error()) << "AMM Deposit: invalid options.";
444 // LCOV_EXCL_STOP
445 }();
446
447 if (result == tesSUCCESS)
448 {
449 XRPL_ASSERT(
450 newLPTokenBalance > beast::zero,
451 "ripple::AMMDeposit::applyGuts : valid new LP token balance");
452 ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
453 // LP depositing into AMM empty state gets the auction slot
454 // and the voting
455 if (lptAMMBalance == beast::zero)
457 sb, ammSle, account_, lptAMMBalance.issue(), tfee);
458
459 sb.update(ammSle);
460 }
461
462 return {result, result == tesSUCCESS};
463}
464
465TER
467{
468 // This is the ledger view that we work against. Transactions are applied
469 // as we go on processing transactions.
470 Sandbox sb(&ctx_.view());
471
472 auto const result = applyGuts(sb);
473 if (result.second)
474 sb.apply(ctx_.rawView());
475
476 return result.first;
477}
478
481 Sandbox& view,
482 AccountID const& ammAccount,
483 STAmount const& amountBalance,
484 STAmount const& amountDeposit,
485 std::optional<STAmount> const& amount2Deposit,
486 STAmount const& lptAMMBalance,
487 STAmount const& lpTokensDeposit,
488 std::optional<STAmount> const& depositMin,
489 std::optional<STAmount> const& deposit2Min,
490 std::optional<STAmount> const& lpTokensDepositMin,
491 std::uint16_t tfee)
492{
493 // Check account has sufficient funds.
494 // Return true if it does, false otherwise.
495 auto checkBalance = [&](auto const& depositAmount) -> TER {
496 if (depositAmount <= beast::zero)
497 return temBAD_AMOUNT;
498 if (isXRP(depositAmount))
499 {
500 auto const& lpIssue = lpTokensDeposit.issue();
501 // Adjust the reserve if LP doesn't have LPToken trustline
502 auto const sle = view.read(
503 keylet::line(account_, lpIssue.account, lpIssue.currency));
504 if (xrpLiquid(view, account_, !sle, j_) >= depositAmount)
505 return tesSUCCESS;
506 }
507 else if (
508 account_ == depositAmount.issue().account ||
510 view,
511 account_,
512 depositAmount.issue(),
514 ctx_.journal) >= depositAmount)
515 return tesSUCCESS;
516 return tecUNFUNDED_AMM;
517 };
518
519 auto const
520 [amountDepositActual, amount2DepositActual, lpTokensDepositActual] =
522 amountBalance,
523 amountDeposit,
524 amount2Deposit,
525 lptAMMBalance,
526 lpTokensDeposit,
527 tfee,
529
530 if (lpTokensDepositActual <= beast::zero)
531 {
532 JLOG(ctx_.journal.debug()) << "AMM Deposit: adjusted tokens zero";
534 }
535
536 if (amountDepositActual < depositMin ||
537 amount2DepositActual < deposit2Min ||
538 lpTokensDepositActual < lpTokensDepositMin)
539 {
540 JLOG(ctx_.journal.debug())
541 << "AMM Deposit: min deposit fails " << amountDepositActual << " "
542 << depositMin.value_or(STAmount{}) << " "
543 << amount2DepositActual.value_or(STAmount{}) << " "
544 << deposit2Min.value_or(STAmount{}) << " " << lpTokensDepositActual
545 << " " << lpTokensDepositMin.value_or(STAmount{});
546 return {tecAMM_FAILED, STAmount{}};
547 }
548
549 // Deposit amountDeposit
550 if (auto const ter = checkBalance(amountDepositActual))
551 {
552 JLOG(ctx_.journal.debug()) << "AMM Deposit: account has insufficient "
553 "checkBalance to deposit or is 0"
554 << amountDepositActual;
555 return {ter, STAmount{}};
556 }
557
558 auto res = accountSend(
559 view,
560 account_,
561 ammAccount,
562 amountDepositActual,
565 if (res != tesSUCCESS)
566 {
567 JLOG(ctx_.journal.debug())
568 << "AMM Deposit: failed to deposit " << amountDepositActual;
569 return {res, STAmount{}};
570 }
571
572 // Deposit amount2Deposit
573 if (amount2DepositActual)
574 {
575 if (auto const ter = checkBalance(*amount2DepositActual))
576 {
577 JLOG(ctx_.journal.debug())
578 << "AMM Deposit: account has insufficient checkBalance to "
579 "deposit or is 0 "
580 << *amount2DepositActual;
581 return {ter, STAmount{}};
582 }
583
584 res = accountSend(
585 view,
586 account_,
587 ammAccount,
588 *amount2DepositActual,
591 if (res != tesSUCCESS)
592 {
593 JLOG(ctx_.journal.debug())
594 << "AMM Deposit: failed to deposit " << *amount2DepositActual;
595 return {res, STAmount{}};
596 }
597 }
598
599 // Deposit LP tokens
600 res = accountSend(
601 view, ammAccount, account_, lpTokensDepositActual, ctx_.journal);
602 if (res != tesSUCCESS)
603 {
604 JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens";
605 return {res, STAmount{}};
606 }
607
608 return {tesSUCCESS, lptAMMBalance + lpTokensDepositActual};
609}
610
611static STAmount
613 Rules const& rules,
614 STAmount const& lptAMMBalance,
615 STAmount const& lpTokensDeposit)
616{
617 if (!rules.enabled(fixAMMv1_3))
618 return lpTokensDeposit;
619 return adjustLPTokens(lptAMMBalance, lpTokensDeposit, IsDeposit::Yes);
620}
621
627 Sandbox& view,
628 AccountID const& ammAccount,
629 STAmount const& amountBalance,
630 STAmount const& amount2Balance,
631 STAmount const& lptAMMBalance,
632 STAmount const& lpTokensDeposit,
633 std::optional<STAmount> const& depositMin,
634 std::optional<STAmount> const& deposit2Min,
635 std::uint16_t tfee)
636{
637 try
638 {
639 auto const tokensAdj =
640 adjustLPTokensOut(view.rules(), lptAMMBalance, lpTokensDeposit);
641 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero)
643 auto const frac =
644 divide(tokensAdj, lptAMMBalance, lptAMMBalance.issue());
645 // amounts factor in the adjusted tokens
646 auto const amountDeposit =
647 getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::Yes);
648 auto const amount2Deposit =
649 getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::Yes);
650 return deposit(
651 view,
652 ammAccount,
653 amountBalance,
654 amountDeposit,
655 amount2Deposit,
656 lptAMMBalance,
657 tokensAdj,
658 depositMin,
659 deposit2Min,
661 tfee);
662 }
663 catch (std::exception const& e)
664 {
665 // LCOV_EXCL_START
666 JLOG(j_.error()) << "AMMDeposit::equalDepositTokens exception "
667 << e.what();
668 return {tecINTERNAL, STAmount{}};
669 // LCOV_EXCL_STOP
670 }
671}
672
703 Sandbox& view,
704 AccountID const& ammAccount,
705 STAmount const& amountBalance,
706 STAmount const& amount2Balance,
707 STAmount const& lptAMMBalance,
708 STAmount const& amount,
709 STAmount const& amount2,
710 std::optional<STAmount> const& lpTokensDepositMin,
711 std::uint16_t tfee)
712{
713 auto frac = Number{amount} / amountBalance;
714 auto tokensAdj =
715 getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::Yes);
716 if (tokensAdj == beast::zero)
717 {
718 if (!view.rules().enabled(fixAMMv1_3))
719 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
720 else
722 }
723 // factor in the adjusted tokens
724 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
725 auto const amount2Deposit =
726 getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::Yes);
727 if (amount2Deposit <= amount2)
728 return deposit(
729 view,
730 ammAccount,
731 amountBalance,
732 amount,
733 amount2Deposit,
734 lptAMMBalance,
735 tokensAdj,
738 lpTokensDepositMin,
739 tfee);
740 frac = Number{amount2} / amount2Balance;
741 tokensAdj =
742 getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::Yes);
743 if (tokensAdj == beast::zero)
744 {
745 if (!view.rules().enabled(fixAMMv1_3))
746 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
747 else
748 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
749 }
750 // factor in the adjusted tokens
751 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
752 auto const amountDeposit =
753 getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::Yes);
754 if (amountDeposit <= amount)
755 return deposit(
756 view,
757 ammAccount,
758 amountBalance,
759 amountDeposit,
760 amount2,
761 lptAMMBalance,
762 tokensAdj,
765 lpTokensDepositMin,
766 tfee);
767 return {tecAMM_FAILED, STAmount{}};
768}
769
780 Sandbox& view,
781 AccountID const& ammAccount,
782 STAmount const& amountBalance,
783 STAmount const& lptAMMBalance,
784 STAmount const& amount,
785 std::optional<STAmount> const& lpTokensDepositMin,
786 std::uint16_t tfee)
787{
788 auto const tokens = adjustLPTokensOut(
789 view.rules(),
790 lptAMMBalance,
791 lpTokensOut(amountBalance, amount, lptAMMBalance, tfee));
792 if (tokens == beast::zero)
793 {
794 if (!view.rules().enabled(fixAMMv1_3))
795 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
796 else
798 }
799 // factor in the adjusted tokens
800 auto const [tokensAdj, amountDepositAdj] = adjustAssetInByTokens(
801 view.rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
802 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero)
803 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
804 return deposit(
805 view,
806 ammAccount,
807 amountBalance,
808 amountDepositAdj,
810 lptAMMBalance,
811 tokensAdj,
814 lpTokensDepositMin,
815 tfee);
816}
817
827 Sandbox& view,
828 AccountID const& ammAccount,
829 STAmount const& amountBalance,
830 STAmount const& amount,
831 STAmount const& lptAMMBalance,
832 STAmount const& lpTokensDeposit,
833 std::uint16_t tfee)
834{
835 auto const tokensAdj =
836 adjustLPTokensOut(view.rules(), lptAMMBalance, lpTokensDeposit);
837 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero)
839 // the adjusted tokens are factored in
840 auto const amountDeposit =
841 ammAssetIn(amountBalance, lptAMMBalance, tokensAdj, tfee);
842 if (amountDeposit > amount)
843 return {tecAMM_FAILED, STAmount{}};
844 return deposit(
845 view,
846 ammAccount,
847 amountBalance,
848 amountDeposit,
850 lptAMMBalance,
851 tokensAdj,
855 tfee);
856}
857
885 Sandbox& view,
886 AccountID const& ammAccount,
887 STAmount const& amountBalance,
888 STAmount const& amount,
889 STAmount const& lptAMMBalance,
890 STAmount const& ePrice,
891 std::uint16_t tfee)
892{
893 if (amount != beast::zero)
894 {
895 auto const tokens = adjustLPTokensOut(
896 view.rules(),
897 lptAMMBalance,
898 lpTokensOut(amountBalance, amount, lptAMMBalance, tfee));
899 if (tokens <= beast::zero)
900 {
901 if (!view.rules().enabled(fixAMMv1_3))
902 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
903 else
905 }
906 // factor in the adjusted tokens
907 auto const [tokensAdj, amountDepositAdj] = adjustAssetInByTokens(
908 view.rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
909 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero)
910 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
911 auto const ep = Number{amountDepositAdj} / tokensAdj;
912 if (ep <= ePrice)
913 return deposit(
914 view,
915 ammAccount,
916 amountBalance,
917 amountDepositAdj,
919 lptAMMBalance,
920 tokensAdj,
924 tfee);
925 }
926
927 // LPTokens is asset out => E = b / t
928 // substituting t in formula (3) as b/E:
929 // b/E = T * [b/B - sqrt(t2**2 + b/(f1*B)) + t2]/
930 // [1 + sqrt(t2**2 + b/(f1*B)) -t2] (A)
931 // where f1 = 1 - fee, f2 = (1 - fee/2)/f1
932 // Let R = b/(f1*B), then b/B = f1*R and b = R*f1*B
933 // Then (A) is
934 // R*f1*B = E*T*[R*f1 -sqrt(f2**2 + R) + f2]/[1 + sqrt(f2**2 + R) - f2] =>
935 // Let c = f1*B/(E*T) =>
936 // R*c*(1 + sqrt(f2**2 + R) + f2) = R*f1 - sqrt(f2**2 + R) - f2 =>
937 // (R*c + 1)*sqrt(f2**2 + R) = R*(f1 + c*f2 - c) + f2 =>
938 // Let d = f1 + c*f2 - c =>
939 // (R*c + 1)*sqrt(f2**2 + R) = R*d + f2 =>
940 // (R*c + 1)**2 * (f2**2 + R) = (R*d + f2)**2 =>
941 // (R*c)**2 + R*((c*f2)**2 + 2*c - d**2) + 2*c*f2**2 + 1 -2*d*f2 = 0 =>
942 // a1 = c**2, b1 = (c*f2)**2 + 2*c - d**2, c1 = 2*c*f2**2 + 1 - 2*d*f2
943 // R = (-b1 + sqrt(b1**2 + 4*a1*c1))/(2*a1)
944 auto const f1 = feeMult(tfee);
945 auto const f2 = feeMultHalf(tfee) / f1;
946 auto const c = f1 * amountBalance / (ePrice * lptAMMBalance);
947 auto const d = f1 + c * f2 - c;
948 auto const a1 = c * c;
949 auto const b1 = c * c * f2 * f2 + 2 * c - d * d;
950 auto const c1 = 2 * c * f2 * f2 + 1 - 2 * d * f2;
951 auto amtNoRoundCb = [&] {
952 return f1 * amountBalance * solveQuadraticEq(a1, b1, c1);
953 };
954 auto amtProdCb = [&] { return f1 * solveQuadraticEq(a1, b1, c1); };
955 auto const amountDeposit = getRoundedAsset(
956 view.rules(), amtNoRoundCb, amountBalance, amtProdCb, IsDeposit::Yes);
957 if (amountDeposit <= beast::zero)
958 return {tecAMM_FAILED, STAmount{}};
959 auto tokNoRoundCb = [&] { return amountDeposit / ePrice; };
960 auto tokProdCb = [&] { return amountDeposit / ePrice; };
961 auto const tokens = getRoundedLPTokens(
962 view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::Yes);
963 // factor in the adjusted tokens
964 auto const [tokensAdj, amountDepositAdj] = adjustAssetInByTokens(
965 view.rules(),
966 amountBalance,
967 amountDeposit,
968 lptAMMBalance,
969 tokens,
970 tfee);
971 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::zero)
972 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
973
974 return deposit(
975 view,
976 ammAccount,
977 amountBalance,
978 amountDepositAdj,
980 lptAMMBalance,
981 tokensAdj,
985 tfee);
986}
987
990 Sandbox& view,
991 AccountID const& ammAccount,
992 STAmount const& amount,
993 STAmount const& amount2,
994 Issue const& lptIssue,
995 std::uint16_t tfee)
996{
997 return deposit(
998 view,
999 ammAccount,
1000 amount,
1001 amount,
1002 amount2,
1003 STAmount{lptIssue, 0},
1004 ammLPTokens(amount, amount2, lptIssue),
1008 tfee);
1009}
1010
1011} // namespace ripple
Stream error() const
Definition Journal.h:327
Stream debug() const
Definition Journal.h:309
std::pair< TER, bool > applyGuts(Sandbox &view)
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...
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
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.
static TER preclaim(PreclaimContext const &ctx)
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.
static NotTEC preflight(PreflightContext const &ctx)
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.
static bool checkExtraFeatures(PreflightContext const &ctx)
TER doApply() override
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)
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.
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.
ApplyView & view()
beast::Journal const journal
A currency issued by an account.
Definition Issue.h:14
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual Rules const & rules() const =0
Returns the tx processing rules.
Rules controlling protocol behavior.
Definition Rules.h:19
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:111
Issue const & issue() const
Definition STAmount.h:477
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:638
std::uint32_t getFlags() const
Definition STObject.cpp:518
Discardable, editable view to a ledger.
Definition Sandbox.h:16
void apply(RawView &to)
Definition Sandbox.h:36
AccountID const account_
Definition Transactor.h:128
ApplyView & view()
Definition Transactor.h:144
beast::Journal const j_
Definition Transactor.h:126
ApplyContext & ctx_
Definition Transactor.h:124
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
T is_same_v
T make_optional(T... args)
T make_pair(T... args)
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:427
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:225
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t tfSingleAsset
Definition TxFlags.h:228
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
Definition AMMCore.cpp:76
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:74
std::pair< STAmount, STAmount > adjustAssetInByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
@ fhZERO_IF_FROZEN
Definition View.h:58
@ fhIGNORE_FREEZE
Definition View.h:58
bool isXRP(AccountID const &c)
Definition AccountID.h:71
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:194
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
Definition AMMUtils.cpp:160
Number solveQuadraticEq(Number const &a, Number const &b, Number const &c)
Positive solution for quadratic equation: x = (-b + sqrt(b**2 + 4*a*c))/(2*a)
constexpr std::uint32_t tfLimitLPToken
Definition TxFlags.h:231
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset deposit given LP Tokens.
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition AMMCore.cpp:110
constexpr std::uint32_t tfOneAssetLPToken
Definition TxFlags.h:230
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
Definition View.cpp:2172
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:228
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account, AuthType authType=AuthType::Legacy)
Check if the account lacks required authorization.
Definition View.cpp:2466
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition AMMCore.h:91
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:321
constexpr std::uint32_t tfTwoAsset
Definition TxFlags.h:229
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:94
std::tuple< STAmount, std::optional< STAmount >, STAmount > adjustAmountsByLPTokens(STAmount const &amountBalance, STAmount const &amount, std::optional< STAmount > const &amount2, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee, IsDeposit isDeposit)
Calls adjustLPTokens() and adjusts deposit or withdraw amounts if the adjusted LP tokens are less tha...
constexpr std::uint32_t tfDepositSubTx
Definition TxFlags.h:236
static STAmount adjustLPTokensOut(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit)
@ tecINSUF_RESERVE_LINE
Definition TER.h:270
@ tecFROZEN
Definition TER.h:285
@ tecAMM_EMPTY
Definition TER.h:314
@ tecINTERNAL
Definition TER.h:292
@ tecAMM_NOT_EMPTY
Definition TER.h:315
@ tecUNFUNDED_AMM
Definition TER.h:310
@ tecAMM_FAILED
Definition TER.h:312
@ tecAMM_INVALID_TOKENS
Definition TER.h:313
constexpr std::uint32_t tfLPToken
Definition TxFlags.h:225
@ tesSUCCESS
Definition TER.h:226
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
Definition AMMCore.cpp:61
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:368
constexpr std::uint32_t tfDepositMask
Definition TxFlags.h:240
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:28
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Issue const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
Definition AMMHelpers.cpp:6
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
constexpr std::uint32_t tfTwoAssetIfEmpty
Definition TxFlags.h:232
Number feeMultHalf(std::uint16_t tfee)
Get fee multiplier (1 - tfee / 2) @tfee trading fee in basis points.
Definition AMMCore.h:100
STAmount lpTokensOut(STAmount const &asset1Balance, STAmount const &asset1Deposit, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's deposit amount.
@ terNO_AMM
Definition TER.h:208
TERSubset< CanCvtToTER > TER
Definition TER.h:630
std::uint16_t constexpr TRADING_FEE_THRESHOLD
Definition AMMCore.h:12
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:659
Number adjustFracByTokens(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &tokens, Number const &frac)
Find a fraction of tokens after the tokens are adjusted.
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition View.cpp:599
@ temBAD_AMOUNT
Definition TER.h:70
@ temBAD_FEE
Definition TER.h:73
@ temMALFORMED
Definition TER.h:68
@ temBAD_AMM_TOKENS
Definition TER.h:110
T popcount(T... args)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
beast::Journal const j
Definition Transactor.h:69
State information when preflighting a tx.
Definition Transactor.h:16
beast::Journal const j
Definition Transactor.h:23
T value_or(T... args)
T what(T... args)