rippled
Loading...
Searching...
No Matches
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 <xrpld/app/tx/detail/AMMDeposit.h>
21
22#include <xrpld/app/misc/AMMHelpers.h>
23#include <xrpld/app/misc/AMMUtils.h>
24#include <xrpld/ledger/Sandbox.h>
25#include <xrpld/ledger/View.h>
26#include <xrpl/protocol/AMMCore.h>
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/STAccount.h>
29#include <xrpl/protocol/TxFlags.h>
30
31#include <bit>
32
33namespace ripple {
34
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].get<Issue>();
104 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
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
168TER
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,
187 ctx.j);
188 if (!expected)
189 return expected.error(); // LCOV_EXCL_LINE
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 // LCOV_EXCL_START
198 JLOG(ctx.j.debug()) << "AMM Deposit: tokens balance is not zero.";
199 return tecINTERNAL;
200 // LCOV_EXCL_STOP
201 }
202 }
203 else
204 {
205 if (lptAMMBalance == beast::zero)
206 return tecAMM_EMPTY;
207 if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
208 lptAMMBalance < beast::zero)
209 {
210 // LCOV_EXCL_START
211 JLOG(ctx.j.debug())
212 << "AMM Deposit: reserves or tokens balance is zero.";
213 return tecINTERNAL;
214 // LCOV_EXCL_STOP
215 }
216 }
217
218 // Check account has sufficient funds.
219 // Return tesSUCCESS if it does, error otherwise.
220 // Have to check again in deposit() because
221 // amounts might be derived based on tokens or
222 // limits.
223 auto balance = [&](auto const& deposit) -> TER {
224 if (isXRP(deposit))
225 {
226 auto const lpIssue = (*ammSle)[sfLPTokenBalance].issue();
227 // Adjust the reserve if LP doesn't have LPToken trustline
228 auto const sle = ctx.view.read(
229 keylet::line(accountID, lpIssue.account, lpIssue.currency));
230 if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit)
231 return TER(tesSUCCESS);
232 if (sle)
233 return tecUNFUNDED_AMM;
235 }
236 return (accountID == deposit.issue().account ||
238 ctx.view,
239 accountID,
240 deposit.issue(),
242 ctx.j) >= deposit)
243 ? TER(tesSUCCESS)
245 };
246
247 if (ctx.view.rules().enabled(featureAMMClawback))
248 {
249 // Check if either of the assets is frozen, AMMDeposit is not allowed
250 // if either asset is frozen
251 auto checkAsset = [&](Issue const& asset) -> TER {
252 if (auto const ter = requireAuth(ctx.view, asset, accountID))
253 {
254 JLOG(ctx.j.debug())
255 << "AMM Deposit: account is not authorized, " << asset;
256 return ter;
257 }
258
259 if (isFrozen(ctx.view, accountID, asset))
260 {
261 JLOG(ctx.j.debug())
262 << "AMM Deposit: account or currency is frozen, "
263 << to_string(accountID) << " " << to_string(asset.currency);
264
265 return tecFROZEN;
266 }
267
268 return tesSUCCESS;
269 };
270
271 if (auto const ter = checkAsset(ctx.tx[sfAsset].get<Issue>()))
272 return ter;
273
274 if (auto const ter = checkAsset(ctx.tx[sfAsset2].get<Issue>()))
275 return ter;
276 }
277
278 auto const amount = ctx.tx[~sfAmount];
279 auto const amount2 = ctx.tx[~sfAmount2];
280 auto const ammAccountID = ammSle->getAccountID(sfAccount);
281
282 auto checkAmount = [&](std::optional<STAmount> const& amount,
283 bool checkBalance) -> TER {
284 if (amount)
285 {
286 // This normally should not happen.
287 // Account is not authorized to hold the assets it's depositing,
288 // or it doesn't even have a trust line for them
289 if (auto const ter =
290 requireAuth(ctx.view, amount->issue(), accountID))
291 {
292 // LCOV_EXCL_START
293 JLOG(ctx.j.debug())
294 << "AMM Deposit: account is not authorized, "
295 << amount->issue();
296 return ter;
297 // LCOV_EXCL_STOP
298 }
299 // AMM account or currency frozen
300 if (isFrozen(ctx.view, ammAccountID, amount->issue()))
301 {
302 JLOG(ctx.j.debug())
303 << "AMM Deposit: AMM account or currency is frozen, "
304 << to_string(accountID);
305 return tecFROZEN;
306 }
307 // Account frozen
308 if (isIndividualFrozen(ctx.view, accountID, amount->issue()))
309 {
310 JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen, "
311 << to_string(accountID) << " "
312 << to_string(amount->issue().currency);
313 return tecFROZEN;
314 }
315 if (checkBalance)
316 {
317 if (auto const ter = balance(*amount))
318 {
319 JLOG(ctx.j.debug())
320 << "AMM Deposit: account has insufficient funds, "
321 << *amount;
322 return ter;
323 }
324 }
325 }
326 return tesSUCCESS;
327 };
328
329 // amount and amount2 are deposit min in case of tfLPToken
330 if (!(ctx.tx.getFlags() & tfLPToken))
331 {
332 if (auto const ter = checkAmount(amount, true))
333 return ter;
334
335 if (auto const ter = checkAmount(amount2, true))
336 return ter;
337 }
338 else
339 {
340 if (auto const ter = checkAmount(amountBalance, false))
341 return ter;
342 if (auto const ter = checkAmount(amount2Balance, false))
343 return ter;
344 }
345
346 // Equal deposit lp tokens
347 if (auto const lpTokens = ctx.tx[~sfLPTokenOut];
348 lpTokens && lpTokens->issue() != lptAMMBalance.issue())
349 {
350 JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens.";
351 return temBAD_AMM_TOKENS;
352 }
353
354 // Check the reserve for LPToken trustline if not LP.
355 // We checked above but need to check again if depositing IOU only.
356 if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::zero)
357 {
358 STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
359 // Insufficient reserve
360 if (xrpBalance <= beast::zero)
361 {
362 JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves";
364 }
365 }
366
367 return tesSUCCESS;
368}
369
372{
373 auto const amount = ctx_.tx[~sfAmount];
374 auto const amount2 = ctx_.tx[~sfAmount2];
375 auto const ePrice = ctx_.tx[~sfEPrice];
376 auto const lpTokensDeposit = ctx_.tx[~sfLPTokenOut];
377 auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
378 if (!ammSle)
379 return {tecINTERNAL, false}; // LCOV_EXCL_LINE
380 auto const ammAccountID = (*ammSle)[sfAccount];
381
382 auto const expected = ammHolds(
383 sb,
384 *ammSle,
385 amount ? amount->issue() : std::optional<Issue>{},
386 amount2 ? amount2->issue() : std::optional<Issue>{},
388 ctx_.journal);
389 if (!expected)
390 return {expected.error(), false}; // LCOV_EXCL_LINE
391 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
392 auto const tfee = (lptAMMBalance == beast::zero)
393 ? ctx_.tx[~sfTradingFee].value_or(0)
394 : getTradingFee(ctx_.view(), *ammSle, account_);
395
396 auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx;
397
398 auto const [result, newLPTokenBalance] =
399 [&,
400 &amountBalance = amountBalance,
401 &amount2Balance = amount2Balance,
402 &lptAMMBalance = lptAMMBalance]() -> std::pair<TER, STAmount> {
403 if (subTxType & tfTwoAsset)
404 return equalDepositLimit(
405 sb,
407 amountBalance,
408 amount2Balance,
409 lptAMMBalance,
410 *amount,
411 *amount2,
412 lpTokensDeposit,
413 tfee);
414 if (subTxType & tfOneAssetLPToken)
415 return singleDepositTokens(
416 sb,
418 amountBalance,
419 *amount,
420 lptAMMBalance,
421 *lpTokensDeposit,
422 tfee);
423 if (subTxType & tfLimitLPToken)
424 return singleDepositEPrice(
425 sb,
427 amountBalance,
428 *amount,
429 lptAMMBalance,
430 *ePrice,
431 tfee);
432 if (subTxType & tfSingleAsset)
433 return singleDeposit(
434 sb,
436 amountBalance,
437 lptAMMBalance,
438 *amount,
439 lpTokensDeposit,
440 tfee);
441 if (subTxType & tfLPToken)
442 return equalDepositTokens(
443 sb,
445 amountBalance,
446 amount2Balance,
447 lptAMMBalance,
448 *lpTokensDeposit,
449 amount,
450 amount2,
451 tfee);
452 if (subTxType & tfTwoAssetIfEmpty)
454 sb,
456 *amount,
457 *amount2,
458 lptAMMBalance.issue(),
459 tfee);
460 // should not happen.
461 // LCOV_EXCL_START
462 JLOG(j_.error()) << "AMM Deposit: invalid options.";
464 // LCOV_EXCL_STOP
465 }();
466
467 if (result == tesSUCCESS)
468 {
469 XRPL_ASSERT(
470 newLPTokenBalance > beast::zero,
471 "ripple::AMMDeposit::applyGuts : valid new LP token balance");
472 ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
473 // LP depositing into AMM empty state gets the auction slot
474 // and the voting
475 if (lptAMMBalance == beast::zero)
477 sb, ammSle, account_, lptAMMBalance.issue(), tfee);
478
479 sb.update(ammSle);
480 }
481
482 return {result, result == tesSUCCESS};
483}
484
485TER
487{
488 // This is the ledger view that we work against. Transactions are applied
489 // as we go on processing transactions.
490 Sandbox sb(&ctx_.view());
491
492 auto const result = applyGuts(sb);
493 if (result.second)
494 sb.apply(ctx_.rawView());
495
496 return result.first;
497}
498
501 Sandbox& view,
502 AccountID const& ammAccount,
503 STAmount const& amountBalance,
504 STAmount const& amountDeposit,
505 std::optional<STAmount> const& amount2Deposit,
506 STAmount const& lptAMMBalance,
507 STAmount const& lpTokensDeposit,
508 std::optional<STAmount> const& depositMin,
509 std::optional<STAmount> const& deposit2Min,
510 std::optional<STAmount> const& lpTokensDepositMin,
511 std::uint16_t tfee)
512{
513 // Check account has sufficient funds.
514 // Return true if it does, false otherwise.
515 auto checkBalance = [&](auto const& depositAmount) -> TER {
516 if (depositAmount <= beast::zero)
517 return temBAD_AMOUNT;
518 if (isXRP(depositAmount))
519 {
520 auto const& lpIssue = lpTokensDeposit.issue();
521 // Adjust the reserve if LP doesn't have LPToken trustline
522 auto const sle = view.read(
523 keylet::line(account_, lpIssue.account, lpIssue.currency));
524 if (xrpLiquid(view, account_, !sle, j_) >= depositAmount)
525 return tesSUCCESS;
526 }
527 else if (
528 account_ == depositAmount.issue().account ||
530 view,
531 account_,
532 depositAmount.issue(),
534 ctx_.journal) >= depositAmount)
535 return tesSUCCESS;
536 return tecUNFUNDED_AMM;
537 };
538
539 auto const
540 [amountDepositActual, amount2DepositActual, lpTokensDepositActual] =
542 amountBalance,
543 amountDeposit,
544 amount2Deposit,
545 lptAMMBalance,
546 lpTokensDeposit,
547 tfee,
548 true);
549
550 if (lpTokensDepositActual <= beast::zero)
551 {
552 JLOG(ctx_.journal.debug()) << "AMM Deposit: adjusted tokens zero";
554 }
555
556 if (amountDepositActual < depositMin ||
557 amount2DepositActual < deposit2Min ||
558 lpTokensDepositActual < lpTokensDepositMin)
559 {
560 JLOG(ctx_.journal.debug())
561 << "AMM Deposit: min deposit fails " << amountDepositActual << " "
562 << depositMin.value_or(STAmount{}) << " "
563 << amount2DepositActual.value_or(STAmount{}) << " "
564 << deposit2Min.value_or(STAmount{}) << " " << lpTokensDepositActual
565 << " " << lpTokensDepositMin.value_or(STAmount{});
566 return {tecAMM_FAILED, STAmount{}};
567 }
568
569 // Deposit amountDeposit
570 if (auto const ter = checkBalance(amountDepositActual))
571 {
572 JLOG(ctx_.journal.debug()) << "AMM Deposit: account has insufficient "
573 "checkBalance to deposit or is 0"
574 << amountDepositActual;
575 return {ter, STAmount{}};
576 }
577
578 auto res = accountSend(
579 view,
580 account_,
581 ammAccount,
582 amountDepositActual,
585 if (res != tesSUCCESS)
586 {
587 JLOG(ctx_.journal.debug())
588 << "AMM Deposit: failed to deposit " << amountDepositActual;
589 return {res, STAmount{}};
590 }
591
592 // Deposit amount2Deposit
593 if (amount2DepositActual)
594 {
595 if (auto const ter = checkBalance(*amount2DepositActual))
596 {
597 JLOG(ctx_.journal.debug())
598 << "AMM Deposit: account has insufficient checkBalance to "
599 "deposit or is 0 "
600 << *amount2DepositActual;
601 return {ter, STAmount{}};
602 }
603
604 res = accountSend(
605 view,
606 account_,
607 ammAccount,
608 *amount2DepositActual,
611 if (res != tesSUCCESS)
612 {
613 JLOG(ctx_.journal.debug())
614 << "AMM Deposit: failed to deposit " << *amount2DepositActual;
615 return {res, STAmount{}};
616 }
617 }
618
619 // Deposit LP tokens
620 res = accountSend(
621 view, ammAccount, account_, lpTokensDepositActual, ctx_.journal);
622 if (res != tesSUCCESS)
623 {
624 JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens";
625 return {res, STAmount{}};
626 }
627
628 return {tesSUCCESS, lptAMMBalance + lpTokensDepositActual};
629}
630
636 Sandbox& view,
637 AccountID const& ammAccount,
638 STAmount const& amountBalance,
639 STAmount const& amount2Balance,
640 STAmount const& lptAMMBalance,
641 STAmount const& lpTokensDeposit,
642 std::optional<STAmount> const& depositMin,
643 std::optional<STAmount> const& deposit2Min,
644 std::uint16_t tfee)
645{
646 try
647 {
648 auto const frac =
649 divide(lpTokensDeposit, lptAMMBalance, lptAMMBalance.issue());
650 return deposit(
651 view,
652 ammAccount,
653 amountBalance,
654 multiply(amountBalance, frac, amountBalance.issue()),
655 multiply(amount2Balance, frac, amount2Balance.issue()),
656 lptAMMBalance,
657 lpTokensDeposit,
658 depositMin,
659 deposit2Min,
660 std::nullopt,
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 tokens = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
715 if (tokens == beast::zero)
716 return {tecAMM_FAILED, STAmount{}};
717 auto const amount2Deposit = amount2Balance * frac;
718 if (amount2Deposit <= amount2)
719 return deposit(
720 view,
721 ammAccount,
722 amountBalance,
723 amount,
724 toSTAmount(amount2Balance.issue(), amount2Deposit),
725 lptAMMBalance,
726 tokens,
727 std::nullopt,
728 std::nullopt,
729 lpTokensDepositMin,
730 tfee);
731 frac = Number{amount2} / amount2Balance;
732 tokens = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
733 if (tokens == beast::zero)
734 return {tecAMM_FAILED, STAmount{}};
735 auto const amountDeposit = amountBalance * frac;
736 if (amountDeposit <= amount)
737 return deposit(
738 view,
739 ammAccount,
740 amountBalance,
741 toSTAmount(amountBalance.issue(), amountDeposit),
742 amount2,
743 lptAMMBalance,
744 tokens,
745 std::nullopt,
746 std::nullopt,
747 lpTokensDepositMin,
748 tfee);
749 return {tecAMM_FAILED, STAmount{}};
750}
751
762 Sandbox& view,
763 AccountID const& ammAccount,
764 STAmount const& amountBalance,
765 STAmount const& lptAMMBalance,
766 STAmount const& amount,
767 std::optional<STAmount> const& lpTokensDepositMin,
768 std::uint16_t tfee)
769{
770 auto const tokens = lpTokensIn(amountBalance, amount, lptAMMBalance, tfee);
771 if (tokens == beast::zero)
772 return {tecAMM_FAILED, STAmount{}};
773 return deposit(
774 view,
775 ammAccount,
776 amountBalance,
777 amount,
778 std::nullopt,
779 lptAMMBalance,
780 tokens,
781 std::nullopt,
782 std::nullopt,
783 lpTokensDepositMin,
784 tfee);
785}
786
796 Sandbox& view,
797 AccountID const& ammAccount,
798 STAmount const& amountBalance,
799 STAmount const& amount,
800 STAmount const& lptAMMBalance,
801 STAmount const& lpTokensDeposit,
802 std::uint16_t tfee)
803{
804 auto const amountDeposit =
805 ammAssetIn(amountBalance, lptAMMBalance, lpTokensDeposit, tfee);
806 if (amountDeposit > amount)
807 return {tecAMM_FAILED, STAmount{}};
808 return deposit(
809 view,
810 ammAccount,
811 amountBalance,
812 amountDeposit,
813 std::nullopt,
814 lptAMMBalance,
815 lpTokensDeposit,
816 std::nullopt,
817 std::nullopt,
818 std::nullopt,
819 tfee);
820}
821
849 Sandbox& view,
850 AccountID const& ammAccount,
851 STAmount const& amountBalance,
852 STAmount const& amount,
853 STAmount const& lptAMMBalance,
854 STAmount const& ePrice,
855 std::uint16_t tfee)
856{
857 if (amount != beast::zero)
858 {
859 auto const tokens =
860 lpTokensIn(amountBalance, amount, lptAMMBalance, tfee);
861 if (tokens <= beast::zero)
862 return {tecAMM_FAILED, STAmount{}};
863 auto const ep = Number{amount} / tokens;
864 if (ep <= ePrice)
865 return deposit(
866 view,
867 ammAccount,
868 amountBalance,
869 amount,
870 std::nullopt,
871 lptAMMBalance,
872 tokens,
873 std::nullopt,
874 std::nullopt,
875 std::nullopt,
876 tfee);
877 }
878
879 // LPTokens is asset out => E = b / t
880 // substituting t in formula (3) as b/E:
881 // b/E = T * [b/B - sqrt(t2**2 + b/(f1*B)) + t2]/
882 // [1 + sqrt(t2**2 + b/(f1*B)) -t2] (A)
883 // where f1 = 1 - fee, f2 = (1 - fee/2)/f1
884 // Let R = b/(f1*B), then b/B = f1*R and b = R*f1*B
885 // Then (A) is
886 // R*f1*B = E*T*[R*f1 -sqrt(f2**2 + R) + f2]/[1 + sqrt(f2**2 + R) - f2] =>
887 // Let c = f1*B/(E*T) =>
888 // R*c*(1 + sqrt(f2**2 + R) + f2) = R*f1 - sqrt(f2**2 + R) - f2 =>
889 // (R*c + 1)*sqrt(f2**2 + R) = R*(f1 + c*f2 - c) + f2 =>
890 // Let d = f1 + c*f2 - c =>
891 // (R*c + 1)*sqrt(f2**2 + R) = R*d + f2 =>
892 // (R*c + 1)**2 * (f2**2 + R) = (R*d + f2)**2 =>
893 // (R*c)**2 + R*((c*f2)**2 + 2*c - d**2) + 2*c*f2**2 + 1 -2*d*f2 = 0 =>
894 // a1 = c**2, b1 = (c*f2)**2 + 2*c - d**2, c1 = 2*c*f2**2 + 1 - 2*d*f2
895 // R = (-b1 + sqrt(b1**2 + 4*a1*c1))/(2*a1)
896 auto const f1 = feeMult(tfee);
897 auto const f2 = feeMultHalf(tfee) / f1;
898 auto const c = f1 * amountBalance / (ePrice * lptAMMBalance);
899 auto const d = f1 + c * f2 - c;
900 auto const a1 = c * c;
901 auto const b1 = c * c * f2 * f2 + 2 * c - d * d;
902 auto const c1 = 2 * c * f2 * f2 + 1 - 2 * d * f2;
903 auto const amountDeposit = toSTAmount(
904 amountBalance.issue(),
905 f1 * amountBalance * solveQuadraticEq(a1, b1, c1));
906 if (amountDeposit <= beast::zero)
907 return {tecAMM_FAILED, STAmount{}};
908 auto const tokens =
909 toSTAmount(lptAMMBalance.issue(), amountDeposit / ePrice);
910 return deposit(
911 view,
912 ammAccount,
913 amountBalance,
914 amountDeposit,
915 std::nullopt,
916 lptAMMBalance,
917 tokens,
918 std::nullopt,
919 std::nullopt,
920 std::nullopt,
921 tfee);
922}
923
926 Sandbox& view,
927 AccountID const& ammAccount,
928 STAmount const& amount,
929 STAmount const& amount2,
930 Issue const& lptIssue,
931 std::uint16_t tfee)
932{
933 return deposit(
934 view,
935 ammAccount,
936 amount,
937 amount,
938 amount2,
939 STAmount{lptIssue, 0},
940 ammLPTokens(amount, amount2, lptIssue),
941 std::nullopt,
942 std::nullopt,
943 std::nullopt,
944 tfee);
945}
946
947} // namespace ripple
Stream error() const
Definition: Journal.h:335
Stream debug() const
Definition: Journal.h:317
std::pair< TER, bool > applyGuts(Sandbox &view)
Definition: AMMDeposit.cpp:371
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:702
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:848
static TER preclaim(PreclaimContext const &ctx)
Definition: AMMDeposit.cpp:169
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:761
static NotTEC preflight(PreflightContext const &ctx)
Definition: AMMDeposit.cpp:36
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:635
TER doApply() override
Definition: AMMDeposit.cpp:486
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:925
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:795
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:500
RawView & rawView()
Definition: ApplyContext.h:67
ApplyView & view()
Definition: ApplyContext.h:54
beast::Journal const journal
Definition: ApplyContext.h:51
A currency issued by an account.
Definition: Issue.h:36
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.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:122
Issue const & issue() const
Definition: STAmount.h:487
std::uint32_t getFlags() const
Definition: STObject.cpp:507
Discardable, editable view to a ledger.
Definition: Sandbox.h:35
void apply(RawView &to)
Definition: Sandbox.h:55
AccountID const account_
Definition: Transactor.h:91
ApplyView & view()
Definition: Transactor.h:107
beast::Journal const j_
Definition: Transactor.h:89
ApplyContext & ctx_
Definition: Transactor.h:88
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 make_optional(T... args)
T make_pair(T... args)
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:422
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:220
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
constexpr std::uint32_t tfSingleAsset
Definition: TxFlags.h:206
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
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:87
STAmount lpTokensIn(STAmount const &asset1Balance, STAmount const &asset1Deposit, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's deposit amount.
Definition: AMMHelpers.cpp:41
@ fhZERO_IF_FROZEN
Definition: View.h:80
@ fhIGNORE_FREEZE
Definition: View.h:80
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
bool isIndividualFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:204
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
Definition: AMMUtils.cpp:177
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)
Definition: AMMHelpers.cpp:220
TER requireAuth(ReadView const &view, Issue const &issue, AccountID const &account)
Check if the account lacks required authorization.
Definition: View.cpp:1899
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
constexpr std::uint32_t tfLimitLPToken
Definition: TxFlags.h:209
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset deposit given LP Tokens.
Definition: AMMHelpers.cpp:67
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:47
bool isTesSuccess(TER x)
Definition: TER.h:656
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition: AMMCore.cpp:128
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:82
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)
Calls adjustLPTokens() and adjusts deposit or withdraw amounts if the adjusted LP tokens are less tha...
Definition: AMMHelpers.cpp:147
constexpr std::uint32_t tfOneAssetLPToken
Definition: TxFlags.h:208
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:238
AccountID ammAccountID(std::uint16_t prefix, uint256 const &parentHash, uint256 const &ammID)
Calculate AMM account ID.
Definition: AMMCore.cpp:30
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition: AMMCore.h:118
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:338
constexpr std::uint32_t tfTwoAsset
Definition: TxFlags.h:207
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:111
constexpr std::uint32_t tfDepositSubTx
Definition: TxFlags.h:214
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:134
@ tecINSUF_RESERVE_LINE
Definition: TER.h:275
@ tecFROZEN
Definition: TER.h:290
@ tecAMM_EMPTY
Definition: TER.h:319
@ tecINTERNAL
Definition: TER.h:297
@ tecAMM_NOT_EMPTY
Definition: TER.h:320
@ tecUNFUNDED_AMM
Definition: TER.h:315
@ tecAMM_FAILED
Definition: TER.h:317
@ tecAMM_INVALID_TOKENS
Definition: TER.h:318
constexpr std::uint32_t tfLPToken
Definition: TxFlags.h:203
@ tesSUCCESS
Definition: TER.h:242
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
Definition: AMMCore.cpp:79
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:308
constexpr std::uint32_t tfDepositMask
Definition: TxFlags.h:218
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
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Issue const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
Definition: AMMHelpers.cpp:25
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
constexpr std::uint32_t tfTwoAssetIfEmpty
Definition: TxFlags.h:210
Number feeMultHalf(std::uint16_t tfee)
Get fee multiplier (1 - tfee / 2) @tfee trading fee in basis points.
Definition: AMMCore.h:127
@ terNO_AMM
Definition: TER.h:227
TERSubset< CanCvtToTER > TER
Definition: TER.h:627
std::uint16_t constexpr TRADING_FEE_THRESHOLD
Definition: AMMCore.h:31
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Calls static accountSendIOU if saAmount represents Issue.
Definition: View.cpp:1609
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:587
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition: View.cpp:506
@ temBAD_AMOUNT
Definition: TER.h:89
@ temBAD_FEE
Definition: TER.h:92
@ temMALFORMED
Definition: TER.h:87
@ temBAD_AMM_TOKENS
Definition: TER.h:129
@ temINVALID_FLAG
Definition: TER.h:111
@ temDISABLED
Definition: TER.h:114
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:53
ReadView const & view
Definition: Transactor.h:56
beast::Journal const j
Definition: Transactor.h:60
State information when preflighting a tx.
Definition: Transactor.h:32
beast::Journal const j
Definition: Transactor.h:38
T value_or(T... args)
T what(T... args)