xrpld
Loading...
Searching...
No Matches
AMMDeposit.cpp
1#include <xrpl/tx/transactors/dex/AMMDeposit.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/beast/utility/instrumentation.h>
7#include <xrpl/ledger/Sandbox.h>
8#include <xrpl/ledger/helpers/AMMHelpers.h>
9#include <xrpl/ledger/helpers/AccountRootHelpers.h>
10#include <xrpl/ledger/helpers/MPTokenHelpers.h>
11#include <xrpl/ledger/helpers/TokenHelpers.h>
12#include <xrpl/protocol/AMMCore.h>
13#include <xrpl/protocol/AccountID.h>
14#include <xrpl/protocol/Asset.h>
15#include <xrpl/protocol/Feature.h>
16#include <xrpl/protocol/Indexes.h>
17#include <xrpl/protocol/MPTIssue.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/STAmount.h>
20#include <xrpl/protocol/STLedgerEntry.h>
21#include <xrpl/protocol/STTx.h>
22#include <xrpl/protocol/TER.h>
23#include <xrpl/protocol/TxFlags.h>
24#include <xrpl/protocol/XRPAmount.h>
25#include <xrpl/tx/Transactor.h>
26
27#include <bit>
28#include <cstdint>
29#include <exception>
30#include <optional>
31#include <stdexcept>
32#include <utility>
33
34namespace xrpl {
35
36bool
38{
39 if (!ammEnabled(ctx.rules))
40 return false;
41
42 auto const amount = ctx.tx[~sfAmount];
43 auto const amount2 = ctx.tx[~sfAmount2];
44
45 return ctx.rules.enabled(featureMPTokensV2) ||
46 (!ctx.tx[sfAsset].holds<MPTIssue>() && !ctx.tx[sfAsset2].holds<MPTIssue>() &&
47 !(amount && amount->holds<MPTIssue>()) && !(amount2 && amount2->holds<MPTIssue>()));
48}
49
52{
53 return tfAMMDepositMask;
54}
55
58{
59 auto const flags = ctx.tx.getFlags();
60 auto const amount = ctx.tx[~sfAmount];
61 auto const amount2 = ctx.tx[~sfAmount2];
62 auto const ePrice = ctx.tx[~sfEPrice];
63 auto const lpTokens = ctx.tx[~sfLPTokenOut];
64 auto const tradingFee = ctx.tx[~sfTradingFee];
65 // Valid options for the flags are:
66 // tfLPTokens: LPTokenOut, [Amount, Amount2]
67 // tfSingleAsset: Amount, [LPTokenOut]
68 // tfTwoAsset: Amount, Amount2, [LPTokenOut]
69 // tfTwoAssetIfEmpty: Amount, Amount2, [sfTradingFee]
70 // tfOnAssetLPToken: Amount and LPTokenOut
71 // tfLimitLPToken: Amount and EPrice
72 if (std::popcount(flags & tfDepositSubTx) != 1)
73 {
74 JLOG(ctx.j.debug()) << "AMM Deposit: invalid flags.";
75 return temMALFORMED;
76 }
77 if (ctx.tx.isFlag(tfLPToken))
78 {
79 // if included then both amount and amount2 are deposit min
80 if (!lpTokens || ePrice || (amount && !amount2) || (!amount && amount2) || tradingFee)
81 return temMALFORMED;
82 }
83 else if (ctx.tx.isFlag(tfSingleAsset))
84 {
85 // if included then lpTokens is deposit min
86 if (!amount || amount2 || ePrice || tradingFee)
87 return temMALFORMED;
88 }
89 else if (ctx.tx.isFlag(tfTwoAsset))
90 {
91 // if included then lpTokens is deposit min
92 if (!amount || !amount2 || ePrice || tradingFee)
93 return temMALFORMED;
94 }
95 else if (ctx.tx.isFlag(tfOneAssetLPToken))
96 {
97 if (!amount || !lpTokens || amount2 || ePrice || tradingFee)
98 return temMALFORMED;
99 }
100 else if (ctx.tx.isFlag(tfLimitLPToken))
101 {
102 if (!amount || !ePrice || lpTokens || amount2 || tradingFee)
103 return temMALFORMED;
104 }
105 else if (ctx.tx.isFlag(tfTwoAssetIfEmpty))
106 {
107 if (!amount || !amount2 || ePrice || lpTokens)
108 return temMALFORMED;
109 }
110
111 auto const asset = ctx.tx[sfAsset];
112 auto const asset2 = ctx.tx[sfAsset2];
113 if (auto const res = invalidAMMAssetPair(asset, asset2))
114 {
115 JLOG(ctx.j.debug()) << "AMM Deposit: invalid asset pair.";
116 return res;
117 }
118
119 if (amount && amount2 && amount->asset() == amount2->asset())
120 {
121 JLOG(ctx.j.debug()) << "AMM Deposit: invalid tokens, same issue." << amount->asset() << " "
122 << amount2->asset();
123 return temBAD_AMM_TOKENS;
124 }
125
126 if (lpTokens && *lpTokens <= beast::kZero)
127 {
128 JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens";
129 return temBAD_AMM_TOKENS;
130 }
131
132 if (amount)
133 {
134 if (auto const res = invalidAMMAmount(
135 *amount, std::make_optional(std::make_pair(asset, asset2)), ePrice.has_value()))
136 {
137 JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount";
138 return res;
139 }
140 }
141
142 if (amount2)
143 {
144 if (auto const res =
145 invalidAMMAmount(*amount2, std::make_optional(std::make_pair(asset, asset2))))
146 {
147 JLOG(ctx.j.debug()) << "AMM Deposit: invalid amount2";
148 return res;
149 }
150 }
151
152 if (amount && ePrice)
153 {
154 auto assets = [&]() -> std::optional<std::pair<Asset, Asset>> {
155 // don't check ePrice issue
156 if (ctx.rules.enabled(featureMPTokensV2))
157 return std::nullopt;
158 // must be amount issue
159 return std::make_optional(std::make_pair(amount->asset(), amount->asset()));
160 }();
161 if (auto const res = invalidAMMAmount(*ePrice, assets))
162 {
163 JLOG(ctx.j.debug()) << "AMM Deposit: invalid EPrice";
164 return res;
165 }
166 }
167
168 if (tradingFee > kTradingFeeThreshold)
169 {
170 JLOG(ctx.j.debug()) << "AMM Deposit: invalid trading fee.";
171 return temBAD_FEE;
172 }
173
174 return tesSUCCESS;
175}
176
177TER
179{
180 auto const accountID = ctx.tx[sfAccount];
181
182 auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
183 if (!ammSle)
184 {
185 JLOG(ctx.j.debug()) << "AMM Deposit: Invalid asset pair.";
186 return terNO_AMM;
187 }
188
189 auto const expected = ammHolds(
190 ctx.view,
191 *ammSle,
192 std::nullopt,
193 std::nullopt,
196 ctx.j);
197 if (!expected)
198 return expected.error(); // LCOV_EXCL_LINE
199 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
200 if (ctx.tx.isFlag(tfTwoAssetIfEmpty))
201 {
202 if (lptAMMBalance != beast::kZero)
203 return tecAMM_NOT_EMPTY;
204 if (amountBalance != beast::kZero || amount2Balance != beast::kZero)
205 {
206 // LCOV_EXCL_START
207 JLOG(ctx.j.debug()) << "AMM Deposit: tokens balance is not zero.";
208 return tecINTERNAL;
209 // LCOV_EXCL_STOP
210 }
211 }
212 else
213 {
214 if (lptAMMBalance == beast::kZero)
215 return tecAMM_EMPTY;
216 if (amountBalance <= beast::kZero || amount2Balance <= beast::kZero ||
217 lptAMMBalance < beast::kZero)
218 {
219 // LCOV_EXCL_START
220 JLOG(ctx.j.debug()) << "AMM Deposit: reserves or tokens balance is zero.";
221 return tecINTERNAL;
222 // LCOV_EXCL_STOP
223 }
224 }
225
226 // Check account has sufficient funds.
227 // Return tesSUCCESS if it does, error otherwise.
228 // Have to check again in deposit() because
229 // amounts might be derived based on tokens or
230 // limits.
231 auto balance = [&](auto const& deposit) -> TER {
232 if (isXRP(deposit))
233 {
234 auto const lpIssue = (*ammSle)[sfLPTokenBalance].get<Issue>();
235 // Adjust the reserve if LP doesn't have LPToken trustline
236 auto const sle =
237 ctx.view.read(keylet::trustLine(accountID, lpIssue.account, lpIssue.currency));
238 if (xrpLiquid(ctx.view, accountID, !sle, ctx.j) >= deposit)
239 return TER(tesSUCCESS);
240 if (sle)
241 return tecUNFUNDED_AMM;
243 }
244 return accountFunds(
245 ctx.view,
246 accountID,
247 deposit,
250 ctx.j) >= deposit
251 ? TER(tesSUCCESS)
253 };
254
255 auto const amount = ctx.tx[~sfAmount];
256 auto const amount2 = ctx.tx[~sfAmount2];
257 auto const ammAccountID = ammSle->getAccountID(sfAccount);
258
259 if (ctx.view.rules().enabled(fixCleanup3_3_0))
260 {
261 // Unified deposit freeze check for both pool assets.
262 // AMMDeposit is not allowed if either asset is frozen.
263 auto checkAsset = [&](Asset const& asset) -> TER {
264 if (auto const ter = requireAuth(ctx.view, asset, accountID, AuthType::WeakAuth))
265 {
266 JLOG(ctx.j.debug()) << "AMM Deposit: account is not authorized, " << asset;
267 return ter;
268 }
269 if (auto const ter = checkDepositFreeze(ctx.view, accountID, ammAccountID, asset))
270 {
271 JLOG(ctx.j.debug())
272 << "AMM Deposit: frozen, " << to_string(accountID) << " " << to_string(asset);
273 return ter;
274 }
275 return tesSUCCESS;
276 };
277
278 if (auto const ter = checkAsset(ctx.tx[sfAsset]))
279 return ter;
280
281 if (auto const ter = checkAsset(ctx.tx[sfAsset2]))
282 return ter;
283 }
284 else if (ctx.view.rules().enabled(featureAMMClawback))
285 {
286 // Check if either of the assets is frozen, AMMDeposit is not allowed
287 // if either asset is frozen
288 auto checkAsset = [&](Asset const& asset) -> TER {
289 // WeakAuth - don't need to check if MPT object exists as might be
290 // depositing into non-MPT pool. It'll fail on send if MPT doesn't
291 // exist.
292 if (auto const ter = requireAuth(ctx.view, asset, accountID, AuthType::WeakAuth))
293 {
294 JLOG(ctx.j.debug()) << "AMM Deposit: account is not authorized, " << asset;
295 return ter;
296 }
297
298 if (auto const ter = checkFrozen(ctx.view, accountID, asset); !isTesSuccess(ter))
299 {
300 JLOG(ctx.j.debug()) << "AMM Deposit: account or currency is frozen or locked, "
301 << to_string(accountID) << " " << to_string(asset);
302
303 return ter;
304 }
305
306 return tesSUCCESS;
307 };
308
309 if (auto const ter = checkAsset(ctx.tx[sfAsset]))
310 return ter;
311
312 if (auto const ter = checkAsset(ctx.tx[sfAsset2]))
313 return ter;
314 }
315
316 auto checkAmount = [&](std::optional<STAmount> const& amount, bool checkBalance) -> TER {
317 if (amount)
318 {
319 // This normally should not happen.
320 // Account is not authorized to hold the assets it's depositing,
321 // or it doesn't even have a trust line or MPT for them.
322 if (auto const ter = requireAuth(ctx.view, amount->asset(), accountID))
323 {
324 // LCOV_EXCL_START
325 JLOG(ctx.j.debug())
326 << "AMM Deposit: account is not authorized, " << amount->asset();
327 return ter;
328 // LCOV_EXCL_STOP
329 }
330 if (!ctx.view.rules().enabled(fixCleanup3_3_0))
331 {
332 // AMM account or currency frozen
333 if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
334 !isTesSuccess(ter))
335 {
336 JLOG(ctx.j.debug())
337 << "AMM Deposit: AMM account or currency is frozen or locked, "
338 << to_string(accountID);
339 return ter;
340 }
341 // Account frozen
342 if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
343 !isTesSuccess(ter))
344 {
345 JLOG(ctx.j.debug())
346 << "AMM Deposit: account is frozen or locked, " << to_string(accountID)
347 << " " << to_string(amount->asset());
348 return ter;
349 }
350 }
351 if (checkBalance)
352 {
353 if (auto const ter = balance(*amount))
354 {
355 JLOG(ctx.j.debug())
356 << "AMM Deposit: account has insufficient funds, " << *amount;
357 return ter;
358 }
359 }
360 }
361 return tesSUCCESS;
362 };
363
364 // amount and amount2 are deposit min in case of tfLPToken
365 if (!ctx.tx.isFlag(tfLPToken))
366 {
367 if (auto const ter = checkAmount(amount, true))
368 return ter;
369
370 if (auto const ter = checkAmount(amount2, true))
371 return ter;
372 }
373 else
374 {
375 if (auto const ter = checkAmount(amountBalance, false))
376 return ter;
377 if (auto const ter = checkAmount(amount2Balance, false))
378 return ter;
379 }
380
381 // Equal deposit lp tokens
382 if (auto const lpTokens = ctx.tx[~sfLPTokenOut];
383 lpTokens && lpTokens->asset() != lptAMMBalance.asset())
384 {
385 JLOG(ctx.j.debug()) << "AMM Deposit: invalid LPTokens.";
386 return temBAD_AMM_TOKENS;
387 }
388
389 // Check the reserve for LPToken trustline if not LP.
390 // We checked above but need to check again if depositing IOU only.
391 if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::kZero)
392 {
393 STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
394 // Insufficient reserve
395 if (xrpBalance <= beast::kZero)
396 {
397 JLOG(ctx.j.debug()) << "AMM Instance: insufficient reserves";
399 }
400 }
401
402 if (auto const ter = canMPTTradeAndTransfer(ctx.view, ctx.tx[sfAsset], accountID, accountID);
403 !isTesSuccess(ter))
404 return ter;
405 if (auto const ter = canMPTTradeAndTransfer(ctx.view, ctx.tx[sfAsset2], accountID, accountID);
406 !isTesSuccess(ter))
407 return ter;
408
409 return tesSUCCESS;
410}
411
414{
415 auto const amount = ctx_.tx[~sfAmount];
416 auto const amount2 = ctx_.tx[~sfAmount2];
417 auto const ePrice = ctx_.tx[~sfEPrice];
418 auto const lpTokensDeposit = ctx_.tx[~sfLPTokenOut];
419 auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
420 if (!ammSle)
421 return {tecINTERNAL, false}; // LCOV_EXCL_LINE
422 auto const ammAccountID = (*ammSle)[sfAccount];
423
424 auto const expected = ammHolds(
425 sb,
426 *ammSle,
427 amount ? amount->asset() : std::optional<Asset>{},
428 amount2 ? amount2->asset() : std::optional<Asset>{},
431 ctx_.journal);
432 if (!expected)
433 return {expected.error(), false}; // LCOV_EXCL_LINE
434 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
435 auto const tfee = (lptAMMBalance == beast::kZero)
436 ? ctx_.tx[~sfTradingFee].value_or(0)
437 : getTradingFee(ctx_.view(), *ammSle, accountID_);
438
439 auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx;
440
441 auto dispatchToDeposit = [&,
442 &amountBalance = amountBalance,
443 &amount2Balance = amount2Balance,
444 &lptAMMBalance = lptAMMBalance]() -> std::pair<TER, STAmount> {
445 if (subTxType & tfTwoAsset)
446 {
447 return equalDepositLimit(
448 sb,
449 ammAccountID,
450 amountBalance,
451 amount2Balance,
452 lptAMMBalance,
453 *amount,
454 *amount2,
455 lpTokensDeposit,
456 tfee);
457 }
458 if (subTxType & tfOneAssetLPToken)
459 {
460 return singleDepositTokens(
461 sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *lpTokensDeposit, tfee);
462 }
463 if (subTxType & tfLimitLPToken)
464 {
465 return singleDepositEPrice(
466 sb, ammAccountID, amountBalance, *amount, lptAMMBalance, *ePrice, tfee);
467 }
468 if (subTxType & tfSingleAsset)
469 {
470 return singleDeposit(
471 sb, ammAccountID, amountBalance, lptAMMBalance, *amount, lpTokensDeposit, tfee);
472 }
473 if (subTxType & tfLPToken)
474 {
475 return equalDepositTokens(
476 sb,
477 ammAccountID,
478 amountBalance,
479 amount2Balance,
480 lptAMMBalance,
481 *lpTokensDeposit,
482 amount,
483 amount2,
484 tfee);
485 }
486 if (subTxType & tfTwoAssetIfEmpty)
487 {
489 sb, ammAccountID, *amount, *amount2, lptAMMBalance.asset(), tfee);
490 }
491 // should not happen.
492 // LCOV_EXCL_START
493 JLOG(j_.error()) << "AMM Deposit: invalid options.";
495 // LCOV_EXCL_STOP
496 };
497
498 auto const [result, newLPTokenBalance] = [&]() -> std::pair<TER, STAmount> {
499 try
500 {
501 return dispatchToDeposit();
502 }
503 catch (std::runtime_error const& e)
504 {
505 REACHABLE("xrpl::AMMDeposit::applyGuts : deposit amount out of range reached");
506 // A deposit whose solved amount exceeds the integral asset's range
507 // throws while converting to STAmount: past int64max
508 // Number::operator rep() throws std::overflow_error; above the asset
509 // maximum STAmount::canonicalize throws std::runtime_error. Fail
510 // cleanly with a tec rather than letting it escape doApply as
511 // tefEXCEPTION. Any other exception is left to propagate.
512 // Gated by fixCleanup3_4_0 to preserve the legacy result pre-amendment.
513 if (!sb.rules().enabled(fixCleanup3_4_0))
514 throw; // LCOV_EXCL_LINE - preserve legacy tefEXCEPTION
515 JLOG(j_.error()) << "AMMDeposit: deposit amount out of range " << e.what();
517 }
518 }();
519
520 if (isTesSuccess(result))
521 {
522 XRPL_ASSERT(
523 newLPTokenBalance > beast::kZero,
524 "xrpl::AMMDeposit::applyGuts : valid new LP token balance");
525 // Defensive check: deposit formulas with fixAMMv1_3 round LP tokens
526 // down and asset amounts up, so sqrt(pool1*pool2) >= newLPTokenBalance
527 // is guaranteed to hold. A precision loss failure is not expected.
528 if (sb.rules().enabled(fixCleanup3_3_0) && sb.rules().enabled(fixAMMv1_3))
529 {
530 if (auto const ter = checkAMMPrecisionLoss(
531 sb, ammAccountID, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], newLPTokenBalance, j_);
532 !isTesSuccess(ter))
533 {
534 UNREACHABLE("xrpl::AMMDeposit::applyGuts : AMM precision loss");
535 return {ter, false}; // LCOV_EXCL_LINE
536 }
537 }
538 ammSle->setFieldAmount(sfLPTokenBalance, newLPTokenBalance);
539 // LP depositing into AMM empty state gets the auction slot
540 // and the voting
541 if (lptAMMBalance == beast::kZero)
542 initializeFeeAuctionVote(sb, ammSle, accountID_, lptAMMBalance.asset(), tfee);
543
544 sb.update(ammSle);
545 }
546
547 return {result, isTesSuccess(result)};
548}
549
550TER
552{
553 // This is the ledger view that we work against. Transactions are applied
554 // as we go on processing transactions.
555 Sandbox sb(&ctx_.view());
556
557 auto const result = applyGuts(sb);
558 if (result.second)
559 sb.apply(ctx_.rawView());
560
561 return result.first;
562}
563
566 Sandbox& view,
567 AccountID const& ammAccount,
568 STAmount const& amountBalance,
569 STAmount const& amountDeposit,
570 std::optional<STAmount> const& amount2Deposit,
571 STAmount const& lptAMMBalance,
572 STAmount const& lpTokensDeposit,
573 std::optional<STAmount> const& depositMin,
574 std::optional<STAmount> const& deposit2Min,
575 std::optional<STAmount> const& lpTokensDepositMin,
576 std::uint16_t tfee)
577{
578 // Check account has sufficient funds.
579 // Return true if it does, false otherwise.
580 auto checkBalance = [&](auto const& depositAmount) -> TER {
581 if (depositAmount <= beast::kZero)
582 return temBAD_AMOUNT;
583 if (isXRP(depositAmount))
584 {
585 auto const& lpIssue = lpTokensDeposit.get<Issue>();
586 // Adjust the reserve if LP doesn't have LPToken trustline
587 auto const sle =
588 view.read(keylet::trustLine(accountID_, lpIssue.account, lpIssue.currency));
589 if (xrpLiquid(view, accountID_, !sle, j_) >= depositAmount)
590 return tesSUCCESS;
591 }
592 else if (
594 view,
596 depositAmount,
599 ctx_.journal) >= depositAmount)
600 {
601 return tesSUCCESS;
602 }
603 return tecUNFUNDED_AMM;
604 };
605
606 auto const [amountDepositActual, amount2DepositActual, lpTokensDepositActual] =
608 amountBalance,
609 amountDeposit,
610 amount2Deposit,
611 lptAMMBalance,
612 lpTokensDeposit,
613 tfee,
615
616 if (lpTokensDepositActual <= beast::kZero)
617 {
618 JLOG(ctx_.journal.debug()) << "AMM Deposit: adjusted tokens zero";
620 }
621
622 if (amountDepositActual < depositMin || amount2DepositActual < deposit2Min ||
623 lpTokensDepositActual < lpTokensDepositMin)
624 {
625 JLOG(ctx_.journal.debug())
626 << "AMM Deposit: min deposit fails " << amountDepositActual << " "
627 << depositMin.value_or(STAmount{}) << " " << amount2DepositActual.value_or(STAmount{})
628 << " " << deposit2Min.value_or(STAmount{}) << " " << lpTokensDepositActual << " "
629 << lpTokensDepositMin.value_or(STAmount{});
630 return {tecAMM_FAILED, STAmount{}};
631 }
632
633 // Deposit amountDeposit
634 if (auto const ter = checkBalance(amountDepositActual))
635 {
636 JLOG(ctx_.journal.debug()) << "AMM Deposit: account has insufficient "
637 "checkBalance to deposit or is 0"
638 << amountDepositActual;
639 return {ter, STAmount{}};
640 }
641
642 auto res = accountSend(
643 view,
645 ammAccount,
646 amountDepositActual,
647 ctx_.journal,
648 {}, // don't sponsor for AMM Trustline
650 if (!isTesSuccess(res))
651 {
652 JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit " << amountDepositActual;
653 return {res, STAmount{}};
654 }
655
656 // Deposit amount2Deposit
657 if (amount2DepositActual)
658 {
659 if (auto const ter = checkBalance(*amount2DepositActual))
660 {
661 JLOG(ctx_.journal.debug()) << "AMM Deposit: account has insufficient checkBalance to "
662 "deposit or is 0 "
663 << *amount2DepositActual;
664 return {ter, STAmount{}};
665 }
666
667 res = accountSend(
668 view,
670 ammAccount,
671 *amount2DepositActual,
672 ctx_.journal,
673 {}, // don't sponsor for AMM Trustline
675 if (!isTesSuccess(res))
676 {
677 JLOG(ctx_.journal.debug())
678 << "AMM Deposit: failed to deposit " << *amount2DepositActual;
679 return {res, STAmount{}};
680 }
681 }
682
683 // Deposit LP tokens
684 res = accountSend(view, ammAccount, accountID_, lpTokensDepositActual, ctx_.journal);
685 if (!isTesSuccess(res))
686 {
687 JLOG(ctx_.journal.debug()) << "AMM Deposit: failed to deposit LPTokens";
688 return {res, STAmount{}};
689 }
690
691 return {tesSUCCESS, lptAMMBalance + lpTokensDepositActual};
692}
693
694static STAmount
696 Rules const& rules,
697 STAmount const& lptAMMBalance,
698 STAmount const& lpTokensDeposit)
699{
700 if (!rules.enabled(fixAMMv1_3))
701 return lpTokensDeposit;
702 return adjustLPTokens(lptAMMBalance, lpTokensDeposit, IsDeposit::Yes);
703}
704
711 Sandbox& view,
712 AccountID const& ammAccount,
713 STAmount const& amountBalance,
714 STAmount const& amount2Balance,
715 STAmount const& lptAMMBalance,
716 STAmount const& lpTokensDeposit,
717 std::optional<STAmount> const& depositMin,
718 std::optional<STAmount> const& deposit2Min,
719 std::uint16_t tfee)
720{
721 try
722 {
723 auto const tokensAdj = adjustLPTokensOut(view.rules(), lptAMMBalance, lpTokensDeposit);
724 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
726 auto const frac = divide(tokensAdj, lptAMMBalance, lptAMMBalance.asset());
727 // amounts factor in the adjusted tokens
728 auto const amountDeposit =
729 getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::Yes);
730 auto const amount2Deposit =
731 getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::Yes);
732 return deposit(
733 view,
734 ammAccount,
735 amountBalance,
736 amountDeposit,
737 amount2Deposit,
738 lptAMMBalance,
739 tokensAdj,
740 depositMin,
741 deposit2Min,
742 std::nullopt,
743 tfee);
744 }
745 catch (std::exception const& e)
746 {
747 // LCOV_EXCL_START
748 JLOG(j_.error()) << "AMMDeposit::equalDepositTokens exception " << e.what();
749 return {tecINTERNAL, STAmount{}};
750 // LCOV_EXCL_STOP
751 }
752}
753
785 Sandbox& view,
786 AccountID const& ammAccount,
787 STAmount const& amountBalance,
788 STAmount const& amount2Balance,
789 STAmount const& lptAMMBalance,
790 STAmount const& amount,
791 STAmount const& amount2,
792 std::optional<STAmount> const& lpTokensDepositMin,
793 std::uint16_t tfee)
794{
795 auto frac = Number{amount} / amountBalance;
796 auto tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::Yes);
797 if (tokensAdj == beast::kZero)
798 {
799 if (!view.rules().enabled(fixAMMv1_3))
800 {
801 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
802 }
803
805 }
806 // factor in the adjusted tokens
807 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
808 auto const amount2Deposit = getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::Yes);
809 if (amount2Deposit <= amount2)
810 {
811 return deposit(
812 view,
813 ammAccount,
814 amountBalance,
815 amount,
816 amount2Deposit,
817 lptAMMBalance,
818 tokensAdj,
819 std::nullopt,
820 std::nullopt,
821 lpTokensDepositMin,
822 tfee);
823 }
824 frac = Number{amount2} / amount2Balance;
825 tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::Yes);
826 if (tokensAdj == beast::kZero)
827 {
828 if (!view.rules().enabled(fixAMMv1_3))
829 {
830 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
831 }
832
833 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
834 }
835 // factor in the adjusted tokens
836 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
837 auto const amountDeposit = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::Yes);
838 if (amountDeposit <= amount)
839 {
840 return deposit(
841 view,
842 ammAccount,
843 amountBalance,
844 amountDeposit,
845 amount2,
846 lptAMMBalance,
847 tokensAdj,
848 std::nullopt,
849 std::nullopt,
850 lpTokensDepositMin,
851 tfee);
852 }
853 return {tecAMM_FAILED, STAmount{}};
854}
855
867 Sandbox& view,
868 AccountID const& ammAccount,
869 STAmount const& amountBalance,
870 STAmount const& lptAMMBalance,
871 STAmount const& amount,
872 std::optional<STAmount> const& lpTokensDepositMin,
873 std::uint16_t tfee)
874{
875 auto const tokens = adjustLPTokensOut(
876 view.rules(), lptAMMBalance, lpTokensOut(amountBalance, amount, lptAMMBalance, tfee));
877 if (tokens == beast::kZero)
878 {
879 if (!view.rules().enabled(fixAMMv1_3))
880 {
881 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
882 }
883
885 }
886 // factor in the adjusted tokens
887 auto const [tokensAdj, amountDepositAdj] =
888 adjustAssetInByTokens(view.rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
889 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
890 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
891 return deposit(
892 view,
893 ammAccount,
894 amountBalance,
895 amountDepositAdj,
896 std::nullopt,
897 lptAMMBalance,
898 tokensAdj,
899 std::nullopt,
900 std::nullopt,
901 lpTokensDepositMin,
902 tfee);
903}
904
915 Sandbox& view,
916 AccountID const& ammAccount,
917 STAmount const& amountBalance,
918 STAmount const& amount,
919 STAmount const& lptAMMBalance,
920 STAmount const& lpTokensDeposit,
921 std::uint16_t tfee)
922{
923 auto const tokensAdj = adjustLPTokensOut(view.rules(), lptAMMBalance, lpTokensDeposit);
924 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
926 // the adjusted tokens are factored in
927 auto const amountDeposit = ammAssetIn(amountBalance, lptAMMBalance, tokensAdj, tfee);
928 if (amountDeposit > amount)
929 return {tecAMM_FAILED, STAmount{}};
930 return deposit(
931 view,
932 ammAccount,
933 amountBalance,
934 amountDeposit,
935 std::nullopt,
936 lptAMMBalance,
937 tokensAdj,
938 std::nullopt,
939 std::nullopt,
940 std::nullopt,
941 tfee);
942}
943
972 Sandbox& view,
973 AccountID const& ammAccount,
974 STAmount const& amountBalance,
975 STAmount const& amount,
976 STAmount const& lptAMMBalance,
977 STAmount const& ePrice,
978 std::uint16_t tfee)
979{
980 if (amount != beast::kZero)
981 {
982 auto const tokens = adjustLPTokensOut(
983 view.rules(), lptAMMBalance, lpTokensOut(amountBalance, amount, lptAMMBalance, tfee));
984 if (tokens <= beast::kZero)
985 {
986 if (!view.rules().enabled(fixAMMv1_3))
987 {
988 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
989 }
990
992 }
993 // factor in the adjusted tokens
994 auto const [tokensAdj, amountDepositAdj] =
995 adjustAssetInByTokens(view.rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
996 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
997 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
998 auto const ep = Number{amountDepositAdj} / tokensAdj;
999 if (ep <= ePrice)
1000 {
1001 return deposit(
1002 view,
1003 ammAccount,
1004 amountBalance,
1005 amountDepositAdj,
1006 std::nullopt,
1007 lptAMMBalance,
1008 tokensAdj,
1009 std::nullopt,
1010 std::nullopt,
1011 std::nullopt,
1012 tfee);
1013 }
1014 }
1015
1016 // LPTokens is asset out => E = b / t
1017 // substituting t in formula (3) as b/E:
1018 // b/E = T * [b/B - sqrt(t2**2 + b/(f1*B)) + t2]/
1019 // [1 + sqrt(t2**2 + b/(f1*B)) -t2] (A)
1020 // where f1 = 1 - fee, f2 = (1 - fee/2)/f1
1021 // Let R = b/(f1*B), then b/B = f1*R and b = R*f1*B
1022 // Then (A) is
1023 // R*f1*B = E*T*[R*f1 -sqrt(f2**2 + R) + f2]/[1 + sqrt(f2**2 + R) - f2] =>
1024 // Let c = f1*B/(E*T) =>
1025 // R*c*(1 + sqrt(f2**2 + R) + f2) = R*f1 - sqrt(f2**2 + R) - f2 =>
1026 // (R*c + 1)*sqrt(f2**2 + R) = R*(f1 + c*f2 - c) + f2 =>
1027 // Let d = f1 + c*f2 - c =>
1028 // (R*c + 1)*sqrt(f2**2 + R) = R*d + f2 =>
1029 // (R*c + 1)**2 * (f2**2 + R) = (R*d + f2)**2 =>
1030 // (R*c)**2 + R*((c*f2)**2 + 2*c - d**2) + 2*c*f2**2 + 1 -2*d*f2 = 0 =>
1031 // a1 = c**2, b1 = (c*f2)**2 + 2*c - d**2, c1 = 2*c*f2**2 + 1 - 2*d*f2
1032 // R = (-b1 + sqrt(b1**2 + 4*a1*c1))/(2*a1)
1033 auto const f1 = feeMult(tfee);
1034 auto const f2 = feeMultHalf(tfee) / f1;
1035 auto const c = f1 * amountBalance / (ePrice * lptAMMBalance);
1036 auto const d = f1 + c * f2 - c;
1037 auto const a1 = c * c;
1038 auto const b1 = c * c * f2 * f2 + 2 * c - d * d;
1039 auto const c1 = 2 * c * f2 * f2 + 1 - 2 * d * f2;
1040 auto amtNoRoundCb = [&] { return f1 * amountBalance * solveQuadraticEq(a1, b1, c1); };
1041 auto amtProdCb = [&] { return f1 * solveQuadraticEq(a1, b1, c1); };
1042 auto const amountDeposit =
1043 getRoundedAsset(view.rules(), amtNoRoundCb, amountBalance, amtProdCb, IsDeposit::Yes);
1044 if (amountDeposit <= beast::kZero)
1045 return {tecAMM_FAILED, STAmount{}};
1046 auto tokNoRoundCb = [&] { return amountDeposit / ePrice; };
1047 auto tokProdCb = [&] { return amountDeposit / ePrice; };
1048 auto const tokens =
1049 getRoundedLPTokens(view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::Yes);
1050 // factor in the adjusted tokens
1051 auto const [tokensAdj, amountDepositAdj] = adjustAssetInByTokens(
1052 view.rules(), amountBalance, amountDeposit, lptAMMBalance, tokens, tfee);
1053 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1054 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
1055
1056 return deposit(
1057 view,
1058 ammAccount,
1059 amountBalance,
1060 amountDepositAdj,
1061 std::nullopt,
1062 lptAMMBalance,
1063 tokensAdj,
1064 std::nullopt,
1065 std::nullopt,
1066 std::nullopt,
1067 tfee);
1068}
1069
1072 Sandbox& view,
1073 AccountID const& ammAccount,
1074 STAmount const& amount,
1075 STAmount const& amount2,
1076 Asset const& lptIssue,
1077 std::uint16_t tfee)
1078{
1079 return deposit(
1080 view,
1081 ammAccount,
1082 amount,
1083 amount,
1084 amount2,
1085 STAmount{lptIssue, 0},
1086 ammLPTokens(amount, amount2, lptIssue),
1087 std::nullopt,
1088 std::nullopt,
1089 std::nullopt,
1090 tfee);
1091}
1092
1093void
1095{
1096 // No transaction-specific invariants yet (future work).
1097}
1098
1099bool
1101{
1102 // No transaction-specific invariants yet (future work).
1103 return true;
1104}
1105
1106} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream debug() const
Definition Journal.h:344
static TER preclaim(PreclaimContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
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 > equalDepositInEmptyState(Sandbox &view, AccountID const &ammAccount, STAmount const &amount, STAmount const &amount2, Asset const &lptIssue, std::uint16_t tfee)
Equal deposit in empty AMM state (LP tokens balance is 0).
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.
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.
std::pair< TER, bool > applyGuts(Sandbox &view)
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)
TER doApply() override
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
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...
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.
A currency issued by an account.
Definition Issue.h:18
Number is a floating point type that can represent a wide range of values.
Definition Number.h:351
A view into a ledger.
Definition ReadView.h:41
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
Rules controlling protocol behavior.
Definition Rules.h:40
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:180
constexpr TIss const & get() const
Asset const & asset() const
Definition STAmount.h:496
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFlag(std::uint32_t) const
Definition STObject.cpp:511
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:643
std::uint32_t getFlags() const
Definition STObject.cpp:517
Discardable, editable view to a ledger.
Definition Sandbox.h:18
void apply(RawView &to)
Definition Sandbox.h:38
beast::Journal const j_
Definition Transactor.h:155
ApplyView & view()
Definition Transactor.h:175
AccountID const accountID_
Definition Transactor.h:157
ApplyContext & ctx_
Definition Transactor.h:153
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
void update(SLE::ref sle) override
Indicate changes to a peeked SLE.
Rules const & rules() const override
Returns the tx processing rules.
T make_optional(T... args)
T make_pair(T... args)
constexpr Zero kZero
Definition Zero.h:30
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:441
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:253
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Asset, Asset > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
Definition AMMCore.cpp:98
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
@ terNO_AMM
Definition TER.h:223
constexpr std::uint16_t kTradingFeeThreshold
Definition AMMCore.h:16
STAmount ammLPHolds(ReadView const &view, Asset const &asset1, Asset const &asset2, AccountID const &ammAccount, AccountID const &lpAccount, beast::Journal const j)
Get the balance of LP tokens.
XRPAmount xrpLiquid(ReadView const &view, AccountID const &id, std::int32_t ownerCountAdj, beast::Journal j)
Calculate liquid XRP balance for an account.
static STAmount adjustLPTokensOut(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &lpTokensDeposit)
Number feeMultHalf(std::uint16_t tfee)
Get fee multiplier (1 - tfee / 2) @tfee trading fee in basis points.
Definition AMMCore.h:109
TER checkIndividualFrozen(ReadView const &view, AccountID const &account, Asset const &asset)
bool ammEnabled(Rules const &)
Return true if required AMM amendment is enabled.
Definition AMMCore.cpp:129
constexpr FlagValue tfDepositSubTx
Definition TxFlags.h:409
bool isXRP(AccountID const &c)
Definition AccountID.h:84
Number adjustFracByTokens(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &tokens, Number const &frac)
Find a fraction of tokens after the tokens are adjusted.
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Asset const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, SLE::ref sponsorSle={}, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
TER checkDepositFreeze(ReadView const &view, AccountID const &srcAcct, AccountID const &pseudoAcct, Asset const &asset)
Checks freeze compliance for depositing an asset into a pseudo-account (e.g.
NotTEC invalidAMMAssetPair(Asset const &asset1, Asset const &asset2, std::optional< std::pair< Asset, Asset > > const &pair=std::nullopt)
Definition AMMCore.cpp:83
void initializeFeeAuctionVote(ApplyView &view, SLE::pointer &ammSle, AccountID const &account, Asset const &lptAsset, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
TER canMPTTradeAndTransfer(ReadView const &v, Asset const &asset, AccountID const &from, AccountID const &to)
Convenience to combine canTrade/Transfer.
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).
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
std::pair< STAmount, STAmount > adjustAssetInByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:607
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:667
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition AMMCore.h:99
TER checkAMMPrecisionLoss(Number const &poolProductMean, STAmount const &newLPTokenBalance)
Check AMM pool product invariant after an AMM operation that changes LP tokens (deposit/withdraw/claw...
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset deposit given LP Tokens.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
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...
@ temBAD_FEE
Definition TER.h:80
@ temBAD_AMM_TOKENS
Definition TER.h:117
@ temMALFORMED
Definition TER.h:75
@ temBAD_AMOUNT
Definition TER.h:77
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
@ tecAMM_EMPTY
Definition TER.h:335
@ tecAMM_INVALID_TOKENS
Definition TER.h:334
@ tecINSUF_RESERVE_LINE
Definition TER.h:291
@ tecAMM_FAILED
Definition TER.h:333
@ tecAMM_NOT_EMPTY
Definition TER.h:336
@ tecUNFUNDED_AMM
Definition TER.h:331
@ tecINTERNAL
Definition TER.h:313
std::expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Asset > const &optAsset1, std::optional< Asset > const &optAsset2, FreezeHandling freezeHandling, AuthHandling authHandling, beast::Journal const j)
Get AMM pool and LP token balances.
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
@ tesSUCCESS
Definition TER.h:245
STAmount lpTokensOut(STAmount const &asset1Balance, STAmount const &asset1Deposit, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's deposit amount.
T popcount(T... args)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:83
ReadView const & view
Definition Transactor.h:86
beast::Journal const j
Definition Transactor.h:91
State information when preflighting a tx.
Definition Transactor.h:38
beast::Journal const j
Definition Transactor.h:45
T value_or(T... args)
T what(T... args)