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/AccountRootHelpers.h>
11#include <xrpl/ledger/helpers/MPTokenHelpers.h>
12#include <xrpl/ledger/helpers/RippleStateHelpers.h>
13#include <xrpl/ledger/helpers/TokenHelpers.h>
14#include <xrpl/protocol/AMMCore.h>
15#include <xrpl/protocol/AccountID.h>
16#include <xrpl/protocol/Asset.h>
17#include <xrpl/protocol/Feature.h>
18#include <xrpl/protocol/IOUAmount.h>
19#include <xrpl/protocol/Indexes.h>
20#include <xrpl/protocol/Issue.h>
21#include <xrpl/protocol/Keylet.h>
22#include <xrpl/protocol/LedgerFormats.h>
23#include <xrpl/protocol/MPTIssue.h>
24#include <xrpl/protocol/SField.h>
25#include <xrpl/protocol/STAmount.h>
26#include <xrpl/protocol/STLedgerEntry.h>
27#include <xrpl/protocol/STTx.h>
28#include <xrpl/protocol/TER.h>
29#include <xrpl/protocol/TxFlags.h>
30#include <xrpl/protocol/XRPAmount.h>
31#include <xrpl/tx/Transactor.h>
32
33#include <algorithm>
34#include <bit>
35#include <cstdint>
36#include <exception>
37#include <optional>
38#include <stdexcept>
39#include <tuple>
40#include <utility>
41
42namespace xrpl {
43
44bool
46{
47 if (!ammEnabled(ctx.rules))
48 return false;
49
50 auto const amount = ctx.tx[~sfAmount];
51 auto const amount2 = ctx.tx[~sfAmount2];
52
53 return ctx.rules.enabled(featureMPTokensV2) ||
54 (!ctx.tx[sfAsset].holds<MPTIssue>() && !ctx.tx[sfAsset2].holds<MPTIssue>() &&
55 !(amount && amount->holds<MPTIssue>()) && !(amount2 && amount2->holds<MPTIssue>()));
56}
57
60{
61 return tfAMMWithdrawMask;
62}
63
66{
67 auto const flags = ctx.tx.getFlags();
68
69 auto const amount = ctx.tx[~sfAmount];
70 auto const amount2 = ctx.tx[~sfAmount2];
71 auto const ePrice = ctx.tx[~sfEPrice];
72 auto const lpTokens = ctx.tx[~sfLPTokenIn];
73 // Valid combinations are:
74 // LPTokens
75 // tfWithdrawAll
76 // Amount
77 // tfOneAssetWithdrawAll & Amount
78 // Amount and Amount2
79 // Amount and LPTokens
80 // Amount and EPrice
81 if (std::popcount(flags & tfWithdrawSubTx) != 1)
82 {
83 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid flags.";
84 return temMALFORMED;
85 }
86 if (ctx.tx.isFlag(tfLPToken))
87 {
88 if (!lpTokens || amount || amount2 || ePrice)
89 return temMALFORMED;
90 }
91 else if (ctx.tx.isFlag(tfWithdrawAll))
92 {
93 if (lpTokens || amount || amount2 || ePrice)
94 return temMALFORMED;
95 }
96 else if (ctx.tx.isFlag(tfOneAssetWithdrawAll) || ctx.tx.isFlag(tfSingleAsset))
97 {
98 if (!amount || lpTokens || amount2 || ePrice)
99 return temMALFORMED;
100 }
101 else if (ctx.tx.isFlag(tfTwoAsset))
102 {
103 if (!amount || !amount2 || lpTokens || ePrice)
104 return temMALFORMED;
105 }
106 else if (ctx.tx.isFlag(tfOneAssetLPToken))
107 {
108 if (!amount || !lpTokens || amount2 || ePrice)
109 return temMALFORMED;
110 }
111 else if (ctx.tx.isFlag(tfLimitLPToken))
112 {
113 if (!amount || !ePrice || lpTokens || amount2)
114 return temMALFORMED;
115 }
116
117 auto const asset = ctx.tx[sfAsset];
118 auto const asset2 = ctx.tx[sfAsset2];
119 if (auto const res = invalidAMMAssetPair(asset, asset2))
120 {
121 JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
122 return res;
123 }
124
125 if (amount && amount2 && amount->asset() == amount2->asset())
126 {
127 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens, same issue." << amount->asset() << " "
128 << amount2->asset();
129 return temBAD_AMM_TOKENS;
130 }
131
132 if (lpTokens && *lpTokens <= beast::kZero)
133 {
134 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
135 return temBAD_AMM_TOKENS;
136 }
137
138 if (amount)
139 {
140 if (auto const res = invalidAMMAmount(
141 *amount,
142 std::make_optional(std::make_pair(asset, asset2)),
143 ((flags & (tfOneAssetWithdrawAll | tfOneAssetLPToken)) != 0u) || ePrice))
144 {
145 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset1Out";
146 return res;
147 }
148 }
149
150 if (amount2)
151 {
152 if (auto const res =
153 invalidAMMAmount(*amount2, std::make_optional(std::make_pair(asset, asset2))))
154 {
155 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid Asset2OutAmount";
156 return res;
157 }
158 }
159
160 if (ePrice)
161 {
162 if (auto const res = invalidAMMAmount(*ePrice))
163 {
164 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice";
165 return res;
166 }
167 }
168
169 return tesSUCCESS;
170}
171
174 STAmount const& lpTokens,
175 std::optional<STAmount> const& tokensIn,
176 std::uint32_t flags)
177{
178 if ((flags & (tfWithdrawAll | tfOneAssetWithdrawAll)) != 0u)
179 return lpTokens;
180 return tokensIn;
181}
182
183TER
185{
186 auto const accountID = ctx.tx[sfAccount];
187
188 auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
189 if (!ammSle)
190 {
191 JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
192 return terNO_AMM;
193 }
194
195 auto const amount = ctx.tx[~sfAmount];
196 auto const amount2 = ctx.tx[~sfAmount2];
197
198 auto const expected = ammHolds(
199 ctx.view,
200 *ammSle,
201 amount ? amount->asset() : std::optional<Asset>{},
202 amount2 ? amount2->asset() : std::optional<Asset>{},
205 ctx.j);
206 if (!expected)
207 return expected.error();
208 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
209 if (lptAMMBalance == beast::kZero)
210 return tecAMM_EMPTY;
211 if (amountBalance <= beast::kZero || amount2Balance <= beast::kZero ||
212 lptAMMBalance < beast::kZero)
213 {
214 // LCOV_EXCL_START
215 JLOG(ctx.j.debug()) << "AMM Withdraw: reserves or tokens balance is zero.";
216 return tecINTERNAL;
217 // LCOV_EXCL_STOP
218 }
219
220 auto const ammAccountID = ammSle->getAccountID(sfAccount);
221
222 auto checkAmount = [&](std::optional<STAmount> const& amount, auto const& balance) -> TER {
223 if (amount)
224 {
225 if (amount > balance)
226 {
227 JLOG(ctx.j.debug())
228 << "AMM Withdraw: withdrawing more than the balance, " << *amount;
229 return tecAMM_BALANCE;
230 }
231 // WeakAuth - MPToken is created if it doesn't exist.
232 if (auto const ter =
233 requireAuth(ctx.view, amount->asset(), accountID, AuthType::WeakAuth))
234 {
235 JLOG(ctx.j.debug())
236 << "AMM Withdraw: account is not authorized, " << amount->asset();
237 return ter;
238 }
239 if (ctx.view.rules().enabled(fixCleanup3_3_0))
240 {
241 if (auto const ret = checkWithdrawFreeze(
242 ctx.view, ammAccountID, accountID, accountID, amount->asset()))
243 {
244 JLOG(ctx.j.debug()) << "AMM Withdraw: frozen, " << to_string(accountID) << " "
245 << to_string(amount->asset());
246 return ret;
247 }
248 }
249 else
250 {
251 // AMM account or currency frozen
252 if (auto const ter = checkFrozen(ctx.view, ammAccountID, amount->asset());
253 !isTesSuccess(ter))
254 {
255 JLOG(ctx.j.debug())
256 << "AMM Withdraw: AMM account or currency is frozen or locked, "
257 << to_string(accountID);
258 return ter;
259 }
260 // Account frozen
261 if (auto const ter = checkIndividualFrozen(ctx.view, accountID, amount->asset());
262 !isTesSuccess(ter))
263 {
264 JLOG(ctx.j.debug())
265 << "AMM Withdraw: account is frozen or locked, " << to_string(accountID)
266 << " " << to_string(amount->asset());
267 return ter;
268 }
269 }
270 }
271 return tesSUCCESS;
272 };
273
274 if (auto const ter = checkAmount(amount, amountBalance))
275 return ter;
276
277 if (auto const ter = checkAmount(amount2, amount2Balance))
278 return ter;
279
280 auto const lpTokens = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
281 auto const lpTokensWithdraw = tokensWithdraw(lpTokens, ctx.tx[~sfLPTokenIn], ctx.tx.getFlags());
282
283 if (lpTokens <= beast::kZero)
284 {
285 JLOG(ctx.j.debug()) << "AMM Withdraw: tokens balance is zero.";
286 return tecAMM_BALANCE;
287 }
288
289 if (lpTokensWithdraw && lpTokensWithdraw->asset() != lpTokens.asset())
290 {
291 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid LPTokens.";
292 return temBAD_AMM_TOKENS;
293 }
294
295 if (lpTokensWithdraw && *lpTokensWithdraw > lpTokens)
296 {
297 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid tokens.";
299 }
300
301 if (auto const ePrice = ctx.tx[~sfEPrice]; ePrice && ePrice->asset() != lpTokens.asset())
302 {
303 JLOG(ctx.j.debug()) << "AMM Withdraw: invalid EPrice.";
304 return temBAD_AMM_TOKENS;
305 }
306
307 if ((ctx.tx.getFlags() & (tfLPToken | tfWithdrawAll)) != 0u)
308 {
309 if (auto const ter = checkAmount(amountBalance, amountBalance))
310 return ter;
311 if (auto const ter = checkAmount(amount2Balance, amount2Balance))
312 return ter;
313 }
314
315 return tesSUCCESS;
316}
317
320{
321 // When the withdrawer is the issuer of a pool asset, the issuer can
322 // always receive their own token — even when the pool is frozen.
323 // Use IgnoreFreeze so ammHolds returns real balances instead of zero.
324 if (!ctx_.view().rules().enabled(fixCleanup3_3_0))
326
327 auto const asset1 = Asset{ctx_.tx[sfAsset]};
328 auto const asset2 = Asset{ctx_.tx[sfAsset2]};
329 if (!asset1.native() && accountID_ == asset1.getIssuer())
331 if (!asset2.native() && accountID_ == asset2.getIssuer())
333
335}
336
339{
340 auto const amount = ctx_.tx[~sfAmount];
341 auto const amount2 = ctx_.tx[~sfAmount2];
342 auto const ePrice = ctx_.tx[~sfEPrice];
343 auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
344 if (!ammSle)
345 return {tecINTERNAL, false}; // LCOV_EXCL_LINE
346 auto const ammAccountID = (*ammSle)[sfAccount];
347 auto const accountSle = sb.read(keylet::account(ammAccountID));
348 if (!accountSle)
349 return {tecINTERNAL, false}; // LCOV_EXCL_LINE
350 auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
351 auto const lpTokensWithdraw =
352 tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags());
353
354 // Due to rounding, the LPTokenBalance of the last LP
355 // might not match the LP's trustline balance
356 if (sb.rules().enabled(fixAMMv1_1))
357 {
358 if (auto const res = verifyAndAdjustLPTokenBalance(sb, lpTokens, ammSle, accountID_); !res)
359 return {res.error(), false};
360 }
361
362 auto const tfee = getTradingFee(ctx_.view(), *ammSle, accountID_);
363
364 auto const freezeHandling = issuerFreezeHandling();
365
366 auto const expected = ammHolds(
367 sb,
368 *ammSle,
369 amount ? amount->asset() : std::optional<Asset>{},
370 amount2 ? amount2->asset() : std::optional<Asset>{},
371 freezeHandling,
373 ctx_.journal);
374 if (!expected)
375 return {expected.error(), false};
376 auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
377 auto const subTxType = ctx_.tx.getFlags() & tfWithdrawSubTx;
378
379 auto dispatchToWithdraw = [&,
380 &amountBalance = amountBalance,
381 &amount2Balance = amount2Balance,
382 &lptAMMBalance = lptAMMBalance]() -> std::pair<TER, STAmount> {
383 if (subTxType & tfTwoAsset)
384 {
385 return equalWithdrawLimit(
386 sb,
387 *ammSle,
388 ammAccountID,
389 amountBalance,
390 amount2Balance,
391 lptAMMBalance,
392 *amount,
393 *amount2,
394 tfee);
395 }
396 if (subTxType & tfOneAssetLPToken || subTxType & tfOneAssetWithdrawAll)
397 {
399 sb,
400 *ammSle,
401 ammAccountID,
402 amountBalance,
403 lptAMMBalance,
404 *amount,
405 *lpTokensWithdraw,
406 tfee);
407 }
408 if (subTxType & tfLimitLPToken)
409 {
411 sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, *ePrice, tfee);
412 }
413 if (subTxType & tfSingleAsset)
414 {
415 return singleWithdraw(
416 sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee);
417 }
418 if (subTxType & tfLPToken || subTxType & tfWithdrawAll)
419 {
420 return equalWithdrawTokens(
421 sb,
422 *ammSle,
423 ammAccountID,
424 amountBalance,
425 amount2Balance,
426 lptAMMBalance,
427 lpTokens,
428 *lpTokensWithdraw,
429 tfee);
430 }
431 // should not happen.
432 // LCOV_EXCL_START
433 JLOG(j_.error()) << "AMM Withdraw: invalid options.";
435 // LCOV_EXCL_STOP
436 };
437
438 auto const [result, newLPTokenBalance] = [&]() -> std::pair<TER, STAmount> {
439 try
440 {
441 return dispatchToWithdraw();
442 }
443 catch (std::runtime_error const& e)
444 {
445 // Defense in-depth for amount overflow/out-of-range: the withdrawal
446 // counterpart of the AMMDeposit guard. Unlike deposit, no known
447 // withdraw path can throw here - preclaim bounds the requested
448 // amounts by the pool balances, and the only historical throw
449 // (denom == 0 in singleWithdrawEPrice) is guarded under
450 // fixCleanup3_3_0. Gated by fixCleanup3_4_0 to preserve the
451 // legacy tefEXCEPTION pre-amendment.
452 if (!sb.rules().enabled(fixCleanup3_4_0))
453 throw;
454 // LCOV_EXCL_START
455 JLOG(j_.error()) << "AMMWithdraw: amount out of range " << e.what();
457 // LCOV_EXCL_STOP
458 }
459 }();
460
461 if (!isTesSuccess(result))
462 return {result, false};
463
464 if (sb.rules().enabled(fixCleanup3_3_0) && sb.rules().enabled(fixAMMv1_3))
465 {
466 if (auto const ter = checkAMMPrecisionLoss(
467 sb, ammAccountID, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], newLPTokenBalance, j_);
468 !isTesSuccess(ter))
469 {
470 return {ter, false};
471 }
472 }
473
474 auto const res = deleteAMMAccountIfEmpty(
475 sb, ammSle, newLPTokenBalance, ctx_.tx[sfAsset], ctx_.tx[sfAsset2], j_);
476 // LCOV_EXCL_START
477 if (!res.second)
478 return {res.first, false};
479 // LCOV_EXCL_STOP
480
481 JLOG(ctx_.journal.trace()) << "AMM Withdraw: tokens " << to_string(newLPTokenBalance.iou())
482 << " " << to_string(lpTokens.iou()) << " "
483 << to_string(lptAMMBalance.iou());
484
485 return {tesSUCCESS, true};
486}
487
488TER
490{
491 // This is the ledger view that we work against. Transactions are applied
492 // as we go on processing transactions.
493 Sandbox sb(&ctx_.view());
494
495 auto const result = applyGuts(sb);
496 if (result.second)
497 sb.apply(ctx_.rawView());
498
499 return result.first;
500}
501
504 Sandbox& view,
505 SLE const& ammSle,
506 AccountID const& ammAccount,
507 STAmount const& amountBalance,
508 STAmount const& amountWithdraw,
509 std::optional<STAmount> const& amount2Withdraw,
510 STAmount const& lpTokensAMMBalance,
511 STAmount const& lpTokensWithdraw,
512 std::uint16_t tfee)
513{
514 TER ter;
515 STAmount newLPTokenBalance;
516 std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = withdraw(
517 view,
518 ammSle,
519 ammAccount,
520 std::nullopt,
522 amountBalance,
523 amountWithdraw,
524 amount2Withdraw,
525 lpTokensAMMBalance,
526 lpTokensWithdraw,
527 tfee,
533 j_);
534 return {ter, newLPTokenBalance};
535}
536
539 Sandbox& view,
540 SLE const& ammSle,
541 AccountID const& ammAccount,
542 std::optional<AccountID> const& clawbackIssuer,
543 AccountID const& account,
544 STAmount const& amountBalance,
545 STAmount const& amountWithdraw,
546 std::optional<STAmount> const& amount2Withdraw,
547 STAmount const& lpTokensAMMBalance,
548 STAmount const& lpTokensWithdraw,
549 std::uint16_t tfee,
550 FreezeHandling freezeHandling,
551 AuthHandling authHandling,
552 ReserveHandling reserveHandling,
553 WithdrawAll withdrawAll,
554 XRPAmount const& priorBalance,
555 beast::Journal const& journal)
556{
557 auto const lpTokens = ammLPHolds(view, ammSle, account, journal);
558 auto const expected = ammHolds(
559 view, ammSle, amountWithdraw.asset(), std::nullopt, freezeHandling, authHandling, journal);
560 // LCOV_EXCL_START
561 if (!expected)
562 return {expected.error(), STAmount{}, STAmount{}, STAmount{}};
563 // LCOV_EXCL_STOP
564 auto const [curBalance, curBalance2, _] = *expected;
565 (void)_;
566
567 auto const [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
569 if (withdrawAll == WithdrawAll::No)
570 {
572 amountBalance,
573 amountWithdraw,
574 amount2Withdraw,
575 lpTokensAMMBalance,
576 lpTokensWithdraw,
577 tfee,
579 }
580 return std::make_tuple(amountWithdraw, amount2Withdraw, lpTokensWithdraw);
581 }();
582
583 if (lpTokensWithdrawActual <= beast::kZero || lpTokensWithdrawActual > lpTokens)
584 {
585 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, invalid LP tokens: "
586 << lpTokensWithdrawActual << " " << lpTokens << " "
587 << lpTokensAMMBalance;
589 }
590
591 // Should not happen since the only LP on last withdraw
592 // has the balance set to the lp token trustline balance.
593 if (view.rules().enabled(fixAMMv1_1) && lpTokensWithdrawActual > lpTokensAMMBalance)
594 {
595 // LCOV_EXCL_START
596 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, unexpected LP tokens: "
597 << lpTokensWithdrawActual << " " << lpTokens << " "
598 << lpTokensAMMBalance;
599 return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}};
600 // LCOV_EXCL_STOP
601 }
602
603 // Withdrawing one side of the pool
604 if ((amountWithdrawActual == curBalance && amount2WithdrawActual != curBalance2) ||
605 (amount2WithdrawActual == curBalance2 && amountWithdrawActual != curBalance))
606 {
607 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw one side of the pool "
608 << " curBalance: " << curBalance << " " << amountWithdrawActual
609 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
610 << lpTokensAMMBalance;
611 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
612 }
613
614 // May happen if withdrawing an amount close to one side of the pool
615 if (lpTokensWithdrawActual == lpTokensAMMBalance &&
616 (amountWithdrawActual != curBalance || amount2WithdrawActual != curBalance2))
617 {
618 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw all tokens "
619 << " curBalance: " << curBalance << " " << amountWithdrawActual
620 << " curBalance2: " << amount2WithdrawActual.value_or(STAmount{0})
621 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
622 << lpTokensAMMBalance;
623 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
624 }
625
626 // Withdrawing more than the pool's balance
627 if (amountWithdrawActual > curBalance || amount2WithdrawActual > curBalance2)
628 {
629 JLOG(journal.debug()) << "AMM Withdraw: withdrawing more than the pool's balance "
630 << " curBalance: " << curBalance << " " << amountWithdrawActual
631 << " curBalance2: " << curBalance2 << " "
632 << (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
633 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
634 << lpTokensAMMBalance;
635 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
636 }
637
638 // Updated pool state must be valid - either all balances are zero
639 // or all balances are non-zero.
640 if (view.rules().enabled(featureMPTokensV2))
641 {
642 bool const newBalanceZero = (curBalance - amountWithdrawActual) == beast::kZero;
643 bool const newBalance2Zero =
644 (curBalance2 - amount2WithdrawActual.value_or(curBalance2.asset())) == beast::kZero;
645 bool const newLPTokensZero = (lpTokensAMMBalance - lpTokensWithdrawActual) == beast::kZero;
646 // newBalance2Zero can be zero if that side of the pool is frozen.
647 // ignore newBalance2Zero if one-sided withdrawal.
648 bool const valid = [&]() {
649 if (!amount2WithdrawActual)
650 return newBalanceZero == newLPTokensZero;
651 return newBalanceZero == newBalance2Zero && newBalance2Zero == newLPTokensZero;
652 }();
653 if (!valid)
654 {
655 JLOG(journal.debug()) << "AMM Withdraw: some balances are zero"
656 << " curBalance: " << curBalance << " " << amountWithdrawActual
657 << " curBalance2: " << curBalance2 << " "
658 << (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
659 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
660 << lpTokensAMMBalance;
661 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
662 }
663 }
664
665 // Check the reserve in case a trustline or MPT has to be created
666 bool const enabledFixAmMv12 = view.rules().enabled(fixAMMv1_2);
667 // If seated after a call to sufficientReserve() then MPToken must be
668 // authorized
669 std::optional<Keylet> mptokenKey;
670 auto sufficientReserve = [&](Asset const& asset) -> TER {
671 mptokenKey = std::nullopt;
672 if (!enabledFixAmMv12 || isXRP(asset))
673 return tesSUCCESS;
674 bool const assetNotExists = asset.visit(
675 [&](Issue const& issue) { return !view.exists(keylet::trustLine(account, issue)); },
676 [&](MPTIssue const& issue) {
677 auto const issuanceKey = keylet::mptokenIssuance(issue);
678 mptokenKey = keylet::mptoken(issuanceKey.key, account);
679 if (!view.exists(*mptokenKey))
680 return true;
681 mptokenKey = std::nullopt;
682 return false;
683 });
684 if (assetNotExists)
685 {
686 // Intentionally ignore the reserve check for AMMClawback, so the
687 // holder can not avoid clawback by deleting the trustline/MPToken
688 // and keeping a low spendable balance. AMMClawback has a higher
689 // priority than the reserve check.
690 if (view.rules().enabled(fixCleanup3_4_0) &&
691 reserveHandling == ReserveHandling::IgnoreReserve)
692 return tesSUCCESS;
693
694 auto sleAccount = view.peek(keylet::account(account));
695 if (!sleAccount)
696 return tecINTERNAL; // LCOV_EXCL_LINE
697
698 auto const balance = (*sleAccount)[sfBalance]->xrp();
699 // See also TrustSet::doApply() and MPTokenAuthorize::authorize()
700 XRPAmount const reserve(
701 (ownerCount(sleAccount, journal) < 2)
703 : accountReserve(view, sleAccount, journal, {.ownerCountDelta = 1}));
704
705 auto const balanceAdj = std::max(priorBalance, balance);
706 if (balanceAdj < reserve)
708 }
709 return tesSUCCESS;
710 };
711
712 // Create MPToken if it doesn't exist
713 auto createMPToken = [&](Asset const& asset) -> TER {
714 // If mptoken is seated then must authorize
715 if (mptokenKey && account != asset.getIssuer())
716 {
717 auto const& mptIssue = asset.get<MPTIssue>();
718 std::uint32_t createFlags = 0;
719 if (auto const err = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
720 !isTesSuccess(err))
721 {
722 if (authHandling != AuthHandling::IgnoreAuth || err != tecNO_AUTH)
723 {
724 // Unreachable in practice. Normal withdraws (authHandling
725 // != IgnoreAuth) are rejected for unauthorized holders in
726 // preclaim, so they never get here. Under clawback
727 // (IgnoreAuth) requireAuth returns a non-tecNO_AUTH error
728 // (e.g. tecEXPIRED) only for a domain-authorized MPT, but no
729 // such MPT can be in an AMM pool: a directly domain-gated
730 // RequireAuth MPT fails AMMCreate/deposit with tecNO_AUTH,
731 // and vault shares (whose recursive auth could yield
732 // tecEXPIRED) are rejected by AMMCreate with tecWRONG_ASSET.
733 return err; // LCOV_EXCL_LINE
734 }
735
736 // AMMClawback ignores authorization so the issuer can recover
737 // MPT locked in the pool even if the holder deleted their
738 // MPToken. Only auto-authorize the recreated MPToken for the
739 // clawback issuer's own asset: authorization is granted by an
740 // asset's issuer, and the clawback transaction is signed by
741 // that issuer only for its own asset. For a paired asset issued
742 // by a different account, recreate the MPToken *unauthorized* so
743 // the clawback does not grant authorization on behalf of that
744 // issuer (which would bypass its lsfMPTRequireAuth). The holder
745 // still receives the paired asset (accountSend only requires the
746 // MPToken to exist, not to be authorized); the balance remains
747 // gated by its issuer until that issuer authorizes it.
748 if (clawbackIssuer && asset.getIssuer() == *clawbackIssuer)
749 createFlags = lsfMPTAuthorized;
750 }
751
752 if (auto const err = checkCreateMPT(view, mptIssue, account, {}, createFlags, journal);
753 !isTesSuccess(err))
754 {
755 // checkCreateMPT only fails on tecDIR_FULL (its source line is
756 // itself LCOV-excluded) or a missing account, which cannot
757 // happen since `account` is the withdrawing LP. Defensive and
758 // unreachable in practice.
759 return err; // LCOV_EXCL_LINE
760 }
761 }
762 return tesSUCCESS;
763 };
764
765 if (auto const err = sufficientReserve(amountWithdrawActual.asset()))
766 return {err, STAmount{}, STAmount{}, STAmount{}};
767
768 if (auto const res = createMPToken(amountWithdrawActual.asset()); !isTesSuccess(res))
769 return {res, STAmount{}, STAmount{}, STAmount{}};
770
771 // Withdraw amountWithdraw
772 auto res = accountSend(
773 view, ammAccount, account, amountWithdrawActual, journal, {}, WaiveTransferFee::Yes);
774 if (!isTesSuccess(res))
775 {
776 // LCOV_EXCL_START
777 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << amountWithdrawActual;
778 return {res, STAmount{}, STAmount{}, STAmount{}};
779 // LCOV_EXCL_STOP
780 }
781
782 // Withdraw amount2Withdraw
783 if (amount2WithdrawActual)
784 {
785 if (auto const err = sufficientReserve(amount2WithdrawActual->asset()); !isTesSuccess(err))
786 return {err, STAmount{}, STAmount{}, STAmount{}};
787
788 if (auto const res = createMPToken(amount2WithdrawActual->asset()); !isTesSuccess(res))
789 return {res, STAmount{}, STAmount{}, STAmount{}};
790
791 res = accountSend(
792 view, ammAccount, account, *amount2WithdrawActual, journal, {}, WaiveTransferFee::Yes);
793 if (!isTesSuccess(res))
794 {
795 // LCOV_EXCL_START
796 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << *amount2WithdrawActual;
797 return {res, STAmount{}, STAmount{}, STAmount{}};
798 // LCOV_EXCL_STOP
799 }
800 }
801
802 // Withdraw LP tokens
803 res = redeemIOU(
804 view, account, lpTokensWithdrawActual, lpTokensWithdrawActual.get<Issue>(), journal);
805 if (!isTesSuccess(res))
806 {
807 // LCOV_EXCL_START
808 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw LPTokens";
809 return {res, STAmount{}, STAmount{}, STAmount{}};
810 // LCOV_EXCL_STOP
811 }
812
813 return std::make_tuple(
815 lpTokensAMMBalance - lpTokensWithdrawActual,
816 amountWithdrawActual,
817 amount2WithdrawActual);
818}
819
820static STAmount
822 Rules const& rules,
823 STAmount const& lptAMMBalance,
824 STAmount const& lpTokensWithdraw,
825 WithdrawAll withdrawAll)
826{
827 if (!rules.enabled(fixAMMv1_3) || withdrawAll == WithdrawAll::Yes)
828 return lpTokensWithdraw;
829 return adjustLPTokens(lptAMMBalance, lpTokensWithdraw, IsDeposit::No);
830}
831
837 Sandbox& view,
838 SLE const& ammSle,
839 AccountID const& ammAccount,
840 STAmount const& amountBalance,
841 STAmount const& amount2Balance,
842 STAmount const& lptAMMBalance,
843 STAmount const& lpTokens,
844 STAmount const& lpTokensWithdraw,
845 std::uint16_t tfee)
846{
847 TER ter;
848 STAmount newLPTokenBalance;
849 std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = equalWithdrawTokens(
850 view,
851 ammSle,
853 std::nullopt,
854 ammAccount,
855 amountBalance,
856 amount2Balance,
857 lptAMMBalance,
858 lpTokens,
859 lpTokensWithdraw,
860 tfee,
866 ctx_.journal);
867 return {ter, newLPTokenBalance};
868}
869
872 Sandbox& sb,
873 SLE::pointer const ammSle,
874 STAmount const& lpTokenBalance,
875 Asset const& asset1,
876 Asset const& asset2,
877 beast::Journal const& journal)
878{
879 TER ter;
880 bool updateBalance = true;
881 if (lpTokenBalance == beast::kZero)
882 {
883 ter = deleteAMMAccount(sb, asset1, asset2, journal);
884 if (!isTesSuccess(ter) && ter != tecINCOMPLETE)
885 return {ter, false}; // LCOV_EXCL_LINE
886
887 updateBalance = (ter == tecINCOMPLETE);
888 }
889
890 if (updateBalance)
891 {
892 ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
893 sb.update(ammSle);
894 }
895
896 return {ter, true};
897}
898
904 Sandbox& view,
905 SLE const& ammSle,
906 AccountID const account,
907 std::optional<AccountID> const& clawbackIssuer,
908 AccountID const& ammAccount,
909 STAmount const& amountBalance,
910 STAmount const& amount2Balance,
911 STAmount const& lptAMMBalance,
912 STAmount const& lpTokens,
913 STAmount const& lpTokensWithdraw,
914 std::uint16_t tfee,
915 FreezeHandling freezeHandling,
916 AuthHandling authHandling,
917 ReserveHandling reserveHandling,
918 WithdrawAll withdrawAll,
919 XRPAmount const& priorBalance,
920 beast::Journal const& journal)
921{
922 try
923 {
924 // Withdrawing all tokens in the pool
925 if (lpTokensWithdraw == lptAMMBalance)
926 {
927 return withdraw(
928 view,
929 ammSle,
930 ammAccount,
931 clawbackIssuer,
932 account,
933 amountBalance,
934 amountBalance,
935 amount2Balance,
936 lptAMMBalance,
937 lpTokensWithdraw,
938 tfee,
939 freezeHandling,
940 authHandling,
941 reserveHandling,
943 priorBalance,
944 journal);
945 }
946
947 auto const tokensAdj =
948 adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, withdrawAll);
949 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
950 return {tecAMM_INVALID_TOKENS, STAmount{}, STAmount{}, std::nullopt};
951 // the adjusted tokens are factored in
952 auto const frac = divide(tokensAdj, lptAMMBalance, noIssue());
953 auto const amountWithdraw =
954 getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
955 auto const amount2Withdraw =
956 getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::No);
957 // LP is making equal withdrawal by tokens but the requested amount
958 // of LP tokens is likely too small and results in one-sided pool
959 // withdrawal due to round off. Fail so the user withdraws
960 // more tokens.
961 if (amountWithdraw == beast::kZero || amount2Withdraw == beast::kZero)
962 return {tecAMM_FAILED, STAmount{}, STAmount{}, STAmount{}};
963
964 return withdraw(
965 view,
966 ammSle,
967 ammAccount,
968 clawbackIssuer,
969 account,
970 amountBalance,
971 amountWithdraw,
972 amount2Withdraw,
973 lptAMMBalance,
974 tokensAdj,
975 tfee,
976 freezeHandling,
977 authHandling,
978 reserveHandling,
979 withdrawAll,
980 priorBalance,
981 journal);
982 }
983 // LCOV_EXCL_START
984 catch (std::exception const& e)
985 {
986 JLOG(journal.error()) << "AMMWithdraw::equalWithdrawTokens exception " << e.what();
987 }
988 return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}};
989 // LCOV_EXCL_STOP
990}
991
1020 Sandbox& view,
1021 SLE const& ammSle,
1022 AccountID const& ammAccount,
1023 STAmount const& amountBalance,
1024 STAmount const& amount2Balance,
1025 STAmount const& lptAMMBalance,
1026 STAmount const& amount,
1027 STAmount const& amount2,
1028 std::uint16_t tfee)
1029{
1030 auto frac = Number{amount} / amountBalance;
1031 auto tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::No);
1032 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1033 return {tecAMM_INVALID_TOKENS, STAmount{}};
1034 // factor in the adjusted tokens
1035 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
1036 auto const amount2Withdraw = getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::No);
1037 if (amount2Withdraw <= amount2)
1038 {
1039 return withdraw(
1040 view,
1041 ammSle,
1042 ammAccount,
1043 amountBalance,
1044 amount,
1045 amount2Withdraw,
1046 lptAMMBalance,
1047 tokensAdj,
1048 tfee);
1049 }
1050
1051 frac = Number{amount2} / amount2Balance;
1052 auto amountWithdraw = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
1053 tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::No);
1054 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1055 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
1056 // factor in the adjusted tokens
1057 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
1058 amountWithdraw = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
1059 if (!view.rules().enabled(fixAMMv1_3))
1060 {
1061 // LCOV_EXCL_START
1062 XRPL_ASSERT(
1063 amountWithdraw <= amount,
1064 "xrpl::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw");
1065 // LCOV_EXCL_STOP
1066 }
1067 else if (amountWithdraw > amount)
1068 {
1069 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
1070 }
1071 return withdraw(
1072 view,
1073 ammSle,
1074 ammAccount,
1075 amountBalance,
1076 amountWithdraw,
1077 amount2,
1078 lptAMMBalance,
1079 tokensAdj,
1080 tfee);
1081}
1082
1091 Sandbox& view,
1092 SLE const& ammSle,
1093 AccountID const& ammAccount,
1094 STAmount const& amountBalance,
1095 STAmount const& lptAMMBalance,
1096 STAmount const& amount,
1097 std::uint16_t tfee)
1098{
1099 auto const tokens = adjustLPTokensIn(
1100 view.rules(),
1101 lptAMMBalance,
1102 lpTokensIn(amountBalance, amount, lptAMMBalance, tfee),
1103 isWithdrawAll(ctx_.tx));
1104 if (tokens == beast::kZero)
1105 {
1106 if (!view.rules().enabled(fixAMMv1_3))
1107 {
1108 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
1109 }
1110
1111 return {tecAMM_INVALID_TOKENS, STAmount{}};
1112 }
1113 // factor in the adjusted tokens
1114 auto const [tokensAdj, amountWithdrawAdj] =
1115 adjustAssetOutByTokens(view.rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
1116 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1117 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
1118 return withdraw(
1119 view,
1120 ammSle,
1121 ammAccount,
1122 amountBalance,
1123 amountWithdrawAdj,
1124 std::nullopt,
1125 lptAMMBalance,
1126 tokensAdj,
1127 tfee);
1128}
1129
1143 Sandbox& view,
1144 SLE const& ammSle,
1145 AccountID const& ammAccount,
1146 STAmount const& amountBalance,
1147 STAmount const& lptAMMBalance,
1148 STAmount const& amount,
1149 STAmount const& lpTokensWithdraw,
1150 std::uint16_t tfee)
1151{
1152 auto const tokensAdj =
1153 adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, isWithdrawAll(ctx_.tx));
1154 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1155 return {tecAMM_INVALID_TOKENS, STAmount{}};
1156 // the adjusted tokens are factored in
1157 auto const amountWithdraw = ammAssetOut(amountBalance, lptAMMBalance, tokensAdj, tfee);
1158 if (amount == beast::kZero || amountWithdraw >= amount)
1159 {
1160 return withdraw(
1161 view,
1162 ammSle,
1163 ammAccount,
1164 amountBalance,
1165 amountWithdraw,
1166 std::nullopt,
1167 lptAMMBalance,
1168 tokensAdj,
1169 tfee);
1170 }
1171
1172 return {tecAMM_FAILED, STAmount{}};
1173}
1174
1197 Sandbox& view,
1198 SLE const& ammSle,
1199 AccountID const& ammAccount,
1200 STAmount const& amountBalance,
1201 STAmount const& lptAMMBalance,
1202 STAmount const& amount,
1203 STAmount const& ePrice,
1204 std::uint16_t tfee)
1205{
1206 // LPTokens is asset in => E = t / a and formula (8) is:
1207 // a = A*(t1**2 + t1*(f - 2))/(t1*f - 1)
1208 // substitute a as t/E =>
1209 // t/E = A*(t1**2 + t1*(f - 2))/(t1*f - 1), t1=t/T => t = t1*T
1210 // t1*T/E = A*((t/T)**2 + t*(f - 2)/T)/(t*f/T - 1) =>
1211 // T/E = A*(t1 + f-2)/(t1*f - 1) =>
1212 // T*(t1*f - 1) = A*E*(t1 + f - 2) =>
1213 // t1*T*f - T = t1*A*E + A*E*(f - 2) =>
1214 // t1*(T*f - A*E) = T + A*E*(f - 2) =>
1215 // t = T*(T + A*E*(f - 2))/(T*f - A*E)
1216 Number const ae = amountBalance * ePrice;
1217 auto const f = getFee(tfee);
1218 auto const denom = lptAMMBalance * f - ae;
1219 // fixCleanup3_3_0: guard against division by zero
1220 // when ePrice == lptAMMBalance*f/amountBalance
1221 if (view.rules().enabled(fixCleanup3_3_0) && denom == beast::kZero)
1222 return {tecAMM_FAILED, STAmount{}};
1223 auto tokNoRoundCb = [&] { return lptAMMBalance * (lptAMMBalance + ae * (f - 2)) / denom; };
1224 auto tokProdCb = [&] { return (lptAMMBalance + ae * (f - 2)) / denom; };
1225 auto const tokensAdj =
1226 getRoundedLPTokens(view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::No);
1227 if (tokensAdj <= beast::kZero)
1228 {
1229 if (!view.rules().enabled(fixAMMv1_3))
1230 {
1231 return {tecAMM_FAILED, STAmount{}};
1232 }
1233
1234 return {tecAMM_INVALID_TOKENS, STAmount{}};
1235 }
1236 auto amtNoRoundCb = [&] { return tokensAdj / ePrice; };
1237 auto amtProdCb = [&] { return tokensAdj / ePrice; };
1238 // the adjusted tokens are factored in
1239 auto const amountWithdraw =
1240 getRoundedAsset(view.rules(), amtNoRoundCb, amount, amtProdCb, IsDeposit::No);
1241 if (amount == beast::kZero || amountWithdraw >= amount)
1242 {
1243 return withdraw(
1244 view,
1245 ammSle,
1246 ammAccount,
1247 amountBalance,
1248 amountWithdraw,
1249 std::nullopt,
1250 lptAMMBalance,
1251 tokensAdj,
1252 tfee);
1253 }
1254
1255 return {tecAMM_FAILED, STAmount{}};
1256}
1257
1260{
1261 if ((tx[sfFlags] & (tfWithdrawAll | tfOneAssetWithdrawAll)) != 0u)
1262 return WithdrawAll::Yes;
1263 return WithdrawAll::No;
1264}
1265void
1267{
1268 // No transaction-specific invariants yet (future work).
1269}
1270
1271bool
1273{
1274 // No transaction-specific invariants yet (future work).
1275 return true;
1276}
1277
1278} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Stream error() const
Definition Journal.h:362
Stream debug() const
Definition Journal.h:344
FreezeHandling issuerFreezeHandling() const
Returns IgnoreFreeze when the withdrawer is the issuer of a pool asset (post-fixCleanup3_3_0),...
static std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > withdraw(Sandbox &view, SLE const &ammSle, AccountID const &ammAccount, std::optional< AccountID > const &clawbackIssuer, 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, ReserveHandling reserveHandling, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Withdraw requested assets and token from AMM into LP account.
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)
static TER preclaim(PreclaimContext const &ctx)
TER doApply() override
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
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.
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.
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)
static std::tuple< TER, STAmount, STAmount, std::optional< STAmount > > equalWithdrawTokens(Sandbox &view, SLE const &ammSle, AccountID const account, std::optional< AccountID > const &clawbackIssuer, 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, ReserveHandling reserveHandling, 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 > 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: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:182
Asset const & asset() const
Definition STAmount.h:496
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & ConstRef
bool isFlag(std::uint32_t) const
Definition STObject.cpp:511
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:164
ApplyView & view()
Definition Transactor.h:184
AccountID const accountID_
Definition Transactor.h:166
XRPAmount preFeeBalance_
Definition Transactor.h:167
ApplyContext & ctx_
Definition Transactor.h:162
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
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.
void update(SLE::Ref sle) override
Indicate changes to a peeked SLE.
T make_optional(T... args)
T make_pair(T... args)
T make_tuple(T... args)
T max(T... args)
constexpr Zero kZero
Definition Zero.h:30
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:471
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:573
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:567
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:275
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:228
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)
std::uint32_t ownerCount(SLE::ConstRef sle, beast::Journal j, std::int32_t ownerCountAdj=0)
Return number of the objects which reserve is covered by the account(sle) (so called "ownercount").
bool ammEnabled(Rules const &)
Return true if required AMM amendment is enabled.
Definition AMMCore.cpp:129
TER checkCreateMPT(xrpl::ApplyView &view, xrpl::MPTIssue const &mptIssue, xrpl::AccountID const &holder, SLE::Ref sponsorSle, std::uint32_t flags, beast::Journal j)
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.
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:83
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
ReserveHandling
Controls whether the recipient owner-reserve check is enforced when auto-creating a trustline or MPTo...
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:657
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:614
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.
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:667
AuthHandling
Controls the treatment of unauthorized MPT balances.
TER checkAMMPrecisionLoss(Number const &poolProductMean, STAmount const &newLPTokenBalance)
Check AMM pool product invariant after an AMM operation that changes LP tokens (deposit/withdraw/claw...
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...
TER redeemIOU(ApplyView &view, AccountID const &account, STAmount const &amount, Issue const &issue, beast::Journal j)
@ temBAD_AMM_TOKENS
Definition TER.h:117
@ temMALFORMED
Definition TER.h:75
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
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:89
TERSubset< CanCvtToTER > TER
Definition TER.h:654
XRPAmount accountReserve(ReadView const &view, SLE::ConstRef sle, beast::Journal j, Adjustment adj={})
Returns the account reserve, in drops.
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:118
@ tecAMM_EMPTY
Definition TER.h:340
@ tecAMM_INVALID_TOKENS
Definition TER.h:339
@ tecAMM_FAILED
Definition TER.h:338
@ tecINCOMPLETE
Definition TER.h:343
@ tecNO_AUTH
Definition TER.h:308
@ tecINTERNAL
Definition TER.h:318
@ tecAMM_BALANCE
Definition TER.h:337
@ tecINSUFFICIENT_RESERVE
Definition TER.h:315
TER createMPToken(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, SLE::Ref sponsorSle, std::uint32_t const flags)
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:413
TER checkWithdrawFreeze(ReadView const &view, AccountID const &pseudoAcct, AccountID const &submitterAcct, AccountID const &dstAcct, Asset const &asset)
Checks freeze compliance for withdrawing an asset from a pseudo-account (e.g.
@ tesSUCCESS
Definition TER.h:250
T popcount(T... args)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:92
ReadView const & view
Definition Transactor.h:95
beast::Journal const j
Definition Transactor.h:100
State information when preflighting a tx.
Definition Transactor.h:39
beast::Journal const j
Definition Transactor.h:46
T tie(T... args)
T what(T... args)