xrpld
Loading...
Searching...
No Matches
AMMHelpers.cpp
1#include <xrpl/ledger/helpers/AMMHelpers.h>
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Number.h>
5#include <xrpl/basics/base_uint.h>
6#include <xrpl/basics/safe_cast.h>
7#include <xrpl/beast/utility/Journal.h>
8#include <xrpl/beast/utility/Zero.h>
9#include <xrpl/beast/utility/instrumentation.h>
10#include <xrpl/ledger/ApplyView.h>
11#include <xrpl/ledger/ReadView.h>
12#include <xrpl/ledger/Sandbox.h>
13#include <xrpl/ledger/View.h>
14#include <xrpl/ledger/helpers/RippleStateHelpers.h>
15#include <xrpl/ledger/helpers/TokenHelpers.h>
16#include <xrpl/protocol/AMMCore.h>
17#include <xrpl/protocol/AccountID.h>
18#include <xrpl/protocol/AmountConversions.h>
19#include <xrpl/protocol/Asset.h>
20#include <xrpl/protocol/Feature.h>
21#include <xrpl/protocol/Indexes.h>
22#include <xrpl/protocol/Issue.h>
23#include <xrpl/protocol/LedgerFormats.h>
24#include <xrpl/protocol/MPTIssue.h>
25#include <xrpl/protocol/Protocol.h>
26#include <xrpl/protocol/Rules.h>
27#include <xrpl/protocol/SField.h>
28#include <xrpl/protocol/STAmount.h>
29#include <xrpl/protocol/STArray.h>
30#include <xrpl/protocol/STLedgerEntry.h>
31#include <xrpl/protocol/TER.h>
32
33#include <algorithm>
34#include <chrono>
35#include <cstdint>
36#include <expected>
37#include <functional>
38#include <optional>
39#include <tuple>
40#include <utility>
41
42namespace xrpl {
43
45ammLPTokens(STAmount const& asset1, STAmount const& asset2, Asset const& lptIssue)
46{
47 // AMM invariant: sqrt(asset1 * asset2) >= LPTokensBalance
48 auto const rounding =
50 NumberRoundModeGuard const g(rounding);
51 auto const tokens = root2(asset1 * asset2);
52 return toSTAmount(lptIssue, tokens);
53}
54
55/*
56 * Equation 3:
57 * t = T * [(b/B - (sqrt(f2**2 - b/(B*f1)) - f2)) /
58 * (1 + sqrt(f2**2 - b/(B*f1)) - f2)]
59 * where f1 = 1 - tfee, f2 = (1 - tfee/2)/f1
60 */
61STAmount
63 STAmount const& asset1Balance,
64 STAmount const& asset1Deposit,
65 STAmount const& lptAMMBalance,
66 std::uint16_t tfee)
67{
68 auto const f1 = feeMult(tfee);
69 auto const f2 = feeMultHalf(tfee) / f1;
70 Number const r = asset1Deposit / asset1Balance;
71 auto const c = root2(f2 * f2 + r / f1) - f2;
72 if (!isFeatureEnabled(fixAMMv1_3))
73 {
74 auto const t = lptAMMBalance * (r - c) / (1 + c);
75 return toSTAmount(lptAMMBalance.asset(), t);
76 }
77
78 // minimize tokens out
79 auto const frac = (r - c) / (1 + c);
80 return multiply(lptAMMBalance, frac, Number::RoundingMode::Downward);
81}
82
83/* Equation 4 solves equation 3 for b:
84 * Let f1 = 1 - tfee, f2 = (1 - tfee/2)/f1, t1 = t/T, t2 = 1 + t1, R = b/B
85 * then
86 * t1 = [R - sqrt(f2**2 + R/f1) + f2] / [1 + sqrt(f2**2 + R/f1] - f2] =>
87 * sqrt(f2**2 + R/f1)*(t1 + 1) = R + f2 + t1*f2 - t1 =>
88 * sqrt(f2**2 + R/f1)*t2 = R + t2*f2 - t1 =>
89 * sqrt(f2**2 + R/f1) = R/t2 + f2 - t1/t2, let d = f2 - t1/t2 =>
90 * sqrt(f2**2 + R/f1) = R/t2 + d =>
91 * f2**2 + R/f1 = (R/t2)**2 +2*d*R/t2 + d**2 =>
92 * (R/t2)**2 + R*(2*d/t2 - 1/f1) + d**2 - f2**2 = 0
93 */
94STAmount
96 STAmount const& asset1Balance,
97 STAmount const& lptAMMBalance,
98 STAmount const& lpTokens,
99 std::uint16_t tfee)
100{
101 auto const f1 = feeMult(tfee);
102 auto const f2 = feeMultHalf(tfee) / f1;
103 auto const t1 = lpTokens / lptAMMBalance;
104 auto const t2 = 1 + t1;
105 auto const d = f2 - t1 / t2;
106 auto const a = 1 / (t2 * t2);
107 auto const b = 2 * d / t2 - 1 / f1;
108 auto const c = d * d - f2 * f2;
109 if (!isFeatureEnabled(fixAMMv1_3))
110 {
111 return toSTAmount(asset1Balance.asset(), asset1Balance * solveQuadraticEq(a, b, c));
112 }
113
114 // maximize deposit
115 auto const frac = solveQuadraticEq(a, b, c);
116 return multiply(asset1Balance, frac, Number::RoundingMode::Upward);
117}
118
119/* Equation 7:
120 * t = T * (c - sqrt(c**2 - 4*R))/2
121 * where R = b/B, c = R*fee + 2 - fee
122 */
123STAmount
125 STAmount const& asset1Balance,
126 STAmount const& asset1Withdraw,
127 STAmount const& lptAMMBalance,
128 std::uint16_t tfee)
129{
130 Number const fr = asset1Withdraw / asset1Balance;
131 auto const f1 = getFee(tfee);
132 auto const c = fr * f1 + 2 - f1;
133 if (!isFeatureEnabled(fixAMMv1_3))
134 {
135 auto const t = lptAMMBalance * (c - root2(c * c - 4 * fr)) / 2;
136 return toSTAmount(lptAMMBalance.asset(), t);
137 }
138
139 // maximize tokens in
140 auto const frac = (c - root2(c * c - 4 * fr)) / 2;
141 return multiply(lptAMMBalance, frac, Number::RoundingMode::Upward);
142}
143
144/* Equation 8 solves equation 7 for b:
145 * c - 2*t/T = sqrt(c**2 - 4*R) =>
146 * c**2 - 4*c*t/T + 4*t**2/T**2 = c**2 - 4*R =>
147 * -4*c*t/T + 4*t**2/T**2 = -4*R =>
148 * -c*t/T + t**2/T**2 = -R -=>
149 * substitute c = R*f + 2 - f =>
150 * -(t/T)*(R*f + 2 - f) + (t/T)**2 = -R, let t1 = t/T =>
151 * -t1*R*f -2*t1 +t1*f +t1**2 = -R =>
152 * R = (t1**2 + t1*(f - 2)) / (t1*f - 1)
153 */
154STAmount
156 STAmount const& assetBalance,
157 STAmount const& lptAMMBalance,
158 STAmount const& lpTokens,
159 std::uint16_t tfee)
160{
161 auto const f = getFee(tfee);
162 Number const t1 = lpTokens / lptAMMBalance;
163 if (!isFeatureEnabled(fixAMMv1_3))
164 {
165 auto const b = assetBalance * (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1);
166 return toSTAmount(assetBalance.asset(), b);
167 }
168
169 // minimize withdraw
170 auto const frac = (t1 * t1 - t1 * (2 - f)) / (t1 * f - 1);
171 return multiply(assetBalance, frac, Number::RoundingMode::Downward);
172}
173
174Number
175square(Number const& n)
176{
177 return n * n;
178}
179
180STAmount
181adjustLPTokens(STAmount const& lptAMMBalance, STAmount const& lpTokens, IsDeposit isDeposit)
182{
183 // Force rounding downward to ensure adjusted tokens are less or equal
184 // to requested tokens.
186 if (isDeposit == IsDeposit::Yes)
187 return (lptAMMBalance + lpTokens) - lptAMMBalance;
188 return (lpTokens - lptAMMBalance) + lptAMMBalance;
189}
190
193 STAmount const& amountBalance,
194 STAmount const& amount,
195 std::optional<STAmount> const& amount2,
196 STAmount const& lptAMMBalance,
197 STAmount const& lpTokens,
198 std::uint16_t tfee,
199 IsDeposit isDeposit)
200{
201 // AMMv1_3 amendment adjusts tokens and amounts in deposit/withdraw
202 if (isFeatureEnabled(fixAMMv1_3))
203 return std::make_tuple(amount, amount2, lpTokens);
204
205 auto const lpTokensActual = adjustLPTokens(lptAMMBalance, lpTokens, isDeposit);
206
207 if (lpTokensActual == beast::kZero)
208 {
209 auto const amount2Opt = amount2 ? std::make_optional(STAmount{}) : std::nullopt;
210 return std::make_tuple(STAmount{}, amount2Opt, lpTokensActual);
211 }
212
213 if (lpTokensActual < lpTokens)
214 {
215 bool const ammRoundingEnabled = [&]() {
216 if (auto const& rules = getCurrentTransactionRules();
217 rules && rules->enabled(fixAMMv1_1))
218 return true;
219 return false;
220 }();
221
222 // Equal trade
223 if (amount2)
224 {
225 Number const fr = lpTokensActual / lpTokens;
226 auto const amountActual = toSTAmount(amount.asset(), fr * amount);
227 auto const amount2Actual = toSTAmount(amount2->asset(), fr * *amount2);
228 if (!ammRoundingEnabled)
229 {
230 return std::make_tuple(
231 amountActual < amount ? amountActual : amount,
232 amount2Actual < amount2 ? amount2Actual : amount2,
233 lpTokensActual);
234 }
235
236 return std::make_tuple(amountActual, amount2Actual, lpTokensActual);
237 }
238
239 // Single trade
240 auto const amountActual = [&]() {
241 if (isDeposit == IsDeposit::Yes)
242 {
243 return ammAssetIn(amountBalance, lptAMMBalance, lpTokensActual, tfee);
244 }
245 if (!ammRoundingEnabled)
246 {
247 return ammAssetOut(amountBalance, lptAMMBalance, lpTokens, tfee);
248 }
249
250 return ammAssetOut(amountBalance, lptAMMBalance, lpTokensActual, tfee);
251 }();
252 if (!ammRoundingEnabled)
253 {
254 return amountActual < amount
255 ? std::make_tuple(amountActual, std::nullopt, lpTokensActual)
256 : std::make_tuple(amount, std::nullopt, lpTokensActual);
257 }
258
259 return std::make_tuple(amountActual, std::nullopt, lpTokensActual);
260 }
261
262 XRPL_ASSERT(
263 lpTokensActual == lpTokens, "xrpl::adjustAmountsByLPTokens : LP tokens match actual");
264
265 return {amount, amount2, lpTokensActual};
266}
267
268Number
269solveQuadraticEq(Number const& a, Number const& b, Number const& c)
270{
271 return (-b + root2(b * b - 4 * a * c)) / (2 * a);
272}
273
274// Minimize takerGets or takerPays
276solveQuadraticEqSmallest(Number const& a, Number const& b, Number const& c)
277{
278 auto const d = b * b - 4 * a * c;
279 if (d < 0)
280 return std::nullopt;
281 // use numerically stable citardauq formula for quadratic equation solution
282 // https://people.csail.mit.edu/bkph/articles/Quadratics.pdf
283 if (b > 0)
284 {
285 return (2 * c) / (-b - root2(d));
286 }
287
288 return (2 * c) / (-b + root2(d));
289}
290
291STAmount
292multiply(STAmount const& amount, Number const& frac, Number::RoundingMode rm)
293{
294 NumberRoundModeGuard const g(rm);
295 auto const t = amount * frac;
296 return toSTAmount(amount.asset(), t, rm);
297}
298
299STAmount
301 Rules const& rules,
302 std::function<Number()> const& noRoundCb,
303 STAmount const& balance,
304 std::function<Number()> const& productCb,
305 IsDeposit isDeposit)
306{
307 if (!rules.enabled(fixAMMv1_3))
308 return toSTAmount(balance.asset(), noRoundCb());
309
310 auto const rm = detail::getAssetRounding(isDeposit);
311 if (isDeposit == IsDeposit::Yes)
312 return multiply(balance, productCb(), rm);
313 NumberRoundModeGuard const g(rm);
314 return toSTAmount(balance.asset(), productCb(), rm);
315}
316
317STAmount
319 Rules const& rules,
320 STAmount const& balance,
321 Number const& frac,
322 IsDeposit isDeposit)
323{
324 if (!rules.enabled(fixAMMv1_3))
325 return toSTAmount(balance.asset(), balance * frac);
326
327 auto const rm = detail::getLPTokenRounding(isDeposit);
328 auto const tokens = multiply(balance, frac, rm);
329 return adjustLPTokens(balance, tokens, isDeposit);
330}
331
332STAmount
334 Rules const& rules,
335 std::function<Number()> const& noRoundCb,
336 STAmount const& lptAMMBalance,
337 std::function<Number()> const& productCb,
338 IsDeposit isDeposit)
339{
340 if (!rules.enabled(fixAMMv1_3))
341 return toSTAmount(lptAMMBalance.asset(), noRoundCb());
342
343 auto const tokens = [&] {
344 auto const rm = detail::getLPTokenRounding(isDeposit);
345 if (isDeposit == IsDeposit::Yes)
346 {
347 NumberRoundModeGuard const g(rm);
348 return toSTAmount(lptAMMBalance.asset(), productCb(), rm);
349 }
350 return multiply(lptAMMBalance, productCb(), rm);
351 }();
352 return adjustLPTokens(lptAMMBalance, tokens, isDeposit);
353}
354
357 Rules const& rules,
358 STAmount const& balance,
359 STAmount const& amount,
360 STAmount const& lptAMMBalance,
361 STAmount const& tokens,
362 std::uint16_t tfee)
363{
364 if (!rules.enabled(fixAMMv1_3))
365 return {tokens, amount};
366 auto assetAdj = ammAssetIn(balance, lptAMMBalance, tokens, tfee);
367 auto tokensAdj = tokens;
368 // Rounding didn't work the right way.
369 // Try to adjust the original deposit amount by difference
370 // in adjust and original amount. Then adjust tokens and deposit amount.
371 if (assetAdj > amount)
372 {
373 auto const adjAmount = amount - (assetAdj - amount);
374 auto const t = lpTokensOut(balance, adjAmount, lptAMMBalance, tfee);
375 tokensAdj = adjustLPTokens(lptAMMBalance, t, IsDeposit::Yes);
376 assetAdj = ammAssetIn(balance, lptAMMBalance, tokensAdj, tfee);
377 }
378 return {tokensAdj, std::min(amount, assetAdj)};
379}
380
383 Rules const& rules,
384 STAmount const& balance,
385 STAmount const& amount,
386 STAmount const& lptAMMBalance,
387 STAmount const& tokens,
388 std::uint16_t tfee)
389{
390 if (!rules.enabled(fixAMMv1_3))
391 return {tokens, amount};
392 auto assetAdj = ammAssetOut(balance, lptAMMBalance, tokens, tfee);
393 auto tokensAdj = tokens;
394 // Rounding didn't work the right way.
395 // Try to adjust the original deposit amount by difference
396 // in adjust and original amount. Then adjust tokens and deposit amount.
397 if (assetAdj > amount)
398 {
399 auto const adjAmount = amount - (assetAdj - amount);
400 auto const t = lpTokensIn(balance, adjAmount, lptAMMBalance, tfee);
401 tokensAdj = adjustLPTokens(lptAMMBalance, t, IsDeposit::No);
402 assetAdj = ammAssetOut(balance, lptAMMBalance, tokensAdj, tfee);
403 }
404 return {tokensAdj, std::min(amount, assetAdj)};
405}
406
407Number
409 Rules const& rules,
410 STAmount const& lptAMMBalance,
411 STAmount const& tokens,
412 Number const& frac)
413{
414 if (!rules.enabled(fixAMMv1_3))
415 return frac;
416 return tokens / lptAMMBalance;
417}
418
421 ReadView const& view,
422 AccountID const& ammAccountID,
423 Asset const& asset1,
424 Asset const& asset2,
425 FreezeHandling freezeHandling,
426 AuthHandling authHandling,
427 beast::Journal const j)
428{
429 auto const assetInBalance =
430 accountHolds(view, ammAccountID, asset1, freezeHandling, authHandling, j);
431 auto const assetOutBalance =
432 accountHolds(view, ammAccountID, asset2, freezeHandling, authHandling, j);
433 return std::make_pair(assetInBalance, assetOutBalance);
434}
435
436TER
437checkAMMPrecisionLoss(Number const& poolProductMean, STAmount const& newLPTokenBalance)
438{
439 if (newLPTokenBalance <= beast::kZero)
440 return tesSUCCESS;
441 if (poolProductMean >= newLPTokenBalance)
442 return tesSUCCESS;
443 // Strong check failed. Allow the same relative tolerance as the invariant
444 // checker's weak check. Only return tecPRECISION_LOSS when both fail.
446 poolProductMean, Number{newLPTokenBalance}, kAMMInvariantRelativeTolerance))
447 return tesSUCCESS;
448 return tecPRECISION_LOSS;
449}
450
451TER
453 ReadView const& view,
454 AccountID const& ammAccountID,
455 Asset const& asset1,
456 Asset const& asset2,
457 STAmount const& newLPTokenBalance,
458 beast::Journal const j)
459{
460 if (newLPTokenBalance <= beast::kZero)
461 return tesSUCCESS;
462 auto const [amount, amount2] = ammPoolHolds(
463 view,
464 ammAccountID,
465 asset1,
466 asset2,
469 j);
470 return checkAMMPrecisionLoss(root2(amount * amount2), newLPTokenBalance);
471}
472
473std::expected<std::tuple<STAmount, STAmount, STAmount>, TER>
475 ReadView const& view,
476 SLE const& ammSle,
477 std::optional<Asset> const& optAsset1,
478 std::optional<Asset> const& optAsset2,
479 FreezeHandling freezeHandling,
480 AuthHandling authHandling,
481 beast::Journal const j)
482{
483 auto const assets = [&]() -> std::optional<std::pair<Asset, Asset>> {
484 auto const asset1 = ammSle[sfAsset];
485 auto const asset2 = ammSle[sfAsset2];
486 if (optAsset1 && optAsset2)
487 {
489 *optAsset1, *optAsset2, std::make_optional(std::make_pair(asset1, asset2))))
490 {
491 // This error can only be hit if the AMM is corrupted
492 // LCOV_EXCL_START
493 JLOG(j.debug()) << "ammHolds: Invalid optAsset1 or optAsset2 " << *optAsset1 << " "
494 << *optAsset2;
495 return std::nullopt;
496 // LCOV_EXCL_STOP
497 }
498 return std::make_optional(std::make_pair(*optAsset1, *optAsset2));
499 }
500 auto const singleAsset = [&asset1, &asset2, &j](
501 Asset checkIssue,
502 char const* label) -> std::optional<std::pair<Asset, Asset>> {
503 if (checkIssue == asset1)
504 {
505 return std::make_optional(std::make_pair(asset1, asset2));
506 }
507 if (checkIssue == asset2)
508 {
509 return std::make_optional(std::make_pair(asset2, asset1));
510 }
511 // Unreachable unless AMM corrupted.
512 // LCOV_EXCL_START
513 JLOG(j.debug()) << "ammHolds: Invalid " << label << " " << checkIssue;
514 return std::nullopt;
515 // LCOV_EXCL_STOP
516 };
517 if (optAsset1)
518 {
519 return singleAsset(*optAsset1, "optAsset1");
520 }
521 if (optAsset2)
522 {
523 // Cannot have Amount2 without Amount.
524 return singleAsset(*optAsset2, "optAsset2"); // LCOV_EXCL_LINE
525 }
526 return std::make_optional(std::make_pair(asset1, asset2));
527 }();
528 if (!assets)
530 auto const [amount1, amount2] = ammPoolHolds(
531 view,
532 ammSle.getAccountID(sfAccount),
533 assets->first,
534 assets->second,
535 freezeHandling,
536 authHandling,
537 j);
538 return std::make_tuple(amount1, amount2, ammSle[sfLPTokenBalance]);
539}
540
541STAmount
543 ReadView const& view,
544 Asset const& asset1,
545 Asset const& asset2,
546 AccountID const& ammAccount,
547 AccountID const& lpAccount,
548 beast::Journal const j)
549{
550 // This function looks similar to `accountHolds`. However, it only checks if
551 // a LPToken holder has enough balance. On the other hand, `accountHolds`
552 // checks if the underlying assets of LPToken are frozen with the
553 // fixFrozenLPTokenTransfer amendment
554
555 auto const currency = ammLPTCurrency(asset1, asset2);
556 STAmount amount;
557
558 auto const sle = view.read(keylet::trustLine(lpAccount, ammAccount, currency));
559 if (!sle)
560 {
561 amount.clear(Issue{currency, ammAccount});
562 JLOG(j.trace()) << "ammLPHolds: no SLE "
563 << " lpAccount=" << to_string(lpAccount)
564 << " amount=" << amount.getFullText();
565 }
566 else if (isFrozen(view, lpAccount, currency, ammAccount))
567 {
568 amount.clear(Issue{currency, ammAccount});
569 JLOG(j.trace()) << "ammLPHolds: frozen currency "
570 << " lpAccount=" << to_string(lpAccount)
571 << " amount=" << amount.getFullText();
572 }
573 else
574 {
575 amount = sle->getFieldAmount(sfBalance);
576 if (lpAccount > ammAccount)
577 {
578 // Put balance in account terms.
579 amount.negate();
580 }
581 amount.get<Issue>().account = ammAccount;
582
583 JLOG(j.trace()) << "ammLPHolds:"
584 << " lpAccount=" << to_string(lpAccount)
585 << " amount=" << amount.getFullText();
586 }
587
588 return view.balanceHookIOU(lpAccount, ammAccount, amount);
589}
590
591STAmount
593 ReadView const& view,
594 SLE const& ammSle,
595 AccountID const& lpAccount,
596 beast::Journal const j)
597{
598 return ammLPHolds(view, ammSle[sfAsset], ammSle[sfAsset2], ammSle[sfAccount], lpAccount, j);
599}
600
602getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account)
603{
604 using namespace std::chrono;
605 XRPL_ASSERT(
606 !view.rules().enabled(fixInnerObjTemplate) || ammSle.isFieldPresent(sfAuctionSlot),
607 "xrpl::getTradingFee : auction present");
608 if (ammSle.isFieldPresent(sfAuctionSlot))
609 {
610 auto const& auctionSlot = safeDowncast<STObject const&>(ammSle.peekAtField(sfAuctionSlot));
611 // Not expired
612 if (auto const expiration = auctionSlot[~sfExpiration];
614 expiration)
615 {
616 if (auctionSlot[~sfAccount] == account)
617 return auctionSlot[sfDiscountedFee];
618 if (auctionSlot.isFieldPresent(sfAuthAccounts))
619 {
620 for (auto const& acct : auctionSlot.getFieldArray(sfAuthAccounts))
621 {
622 if (acct[~sfAccount] == account)
623 return auctionSlot[sfDiscountedFee];
624 }
625 }
626 }
627 }
628 return ammSle[sfTradingFee];
629}
630
631STAmount
632ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Asset const& asset)
633{
634 // Get the actual AMM balance without factoring in the balance hook
635 return asset.visit(
636 [&](MPTIssue const& issue) {
637 if (auto const sle = view.read(keylet::mptoken(issue, ammAccountID));
638 sle && !isFrozen(view, ammAccountID, issue))
639 return STAmount{issue, (*sle)[sfMPTAmount]};
640 return STAmount{asset};
641 },
642 [&](Issue const& issue) {
643 if (isXRP(issue))
644 {
645 if (auto const sle = view.read(keylet::account(ammAccountID)))
646 return (*sle)[sfBalance];
647 }
648 else if (
649 auto const sle =
650 view.read(keylet::trustLine(ammAccountID, issue.account, issue.currency));
651 sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
652 {
653 STAmount amount = (*sle)[sfBalance];
654 if (ammAccountID > issue.account)
655 amount.negate();
656 amount.get<Issue>().account = issue.account;
657 return amount;
658 }
659 return STAmount{asset};
660 });
661}
662
663static TER
665 Sandbox& sb,
666 AccountID const& ammAccountID,
667 std::uint16_t maxTrustlinesToDelete,
669{
671 sb,
672 keylet::ownerDir(ammAccountID),
673 [&](LedgerEntryType nodeType,
674 uint256 const&,
676 // Skip AMM and MPToken
677 if (nodeType == ltAMM || nodeType == ltMPTOKEN)
678 return {tesSUCCESS, SkipEntry::Yes};
679
680 if (nodeType == ltRIPPLE_STATE)
681 {
682 // Trustlines must have zero balance
683 if (sleItem->getFieldAmount(sfBalance) != beast::kZero)
684 {
685 // LCOV_EXCL_START
686 JLOG(j.error()) << "deleteAMMObjects: deleting trustline with "
687 "non-zero balance.";
688 return {tecINTERNAL, SkipEntry::No};
689 // LCOV_EXCL_STOP
690 }
691
692 return {deleteAMMTrustLine(sb, sleItem, ammAccountID, j), SkipEntry::No};
693 }
694 // LCOV_EXCL_START
695 JLOG(j.error()) << "deleteAMMObjects: deleting non-trustline or non-MPT " << nodeType;
696 return {tecINTERNAL, SkipEntry::No};
697 // LCOV_EXCL_STOP
698 },
699 j,
700 maxTrustlinesToDelete);
701}
702
703static TER
705{
707 sb,
708 keylet::ownerDir(ammAccountID),
709 [&](LedgerEntryType nodeType,
710 uint256 const&,
712 // Skip AMM
713 if (nodeType == ltAMM)
714 return {tesSUCCESS, SkipEntry::Yes};
715
716 if (nodeType == ltMPTOKEN)
717 {
718 // MPT must have zero balance
719 if (sleItem->getFieldU64(sfMPTAmount) != 0 ||
720 (*sleItem)[~sfLockedAmount].valueOr(0) != 0)
721 {
722 // LCOV_EXCL_START
723 JLOG(j.error()) << "deleteAMMObjects: deleting MPT with "
724 "non-zero balance.";
725 return {tecINTERNAL, SkipEntry::No};
726 // LCOV_EXCL_STOP
727 }
728
729 return {deleteAMMMPToken(sb, sleItem, ammAccountID, j), SkipEntry::No};
730 }
731 if (nodeType == ltRIPPLE_STATE)
732 {
733 // Trustlines should have been deleted
734 // LCOV_EXCL_START
735 JLOG(j.error()) << "deleteAMMObjects: trustlines should have been deleted";
736 return {tecINTERNAL, SkipEntry::No};
737 // LCOV_EXCL_STOP
738 }
739 // LCOV_EXCL_START
740 JLOG(j.error()) << "deleteAMMObjects: deleting non-trustline or non-MPT " << nodeType;
741 return {tecINTERNAL, SkipEntry::No};
742 // LCOV_EXCL_STOP
743 },
744 j,
745 3); // At most two MPToken plus AMM object
746}
747
748TER
749deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Journal j)
750{
751 auto ammSle = sb.peek(keylet::amm(asset, asset2));
752 if (!ammSle)
753 {
754 // LCOV_EXCL_START
755 JLOG(j.error()) << "deleteAMMAccount: AMM object does not exist " << asset << " " << asset2;
756 return tecINTERNAL;
757 // LCOV_EXCL_STOP
758 }
759
760 auto const ammAccountID = (*ammSle)[sfAccount];
761 auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
762 if (!sleAMMRoot)
763 {
764 // LCOV_EXCL_START
765 JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist "
766 << to_string(ammAccountID);
767 return tecINTERNAL;
768 // LCOV_EXCL_STOP
769 }
770
771 if (auto const ter = deleteAMMTrustLines(sb, ammAccountID, kMaxDeletableAmmTrustLines, j);
772 !isTesSuccess(ter))
773 return ter;
774
775 // Delete AMM's MPTokens only if all trustlines are deleted. If trustlines
776 // are not deleted then AMM can be re-created with Deposit and
777 // AMM's MPToken(s) must exist.
778 if (auto const ter = deleteAMMMPTokens(sb, ammAccountID, j); !isTesSuccess(ter))
779 return ter;
780
781 auto const ownerDirKeylet = keylet::ownerDir(ammAccountID);
782 if (!sb.dirRemove(ownerDirKeylet, (*ammSle)[sfOwnerNode], ammSle->key(), false))
783 {
784 // LCOV_EXCL_START
785 JLOG(j.error()) << "deleteAMMAccount: failed to remove dir link";
786 return tecINTERNAL;
787 // LCOV_EXCL_STOP
788 }
789 if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet))
790 {
791 // LCOV_EXCL_START
792 JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of "
793 << toBase58(ammAccountID);
794 return tecINTERNAL;
795 // LCOV_EXCL_STOP
796 }
797
798 sb.erase(ammSle);
799 sb.erase(sleAMMRoot);
800
801 return tesSUCCESS;
802}
803
804void
806 ApplyView& view,
807 SLE::pointer& ammSle,
808 AccountID const& account,
809 Asset const& lptAsset,
810 std::uint16_t tfee)
811{
812 auto const& rules = view.rules();
813 // AMM creator gets the voting slot.
814 STArray voteSlots;
815 STObject voteEntry = STObject::makeInnerObject(sfVoteEntry);
816 if (tfee != 0)
817 voteEntry.setFieldU16(sfTradingFee, tfee);
818 voteEntry.setFieldU32(sfVoteWeight, kVoteWeightScaleFactor);
819 voteEntry.setAccountID(sfAccount, account);
820 voteSlots.pushBack(voteEntry);
821 ammSle->setFieldArray(sfVoteSlots, voteSlots);
822 // AMM creator gets the auction slot for free.
823 // AuctionSlot is created on AMMCreate and updated on AMMDeposit
824 // when AMM is in an empty state
825 if (rules.enabled(fixInnerObjTemplate) && !ammSle->isFieldPresent(sfAuctionSlot))
826 {
827 STObject auctionSlot = STObject::makeInnerObject(sfAuctionSlot);
828 ammSle->set(std::move(auctionSlot));
829 }
830 STObject& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
831 auctionSlot.setAccountID(sfAccount, account);
832 // current + sec in 24h
835 .count() +
837 auctionSlot.setFieldU32(sfExpiration, expiration);
838 auctionSlot.setFieldAmount(sfPrice, STAmount{lptAsset, 0});
839 // Set the fee
840 if (tfee != 0)
841 {
842 ammSle->setFieldU16(sfTradingFee, tfee);
843 }
844 else if (ammSle->isFieldPresent(sfTradingFee))
845 {
846 ammSle->makeFieldAbsent(sfTradingFee); // LCOV_EXCL_LINE
847 }
848 if (auto const dfee = tfee / kAuctionSlotDiscountedFeeFraction)
849 {
850 auctionSlot.setFieldU16(sfDiscountedFee, dfee);
851 }
852 else if (auctionSlot.isFieldPresent(sfDiscountedFee))
853 {
854 auctionSlot.makeFieldAbsent(sfDiscountedFee); // LCOV_EXCL_LINE
855 }
856 // Clear stale auth accounts from any previous auction slot holder.
857 if (rules.enabled(fixCleanup3_2_0) && auctionSlot.isFieldPresent(sfAuthAccounts))
858 auctionSlot.makeFieldAbsent(sfAuthAccounts);
859}
860
861std::expected<bool, TER>
862isOnlyLiquidityProvider(ReadView const& view, Issue const& ammIssue, AccountID const& lpAccount)
863{
864 // Liquidity Provider (LP) must have one LPToken trustline
865 std::uint8_t nLPTokenTrustLines = 0;
866 // AMM account has at most two IOU (pool tokens, not LPToken) trustlines.
867 // One or both trustlines could be to the LP if LP is the issuer,
868 // or a different account if LP is not an issuer. For instance,
869 // if AMM has two tokens USD and EUR and LP is not the issuer of the tokens
870 // then the trustlines are between AMM account and the issuer.
871 // There is one LPToken trustline for each LP. Only remaining LP has
872 // exactly one LPToken trustlines and at most two IOU trustline for each
873 // pool token. One or both tokens could be MPT.
874 std::uint8_t nIOUTrustLines = 0;
875 // There are at most two MPT objects, one for each side of the pool.
876 std::uint8_t nMPT = 0;
877 // There is only one AMM object
878 bool hasAMM = false;
879 // AMM LP has at most three trustlines, at most two MPTs, and only one
880 // AMM object must exist. If there are more than four objects then
881 // it's either an error or there are more than one LP. Ten pages should
882 // be sufficient to include four objects.
883 std::uint8_t limit = 10;
884 auto const root = keylet::ownerDir(ammIssue.account);
885 auto currentIndex = root;
886
887 // Iterate over AMM owner directory objects.
888 while (limit-- >= 1)
889 {
890 auto const ownerDir = view.read(currentIndex);
891 if (!ownerDir)
892 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
893 for (auto const& key : ownerDir->getFieldV256(sfIndexes))
894 {
895 auto const sle = view.read(keylet::child(key));
896 if (!sle)
897 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
898 auto const entryType = sle->getFieldU16(sfLedgerEntryType);
899 // Only one AMM object
900 if (entryType == ltAMM)
901 {
902 if (hasAMM)
903 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
904 hasAMM = true;
905 continue;
906 }
907 if (entryType == ltMPTOKEN)
908 {
909 ++nMPT;
910 continue;
911 }
912 if (entryType != ltRIPPLE_STATE)
913 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
914 auto const lowLimit = sle->getFieldAmount(sfLowLimit);
915 auto const highLimit = sle->getFieldAmount(sfHighLimit);
916 auto const isLPTrustline =
917 lowLimit.getIssuer() == lpAccount || highLimit.getIssuer() == lpAccount;
918 auto const isLPTokenTrustline =
919 lowLimit.asset() == ammIssue || highLimit.asset() == ammIssue;
920
921 // Liquidity Provider trustline
922 if (isLPTrustline)
923 {
924 // LPToken trustline
925 if (isLPTokenTrustline)
926 {
927 // LP has exactly one LPToken trustline
928 if (++nLPTokenTrustLines > 1)
929 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
930 }
931 // AMM account has at most two IOU trustlines
932 else if (++nIOUTrustLines > 2)
933 {
934 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
935 }
936 }
937 // Another Liquidity Provider LPToken trustline
938 else if (isLPTokenTrustline)
939 {
940 return false;
941 }
942 // AMM account has at most two IOU trustlines
943 else if (++nIOUTrustLines > 2)
944 {
945 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
946 }
947 }
948 auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
949 if (uNodeNext == 0)
950 {
951 if (nLPTokenTrustLines != 1 || (nIOUTrustLines == 0 && nMPT == 0) ||
952 (nIOUTrustLines > 2 || nMPT > 2) || (nIOUTrustLines + nMPT) > 2)
953 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
954 return true;
955 }
956 currentIndex = keylet::page(root, uNodeNext);
957 }
958 return std::unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
959}
960
961std::expected<bool, TER>
963 Sandbox& sb,
964 STAmount const& lpTokens,
965 SLE::pointer& ammSle,
966 AccountID const& account)
967{
968 auto const res = isOnlyLiquidityProvider(sb, lpTokens.get<Issue>(), account);
969 if (!res.has_value())
970 {
971 return std::unexpected<TER>(res.error());
972 }
973
974 if (res.value())
975 {
977 lpTokens, ammSle->getFieldAmount(sfLPTokenBalance), Number{1, -3}))
978 {
979 ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
980 sb.update(ammSle);
981 }
982 else
983 {
985 }
986 }
987 return true;
988}
989
990} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:38
Stream error() const
Definition Journal.h:315
Stream debug() const
Definition Journal.h:297
Stream trace() const
Severity stream access functions.
Definition Journal.h:291
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:118
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
bool emptyDirDelete(Keylet const &directory)
Remove the specified directory, if it is empty.
constexpr auto visit(Visitors &&... visitors) const -> decltype(auto)
Definition Asset.h:107
A currency issued by an account.
Definition Issue.h:13
AccountID account
Definition Issue.h:16
Number is a floating point type that can represent a wide range of values.
Definition Number.h:306
static RoundingMode setround(RoundingMode inMode)
Definition Number.cpp:111
static RoundingMode getround()
Definition Number.cpp:105
A view into a ledger.
Definition ReadView.h:31
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
virtual LedgerHeader const & header() const =0
Returns information about the ledger.
virtual STAmount balanceHookIOU(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
Definition ReadView.h:155
Rules controlling protocol behavior.
Definition Rules.h:33
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:171
constexpr TIss const & get() const
std::string getFullText() const override
Definition STAmount.cpp:636
void negate()
Definition STAmount.h:568
Asset const & asset() const
Definition STAmount.h:478
void pushBack(STObject const &object)
Definition STArray.h:204
std::shared_ptr< STLedgerEntry > pointer
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:733
void setFieldArray(SField const &field, STArray const &v)
Definition STObject.cpp:823
STObject & peekFieldObject(SField const &field)
Definition STObject.cpp:465
void setFieldAmount(SField const &field, STAmount const &)
Definition STObject.cpp:793
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:454
void setFieldU16(SField const &field, std::uint16_t)
Definition STObject.cpp:727
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:74
STBase const & peekAtField(SField const &field) const
Definition STObject.cpp:399
void set(SOTemplate const &)
Definition STObject.cpp:135
std::uint64_t getFieldU64(SField const &field) const
Definition STObject.cpp:597
void setAccountID(SField const &field, AccountID const &)
Definition STObject.cpp:775
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:633
void makeFieldAbsent(SField const &field)
Definition STObject.cpp:540
STAmount const & getFieldAmount(SField const &field) const
Definition STObject.cpp:647
Discardable, editable view to a ledger.
Definition Sandbox.h:15
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.
void erase(SLE::ref sle) override
Remove a peeked SLE.
bool exists(Keylet const &k) const override
Determine if a state item exists.
T duration_cast(T... args)
T make_optional(T... args)
T make_pair(T... args)
T make_tuple(T... args)
T min(T... args)
Number::RoundingMode getLPTokenRounding(IsDeposit isDeposit)
Definition AMMHelpers.h:600
Number::RoundingMode getAssetRounding(IsDeposit isDeposit)
Definition AMMHelpers.h:609
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:357
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:425
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition Indexes.cpp:192
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:533
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:363
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:241
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static TER deleteAMMTrustLines(Sandbox &sb, AccountID const &ammAccountID, std::uint16_t maxTrustlinesToDelete, beast::Journal j)
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.
constexpr std::uint32_t kTotalTimeSlotSecs
Definition AMMCore.h:14
bool isFeatureEnabled(uint256 const &feature, bool resultIfNoRules)
Check whether a feature is enabled in the current ledger rules.
Definition Rules.cpp:194
FreezeHandling
Controls the treatment of frozen account balances.
Number feeMultHalf(std::uint16_t tfee)
Get fee multiplier (1 - tfee / 2) @tfee trading fee in basis points.
Definition AMMCore.h:96
Dest safeDowncast(Src *s) noexcept
Definition safe_cast.h:78
constexpr std::uint32_t kAuctionSlotDiscountedFeeFraction
Definition AMMCore.h:18
bool isXRP(AccountID const &c)
Definition AccountID.h:70
Number adjustFracByTokens(Rules const &rules, STAmount const &lptAMMBalance, STAmount const &tokens, Number const &frac)
Find a fraction of tokens after the tokens are adjusted.
STAmount ammLPTokens(STAmount const &asset1, STAmount const &asset2, Asset const &lptIssue)
Calculate LP Tokens given AMM pool reserves.
TER deleteAMMTrustLine(ApplyView &view, SLE::pointer sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
std::expected< bool, TER > isOnlyLiquidityProvider(ReadView const &view, Issue const &ammIssue, AccountID const &lpAccount)
Return true if the Liquidity Provider is the only AMM provider, false otherwise.
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.
IsDeposit
Definition AMMHelpers.h:38
NotTEC invalidAMMAssetPair(Asset const &asset1, Asset const &asset2, std::optional< std::pair< Asset, Asset > > const &pair=std::nullopt)
Definition AMMCore.cpp:82
void initializeFeeAuctionVote(ApplyView &view, SLE::pointer &ammSle, AccountID const &account, Asset const &lptAsset, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
TER deleteAMMMPToken(ApplyView &view, SLE::pointer sleMPT, AccountID const &ammAccountID, beast::Journal j)
Delete AMMs MPToken.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:93
STAmount adjustLPTokens(STAmount const &lptAMMBalance, STAmount const &lpTokens, IsDeposit isDeposit)
Adjust LP tokens to deposit/withdraw.
Number root(Number f, unsigned d)
Definition Number.cpp:1201
std::optional< Rules > const & getCurrentTransactionRules()
Definition Rules.cpp:30
Number solveQuadraticEq(Number const &a, Number const &b, Number const &c)
Positive solution for quadratic equation: x = (-b + sqrt(b**2 + 4*a*c))/(2*a).
Currency ammLPTCurrency(Asset const &asset1, Asset const &asset2)
Calculate Liquidity Provider Token (LPT) Currency.
Definition AMMCore.cpp:29
STAmount ammAssetOut(STAmount const &assetBalance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset withdrawal by tokens.
constexpr std::uint16_t kMaxDeletableAmmTrustLines
The maximum number of trustlines to delete as part of AMM account deletion cleanup.
Definition Protocol.h:280
STAmount getRoundedLPTokens(Rules const &rules, STAmount const &balance, Number const &frac, IsDeposit isDeposit)
Round AMM deposit/withdrawal LPToken amount.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
std::pair< STAmount, STAmount > ammPoolHolds(ReadView const &view, AccountID const &ammAccountID, Asset const &asset1, Asset const &asset2, FreezeHandling freezeHandling, AuthHandling authHandling, beast::Journal const j)
Get AMM pool balances.
STLedgerEntry SLE
std::pair< STAmount, STAmount > adjustAssetInByTokens(Rules const &rules, STAmount const &balance, STAmount const &amount, STAmount const &lptAMMBalance, STAmount const &tokens, std::uint16_t tfee)
STAmount ammAccountHolds(ReadView const &view, AccountID const &ammAccountID, Asset const &asset)
Returns total amount held by AMM for the given token.
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.
static TER deleteAMMMPTokens(Sandbox &sb, AccountID const &ammAccountID, beast::Journal j)
STAmount getRoundedAsset(Rules const &rules, STAmount const &balance, A const &frac, IsDeposit isDeposit)
Round AMM equal deposit/withdrawal amount.
Definition AMMHelpers.h:626
AuthHandling
Controls the treatment of unauthorized MPT balances.
Number feeMult(std::uint16_t tfee)
Get fee multiplier (1 - tfee) @tfee trading fee in basis points.
Definition AMMCore.h:87
TER checkAMMPrecisionLoss(Number const &poolProductMean, STAmount const &newLPTokenBalance)
Check AMM pool product invariant after an AMM operation that changes LP tokens (deposit/withdraw/claw...
std::optional< Number > solveQuadraticEqSmallest(Number const &a, Number const &b, Number const &c)
Solve quadratic equation to find takerGets or takerPays.
bool isFrozen(ReadView const &view, AccountID const &account, MPTIssue const &mptIssue, std::uint8_t depth=0)
Number root2(Number f)
Definition Number.cpp:1275
STAmount ammAssetIn(STAmount const &asset1Balance, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee)
Calculate asset deposit given LP Tokens.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
std::tuple< STAmount, std::optional< STAmount >, STAmount > adjustAmountsByLPTokens(STAmount const &amountBalance, STAmount const &amount, std::optional< STAmount > const &amount2, STAmount const &lptAMMBalance, STAmount const &lpTokens, std::uint16_t tfee, IsDeposit isDeposit)
Calls adjustLPTokens() and adjusts deposit or withdraw amounts if the adjusted LP tokens are less tha...
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
STAmount lpTokensIn(STAmount const &asset1Balance, STAmount const &asset1Withdraw, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's withdraw amount.
Number getFee(std::uint16_t tfee)
Convert to the fee from the basis points.
Definition AMMCore.h:78
TERSubset< CanCvtToTER > TER
Definition TER.h:634
Number const kAMMInvariantRelativeTolerance
Definition AMMHelpers.h:40
LedgerEntryType
Identifiers for on-ledger objects.
bool withinRelativeDistance(Quality const &calcQuality, Quality const &reqQuality, Number const &dist)
Check if the relative distance between the qualities is within the requested distance.
Definition AMMHelpers.h:115
@ tecAMM_INVALID_TOKENS
Definition TER.h:329
@ tecINTERNAL
Definition TER.h:308
@ tecPRECISION_LOSS
Definition TER.h:361
STAmount multiply(STAmount const &amount, Number const &frac, Number::RoundingMode rm)
constexpr std::uint32_t kVoteWeightScaleFactor
Definition AMMCore.h:25
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.
BaseUInt< 256 > uint256
Definition base_uint.h:562
Number square(Number const &n)
Return square of n.
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
@ tesSUCCESS
Definition TER.h:240
STAmount toSTAmount(IOUAmount const &iou, Asset const &asset)
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< std::uint16_t > maxNodesToDelete=std::nullopt)
Cleanup owner directory entries on account delete.
STAmount lpTokensOut(STAmount const &asset1Balance, STAmount const &asset1Deposit, STAmount const &lptAMMBalance, std::uint16_t tfee)
Calculate LP Tokens given asset's deposit amount.
NetClock::time_point parentCloseTime
T time_since_epoch(T... args)
T unexpected(T... args)