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/misc/AMMHelpers.h>
21#include <xrpld/app/misc/AMMUtils.h>
22#include <xrpld/app/tx/detail/AMMDeposit.h>
23#include <xrpld/ledger/Sandbox.h>
24#include <xrpld/ledger/View.h>
25
26#include <xrpl/protocol/AMMCore.h>
27#include <xrpl/protocol/Feature.h>
28#include <xrpl/protocol/TxFlags.h>
29
30namespace ripple {
31
34{
35 if (!ammEnabled(ctx.rules))
36 return temDISABLED;
37
38 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
39 return ret;
40
41 auto const flags = ctx.tx.getFlags();
42 if (flags & tfDepositMask)
43 {
44 JLOG(ctx.j.debug()) << "AMM Deposit: invalid flags.";
45 return temINVALID_FLAG;
46 }
47
48 auto const amount = ctx.tx[~sfAmount];
49 auto const amount2 = ctx.tx[~sfAmount2];
50 auto const ePrice = ctx.tx[~sfEPrice];
51 auto const lpTokens = ctx.tx[~sfLPTokenOut];
52 auto const tradingFee = ctx.tx[~sfTradingFee];
53 // Valid options for the flags are:
54 // tfLPTokens: LPTokenOut, [Amount, Amount2]
55 // tfSingleAsset: Amount, [LPTokenOut]
56 // tfTwoAsset: Amount, Amount2, [LPTokenOut]
57 // tfTwoAssetIfEmpty: Amount, Amount2, [sfTradingFee]
58 // tfOnAssetLPToken: Amount and LPTokenOut
59 // tfLimitLPToken: Amount and EPrice
60 if (std::popcount(flags & tfDepositSubTx) != 1)
61 {
62 JLOG(ctx.j.debug()) << "AMM Deposit: invalid flags.";
63 return temMALFORMED;
64 }
65 if (flags & tfLPToken)
66 {
67 // if included then both amount and amount2 are deposit min
68 if (!lpTokens || ePrice || (amount && !amount2) ||
69 (!amount && amount2) || tradingFee)
70 return temMALFORMED;
71 }
72 else if (flags & tfSingleAsset)
73 {
74 // if included then lpTokens is deposit min
75 if (!amount || amount2 || ePrice || tradingFee)
76 return temMALFORMED;
77 }
78 else if (flags & tfTwoAsset)
79 {
80 // if included then lpTokens is deposit min
81 if (!amount || !amount2 || ePrice || tradingFee)
82 return temMALFORMED;
83 }
84 else if (flags & tfOneAssetLPToken)
85 {
86 if (!amount || !lpTokens || amount2 || ePrice || tradingFee)
87 return temMALFORMED;
88 }
89 else if (flags & tfLimitLPToken)
90 {
91 if (!amount || !ePrice || lpTokens || amount2 || tradingFee)
92 return temMALFORMED;
93 }
94 else if (flags & tfTwoAssetIfEmpty)
95 {
96 if (!amount || !amount2 || ePrice || lpTokens)
97 return temMALFORMED;
98 }
99
100 auto const asset = ctx.tx[sfAsset].get<Issue>();
101 auto const asset2 = ctx.tx[sfAsset2].get<Issue>();
102 if (auto const res = invalidAMMAssetPair(asset, asset2))
103 {
104 JLOG(ctx.j.debug()) << "AMM Deposit: invalid asset pair.";
105 return res;
106 }
107
108 if (amount && amount2 && amount->issue() == amount2->issue())
109 {
110 JLOG(ctx.j.debug()) << "AMM Deposit: invalid tokens, same issue."
111 << amount->issue() << " " << amount2->issue();
112 return temBAD_AMM_TOKENS;
113 }
114
115 if (lpTokens && *lpTokens <= beast::zero)
116 {
117 JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens";
118 return temBAD_AMM_TOKENS;
119 }
120
121 if (amount)
122 {
123 if (auto const res = invalidAMMAmount(
124 *amount,
125 std::make_optional(std::make_pair(asset, asset2)),
126 ePrice.has_value()))
127 {
128 JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount";
129 return res;
130 }
131 }
132
133 if (amount2)
134 {
135 if (auto const res = invalidAMMAmount(
136 *amount2, std::make_optional(std::make_pair(asset, asset2))))
137 {
138 JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount2";
139 return res;
140 }
141 }
142
143 // must be amount issue
144 if (amount && ePrice)
145 {
146 if (auto const res = invalidAMMAmount(
147 *ePrice,
149 std::make_pair(amount->issue(), amount->issue()))))
150 {
151 JLOG(ctx.j.debug()) << "AMM Deposit: invalid EPrice";
152 return res;
153 }
154 }
155
156 if (tradingFee > TRADING_FEE_THRESHOLD)
157 {
158 JLOG(ctx.j.debug()) << "AMM Deposit: invalid trading fee.";
159 return temBAD_FEE;
160 }
161
162 return preflight2(ctx);
163}
164
165TER
167{
168 auto const accountID = ctx.tx[sfAccount];
169
170 auto const ammSle =
171 ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
172 if (!ammSle)
173 {
174 JLOG(ctx.j.debug()) << "AMM Deposit: Invalid asset pair.";
175 return terNO_AMM;
176 }
177
178 auto const expected = ammHolds(
179 ctx.view,
180 *ammSle,
181 std::nullopt,
182 std::nullopt,
184 ctx.j);
185 if (!expected)
186 return expected.error(); // LCOV_EXCL_LINE
187 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
188 if (ctx.tx.getFlags() & tfTwoAssetIfEmpty)
189 {
190 if (lptAMMBalance != beast::zero)
191 return tecAMM_NOT_EMPTY;
192 if (amountBalance != beast::zero || amount2Balance != beast::zero)
193 {
194 // LCOV_EXCL_START
195 JLOG(ctx.j.debug()) << "AMM Deposit: tokens balance is not zero.";
196 return tecINTERNAL;
197 // LCOV_EXCL_STOP
198 }
199 }
200 else
201 {
202 if (lptAMMBalance == beast::zero)
203 return tecAMM_EMPTY;
204 if (amountBalance <= beast::zero || amount2Balance <= beast::zero ||
205 lptAMMBalance < beast::zero)
206 {
207 // LCOV_EXCL_START
208 JLOG(ctx.j.debug())
209 << "AMM Deposit: reserves or tokens balance is zero.";
210 return tecINTERNAL;
211 // LCOV_EXCL_STOP
212 }
213 }
214
215 // Check account has sufficient funds.
216 // Return tesSUCCESS if it does, error otherwise.
217 // Have to check again in deposit() because
218 // amounts might be derived based on tokens or
219 // limits.
220 auto balance = [&](auto const& deposit) -> TER {
221 if (isXRP(deposit))
222 {
223 auto const lpIssue = (*ammSle)[sfLPTokenBalance].issue();
224 // Adjust the reserve if LP doesn't have LPToken trustline
225 auto const sle = ctx.view.read(
226 keylet::line(accountID, lpIssue.account, lpIssue.currency));
227 if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit)
228 return TER(tesSUCCESS);
229 if (sle)
230 return tecUNFUNDED_AMM;
232 }
233 return (accountID == deposit.issue().account ||
235 ctx.view,
236 accountID,
237 deposit.issue(),
239 ctx.j) >= deposit)
240 ? TER(tesSUCCESS)
242 };
243
244 if (ctx.view.rules().enabled(featureAMMClawback))
245 {
246 // Check if either of the assets is frozen, AMMDeposit is not allowed
247 // if either asset is frozen
248 auto checkAsset = [&](Issue const& asset) -> TER {
249 if (auto const ter = requireAuth(ctx.view, asset, accountID))
250 {
251 JLOG(ctx.j.debug())
252 << "AMM Deposit: account is not authorized, " << asset;
253 return ter;
254 }
255
256 if (isFrozen(ctx.view, accountID, asset))
257 {
258 JLOG(ctx.j.debug())
259 << "AMM Deposit: account or currency is frozen, "
260 << to_string(accountID) << " " << to_string(asset.currency);
261
262 return tecFROZEN;
263 }
264
265 return tesSUCCESS;
266 };
267
268 if (auto const ter = checkAsset(ctx.tx[sfAsset].get<Issue>()))
269 return ter;
270
271 if (auto const ter = checkAsset(ctx.tx[sfAsset2].get<Issue>()))
272 return ter;
273 }
274
275 auto const amount = ctx.tx[~sfAmount];
276 auto const amount2 = ctx.tx[~sfAmount2];
277 auto const ammAccountID = ammSle->getAccountID(sfAccount);
278
279 auto checkAmount = [&](std::optional<STAmount> const& amount,
280 bool checkBalance) -> TER {
281 if (amount)
282 {
283 // This normally should not happen.
284 // Account is not authorized to hold the assets it's depositing,
285 // or it doesn't even have a trust line for them
286 if (auto const ter =
287 requireAuth(ctx.view, amount->issue(), accountID))
288 {
289 // LCOV_EXCL_START
290 JLOG(ctx.j.debug())
291 << "AMM Deposit: account is not authorized, "
292 << amount->issue();
293 return ter;
294 // LCOV_EXCL_STOP
295 }
296 // AMM account or currency frozen
297 if (isFrozen(ctx.view, ammAccountID, amount->issue()))
298 {
299 JLOG(ctx.j.debug())
300 << "AMM Deposit: AMM account or currency is frozen, "
301 << to_string(accountID);
302 return tecFROZEN;
303 }
304 // Account frozen
305 if (isIndividualFrozen(ctx.view, accountID, amount->issue()))
306 {
307 JLOG(ctx.j.debug()) << "AMM Deposit: account is frozen, "
308 << to_string(accountID) << " "
309 << to_string(amount->issue().currency);
310 return tecFROZEN;
311 }
312 if (checkBalance)
313 {
314 if (auto const ter = balance(*amount))
315 {
316 JLOG(ctx.j.debug())
317 << "AMM Deposit: account has insufficient funds, "
318 << *amount;
319 return ter;
320 }
321 }
322 }
323 return tesSUCCESS;
324 };
325
326 // amount and amount2 are deposit min in case of tfLPToken
327 if (!(ctx.tx.getFlags() & tfLPToken))
328 {
329 if (auto const ter = checkAmount(amount, true))
330 return ter;
331
332 if (auto const ter = checkAmount(amount2, true))
333 return ter;
334 }
335 else
336 {
337 if (auto const ter = checkAmount(amountBalance, false))
338 return ter;
339 if (auto const ter = checkAmount(amount2Balance, false))
340 return ter;
341 }
342
343 // Equal deposit lp tokens
344 if (auto const lpTokens = ctx.tx[~sfLPTokenOut];
345 lpTokens && lpTokens->issue() != lptAMMBalance.issue())
346 {
347 JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens.";
348 return temBAD_AMM_TOKENS;
349 }
350
351 // Check the reserve for LPToken trustline if not LP.
352 // We checked above but need to check again if depositing IOU only.
353 if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::zero)
354 {
355 STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
356 // Insufficient reserve
357 if (xrpBalance <= beast::zero)
358 {
359 JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves";
361 }
362 }
363
364 return tesSUCCESS;
365}
366
369{
370 auto const amount = ctx_.tx[~sfAmount];
371 auto const amount2 = ctx_.tx[~sfAmount2];
372 auto const ePrice = ctx_.tx[~sfEPrice];
373 auto const lpTokensDeposit = ctx_.tx[~sfLPTokenOut];
374 auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
375 if (!ammSle)
376 return {tecINTERNAL, false}; // LCOV_EXCL_LINE
377 auto const ammAccountID = (*ammSle)[sfAccount];
378
379 auto const expected = ammHolds(
380 sb,
381 *ammSle,
382 amount ? amount->issue() : std::optional<Issue>{},
383 amount2 ? amount2->issue() : std::optional<Issue>{},
385 ctx_.journal);
386 if (!expected)
387 return {expected.error(), false}; // LCOV_EXCL_LINE
388 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
389 auto const tfee = (lptAMMBalance == beast::zero)
390 ? ctx_.tx[~sfTradingFee].value_or(0)
391 : getTradingFee(ctx_.view(), *ammSle, account_);
392
393 auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx;
394
395 auto const [result, newLPTokenBalance] =
396 [&,
397 &amountBalance = amountBalance,
398 &amount2Balance = amount2Balance,
399 &lptAMMBalance = lptAMMBalance]() -> std::pair<TER, STAmount> {
400 if (subTxType & tfTwoAsset)
401 return equalDepositLimit(
402 sb,
403 ammAccountID,
404 amountBalance,
405 amount2Balance,
406 lptAMMBalance,
407 *amount,
408 *amount2,
409 lpTokensDeposit,
410 tfee);
411 if (subTxType & tfOneAssetLPToken)
412 return singleDepositTokens(
413 sb,
414 ammAccountID,
415 amountBalance,
416 *amount,
417 lptAMMBalance,
418 *lpTokensDeposit,
419 tfee);
420 if (subTxType & tfLimitLPToken)
421 return singleDepositEPrice(
422 sb,
423 ammAccountID,
424 amountBalance,
425 *amount,
426 lptAMMBalance,
427 *ePrice,
428 tfee);
429 if (subTxType & tfSingleAsset)
430 return singleDeposit(
431 sb,
432 ammAccountID,
433 amountBalance,
434 lptAMMBalance,
435 *amount,
436 lpTokensDeposit,
437 tfee);
438 if (subTxType & tfLPToken)
439 return equalDepositTokens(
440 sb,
441 ammAccountID,
442 amountBalance,
443 amount2Balance,
444 lptAMMBalance,
445 *lpTokensDeposit,
446 amount,
447 amount2,
448 tfee);
449 if (subTxType & tfTwoAssetIfEmpty)
451 sb,
452 ammAccountID,
453 *amount,
454 *amount2,
455 lptAMMBalance.issue(),
456 tfee);
457 // should not happen.
458 // LCOV_EXCL_START
459 JLOG(j_.error()) << "AMM Deposit: invalid options.";
461 // LCOV_EXCL_STOP
462 }();
463
464 if (result == tesSUCCESS)
465 {
466 XRPL_ASSERT(
467 newLPTokenBalance > beast::zero,
468 "ripple::AMMDeposit::applyGuts : valid new LP token balance");
469 ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
470 // LP depositing into AMM empty state gets the auction slot
471 // and the voting
472 if (lptAMMBalance == beast::zero)
474 sb, ammSle, account_, lptAMMBalance.issue(), tfee);
475
476 sb.update(ammSle);
477 }
478
479 return {result, result == tesSUCCESS};
480}
481
482TER
484{
485 // This is the ledger view that we work against. Transactions are applied
486 // as we go on processing transactions.
487 Sandbox sb(&ctx_.view());
488
489 auto const result = applyGuts(sb);
490 if (result.second)
491 sb.apply(ctx_.rawView());
492
493 return result.first;
494}
495
498 Sandbox& view,
499 AccountID const& ammAccount,
500 STAmount const& amountBalance,
501 STAmount const& amountDeposit,
502 std::optional<STAmount> const& amount2Deposit,
503 STAmount const& lptAMMBalance,
504 STAmount const& lpTokensDeposit,
505 std::optional<STAmount> const& depositMin,
506 std::optional<STAmount> const& deposit2Min,
507 std::optional<STAmount> const& lpTokensDepositMin,
508 std::uint16_t tfee)
509{
510 // Check account has sufficient funds.
511 // Return true if it does, false otherwise.
512 auto checkBalance = [&](auto const& depositAmount) -> TER {
513 if (depositAmount <= beast::zero)
514 return temBAD_AMOUNT;
515 if (isXRP(depositAmount))
516 {
517 auto const& lpIssue = lpTokensDeposit.issue();
518 // Adjust the reserve if LP doesn't have LPToken trustline
519 auto const sle = view.read(
520 keylet::line(account_, lpIssue.account, lpIssue.currency));
521 if (xrpLiquid(view, account_, !sle, j_) >= depositAmount)
522 return tesSUCCESS;
523 }
524 else if (
525 account_ == depositAmount.issue().account ||
527 view,
528 account_,
529 depositAmount.issue(),
531 ctx_.journal) >= depositAmount)
532 return tesSUCCESS;
533 return tecUNFUNDED_AMM;
534 };
535
536 auto const
537 [amountDepositActual, amount2DepositActual, lpTokensDepositActual] =
539 amountBalance,
540 amountDeposit,
541 amount2Deposit,
542 lptAMMBalance,
543 lpTokensDeposit,
544 tfee,
545 true);
546
547 if (lpTokensDepositActual <= beast::zero)
548 {
549 JLOG(ctx_.journal.debug()) << "AMM Deposit: adjusted tokens zero";
551 }
552
553 if (amountDepositActual < depositMin ||
554 amount2DepositActual < deposit2Min ||
555 lpTokensDepositActual < lpTokensDepositMin)
556 {
557 JLOG(ctx_.journal.debug())
558 << "AMM Deposit: min deposit fails " << amountDepositActual << " "
559 << depositMin.value_or(STAmount{}) << " "
560 << amount2DepositActual.value_or(STAmount{}) << " "
561 << deposit2Min.value_or(STAmount{}) << " " << lpTokensDepositActual
562 << " " << lpTokensDepositMin.value_or(STAmount{});
563 return {tecAMM_FAILED, STAmount{}};
564 }
565
566 // Deposit amountDeposit
567 if (auto const ter = checkBalance(amountDepositActual))
568 {
569 JLOG(ctx_.journal.debug()) << "AMM Deposit: account has insufficient "
570 "checkBalance to deposit or is 0"
571 << amountDepositActual;
572 return {ter, STAmount{}};
573 }
574
575 auto res = accountSend(
576 view,
577 account_,
578 ammAccount,
579 amountDepositActual,
582 if (res != tesSUCCESS)
583 {
584 JLOG(ctx_.journal.debug())
585 << "AMM Deposit: failed to deposit " << amountDepositActual;
586 return {res, STAmount{}};
587 }
588
589 // Deposit amount2Deposit
590 if (amount2DepositActual)
591 {
592 if (auto const ter = checkBalance(*amount2DepositActual))
593 {
594 JLOG(ctx_.journal.debug())
595 << "AMM Deposit: account has insufficient checkBalance to "
596 "deposit or is 0 "
597 << *amount2DepositActual;
598 return {ter, STAmount{}};
599 }
600
601 res = accountSend(
602 view,
603 account_,
604 ammAccount,
605 *amount2DepositActual,
608 if (res != tesSUCCESS)
609 {
610 JLOG(ctx_.journal.debug())
611 << "AMM Deposit: failed to deposit " << *amount2DepositActual;
612 return {res, STAmount{}};
613 }
614 }
615
616 // Deposit LP tokens
617 res = accountSend(
618 view, ammAccount, account_, lpTokensDepositActual, ctx_.journal);
619 if (res != tesSUCCESS)
620 {
621 JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens";
622 return {res, STAmount{}};
623 }
624
625 return {tesSUCCESS, lptAMMBalance + lpTokensDepositActual};
626}
627
633 Sandbox& view,
634 AccountID const& ammAccount,
635 STAmount const& amountBalance,
636 STAmount const& amount2Balance,
637 STAmount const& lptAMMBalance,
638 STAmount const& lpTokensDeposit,
639 std::optional<STAmount> const& depositMin,
640 std::optional<STAmount> const& deposit2Min,
641 std::uint16_t tfee)
642{
643 try
644 {
645 auto const frac =
646 divide(lpTokensDeposit, lptAMMBalance, lptAMMBalance.issue());
647 return deposit(
648 view,
649 ammAccount,
650 amountBalance,
651 multiply(amountBalance, frac, amountBalance.issue()),
652 multiply(amount2Balance, frac, amount2Balance.issue()),
653 lptAMMBalance,
654 lpTokensDeposit,
655 depositMin,
656 deposit2Min,
657 std::nullopt,
658 tfee);
659 }
660 catch (std::exception const& e)
661 {
662 // LCOV_EXCL_START
663 JLOG(j_.error()) << "AMMDeposit::equalDepositTokens exception "
664 << e.what();
665 return {tecINTERNAL, STAmount{}};
666 // LCOV_EXCL_STOP
667 }
668}
669
700 Sandbox& view,
701 AccountID const& ammAccount,
702 STAmount const& amountBalance,
703 STAmount const& amount2Balance,
704 STAmount const& lptAMMBalance,
705 STAmount const& amount,
706 STAmount const& amount2,
707 std::optional<STAmount> const& lpTokensDepositMin,
708 std::uint16_t tfee)
709{
710 auto frac = Number{amount} / amountBalance;
711 auto tokens = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
712 if (tokens == beast::zero)
713 return {tecAMM_FAILED, STAmount{}};
714 auto const amount2Deposit = amount2Balance * frac;
715 if (amount2Deposit <= amount2)
716 return deposit(
717 view,
718 ammAccount,
719 amountBalance,
720 amount,
721 toSTAmount(amount2Balance.issue(), amount2Deposit),
722 lptAMMBalance,
723 tokens,
724 std::nullopt,
725 std::nullopt,
726 lpTokensDepositMin,
727 tfee);
728 frac = Number{amount2} / amount2Balance;
729 tokens = toSTAmount(lptAMMBalance.issue(), lptAMMBalance * frac);
730 if (tokens == beast::zero)
731 return {tecAMM_FAILED, STAmount{}};
732 auto const amountDeposit = amountBalance * frac;
733 if (amountDeposit <= amount)
734 return deposit(
735 view,
736 ammAccount,
737 amountBalance,
738 toSTAmount(amountBalance.issue(), amountDeposit),
739 amount2,
740 lptAMMBalance,
741 tokens,
742 std::nullopt,
743 std::nullopt,
744 lpTokensDepositMin,
745 tfee);
746 return {tecAMM_FAILED, STAmount{}};
747}
748
759 Sandbox& view,
760 AccountID const& ammAccount,
761 STAmount const& amountBalance,
762 STAmount const& lptAMMBalance,
763 STAmount const& amount,
764 std::optional<STAmount> const& lpTokensDepositMin,
765 std::uint16_t tfee)
766{
767 auto const tokens = lpTokensIn(amountBalance, amount, lptAMMBalance, tfee);
768 if (tokens == beast::zero)
769 return {tecAMM_FAILED, STAmount{}};
770 return deposit(
771 view,
772 ammAccount,
773 amountBalance,
774 amount,
775 std::nullopt,
776 lptAMMBalance,
777 tokens,
778 std::nullopt,
779 std::nullopt,
780 lpTokensDepositMin,
781 tfee);
782}
783
793 Sandbox& view,
794 AccountID const& ammAccount,
795 STAmount const& amountBalance,
796 STAmount const& amount,
797 STAmount const& lptAMMBalance,
798 STAmount const& lpTokensDeposit,
799 std::uint16_t tfee)
800{
801 auto const amountDeposit =
802 ammAssetIn(amountBalance, lptAMMBalance, lpTokensDeposit, tfee);
803 if (amountDeposit > amount)
804 return {tecAMM_FAILED, STAmount{}};
805 return deposit(
806 view,
807 ammAccount,
808 amountBalance,
809 amountDeposit,
810 std::nullopt,
811 lptAMMBalance,
812 lpTokensDeposit,
813 std::nullopt,
814 std::nullopt,
815 std::nullopt,
816 tfee);
817}
818
846 Sandbox& view,
847 AccountID const& ammAccount,
848 STAmount const& amountBalance,
849 STAmount const& amount,
850 STAmount const& lptAMMBalance,
851 STAmount const& ePrice,
852 std::uint16_t tfee)
853{
854 if (amount != beast::zero)
855 {
856 auto const tokens =
857 lpTokensIn(amountBalance, amount, lptAMMBalance, tfee);
858 if (tokens <= beast::zero)
859 return {tecAMM_FAILED, STAmount{}};
860 auto const ep = Number{amount} / tokens;
861 if (ep <= ePrice)
862 return deposit(
863 view,
864 ammAccount,
865 amountBalance,
866 amount,
867 std::nullopt,
868 lptAMMBalance,
869 tokens,
870 std::nullopt,
871 std::nullopt,
872 std::nullopt,
873 tfee);
874 }
875
876 // LPTokens is asset out => E = b / t
877 // substituting t in formula (3) as b/E:
878 // b/E = T * [b/B - sqrt(t2**2 + b/(f1*B)) + t2]/
879 // [1 + sqrt(t2**2 + b/(f1*B)) -t2] (A)
880 // where f1 = 1 - fee, f2 = (1 - fee/2)/f1
881 // Let R = b/(f1*B), then b/B = f1*R and b = R*f1*B
882 // Then (A) is
883 // R*f1*B = E*T*[R*f1 -sqrt(f2**2 + R) + f2]/[1 + sqrt(f2**2 + R) - f2] =>
884 // Let c = f1*B/(E*T) =>
885 // R*c*(1 + sqrt(f2**2 + R) + f2) = R*f1 - sqrt(f2**2 + R) - f2 =>
886 // (R*c + 1)*sqrt(f2**2 + R) = R*(f1 + c*f2 - c) + f2 =>
887 // Let d = f1 + c*f2 - c =>
888 // (R*c + 1)*sqrt(f2**2 + R) = R*d + f2 =>
889 // (R*c + 1)**2 * (f2**2 + R) = (R*d + f2)**2 =>
890 // (R*c)**2 + R*((c*f2)**2 + 2*c - d**2) + 2*c*f2**2 + 1 -2*d*f2 = 0 =>
891 // a1 = c**2, b1 = (c*f2)**2 + 2*c - d**2, c1 = 2*c*f2**2 + 1 - 2*d*f2
892 // R = (-b1 + sqrt(b1**2 + 4*a1*c1))/(2*a1)
893 auto const f1 = feeMult(tfee);
894 auto const f2 = feeMultHalf(tfee) / f1;
895 auto const c = f1 * amountBalance / (ePrice * lptAMMBalance);
896 auto const d = f1 + c * f2 - c;
897 auto const a1 = c * c;
898 auto const b1 = c * c * f2 * f2 + 2 * c - d * d;
899 auto const c1 = 2 * c * f2 * f2 + 1 - 2 * d * f2;
900 auto const amountDeposit = toSTAmount(
901 amountBalance.issue(),
902 f1 * amountBalance * solveQuadraticEq(a1, b1, c1));
903 if (amountDeposit <= beast::zero)
904 return {tecAMM_FAILED, STAmount{}};
905 auto const tokens =
906 toSTAmount(lptAMMBalance.issue(), amountDeposit / ePrice);
907 return deposit(
908 view,
909 ammAccount,
910 amountBalance,
911 amountDeposit,
912 std::nullopt,
913 lptAMMBalance,
914 tokens,
915 std::nullopt,
916 std::nullopt,
917 std::nullopt,
918 tfee);
919}
920
923 Sandbox& view,
924 AccountID const& ammAccount,
925 STAmount const& amount,
926 STAmount const& amount2,
927 Issue const& lptIssue,
928 std::uint16_t tfee)
929{
930 return deposit(
931 view,
932 ammAccount,
933 amount,
934 amount,
935 amount2,
936 STAmount{lptIssue, 0},
937 ammLPTokens(amount, amount2, lptIssue),
938 std::nullopt,
939 std::nullopt,
940 std::nullopt,
941 tfee);
942}
943
944} // namespace ripple
Stream error() const
Definition: Journal.h:346
Stream debug() const
Definition: Journal.h:328
std::pair< TER, bool > applyGuts(Sandbox &view)
Definition: AMMDeposit.cpp:368
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:699
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:845
static TER preclaim(PreclaimContext const &ctx)
Definition: AMMDeposit.cpp:166
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:758
static NotTEC preflight(PreflightContext const &ctx)
Definition: AMMDeposit.cpp:33
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:632
TER doApply() override
Definition: AMMDeposit.cpp:483
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:922
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:792
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:497
RawView & rawView()
Definition: ApplyContext.h:68
ApplyView & view()
Definition: ApplyContext.h:55
beast::Journal const journal
Definition: ApplyContext.h:52
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:130
Issue const & issue() const
Definition: STAmount.h:496
AccountID getAccountID(SField const &field) const
Definition: STObject.cpp:651
std::uint32_t getFlags() const
Definition: STObject.cpp:537
Discardable, editable view to a ledger.
Definition: Sandbox.h:35
void apply(RawView &to)
Definition: Sandbox.h:55
AccountID const account_
Definition: Transactor.h:93
ApplyView & view()
Definition: Transactor.h:109
beast::Journal const j_
Definition: Transactor.h:91
ApplyContext & ctx_
Definition: Transactor.h:90
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.
A balance matches.
Definition: balance.h:39
Match set account flags.
Definition: flags.h:125
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
T make_optional(T... args)
T make_pair(T... args)
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:439
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:237
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:215
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
Definition: AMMCore.cpp:95
STAmount divide(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:93
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:78
@ fhIGNORE_FREEZE
Definition: View.h:78
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:216
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
Definition: AMMUtils.cpp:178
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:2268
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
constexpr std::uint32_t tfLimitLPToken
Definition: TxFlags.h:218
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:53
bool isTesSuccess(TER x)
Definition: TER.h:672
bool ammEnabled(Rules const &)
Return true if required AMM amendments are enabled.
Definition: AMMCore.cpp:129
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:83
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:217
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:250
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition: AMMCore.h:110
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:339
constexpr std::uint32_t tfTwoAsset
Definition: TxFlags.h:216
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:112
constexpr std::uint32_t tfDepositSubTx
Definition: TxFlags.h:223
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:144
@ tecINSUF_RESERVE_LINE
Definition: TER.h:288
@ tecFROZEN
Definition: TER.h:303
@ tecAMM_EMPTY
Definition: TER.h:332
@ tecINTERNAL
Definition: TER.h:310
@ tecAMM_NOT_EMPTY
Definition: TER.h:333
@ tecUNFUNDED_AMM
Definition: TER.h:328
@ tecAMM_FAILED
Definition: TER.h:330
@ tecAMM_INVALID_TOKENS
Definition: TER.h:331
constexpr std::uint32_t tfLPToken
Definition: TxFlags.h:212
@ tesSUCCESS
Definition: TER.h:244
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
Definition: AMMCore.cpp:80
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:387
constexpr std::uint32_t tfDepositMask
Definition: TxFlags.h:227
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:46
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:630
constexpr std::uint32_t tfTwoAssetIfEmpty
Definition: TxFlags.h:219
Number feeMultHalf(std::uint16_t tfee)
Get fee multiplier (1 - tfee / 2) @tfee trading fee in basis points.
Definition: AMMCore.h:119
@ terNO_AMM
Definition: TER.h:227
TERSubset< CanCvtToTER > TER
Definition: TER.h:643
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:1978
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:603
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Definition: View.cpp:618
@ 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:55
ReadView const & view
Definition: Transactor.h:58
beast::Journal const j
Definition: Transactor.h:62
State information when preflighting a tx.
Definition: Transactor.h:34
beast::Journal const j
Definition: Transactor.h:40
T value_or(T... args)
T what(T... args)