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