xrpld
Loading...
Searching...
No Matches
AMMWithdraw.cpp
1#include <xrpl/tx/transactors/dex/AMMWithdraw.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/core/ServiceRegistry.h>
8#include <xrpl/ledger/Sandbox.h>
9#include <xrpl/ledger/helpers/AMMHelpers.h>
10#include <xrpl/ledger/helpers/MPTokenHelpers.h>
11#include <xrpl/ledger/helpers/RippleStateHelpers.h>
12#include <xrpl/ledger/helpers/TokenHelpers.h>
13#include <xrpl/protocol/AMMCore.h>
14#include <xrpl/protocol/AccountID.h>
15#include <xrpl/protocol/Asset.h>
16#include <xrpl/protocol/Feature.h>
17#include <xrpl/protocol/IOUAmount.h>
18#include <xrpl/protocol/Indexes.h>
19#include <xrpl/protocol/Issue.h>
20#include <xrpl/protocol/Keylet.h>
21#include <xrpl/protocol/MPTIssue.h>
22#include <xrpl/protocol/SField.h>
23#include <xrpl/protocol/STAmount.h>
24#include <xrpl/protocol/STLedgerEntry.h>
25#include <xrpl/protocol/STTx.h>
26#include <xrpl/protocol/TER.h>
27#include <xrpl/protocol/TxFlags.h>
28#include <xrpl/protocol/XRPAmount.h>
29#include <xrpl/tx/Transactor.h>
30
31#include <algorithm>
32#include <bit>
33#include <cstdint>
34#include <exception>
35#include <optional>
36#include <tuple>
37#include <utility>
38
39namespace xrpl {
40
41bool
43{
44 if (!ammEnabled(ctx.rules))
45 return false;
46
47 auto const amount = ctx.tx[~sfAmount];
48 auto const amount2 = ctx.tx[~sfAmount2];
49
50 return ctx.rules.enabled(featureMPTokensV2) ||
51 (!ctx.tx[sfAsset].holds<MPTIssue>() && !ctx.tx[sfAsset2].holds<MPTIssue>() &&
52 !(amount && amount->holds<MPTIssue>()) && !(amount2 && amount2->holds<MPTIssue>()));
53}
54
57{
58 return tfAMMWithdrawMask;
59}
60
63{
64 auto const flags = ctx.tx.getFlags();
65
66 auto const amount = ctx.tx[~sfAmount];
67 auto const amount2 = ctx.tx[~sfAmount2];
68 auto const ePrice = ctx.tx[~sfEPrice];
69 auto const lpTokens = ctx.tx[~sfLPTokenIn];
70 // Valid combinations are:
71 // LPTokens
72 // tfWithdrawAll
73 // Amount
74 // tfOneAssetWithdrawAll & Amount
75 // Amount and Amount2
76 // Amount and LPTokens
77 // Amount and EPrice
78 if (std::popcount(flags & tfWithdrawSubTx) != 1)
79 {
80 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid flags.";
81 return temMALFORMED;
82 }
83 if (ctx.tx.isFlag(tfLPToken))
84 {
85 if (!lpTokens || amount || amount2 || ePrice)
86 return temMALFORMED;
87 }
88 else if (ctx.tx.isFlag(tfWithdrawAll))
89 {
90 if (lpTokens || amount || amount2 || ePrice)
91 return temMALFORMED;
92 }
93 else if (ctx.tx.isFlag(tfOneAssetWithdrawAll) || ctx.tx.isFlag(tfSingleAsset))
94 {
95 if (!amount || lpTokens || amount2 || ePrice)
96 return temMALFORMED;
97 }
98 else if (ctx.tx.isFlag(tfTwoAsset))
99 {
100 if (!amount || !amount2 || lpTokens || ePrice)
101 return temMALFORMED;
102 }
103 else if (ctx.tx.isFlag(tfOneAssetLPToken))
104 {
105 if (!amount || !lpTokens || amount2 || ePrice)
106 return temMALFORMED;
107 }
108 else if (ctx.tx.isFlag(tfLimitLPToken))
109 {
110 if (!amount || !ePrice || lpTokens || amount2)
111 return temMALFORMED;
112 }
113
114 auto const asset = ctx.tx[sfAsset];
115 auto const asset2 = ctx.tx[sfAsset2];
116 if (auto const res = invalidAMMAssetPair(asset, asset2))
117 {
118 JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
119 return res;
120 }
121
122 if (amount && amount2 && amount->asset() == amount2->asset())
123 {
124 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens, same issue." << amount->asset() << " "
125 << amount2->asset();
126 return temBAD_AMM_TOKENS;
127 }
128
129 if (lpTokens && *lpTokens <= beast::kZero)
130 {
131 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
132 return temBAD_AMM_TOKENS;
133 }
134
135 if (amount)
136 {
137 if (auto const res = invalidAMMAmount(
138 *amount,
139 std::make_optional(std::make_pair(asset, asset2)),
140 ((flags & (tfOneAssetWithdrawAll | tfOneAssetLPToken)) != 0u) || ePrice))
141 {
142 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset1Out";
143 return res;
144 }
145 }
146
147 if (amount2)
148 {
149 if (auto const res =
150 invalidAMMAmount(*amount2, std::make_optional(std::make_pair(asset, asset2))))
151 {
152 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset2OutAmount";
153 return res;
154 }
155 }
156
157 if (ePrice)
158 {
159 if (auto const res = invalidAMMAmount(*ePrice))
160 {
161 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice";
162 return res;
163 }
164 }
165
166 return tesSUCCESS;
167}
168
171 STAmount const& lpTokens,
172 std::optional<STAmount> const& tokensIn,
173 std::uint32_t flags)
174{
175 if ((flags & (tfWithdrawAll | tfOneAssetWithdrawAll)) != 0u)
176 return lpTokens;
177 return tokensIn;
178}
179
180TER
182{
183 auto const accountID = ctx.tx[sfAccount];
184
185 auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
186 if (!ammSle)
187 {
188 JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
189 return terNO_AMM;
190 }
191
192 auto const amount = ctx.tx[~sfAmount];
193 auto const amount2 = ctx.tx[~sfAmount2];
194
195 auto const expected = ammHolds(
196 ctx.view,
197 *ammSle,
198 amount ? amount->asset() : std::optional<Asset>{},
199 amount2 ? amount2->asset() : std::optional<Asset>{},
202 ctx.j);
203 if (!expected)
204 return expected.error();
205 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
206 if (lptAMMBalance == beast::kZero)
207 return tecAMM_EMPTY;
208 if (amountBalance <= beast::kZero || amount2Balance <= beast::kZero ||
209 lptAMMBalance < beast::kZero)
210 {
211 // LCOV_EXCL_START
212 JLOG(ctx.j.debug()) << "AMM Withdraw: reserves or tokens balance is zero.";
213 return tecINTERNAL;
214 // LCOV_EXCL_STOP
215 }
216
217 auto const ammAccountID = ammSle->getAccountID(sfAccount);
218
219 auto checkAmount = [&](std::optional<STAmount> const& amount, auto const& balance) -> TER {
220 if (amount)
221 {
222 if (amount > balance)
223 {
224 JLOG(ctx.j.debug())
225 << "AMM Withdraw: withdrawing more than the balance, " << *amount;
226 return tecAMM_BALANCE;
227 }
228 // WeakAuth - MPToken is created if it doesn't exist.
229 if (auto const ter =
230 requireAuth(ctx.view, amount->asset(), accountID, AuthType::WeakAuth))
231 {
232 JLOG(ctx.j.debug())
233 << "AMM Withdraw: account is not authorized, " << amount->asset();
234 return ter;
235 }
236 if (ctx.view.rules().enabled(fixCleanup3_3_0))
237 {
238 if (auto const ret = checkWithdrawFreeze(
239 ctx.view, ammAccountID, accountID, accountID, amount->asset()))
240 {
241 JLOG(ctx.j.debug()) << "AMM Withdraw: frozen, " << to_string(accountID) << " "
242 << to_string(amount->asset());
243 return ret;
244 }
245 }
246 else
247 {
248 // AMM account or currency frozen
249 if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
250 !isTesSuccess(ter))
251 {
252 JLOG(ctx.j.debug())
253 << "AMM Withdraw: AMM account or currency is frozen or locked, "
254 << to_string(accountID);
255 return ter;
256 }
257 // Account frozen
258 if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
259 !isTesSuccess(ter))
260 {
261 JLOG(ctx.j.debug())
262 << "AMM Withdraw: account is frozen or locked, " << to_string(accountID)
263 << " " << to_string(amount->asset());
264 return ter;
265 }
266 }
267 }
268 return tesSUCCESS;
269 };
270
271 if (auto const ter = checkAmount(amount, amountBalance))
272 return ter;
273
274 if (auto const ter = checkAmount(amount2, amount2Balance))
275 return ter;
276
277 auto const lpTokens = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
278 auto const lpTokensWithdraw = tokensWithdraw(lpTokens, ctx.tx[~sfLPTokenIn], ctx.tx.getFlags());
279
280 if (lpTokens <= beast::kZero)
281 {
282 JLOG(ctx.j.debug()) << "AMM Withdraw: tokens balance is zero.";
283 return tecAMM_BALANCE;
284 }
285
286 if (lpTokensWithdraw && lpTokensWithdraw->asset() != lpTokens.asset())
287 {
288 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid LPTokens.";
289 return temBAD_AMM_TOKENS;
290 }
291
292 if (lpTokensWithdraw && *lpTokensWithdraw > lpTokens)
293 {
294 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
296 }
297
298 if (auto const ePrice = ctx.tx[~sfEPrice]; ePrice && ePrice->asset() != lpTokens.asset())
299 {
300 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice.";
301 return temBAD_AMM_TOKENS;
302 }
303
304 if ((ctx.tx.getFlags() & (tfLPToken | tfWithdrawAll)) != 0u)
305 {
306 if (auto const ter = checkAmount(amountBalance, amountBalance))
307 return ter;
308 if (auto const ter = checkAmount(amount2Balance, amount2Balance))
309 return ter;
310 }
311
312 return tesSUCCESS;
313}
314
317{
318 // When the withdrawer is the issuer of a pool asset, the issuer can
319 // always receive their own token — even when the pool is frozen.
320 // Use IgnoreFreeze so ammHolds returns real balances instead of zero.
321 if (!ctx_.view().rules().enabled(fixCleanup3_3_0))
323
324 auto const asset1 = Asset{ctx_.tx[sfAsset]};
325 auto const asset2 = Asset{ctx_.tx[sfAsset2]};
326 if (!asset1.native() && accountID_ == asset1.getIssuer())
328 if (!asset2.native() && accountID_ == asset2.getIssuer())
330
332}
333
336{
337 auto const amount = ctx_.tx[~sfAmount];
338 auto const amount2 = ctx_.tx[~sfAmount2];
339 auto const ePrice = ctx_.tx[~sfEPrice];
340 auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
341 if (!ammSle)
342 return {tecINTERNAL, false}; // LCOV_EXCL_LINE
343 auto const ammAccountID = (*ammSle)[sfAccount];
344 auto const accountSle = sb.read(keylet::account(ammAccountID));
345 if (!accountSle)
346 return {tecINTERNAL, false}; // LCOV_EXCL_LINE
347 auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
348 auto const lpTokensWithdraw =
349 tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags());
350
351 // Due to rounding, the LPTokenBalance of the last LP
352 // might not match the LP's trustline balance
353 if (sb.rules().enabled(fixAMMv1_1))
354 {
355 if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokens, ammSle, accountID_); !res)
356 return {res.error(), false};
357 }
358
359 auto const tfee = getTradingFee(ctx_.view(), *ammSle, accountID_);
360
361 auto const freezeHandling = issuerFreezeHandling();
362
363 auto const expected = ammHolds(
364 sb,
365 *ammSle,
366 amount ? amount->asset() : std::optional<Asset>{},
367 amount2 ? amount2->asset() : std::optional<Asset>{},
368 freezeHandling,
370 ctx_.journal);
371 if (!expected)
372 return {expected.error(), false};
373 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
374 auto const subTxType = ctx_.tx.getFlags() & tfWithdrawSubTx;
375
376 auto const [result, newLPTokenBalance] = [&,
377 &amountBalance = amountBalance,
378 &amount2Balance = amount2Balance,
379 &lptAMMBalance =
380 lptAMMBalance]() -> std::pair<TER, STAmount> {
381 if (subTxType & tfTwoAsset)
382 {
383 return equalWithdrawLimit(
384 sb,
385 *ammSle,
386 ammAccountID,
387 amountBalance,
388 amount2Balance,
389 lptAMMBalance,
390 *amount,
391 *amount2,
392 tfee);
393 }
394 if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll)
395 {
397 sb,
398 *ammSle,
399 ammAccountID,
400 amountBalance,
401 lptAMMBalance,
402 *amount,
403 *lpTokensWithdraw,
404 tfee);
405 }
406 if (subTxType & tfLimitLPToken)
407 {
409 sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, *ePrice, tfee);
410 }
411 if (subTxType & tfSingleAsset)
412 {
413 return singleWithdraw(
414 sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee);
415 }
416 if (subTxType & tfLPToken || subTxType & tfWithdrawAll)
417 {
418 return equalWithdrawTokens(
419 sb,
420 *ammSle,
421 ammAccountID,
422 amountBalance,
423 amount2Balance,
424 lptAMMBalance,
425 lpTokens,
426 *lpTokensWithdraw,
427 tfee);
428 }
429 // should not happen.
430 // LCOV_EXCL_START
431 JLOG(j_.error()) << "AMM Withdraw: invalid options.";
433 // LCOV_EXCL_STOP
434 }();
435
436 if (!isTesSuccess(result))
437 return {result, false};
438
439 if (sb.rules().enabled(fixCleanup3_3_0) && sb.rules().enabled(fixAMMv1_3))
440 {
441 if (auto const ter = checkAMMPrecisionLoss(
442 sb, ammAccountID, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], newLPTokenBalance, j_);
443 !isTesSuccess(ter))
444 {
445 return {ter, false};
446 }
447 }
448
449 auto const res = deleteAMMAccountIfEmpty(
450 sb, ammSle, newLPTokenBalance, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], j_);
451 // LCOV_EXCL_START
452 if (!res.second)
453 return {res.first, false};
454 // LCOV_EXCL_STOP
455
456 JLOG(ctx_.journal.trace()) << "AMM Withdraw: tokens " << to_string(newLPTokenBalance.iou())
457 << " " << to_string(lpTokens.iou()) << " "
458 << to_string(lptAMMBalance.iou());
459
460 return {tesSUCCESS, true};
461}
462
463TER
465{
466 // This is the ledger view that we work against. Transactions are applied
467 // as we go on processing transactions.
468 Sandbox sb(&ctx_.view());
469
470 auto const result = applyGuts(sb);
471 if (result.second)
472 sb.apply(ctx_.rawView());
473
474 return result.first;
475}
476
479 Sandbox& view,
480 SLE const& ammSle,
481 AccountID const& ammAccount,
482 STAmount const& amountBalance,
483 STAmount const& amountWithdraw,
484 std::optional<STAmount> const& amount2Withdraw,
485 STAmount const& lpTokensAMMBalance,
486 STAmount const& lpTokensWithdraw,
487 std::uint16_t tfee)
488{
489 TER ter;
490 STAmount newLPTokenBalance;
491 std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = withdraw(
492 view,
493 ammSle,
494 ammAccount,
496 amountBalance,
497 amountWithdraw,
498 amount2Withdraw,
499 lpTokensAMMBalance,
500 lpTokensWithdraw,
501 tfee,
506 j_);
507 return {ter, newLPTokenBalance};
508}
509
512 Sandbox& view,
513 SLE const& ammSle,
514 AccountID const& ammAccount,
515 AccountID const& account,
516 STAmount const& amountBalance,
517 STAmount const& amountWithdraw,
518 std::optional<STAmount> const& amount2Withdraw,
519 STAmount const& lpTokensAMMBalance,
520 STAmount const& lpTokensWithdraw,
521 std::uint16_t tfee,
522 FreezeHandling freezeHandling,
523 AuthHandling authHandling,
524 WithdrawAll withdrawAll,
525 XRPAmount const& priorBalance,
526 beast::Journal const& journal)
527{
528 auto const lpTokens = ammLPHolds(view, ammSle, account, journal);
529 auto const expected = ammHolds(
530 view, ammSle, amountWithdraw.asset(), std::nullopt, freezeHandling, authHandling, journal);
531 // LCOV_EXCL_START
532 if (!expected)
533 return {expected.error(), STAmount{}, STAmount{}, STAmount{}};
534 // LCOV_EXCL_STOP
535 auto const [curBalance, curBalance2, _] = *expected;
536 (void)_;
537
538 auto const [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
540 if (withdrawAll == WithdrawAll::No)
541 {
543 amountBalance,
544 amountWithdraw,
545 amount2Withdraw,
546 lpTokensAMMBalance,
547 lpTokensWithdraw,
548 tfee,
550 }
551 return std::make_tuple(amountWithdraw, amount2Withdraw, lpTokensWithdraw);
552 }();
553
554 if (lpTokensWithdrawActual <= beast::kZero || lpTokensWithdrawActual > lpTokens)
555 {
556 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, invalid LP tokens: "
557 << lpTokensWithdrawActual << " " << lpTokens << " "
558 << lpTokensAMMBalance;
560 }
561
562 // Should not happen since the only LP on last withdraw
563 // has the balance set to the lp token trustline balance.
564 if (view.rules().enabled(fixAMMv1_1) && lpTokensWithdrawActual > lpTokensAMMBalance)
565 {
566 // LCOV_EXCL_START
567 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, unexpected LP tokens: "
568 << lpTokensWithdrawActual << " " << lpTokens << " "
569 << lpTokensAMMBalance;
570 return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}};
571 // LCOV_EXCL_STOP
572 }
573
574 // Withdrawing one side of the pool
575 if ((amountWithdrawActual == curBalance && amount2WithdrawActual != curBalance2) ||
576 (amount2WithdrawActual == curBalance2 && amountWithdrawActual != curBalance))
577 {
578 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw one side of the pool "
579 << " curBalance: " << curBalance << " " << amountWithdrawActual
580 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
581 << lpTokensAMMBalance;
582 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
583 }
584
585 // May happen if withdrawing an amount close to one side of the pool
586 if (lpTokensWithdrawActual == lpTokensAMMBalance &&
587 (amountWithdrawActual != curBalance || amount2WithdrawActual != curBalance2))
588 {
589 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw all tokens "
590 << " curBalance: " << curBalance << " " << amountWithdrawActual
591 << " curBalance2: " << amount2WithdrawActual.value_or(STAmount{0})
592 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
593 << lpTokensAMMBalance;
594 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
595 }
596
597 // Withdrawing more than the pool's balance
598 if (amountWithdrawActual > curBalance || amount2WithdrawActual > curBalance2)
599 {
600 JLOG(journal.debug()) << "AMM Withdraw: withdrawing more than the pool's balance "
601 << " curBalance: " << curBalance << " " << amountWithdrawActual
602 << " curBalance2: " << curBalance2 << " "
603 << (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
604 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
605 << lpTokensAMMBalance;
606 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
607 }
608
609 // Updated pool state must be valid - either all balances are zero
610 // or all balances are non-zero.
611 if (view.rules().enabled(featureMPTokensV2))
612 {
613 bool const newBalanceZero = (curBalance - amountWithdrawActual) == beast::kZero;
614 bool const newBalance2Zero =
615 (curBalance2 - amount2WithdrawActual.value_or(curBalance2.asset())) == beast::kZero;
616 bool const newLPTokensZero = (lpTokensAMMBalance - lpTokensWithdrawActual) == beast::kZero;
617 // newBalance2Zero can be zero if that side of the pool is frozen.
618 // ignore newBalance2Zero if one-sided withdrawal.
619 bool const valid = [&]() {
620 if (!amount2WithdrawActual)
621 return newBalanceZero == newLPTokensZero;
622 return newBalanceZero == newBalance2Zero && newBalance2Zero == newLPTokensZero;
623 }();
624 if (!valid)
625 {
626 JLOG(journal.debug()) << "AMM Withdraw: some balances are zero"
627 << " curBalance: " << curBalance << " " << amountWithdrawActual
628 << " curBalance2: " << curBalance2 << " "
629 << (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
630 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
631 << lpTokensAMMBalance;
632 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
633 }
634 }
635
636 // Check the reserve in case a trustline or MPT has to be created
637 bool const enabledFixAmMv12 = view.rules().enabled(fixAMMv1_2);
638 // If seated after a call to sufficientReserve() then MPToken must be
639 // authorized
640 std::optional<Keylet> mptokenKey;
641 auto sufficientReserve = [&](Asset const& asset) -> TER {
642 mptokenKey = std::nullopt;
643 if (!enabledFixAmMv12 || isXRP(asset))
644 return tesSUCCESS;
645 bool const isIssue = asset.holds<Issue>();
646 bool const assetNotExists = [&] {
647 if (isIssue)
648 return !view.exists(keylet::trustLine(account, asset.get<Issue>()));
649 auto const issuanceKey = keylet::mptokenIssuance(asset.get<MPTIssue>());
650 mptokenKey = keylet::mptoken(issuanceKey.key, account);
651 if (!view.exists(*mptokenKey))
652 return true;
653 mptokenKey = std::nullopt;
654 return false;
655 }();
656 if (assetNotExists)
657 {
658 auto sleAccount = view.peek(keylet::account(account));
659 if (!sleAccount)
660 return tecINTERNAL; // LCOV_EXCL_LINE
661 STAmount const balance = (*sleAccount)[sfBalance];
662 std::uint32_t const ownerCount = sleAccount->at(sfOwnerCount);
663
664 // See also TrustSet::doApply() and MPTokenAuthorize::authorize()
665 XRPAmount const reserve(
666 (ownerCount < 2) ? XRPAmount(beast::kZero)
667 : view.fees().accountReserve(ownerCount + 1));
668
669 auto const balanceAdj = isIssue ? std::max(priorBalance, balance.xrp()) : priorBalance;
670 if (balanceAdj < reserve)
672 }
673 return tesSUCCESS;
674 };
675
676 // Create MPToken if it doesn't exist
677 auto createMPToken = [&](Asset const& asset) -> TER {
678 // If mptoken is seated then must authorize
679 if (mptokenKey && account != asset.getIssuer())
680 {
681 auto const& mptIssue = asset.get<MPTIssue>();
682 if (auto const err = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
683 !isTesSuccess(err))
684 return err;
685
686 if (auto const err = checkCreateMPT(view, mptIssue, account, journal);
687 !isTesSuccess(err))
688 {
689 return err;
690 }
691 }
692 return tesSUCCESS;
693 };
694
695 if (auto const err = sufficientReserve(amountWithdrawActual.asset()))
696 return {err, STAmount{}, STAmount{}, STAmount{}};
697
698 if (auto const res = createMPToken(amountWithdrawActual.asset()); !isTesSuccess(res))
699 return {res, STAmount{}, STAmount{}, STAmount{}};
700
701 // Withdraw amountWithdraw
702 auto res = accountSend(
703 view, ammAccount, account, amountWithdrawActual, journal, WaiveTransferFee::Yes);
704 if (!isTesSuccess(res))
705 {
706 // LCOV_EXCL_START
707 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << amountWithdrawActual;
708 return {res, STAmount{}, STAmount{}, STAmount{}};
709 // LCOV_EXCL_STOP
710 }
711
712 // Withdraw amount2Withdraw
713 if (amount2WithdrawActual)
714 {
715 if (auto const err = sufficientReserve(amount2WithdrawActual->asset()); !isTesSuccess(err))
716 return {err, STAmount{}, STAmount{}, STAmount{}};
717
718 if (auto const res = createMPToken(amount2WithdrawActual->asset()); !isTesSuccess(res))
719 return {res, STAmount{}, STAmount{}, STAmount{}};
720
721 res = accountSend(
722 view, ammAccount, account, *amount2WithdrawActual, journal, WaiveTransferFee::Yes);
723 if (!isTesSuccess(res))
724 {
725 // LCOV_EXCL_START
726 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << *amount2WithdrawActual;
727 return {res, STAmount{}, STAmount{}, STAmount{}};
728 // LCOV_EXCL_STOP
729 }
730 }
731
732 // Withdraw LP tokens
733 res = redeemIOU(
734 view, account, lpTokensWithdrawActual, lpTokensWithdrawActual.get<Issue>(), journal);
735 if (!isTesSuccess(res))
736 {
737 // LCOV_EXCL_START
738 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw LPTokens";
739 return {res, STAmount{}, STAmount{}, STAmount{}};
740 // LCOV_EXCL_STOP
741 }
742
743 return std::make_tuple(
745 lpTokensAMMBalance - lpTokensWithdrawActual,
746 amountWithdrawActual,
747 amount2WithdrawActual);
748}
749
750static STAmount
752 Rules const& rules,
753 STAmount const& lptAMMBalance,
754 STAmount const& lpTokensWithdraw,
755 WithdrawAll withdrawAll)
756{
757 if (!rules.enabled(fixAMMv1_3) || withdrawAll == WithdrawAll::Yes)
758 return lpTokensWithdraw;
759 return adjustLPTokens(lptAMMBalance, lpTokensWithdraw, IsDeposit::No);
760}
761
766 Sandbox& view,
767 SLE const& ammSle,
768 AccountID const& ammAccount,
769 STAmount const& amountBalance,
770 STAmount const& amount2Balance,
771 STAmount const& lptAMMBalance,
772 STAmount const& lpTokens,
773 STAmount const& lpTokensWithdraw,
774 std::uint16_t tfee)
775{
776 TER ter;
777 STAmount newLPTokenBalance;
778 std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = equalWithdrawTokens(
779 view,
780 ammSle,
782 ammAccount,
783 amountBalance,
784 amount2Balance,
785 lptAMMBalance,
786 lpTokens,
787 lpTokensWithdraw,
788 tfee,
793 ctx_.journal);
794 return {ter, newLPTokenBalance};
795}
796
799 Sandbox& sb,
800 SLE::pointer const ammSle,
801 STAmount const& lpTokenBalance,
802 Asset const& asset1,
803 Asset const& asset2,
804 beast::Journal const& journal)
805{
806 TER ter;
807 bool updateBalance = true;
808 if (lpTokenBalance == beast::kZero)
809 {
810 ter = deleteAMMAccount(sb, asset1, asset2, journal);
811 if (!isTesSuccess(ter) && ter != tecINCOMPLETE)
812 return {ter, false}; // LCOV_EXCL_LINE
813
814 updateBalance = (ter == tecINCOMPLETE);
815 }
816
817 if (updateBalance)
818 {
819 ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
820 sb.update(ammSle);
821 }
822
823 return {ter, true};
824}
825
830 Sandbox& view,
831 SLE const& ammSle,
832 AccountID const account,
833 AccountID const& ammAccount,
834 STAmount const& amountBalance,
835 STAmount const& amount2Balance,
836 STAmount const& lptAMMBalance,
837 STAmount const& lpTokens,
838 STAmount const& lpTokensWithdraw,
839 std::uint16_t tfee,
840 FreezeHandling freezeHandling,
841 AuthHandling authHandling,
842 WithdrawAll withdrawAll,
843 XRPAmount const& priorBalance,
844 beast::Journal const& journal)
845{
846 try
847 {
848 // Withdrawing all tokens in the pool
849 if (lpTokensWithdraw == lptAMMBalance)
850 {
851 return withdraw(
852 view,
853 ammSle,
854 ammAccount,
855 account,
856 amountBalance,
857 amountBalance,
858 amount2Balance,
859 lptAMMBalance,
860 lpTokensWithdraw,
861 tfee,
862 freezeHandling,
863 authHandling,
865 priorBalance,
866 journal);
867 }
868
869 auto const tokensAdj =
870 adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, withdrawAll);
871 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
872 return {tecAMM_INVALID_TOKENS, STAmount{}, STAmount{}, std::nullopt};
873 // the adjusted tokens are factored in
874 auto const frac = divide(tokensAdj, lptAMMBalance, noIssue());
875 auto const amountWithdraw =
876 getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
877 auto const amount2Withdraw =
878 getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::No);
879 // LP is making equal withdrawal by tokens but the requested amount
880 // of LP tokens is likely too small and results in one-sided pool
881 // withdrawal due to round off. Fail so the user withdraws
882 // more tokens.
883 if (amountWithdraw == beast::kZero || amount2Withdraw == beast::kZero)
884 return {tecAMM_FAILED, STAmount{}, STAmount{}, STAmount{}};
885
886 return withdraw(
887 view,
888 ammSle,
889 ammAccount,
890 account,
891 amountBalance,
892 amountWithdraw,
893 amount2Withdraw,
894 lptAMMBalance,
895 tokensAdj,
896 tfee,
897 freezeHandling,
898 authHandling,
899 withdrawAll,
900 priorBalance,
901 journal);
902 }
903 // LCOV_EXCL_START
904 catch (std::exception const& e)
905 {
906 JLOG(journal.error()) << "AMMWithdraw::equalWithdrawTokens exception " << e.what();
907 }
908 return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}};
909 // LCOV_EXCL_STOP
910}
911
939 Sandbox& view,
940 SLE const& ammSle,
941 AccountID const& ammAccount,
942 STAmount const& amountBalance,
943 STAmount const& amount2Balance,
944 STAmount const& lptAMMBalance,
945 STAmount const& amount,
946 STAmount const& amount2,
947 std::uint16_t tfee)
948{
949 auto frac = Number{amount} / amountBalance;
950 auto tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::No);
951 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
953 // factor in the adjusted tokens
954 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
955 auto const amount2Withdraw = getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::No);
956 if (amount2Withdraw <= amount2)
957 {
958 return withdraw(
959 view,
960 ammSle,
961 ammAccount,
962 amountBalance,
963 amount,
964 amount2Withdraw,
965 lptAMMBalance,
966 tokensAdj,
967 tfee);
968 }
969
970 frac = Number{amount2} / amount2Balance;
971 auto amountWithdraw = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
972 tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::No);
973 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
974 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
975 // factor in the adjusted tokens
976 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
977 amountWithdraw = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
978 if (!view.rules().enabled(fixAMMv1_3))
979 {
980 // LCOV_EXCL_START
981 XRPL_ASSERT(
982 amountWithdraw <= amount,
983 "xrpl::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw");
984 // LCOV_EXCL_STOP
985 }
986 else if (amountWithdraw > amount)
987 {
988 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
989 }
990 return withdraw(
991 view,
992 ammSle,
993 ammAccount,
994 amountBalance,
995 amountWithdraw,
996 amount2,
997 lptAMMBalance,
998 tokensAdj,
999 tfee);
1000}
1001
1009 Sandbox& view,
1010 SLE const& ammSle,
1011 AccountID const& ammAccount,
1012 STAmount const& amountBalance,
1013 STAmount const& lptAMMBalance,
1014 STAmount const& amount,
1015 std::uint16_t tfee)
1016{
1017 auto const tokens = adjustLPTokensIn(
1018 view.rules(),
1019 lptAMMBalance,
1020 lpTokensIn(amountBalance, amount, lptAMMBalance, tfee),
1021 isWithdrawAll(ctx_.tx));
1022 if (tokens == beast::kZero)
1023 {
1024 if (!view.rules().enabled(fixAMMv1_3))
1025 {
1026 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
1027 }
1028
1029 return {tecAMM_INVALID_TOKENS, STAmount{}};
1030 }
1031 // factor in the adjusted tokens
1032 auto const [tokensAdj, amountWithdrawAdj] =
1033 adjustAssetOutByTokens(view.rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
1034 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1035 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
1036 return withdraw(
1037 view,
1038 ammSle,
1039 ammAccount,
1040 amountBalance,
1041 amountWithdrawAdj,
1042 std::nullopt,
1043 lptAMMBalance,
1044 tokensAdj,
1045 tfee);
1046}
1047
1060 Sandbox& view,
1061 SLE const& ammSle,
1062 AccountID const& ammAccount,
1063 STAmount const& amountBalance,
1064 STAmount const& lptAMMBalance,
1065 STAmount const& amount,
1066 STAmount const& lpTokensWithdraw,
1067 std::uint16_t tfee)
1068{
1069 auto const tokensAdj =
1070 adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, isWithdrawAll(ctx_.tx));
1071 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1072 return {tecAMM_INVALID_TOKENS, STAmount{}};
1073 // the adjusted tokens are factored in
1074 auto const amountWithdraw = ammAssetOut(amountBalance, lptAMMBalance, tokensAdj, tfee);
1075 if (amount == beast::kZero || amountWithdraw >= amount)
1076 {
1077 return withdraw(
1078 view,
1079 ammSle,
1080 ammAccount,
1081 amountBalance,
1082 amountWithdraw,
1083 std::nullopt,
1084 lptAMMBalance,
1085 tokensAdj,
1086 tfee);
1087 }
1088
1089 return {tecAMM_FAILED, STAmount{}};
1090}
1091
1113 Sandbox& view,
1114 SLE const& ammSle,
1115 AccountID const& ammAccount,
1116 STAmount const& amountBalance,
1117 STAmount const& lptAMMBalance,
1118 STAmount const& amount,
1119 STAmount const& ePrice,
1120 std::uint16_t tfee)
1121{
1122 // LPTokens is asset in => E = t / a and formula (8) is:
1123 // a = A*(t1**2 + t1*(f - 2))/(t1*f - 1)
1124 // substitute a as t/E =>
1125 // t/E = A*(t1**2 + t1*(f - 2))/(t1*f - 1), t1=t/T => t = t1*T
1126 // t1*T/E = A*((t/T)**2 + t*(f - 2)/T)/(t*f/T - 1) =>
1127 // T/E = A*(t1 + f-2)/(t1*f - 1) =>
1128 // T*(t1*f - 1) = A*E*(t1 + f - 2) =>
1129 // t1*T*f - T = t1*A*E + A*E*(f - 2) =>
1130 // t1*(T*f - A*E) = T + A*E*(f - 2) =>
1131 // t = T*(T + A*E*(f - 2))/(T*f - A*E)
1132 Number const ae = amountBalance * ePrice;
1133 auto const f = getFee(tfee);
1134 auto const denom = lptAMMBalance * f - ae;
1135 // fixCleanup3_3_0: guard against division by zero
1136 // when ePrice == lptAMMBalance*f/amountBalance
1137 if (view.rules().enabled(fixCleanup3_3_0) && denom == beast::kZero)
1138 return {tecAMM_FAILED, STAmount{}};
1139 auto tokNoRoundCb = [&] { return lptAMMBalance * (lptAMMBalance + ae * (f - 2)) / denom; };
1140 auto tokProdCb = [&] { return (lptAMMBalance + ae * (f - 2)) / denom; };
1141 auto const tokensAdj =
1142 getRoundedLPTokens(view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::No);
1143 if (tokensAdj <= beast::kZero)
1144 {
1145 if (!view.rules().enabled(fixAMMv1_3))
1146 {
1147 return {tecAMM_FAILED, STAmount{}};
1148 }
1149
1150 return {tecAMM_INVALID_TOKENS, STAmount{}};
1151 }
1152 auto amtNoRoundCb = [&] { return tokensAdj / ePrice; };
1153 auto amtProdCb = [&] { return tokensAdj / ePrice; };
1154 // the adjusted tokens are factored in
1155 auto const amountWithdraw =
1156 getRoundedAsset(view.rules(), amtNoRoundCb, amount, amtProdCb, IsDeposit::No);
1157 if (amount == beast::kZero || amountWithdraw >= amount)
1158 {
1159 return withdraw(
1160 view,
1161 ammSle,
1162 ammAccount,
1163 amountBalance,
1164 amountWithdraw,
1165 std::nullopt,
1166 lptAMMBalance,
1167 tokensAdj,
1168 tfee);
1169 }
1170
1171 return {tecAMM_FAILED, STAmount{}};
1172}
1173
1176{
1177 if ((tx[sfFlags] & (tfWithdrawAll | tfOneAssetWithdrawAll)) != 0u)
1178 return WithdrawAll::Yes;
1179 return WithdrawAll::No;
1180}
1181void
1183{
1184 // No transaction-specific invariants yet (future work).
1185}
1186
1187bool
1189{
1190 // No transaction-specific invariants yet (future work).
1191 return true;
1192}
1193
1194} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
Stream error() const
Definition Journal.h:315
Stream debug() const
Definition Journal.h:297
FreezeHandling issuerFreezeHandling() const
Returns IgnoreFreeze when the withdrawer is the issuer of a pool asset (post-fixCleanup3_3_0),...
static std::pair< TER, bool > deleteAMMAccountIfEmpty(Sandbox &sb, SLE::pointer const ammSle, STAmount const &lpTokenBalance, Asset const &asset1, Asset const &asset2, beast::Journal const &journal)
static WithdrawAll isWithdrawAll(STTx const &tx)
Check from the flags if it's withdraw all.
static NotTEC preflight(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.
static TER preclaim(PreclaimContext const &ctx)
TER doApply() override
std::pair< TER, STAmount > equalWithdrawLimit(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &amount, STAmount const &amount2, std::uint16_t tfee)
Withdraw both assets (Asset1Out, Asset2Out) with the constraints on the maximum amount of each asset ...
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.
static std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > withdraw(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, AccountID const &account, STAmount const &amountBalance, STAmount const &amountWithdraw, std::optional< STAmount > const &amount2Withdraw, STAmount const &lpTokensAMMBalance, STAmount const &lpTokensWithdraw, std::uint16_t tfee, FreezeHandling freezeHandling, AuthHandling authHandling, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Withdraw requested assets and token from AMM into LP account.
std::pair< TER, STAmount > singleWithdrawTokens(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &lptAMMBalance, STAmount const &amount, STAmount const &lpTokensWithdraw, std::uint16_t tfee)
Single asset withdrawal (Asset1Out, LPTokens) proportional to the share specified by tokens.
static std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > equalWithdrawTokens(Sandbox &view, SLE const &ammSle, AccountID const account, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &amount2Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, STAmount const &lpTokensWithdraw, std::uint16_t tfee, FreezeHandling freezeHandling, AuthHandling authHandling, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Equal-asset withdrawal (LPTokens) of some AMM instance pools shares represented by the number of LPTo...
std::pair< TER, STAmount > singleWithdraw(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &lptAMMBalance, STAmount const &amount, std::uint16_t tfee)
Single asset withdrawal (Asset1Out) equivalent to the amount specified in Asset1Out.
static bool checkExtraFeatures(PreflightContext const &ctx)
std::pair< TER, bool > applyGuts(Sandbox &view)
std::pair< TER, STAmount > singleWithdrawEPrice(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, STAmount const &amountBalance, STAmount const &lptAMMBalance, STAmount const &amount, STAmount const &ePrice, std::uint16_t tfee)
Withdraw single asset (Asset1Out, EPrice) with two constraints.
A currency issued by an account.
Definition Issue.h:13
Number is a floating point type that can represent a wide range of values.
Definition Number.h:306
A view into a ledger.
Definition ReadView.h:31
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:33
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:171
Asset const & asset() const
Definition STAmount.h:478
XRPAmount xrp() const
Definition STAmount.cpp:271
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFlag(std::uint32_t) const
Definition STObject.cpp:501
std::uint32_t getFlags() const
Definition STObject.cpp:507
Discardable, editable view to a ledger.
Definition Sandbox.h:15
void apply(RawView &to)
Definition Sandbox.h:35
beast::Journal const j_
Definition Transactor.h:118
ApplyView & view()
Definition Transactor.h:136
AccountID const accountID_
Definition Transactor.h:120
XRPAmount preFeeBalance_
Definition Transactor.h:121
ApplyContext & ctx_
Definition Transactor.h:116
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.
SLE::const_pointer read(Keylet const &k) const override
Return the state item associated with a key.
Rules const & rules() const override
Returns the tx processing rules.
T make_optional(T... args)
T make_pair(T... args)
T make_tuple(T... args)
T max(T... args)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet mptokenIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:521
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:425
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:533
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
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:241
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:97
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
@ terNO_AMM
Definition TER.h:219
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.
FreezeHandling
Controls the treatment of frozen account balances.
WithdrawAll
AMMWithdraw implements AMM withdraw Transactor.
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:128
bool isXRP(AccountID const &c)
Definition AccountID.h:70
Number adjustFracByTokens(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &tokens, Number const &frac)
Find a fraction of tokens after the tokens are adjusted.
TER checkCreateMPT(xrpl::ApplyView &view, xrpl::MPTIssue const &mptIssue, xrpl::AccountID const &holder, beast::Journal j)
std::expected< bool, TER > verifyAndAdjustLPTokenBalance(Sandbox &sb, STAmount const &lpTokens, SLE::pointer &ammSle, AccountID const &account)
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance.
NotTEC invalidAMMAssetPair(Asset const &asset1, Asset const &asset2, std::optional< std::pair< Asset, Asset > > const &pair=std::nullopt)
Definition AMMCore.cpp:82
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
static std::optional< STAmount > tokensWithdraw(STAmount const &lpTokens, std::optional< STAmount > const &tokensIn, std::uint32_t flags)
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
STAmount ammAssetOut(STAmount const &assetBalance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset withdrawal by tokens.
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:633
STLedgerEntry SLE
static STAmount adjustLPTokensIn(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &lpTokensWithdraw, WithdrawAll withdrawAll)
std::pair< STAmount, STAmount > adjustAssetOutByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
TER deleteAMMAccount(Sandbox &view, Asset const &asset, Asset const &asset2, beast::Journal j)
Delete trustlines to AMM.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:594
TER createMPToken(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, std::uint32_t const flags)
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:626
AuthHandling
Controls the treatment of unauthorized MPT balances.
TER checkWithdrawFreeze(ReadView const &view, AccountID const &srcAcct, AccountID const &submitterAcct, AccountID const &dstAcct, Asset const &asset)
Checks freeze compliance for withdrawing an asset from a pseudo-account (e.g.
TER checkAMMPrecisionLoss(Number const &poolProductMean, STAmount const &newLPTokenBalance)
Check AMM pool product invariant after an AMM operation that changes LP tokens (deposit/withdraw/claw...
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
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...
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
@ temBAD_AMM_TOKENS
Definition TER.h:115
@ temMALFORMED
Definition TER.h:73
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
STAmount lpTokensIn(STAmount const &asset1Balance, STAmount const &asset1Withdraw, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's withdraw amount.
Number getFee(std::uint16_t tfee)
Convert to the fee from the basis points.
Definition AMMCore.h:78
TERSubset< CanCvtToTER > TER
Definition TER.h:634
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.
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:105
@ tecAMM_EMPTY
Definition TER.h:330
@ tecAMM_INVALID_TOKENS
Definition TER.h:329
@ tecAMM_FAILED
Definition TER.h:328
@ tecINCOMPLETE
Definition TER.h:333
@ tecINTERNAL
Definition TER.h:308
@ tecAMM_BALANCE
Definition TER.h:327
@ tecINSUFFICIENT_RESERVE
Definition TER.h:305
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.
constexpr FlagValue tfWithdrawSubTx
Definition TxFlags.h:396
@ tesSUCCESS
Definition TER.h:240
T popcount(T... args)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
beast::Journal const j
Definition Transactor.h:69
State information when preflighting a tx.
Definition Transactor.h:18
beast::Journal const j
Definition Transactor.h:25
T tie(T... args)
T what(T... args)