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,
532 j_);
533 return {ter, newLPTokenBalance};
534}
535
538 Sandbox& view,
539 SLE const& ammSle,
540 AccountID const& ammAccount,
541 std::optional<AccountID> const& clawbackIssuer,
542 AccountID const& account,
543 STAmount const& amountBalance,
544 STAmount const& amountWithdraw,
545 std::optional<STAmount> const& amount2Withdraw,
546 STAmount const& lpTokensAMMBalance,
547 STAmount const& lpTokensWithdraw,
548 std::uint16_t tfee,
549 FreezeHandling freezeHandling,
550 AuthHandling authHandling,
551 WithdrawAll withdrawAll,
552 XRPAmount const& priorBalance,
553 beast::Journal const& journal)
554{
555 auto const lpTokens = ammLPHolds(view, ammSle, account, journal);
556 auto const expected = ammHolds(
557 view, ammSle, amountWithdraw.asset(), std::nullopt, freezeHandling, authHandling, journal);
558 // LCOV_EXCL_START
559 if (!expected)
560 return {expected.error(), STAmount{}, STAmount{}, STAmount{}};
561 // LCOV_EXCL_STOP
562 auto const [curBalance, curBalance2, _] = *expected;
563 (void)_;
564
565 auto const [amountWithdrawActual, amount2WithdrawActual, lpTokensWithdrawActual] =
567 if (withdrawAll == WithdrawAll::No)
568 {
570 amountBalance,
571 amountWithdraw,
572 amount2Withdraw,
573 lpTokensAMMBalance,
574 lpTokensWithdraw,
575 tfee,
577 }
578 return std::make_tuple(amountWithdraw, amount2Withdraw, lpTokensWithdraw);
579 }();
580
581 if (lpTokensWithdrawActual <= beast::kZero || lpTokensWithdrawActual > lpTokens)
582 {
583 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, invalid LP tokens: "
584 << lpTokensWithdrawActual << " " << lpTokens << " "
585 << lpTokensAMMBalance;
587 }
588
589 // Should not happen since the only LP on last withdraw
590 // has the balance set to the lp token trustline balance.
591 if (view.rules().enabled(fixAMMv1_1) && lpTokensWithdrawActual > lpTokensAMMBalance)
592 {
593 // LCOV_EXCL_START
594 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw, unexpected LP tokens: "
595 << lpTokensWithdrawActual << " " << lpTokens << " "
596 << lpTokensAMMBalance;
597 return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}};
598 // LCOV_EXCL_STOP
599 }
600
601 // Withdrawing one side of the pool
602 if ((amountWithdrawActual == curBalance && amount2WithdrawActual != curBalance2) ||
603 (amount2WithdrawActual == curBalance2 && amountWithdrawActual != curBalance))
604 {
605 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw one side of the pool "
606 << " curBalance: " << curBalance << " " << amountWithdrawActual
607 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
608 << lpTokensAMMBalance;
609 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
610 }
611
612 // May happen if withdrawing an amount close to one side of the pool
613 if (lpTokensWithdrawActual == lpTokensAMMBalance &&
614 (amountWithdrawActual != curBalance || amount2WithdrawActual != curBalance2))
615 {
616 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw all tokens "
617 << " curBalance: " << curBalance << " " << amountWithdrawActual
618 << " curBalance2: " << amount2WithdrawActual.value_or(STAmount{0})
619 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
620 << lpTokensAMMBalance;
621 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
622 }
623
624 // Withdrawing more than the pool's balance
625 if (amountWithdrawActual > curBalance || amount2WithdrawActual > curBalance2)
626 {
627 JLOG(journal.debug()) << "AMM Withdraw: withdrawing more than the pool's balance "
628 << " curBalance: " << curBalance << " " << amountWithdrawActual
629 << " curBalance2: " << curBalance2 << " "
630 << (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
631 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
632 << lpTokensAMMBalance;
633 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
634 }
635
636 // Updated pool state must be valid - either all balances are zero
637 // or all balances are non-zero.
638 if (view.rules().enabled(featureMPTokensV2))
639 {
640 bool const newBalanceZero = (curBalance - amountWithdrawActual) == beast::kZero;
641 bool const newBalance2Zero =
642 (curBalance2 - amount2WithdrawActual.value_or(curBalance2.asset())) == beast::kZero;
643 bool const newLPTokensZero = (lpTokensAMMBalance - lpTokensWithdrawActual) == beast::kZero;
644 // newBalance2Zero can be zero if that side of the pool is frozen.
645 // ignore newBalance2Zero if one-sided withdrawal.
646 bool const valid = [&]() {
647 if (!amount2WithdrawActual)
648 return newBalanceZero == newLPTokensZero;
649 return newBalanceZero == newBalance2Zero && newBalance2Zero == newLPTokensZero;
650 }();
651 if (!valid)
652 {
653 JLOG(journal.debug()) << "AMM Withdraw: some balances are zero"
654 << " curBalance: " << curBalance << " " << amountWithdrawActual
655 << " curBalance2: " << curBalance2 << " "
656 << (amount2WithdrawActual ? *amount2WithdrawActual : STAmount{})
657 << " lpTokensBalance: " << lpTokensWithdraw << " lptBalance "
658 << lpTokensAMMBalance;
659 return {tecAMM_BALANCE, STAmount{}, STAmount{}, STAmount{}};
660 }
661 }
662
663 // Check the reserve in case a trustline or MPT has to be created
664 bool const enabledFixAmMv12 = view.rules().enabled(fixAMMv1_2);
665 // If seated after a call to sufficientReserve() then MPToken must be
666 // authorized
667 std::optional<Keylet> mptokenKey;
668 auto sufficientReserve = [&](Asset const& asset) -> TER {
669 mptokenKey = std::nullopt;
670 if (!enabledFixAmMv12 || isXRP(asset))
671 return tesSUCCESS;
672 bool const isIssue = asset.holds<Issue>();
673 bool const assetNotExists = [&] {
674 if (isIssue)
675 return !view.exists(keylet::trustLine(account, asset.get<Issue>()));
676 auto const issuanceKey = keylet::mptokenIssuance(asset.get<MPTIssue>());
677 mptokenKey = keylet::mptoken(issuanceKey.key, account);
678 if (!view.exists(*mptokenKey))
679 return true;
680 mptokenKey = std::nullopt;
681 return false;
682 }();
683 if (assetNotExists)
684 {
685 auto sleAccount = view.peek(keylet::account(account));
686 if (!sleAccount)
687 return tecINTERNAL; // LCOV_EXCL_LINE
688
689 auto const balance = (*sleAccount)[sfBalance]->xrp();
690 // See also TrustSet::doApply() and MPTokenAuthorize::authorize()
691 XRPAmount const reserve(
692 (ownerCount(sleAccount, journal) < 2)
694 : accountReserve(view, sleAccount, journal, {.ownerCountDelta = 1}));
695
696 auto const balanceAdj = isIssue ? std::max(priorBalance, balance) : priorBalance;
697 if (balanceAdj < reserve)
699 }
700 return tesSUCCESS;
701 };
702
703 // Create MPToken if it doesn't exist
704 auto createMPToken = [&](Asset const& asset) -> TER {
705 // If mptoken is seated then must authorize
706 if (mptokenKey && account != asset.getIssuer())
707 {
708 auto const& mptIssue = asset.get<MPTIssue>();
709 std::uint32_t createFlags = 0;
710 if (auto const err = requireAuth(view, mptIssue, account, AuthType::WeakAuth);
711 !isTesSuccess(err))
712 {
713 if (authHandling != AuthHandling::IgnoreAuth || err != tecNO_AUTH)
714 {
715 // Unreachable in practice. Normal withdraws (authHandling
716 // != IgnoreAuth) are rejected for unauthorized holders in
717 // preclaim, so they never get here. Under clawback
718 // (IgnoreAuth) requireAuth returns a non-tecNO_AUTH error
719 // (e.g. tecEXPIRED) only for a domain-authorized MPT, but no
720 // such MPT can be in an AMM pool: a directly domain-gated
721 // RequireAuth MPT fails AMMCreate/deposit with tecNO_AUTH,
722 // and vault shares (whose recursive auth could yield
723 // tecEXPIRED) are rejected by AMMCreate with tecWRONG_ASSET.
724 return err; // LCOV_EXCL_LINE
725 }
726
727 // AMMClawback ignores authorization so the issuer can recover
728 // MPT locked in the pool even if the holder deleted their
729 // MPToken. Only auto-authorize the recreated MPToken for the
730 // clawback issuer's own asset: authorization is granted by an
731 // asset's issuer, and the clawback transaction is signed by
732 // that issuer only for its own asset. For a paired asset issued
733 // by a different account, recreate the MPToken *unauthorized* so
734 // the clawback does not grant authorization on behalf of that
735 // issuer (which would bypass its lsfMPTRequireAuth). The holder
736 // still receives the paired asset (accountSend only requires the
737 // MPToken to exist, not to be authorized); the balance remains
738 // gated by its issuer until that issuer authorizes it.
739 if (clawbackIssuer && asset.getIssuer() == *clawbackIssuer)
740 createFlags = lsfMPTAuthorized;
741 }
742
743 if (auto const err = checkCreateMPT(view, mptIssue, account, {}, createFlags, journal);
744 !isTesSuccess(err))
745 {
746 // checkCreateMPT only fails on tecDIR_FULL (its source line is
747 // itself LCOV-excluded) or a missing account, which cannot
748 // happen since `account` is the withdrawing LP. Defensive and
749 // unreachable in practice.
750 return err; // LCOV_EXCL_LINE
751 }
752 }
753 return tesSUCCESS;
754 };
755
756 if (auto const err = sufficientReserve(amountWithdrawActual.asset()))
757 return {err, STAmount{}, STAmount{}, STAmount{}};
758
759 if (auto const res = createMPToken(amountWithdrawActual.asset()); !isTesSuccess(res))
760 return {res, STAmount{}, STAmount{}, STAmount{}};
761
762 // Withdraw amountWithdraw
763 auto res = accountSend(
764 view, ammAccount, account, amountWithdrawActual, journal, {}, WaiveTransferFee::Yes);
765 if (!isTesSuccess(res))
766 {
767 // LCOV_EXCL_START
768 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << amountWithdrawActual;
769 return {res, STAmount{}, STAmount{}, STAmount{}};
770 // LCOV_EXCL_STOP
771 }
772
773 // Withdraw amount2Withdraw
774 if (amount2WithdrawActual)
775 {
776 if (auto const err = sufficientReserve(amount2WithdrawActual->asset()); !isTesSuccess(err))
777 return {err, STAmount{}, STAmount{}, STAmount{}};
778
779 if (auto const res = createMPToken(amount2WithdrawActual->asset()); !isTesSuccess(res))
780 return {res, STAmount{}, STAmount{}, STAmount{}};
781
782 res = accountSend(
783 view, ammAccount, account, *amount2WithdrawActual, journal, {}, WaiveTransferFee::Yes);
784 if (!isTesSuccess(res))
785 {
786 // LCOV_EXCL_START
787 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw " << *amount2WithdrawActual;
788 return {res, STAmount{}, STAmount{}, STAmount{}};
789 // LCOV_EXCL_STOP
790 }
791 }
792
793 // Withdraw LP tokens
794 res = redeemIOU(
795 view, account, lpTokensWithdrawActual, lpTokensWithdrawActual.get<Issue>(), journal);
796 if (!isTesSuccess(res))
797 {
798 // LCOV_EXCL_START
799 JLOG(journal.debug()) << "AMM Withdraw: failed to withdraw LPTokens";
800 return {res, STAmount{}, STAmount{}, STAmount{}};
801 // LCOV_EXCL_STOP
802 }
803
804 return std::make_tuple(
806 lpTokensAMMBalance - lpTokensWithdrawActual,
807 amountWithdrawActual,
808 amount2WithdrawActual);
809}
810
811static STAmount
813 Rules const& rules,
814 STAmount const& lptAMMBalance,
815 STAmount const& lpTokensWithdraw,
816 WithdrawAll withdrawAll)
817{
818 if (!rules.enabled(fixAMMv1_3) || withdrawAll == WithdrawAll::Yes)
819 return lpTokensWithdraw;
820 return adjustLPTokens(lptAMMBalance, lpTokensWithdraw, IsDeposit::No);
821}
822
828 Sandbox& view,
829 SLE const& ammSle,
830 AccountID const& ammAccount,
831 STAmount const& amountBalance,
832 STAmount const& amount2Balance,
833 STAmount const& lptAMMBalance,
834 STAmount const& lpTokens,
835 STAmount const& lpTokensWithdraw,
836 std::uint16_t tfee)
837{
838 TER ter;
839 STAmount newLPTokenBalance;
840 std::tie(ter, newLPTokenBalance, std::ignore, std::ignore) = equalWithdrawTokens(
841 view,
842 ammSle,
844 std::nullopt,
845 ammAccount,
846 amountBalance,
847 amount2Balance,
848 lptAMMBalance,
849 lpTokens,
850 lpTokensWithdraw,
851 tfee,
856 ctx_.journal);
857 return {ter, newLPTokenBalance};
858}
859
862 Sandbox& sb,
863 SLE::pointer const ammSle,
864 STAmount const& lpTokenBalance,
865 Asset const& asset1,
866 Asset const& asset2,
867 beast::Journal const& journal)
868{
869 TER ter;
870 bool updateBalance = true;
871 if (lpTokenBalance == beast::kZero)
872 {
873 ter = deleteAMMAccount(sb, asset1, asset2, journal);
874 if (!isTesSuccess(ter) && ter != tecINCOMPLETE)
875 return {ter, false}; // LCOV_EXCL_LINE
876
877 updateBalance = (ter == tecINCOMPLETE);
878 }
879
880 if (updateBalance)
881 {
882 ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
883 sb.update(ammSle);
884 }
885
886 return {ter, true};
887}
888
894 Sandbox& view,
895 SLE const& ammSle,
896 AccountID const account,
897 std::optional<AccountID> const& clawbackIssuer,
898 AccountID const& ammAccount,
899 STAmount const& amountBalance,
900 STAmount const& amount2Balance,
901 STAmount const& lptAMMBalance,
902 STAmount const& lpTokens,
903 STAmount const& lpTokensWithdraw,
904 std::uint16_t tfee,
905 FreezeHandling freezeHandling,
906 AuthHandling authHandling,
907 WithdrawAll withdrawAll,
908 XRPAmount const& priorBalance,
909 beast::Journal const& journal)
910{
911 try
912 {
913 // Withdrawing all tokens in the pool
914 if (lpTokensWithdraw == lptAMMBalance)
915 {
916 return withdraw(
917 view,
918 ammSle,
919 ammAccount,
920 clawbackIssuer,
921 account,
922 amountBalance,
923 amountBalance,
924 amount2Balance,
925 lptAMMBalance,
926 lpTokensWithdraw,
927 tfee,
928 freezeHandling,
929 authHandling,
931 priorBalance,
932 journal);
933 }
934
935 auto const tokensAdj =
936 adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, withdrawAll);
937 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
938 return {tecAMM_INVALID_TOKENS, STAmount{}, STAmount{}, std::nullopt};
939 // the adjusted tokens are factored in
940 auto const frac = divide(tokensAdj, lptAMMBalance, noIssue());
941 auto const amountWithdraw =
942 getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
943 auto const amount2Withdraw =
944 getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::No);
945 // LP is making equal withdrawal by tokens but the requested amount
946 // of LP tokens is likely too small and results in one-sided pool
947 // withdrawal due to round off. Fail so the user withdraws
948 // more tokens.
949 if (amountWithdraw == beast::kZero || amount2Withdraw == beast::kZero)
950 return {tecAMM_FAILED, STAmount{}, STAmount{}, STAmount{}};
951
952 return withdraw(
953 view,
954 ammSle,
955 ammAccount,
956 clawbackIssuer,
957 account,
958 amountBalance,
959 amountWithdraw,
960 amount2Withdraw,
961 lptAMMBalance,
962 tokensAdj,
963 tfee,
964 freezeHandling,
965 authHandling,
966 withdrawAll,
967 priorBalance,
968 journal);
969 }
970 // LCOV_EXCL_START
971 catch (std::exception const& e)
972 {
973 JLOG(journal.error()) << "AMMWithdraw::equalWithdrawTokens exception " << e.what();
974 }
975 return {tecINTERNAL, STAmount{}, STAmount{}, STAmount{}};
976 // LCOV_EXCL_STOP
977}
978
1007 Sandbox& view,
1008 SLE const& ammSle,
1009 AccountID const& ammAccount,
1010 STAmount const& amountBalance,
1011 STAmount const& amount2Balance,
1012 STAmount const& lptAMMBalance,
1013 STAmount const& amount,
1014 STAmount const& amount2,
1015 std::uint16_t tfee)
1016{
1017 auto frac = Number{amount} / amountBalance;
1018 auto tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::No);
1019 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1020 return {tecAMM_INVALID_TOKENS, STAmount{}};
1021 // factor in the adjusted tokens
1022 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
1023 auto const amount2Withdraw = getRoundedAsset(view.rules(), amount2Balance, frac, IsDeposit::No);
1024 if (amount2Withdraw <= amount2)
1025 {
1026 return withdraw(
1027 view,
1028 ammSle,
1029 ammAccount,
1030 amountBalance,
1031 amount,
1032 amount2Withdraw,
1033 lptAMMBalance,
1034 tokensAdj,
1035 tfee);
1036 }
1037
1038 frac = Number{amount2} / amount2Balance;
1039 auto amountWithdraw = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
1040 tokensAdj = getRoundedLPTokens(view.rules(), lptAMMBalance, frac, IsDeposit::No);
1041 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1042 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
1043 // factor in the adjusted tokens
1044 frac = adjustFracByTokens(view.rules(), lptAMMBalance, tokensAdj, frac);
1045 amountWithdraw = getRoundedAsset(view.rules(), amountBalance, frac, IsDeposit::No);
1046 if (!view.rules().enabled(fixAMMv1_3))
1047 {
1048 // LCOV_EXCL_START
1049 XRPL_ASSERT(
1050 amountWithdraw <= amount,
1051 "xrpl::AMMWithdraw::equalWithdrawLimit : maximum amountWithdraw");
1052 // LCOV_EXCL_STOP
1053 }
1054 else if (amountWithdraw > amount)
1055 {
1056 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
1057 }
1058 return withdraw(
1059 view,
1060 ammSle,
1061 ammAccount,
1062 amountBalance,
1063 amountWithdraw,
1064 amount2,
1065 lptAMMBalance,
1066 tokensAdj,
1067 tfee);
1068}
1069
1078 Sandbox& view,
1079 SLE const& ammSle,
1080 AccountID const& ammAccount,
1081 STAmount const& amountBalance,
1082 STAmount const& lptAMMBalance,
1083 STAmount const& amount,
1084 std::uint16_t tfee)
1085{
1086 auto const tokens = adjustLPTokensIn(
1087 view.rules(),
1088 lptAMMBalance,
1089 lpTokensIn(amountBalance, amount, lptAMMBalance, tfee),
1090 isWithdrawAll(ctx_.tx));
1091 if (tokens == beast::kZero)
1092 {
1093 if (!view.rules().enabled(fixAMMv1_3))
1094 {
1095 return {tecAMM_FAILED, STAmount{}}; // LCOV_EXCL_LINE
1096 }
1097
1098 return {tecAMM_INVALID_TOKENS, STAmount{}};
1099 }
1100 // factor in the adjusted tokens
1101 auto const [tokensAdj, amountWithdrawAdj] =
1102 adjustAssetOutByTokens(view.rules(), amountBalance, amount, lptAMMBalance, tokens, tfee);
1103 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1104 return {tecAMM_INVALID_TOKENS, STAmount{}}; // LCOV_EXCL_LINE
1105 return withdraw(
1106 view,
1107 ammSle,
1108 ammAccount,
1109 amountBalance,
1110 amountWithdrawAdj,
1111 std::nullopt,
1112 lptAMMBalance,
1113 tokensAdj,
1114 tfee);
1115}
1116
1130 Sandbox& view,
1131 SLE const& ammSle,
1132 AccountID const& ammAccount,
1133 STAmount const& amountBalance,
1134 STAmount const& lptAMMBalance,
1135 STAmount const& amount,
1136 STAmount const& lpTokensWithdraw,
1137 std::uint16_t tfee)
1138{
1139 auto const tokensAdj =
1140 adjustLPTokensIn(view.rules(), lptAMMBalance, lpTokensWithdraw, isWithdrawAll(ctx_.tx));
1141 if (view.rules().enabled(fixAMMv1_3) && tokensAdj == beast::kZero)
1142 return {tecAMM_INVALID_TOKENS, STAmount{}};
1143 // the adjusted tokens are factored in
1144 auto const amountWithdraw = ammAssetOut(amountBalance, lptAMMBalance, tokensAdj, tfee);
1145 if (amount == beast::kZero || amountWithdraw >= amount)
1146 {
1147 return withdraw(
1148 view,
1149 ammSle,
1150 ammAccount,
1151 amountBalance,
1152 amountWithdraw,
1153 std::nullopt,
1154 lptAMMBalance,
1155 tokensAdj,
1156 tfee);
1157 }
1158
1159 return {tecAMM_FAILED, STAmount{}};
1160}
1161
1184 Sandbox& view,
1185 SLE const& ammSle,
1186 AccountID const& ammAccount,
1187 STAmount const& amountBalance,
1188 STAmount const& lptAMMBalance,
1189 STAmount const& amount,
1190 STAmount const& ePrice,
1191 std::uint16_t tfee)
1192{
1193 // LPTokens is asset in => E = t / a and formula (8) is:
1194 // a = A*(t1**2 + t1*(f - 2))/(t1*f - 1)
1195 // substitute a as t/E =>
1196 // t/E = A*(t1**2 + t1*(f - 2))/(t1*f - 1), t1=t/T => t = t1*T
1197 // t1*T/E = A*((t/T)**2 + t*(f - 2)/T)/(t*f/T - 1) =>
1198 // T/E = A*(t1 + f-2)/(t1*f - 1) =>
1199 // T*(t1*f - 1) = A*E*(t1 + f - 2) =>
1200 // t1*T*f - T = t1*A*E + A*E*(f - 2) =>
1201 // t1*(T*f - A*E) = T + A*E*(f - 2) =>
1202 // t = T*(T + A*E*(f - 2))/(T*f - A*E)
1203 Number const ae = amountBalance * ePrice;
1204 auto const f = getFee(tfee);
1205 auto const denom = lptAMMBalance * f - ae;
1206 // fixCleanup3_3_0: guard against division by zero
1207 // when ePrice == lptAMMBalance*f/amountBalance
1208 if (view.rules().enabled(fixCleanup3_3_0) && denom == beast::kZero)
1209 return {tecAMM_FAILED, STAmount{}};
1210 auto tokNoRoundCb = [&] { return lptAMMBalance * (lptAMMBalance + ae * (f - 2)) / denom; };
1211 auto tokProdCb = [&] { return (lptAMMBalance + ae * (f - 2)) / denom; };
1212 auto const tokensAdj =
1213 getRoundedLPTokens(view.rules(), tokNoRoundCb, lptAMMBalance, tokProdCb, IsDeposit::No);
1214 if (tokensAdj <= beast::kZero)
1215 {
1216 if (!view.rules().enabled(fixAMMv1_3))
1217 {
1218 return {tecAMM_FAILED, STAmount{}};
1219 }
1220
1221 return {tecAMM_INVALID_TOKENS, STAmount{}};
1222 }
1223 auto amtNoRoundCb = [&] { return tokensAdj / ePrice; };
1224 auto amtProdCb = [&] { return tokensAdj / ePrice; };
1225 // the adjusted tokens are factored in
1226 auto const amountWithdraw =
1227 getRoundedAsset(view.rules(), amtNoRoundCb, amount, amtProdCb, IsDeposit::No);
1228 if (amount == beast::kZero || amountWithdraw >= amount)
1229 {
1230 return withdraw(
1231 view,
1232 ammSle,
1233 ammAccount,
1234 amountBalance,
1235 amountWithdraw,
1236 std::nullopt,
1237 lptAMMBalance,
1238 tokensAdj,
1239 tfee);
1240 }
1241
1242 return {tecAMM_FAILED, STAmount{}};
1243}
1244
1247{
1248 if ((tx[sfFlags] & (tfWithdrawAll | tfOneAssetWithdrawAll)) != 0u)
1249 return WithdrawAll::Yes;
1250 return WithdrawAll::No;
1251}
1252void
1254{
1255 // No transaction-specific invariants yet (future work).
1256}
1257
1258bool
1260{
1261 // No transaction-specific invariants yet (future work).
1262 return true;
1263}
1264
1265} // 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 > > 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, 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...
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
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, WithdrawAll withdrawAll, XRPAmount const &priorBalance, beast::Journal const &journal)
Withdraw requested assets and token from AMM into LP account.
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)
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:180
Asset const & asset() const
Definition STAmount.h:496
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
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:155
ApplyView & view()
Definition Transactor.h:175
AccountID const accountID_
Definition Transactor.h:157
XRPAmount preFeeBalance_
Definition Transactor.h:158
ApplyContext & ctx_
Definition Transactor.h:153
SLE::pointer peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
void update(SLE::ref sle) override
Indicate changes to a peeked SLE.
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)
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:441
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:543
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:537
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:253
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
NotTEC invalidAMMAmount(STAmount const &amount, std::optional< std::pair< Asset, Asset > > const &pair=std::nullopt, bool validZero=false)
Validate the amount.
Definition AMMCore.cpp:98
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
@ terNO_AMM
Definition TER.h:223
TER createMPToken(ApplyView &view, MPTID const &mptIssuanceID, AccountID const &account, SLE::ref sponsorSle, std::uint32_t const flags)
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 checkCreateMPT(xrpl::ApplyView &view, xrpl::MPTIssue const &mptIssue, xrpl::AccountID const &holder, SLE::ref sponsorSle, std::uint32_t flags, beast::Journal j)
TER checkIndividualFrozen(ReadView const &view, AccountID const &account, Asset const &asset)
bool ammEnabled(Rules const &)
Return true if required AMM amendment is enabled.
Definition AMMCore.cpp:129
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.
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.
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)
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:651
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:607
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:676
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:647
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:118
@ tecAMM_EMPTY
Definition TER.h:335
@ tecAMM_INVALID_TOKENS
Definition TER.h:334
@ tecAMM_FAILED
Definition TER.h:333
@ tecINCOMPLETE
Definition TER.h:338
@ tecNO_AUTH
Definition TER.h:303
@ tecINTERNAL
Definition TER.h:313
@ tecAMM_BALANCE
Definition TER.h:332
@ tecINSUFFICIENT_RESERVE
Definition TER.h:310
std::uint32_t ownerCount(SLE::const_ref 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").
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:407
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.
XRPAmount accountReserve(ReadView const &view, SLE::const_ref sle, beast::Journal j, Adjustment adj={})
Returns the account reserve, in drops.
@ tesSUCCESS
Definition TER.h:245
T popcount(T... args)
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:83
ReadView const & view
Definition Transactor.h:86
beast::Journal const j
Definition Transactor.h:91
State information when preflighting a tx.
Definition Transactor.h:38
beast::Journal const j
Definition Transactor.h:45
T tie(T... args)
T what(T... args)