rippled
Loading...
Searching...
No Matches
AMMUtils.cpp
1#include <xrpld/app/misc/AMMHelpers.h>
2#include <xrpld/app/misc/AMMUtils.h>
3
4#include <xrpl/basics/Log.h>
5#include <xrpl/ledger/Sandbox.h>
6#include <xrpl/protocol/AMMCore.h>
7#include <xrpl/protocol/STObject.h>
8
9namespace ripple {
10
13 ReadView const& view,
14 AccountID const& ammAccountID,
15 Issue const& issue1,
16 Issue const& issue2,
17 FreezeHandling freezeHandling,
18 beast::Journal const j)
19{
20 auto const assetInBalance =
21 accountHolds(view, ammAccountID, issue1, freezeHandling, j);
22 auto const assetOutBalance =
23 accountHolds(view, ammAccountID, issue2, freezeHandling, j);
24 return std::make_pair(assetInBalance, assetOutBalance);
25}
26
27Expected<std::tuple<STAmount, STAmount, STAmount>, TER>
29 ReadView const& view,
30 SLE const& ammSle,
31 std::optional<Issue> const& optIssue1,
32 std::optional<Issue> const& optIssue2,
33 FreezeHandling freezeHandling,
34 beast::Journal const j)
35{
36 auto const issues = [&]() -> std::optional<std::pair<Issue, Issue>> {
37 auto const issue1 = ammSle[sfAsset].get<Issue>();
38 auto const issue2 = ammSle[sfAsset2].get<Issue>();
39 if (optIssue1 && optIssue2)
40 {
42 *optIssue1,
43 *optIssue2,
44 std::make_optional(std::make_pair(issue1, issue2))))
45 {
46 // This error can only be hit if the AMM is corrupted
47 // LCOV_EXCL_START
48 JLOG(j.debug()) << "ammHolds: Invalid optIssue1 or optIssue2 "
49 << *optIssue1 << " " << *optIssue2;
50 return std::nullopt;
51 // LCOV_EXCL_STOP
52 }
53 return std::make_optional(std::make_pair(*optIssue1, *optIssue2));
54 }
55 auto const singleIssue =
56 [&issue1, &issue2, &j](
57 Issue checkIssue,
58 char const* label) -> std::optional<std::pair<Issue, Issue>> {
59 if (checkIssue == issue1)
60 return std::make_optional(std::make_pair(issue1, issue2));
61 else if (checkIssue == issue2)
62 return std::make_optional(std::make_pair(issue2, issue1));
63 // Unreachable unless AMM corrupted.
64 // LCOV_EXCL_START
65 JLOG(j.debug())
66 << "ammHolds: Invalid " << label << " " << checkIssue;
67 return std::nullopt;
68 // LCOV_EXCL_STOP
69 };
70 if (optIssue1)
71 {
72 return singleIssue(*optIssue1, "optIssue1");
73 }
74 else if (optIssue2)
75 {
76 // Cannot have Amount2 without Amount.
77 return singleIssue(*optIssue2, "optIssue2"); // LCOV_EXCL_LINE
78 }
79 return std::make_optional(std::make_pair(issue1, issue2));
80 }();
81 if (!issues)
83 auto const [asset1, asset2] = ammPoolHolds(
84 view,
85 ammSle.getAccountID(sfAccount),
86 issues->first,
87 issues->second,
88 freezeHandling,
89 j);
90 return std::make_tuple(asset1, asset2, ammSle[sfLPTokenBalance]);
91}
92
93STAmount
95 ReadView const& view,
96 Currency const& cur1,
97 Currency const& cur2,
98 AccountID const& ammAccount,
99 AccountID const& lpAccount,
100 beast::Journal const j)
101{
102 // This function looks similar to `accountHolds`. However, it only checks if
103 // a LPToken holder has enough balance. On the other hand, `accountHolds`
104 // checks if the underlying assets of LPToken are frozen with the
105 // fixFrozenLPTokenTransfer amendment
106
107 auto const currency = ammLPTCurrency(cur1, cur2);
108 STAmount amount;
109
110 auto const sle = view.read(keylet::line(lpAccount, ammAccount, currency));
111 if (!sle)
112 {
113 amount.clear(Issue{currency, ammAccount});
114 JLOG(j.trace()) << "ammLPHolds: no SLE "
115 << " lpAccount=" << to_string(lpAccount)
116 << " amount=" << amount.getFullText();
117 }
118 else if (isFrozen(view, lpAccount, currency, ammAccount))
119 {
120 amount.clear(Issue{currency, ammAccount});
121 JLOG(j.trace()) << "ammLPHolds: frozen currency "
122 << " lpAccount=" << to_string(lpAccount)
123 << " amount=" << amount.getFullText();
124 }
125 else
126 {
127 amount = sle->getFieldAmount(sfBalance);
128 if (lpAccount > ammAccount)
129 {
130 // Put balance in account terms.
131 amount.negate();
132 }
133 amount.setIssuer(ammAccount);
134
135 JLOG(j.trace()) << "ammLPHolds:"
136 << " lpAccount=" << to_string(lpAccount)
137 << " amount=" << amount.getFullText();
138 }
139
140 return view.balanceHook(lpAccount, ammAccount, amount);
141}
142
143STAmount
145 ReadView const& view,
146 SLE const& ammSle,
147 AccountID const& lpAccount,
148 beast::Journal const j)
149{
150 return ammLPHolds(
151 view,
152 ammSle[sfAsset].get<Issue>().currency,
153 ammSle[sfAsset2].get<Issue>().currency,
154 ammSle[sfAccount],
155 lpAccount,
156 j);
157}
158
160getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account)
161{
162 using namespace std::chrono;
163 XRPL_ASSERT(
164 !view.rules().enabled(fixInnerObjTemplate) ||
165 ammSle.isFieldPresent(sfAuctionSlot),
166 "ripple::getTradingFee : auction present");
167 if (ammSle.isFieldPresent(sfAuctionSlot))
168 {
169 auto const& auctionSlot =
170 static_cast<STObject const&>(ammSle.peekAtField(sfAuctionSlot));
171 // Not expired
172 if (auto const expiration = auctionSlot[~sfExpiration];
173 duration_cast<seconds>(
175 .count() < expiration)
176 {
177 if (auctionSlot[~sfAccount] == account)
178 return auctionSlot[sfDiscountedFee];
179 if (auctionSlot.isFieldPresent(sfAuthAccounts))
180 {
181 for (auto const& acct :
182 auctionSlot.getFieldArray(sfAuthAccounts))
183 if (acct[~sfAccount] == account)
184 return auctionSlot[sfDiscountedFee];
185 }
186 }
187 }
188 return ammSle[sfTradingFee];
189}
190
191STAmount
193 ReadView const& view,
194 AccountID const& ammAccountID,
195 Issue const& issue)
196{
197 if (isXRP(issue))
198 {
199 if (auto const sle = view.read(keylet::account(ammAccountID)))
200 return (*sle)[sfBalance];
201 }
202 else if (auto const sle = view.read(
203 keylet::line(ammAccountID, issue.account, issue.currency));
204 sle &&
205 !isFrozen(view, ammAccountID, issue.currency, issue.account))
206 {
207 auto amount = (*sle)[sfBalance];
208 if (ammAccountID > issue.account)
209 amount.negate();
210 amount.setIssuer(issue.account);
211 return amount;
212 }
213
214 return STAmount{issue};
215}
216
217static TER
219 Sandbox& sb,
220 AccountID const& ammAccountID,
221 std::uint16_t maxTrustlinesToDelete,
223{
225 sb,
226 keylet::ownerDir(ammAccountID),
227 [&](LedgerEntryType nodeType,
228 uint256 const&,
230 // Skip AMM
231 if (nodeType == LedgerEntryType::ltAMM)
232 return {tesSUCCESS, SkipEntry::Yes};
233 // Should only have the trustlines
234 if (nodeType != LedgerEntryType::ltRIPPLE_STATE)
235 {
236 // LCOV_EXCL_START
237 JLOG(j.error())
238 << "deleteAMMTrustLines: deleting non-trustline "
239 << nodeType;
240 return {tecINTERNAL, SkipEntry::No};
241 // LCOV_EXCL_STOP
242 }
243
244 // Trustlines must have zero balance
245 if (sleItem->getFieldAmount(sfBalance) != beast::zero)
246 {
247 // LCOV_EXCL_START
248 JLOG(j.error())
249 << "deleteAMMTrustLines: deleting trustline with "
250 "non-zero balance.";
251 return {tecINTERNAL, SkipEntry::No};
252 // LCOV_EXCL_STOP
253 }
254
255 return {
256 deleteAMMTrustLine(sb, sleItem, ammAccountID, j),
258 },
259 j,
260 maxTrustlinesToDelete);
261}
262
263TER
265 Sandbox& sb,
266 Issue const& asset,
267 Issue const& asset2,
269{
270 auto ammSle = sb.peek(keylet::amm(asset, asset2));
271 if (!ammSle)
272 {
273 // LCOV_EXCL_START
274 JLOG(j.error()) << "deleteAMMAccount: AMM object does not exist "
275 << asset << " " << asset2;
276 return tecINTERNAL;
277 // LCOV_EXCL_STOP
278 }
279
280 auto const ammAccountID = (*ammSle)[sfAccount];
281 auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
282 if (!sleAMMRoot)
283 {
284 // LCOV_EXCL_START
285 JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist "
286 << to_string(ammAccountID);
287 return tecINTERNAL;
288 // LCOV_EXCL_STOP
289 }
290
291 if (auto const ter =
293 ter != tesSUCCESS)
294 return ter;
295
296 auto const ownerDirKeylet = keylet::ownerDir(ammAccountID);
297 if (!sb.dirRemove(
298 ownerDirKeylet, (*ammSle)[sfOwnerNode], ammSle->key(), false))
299 {
300 // LCOV_EXCL_START
301 JLOG(j.error()) << "deleteAMMAccount: failed to remove dir link";
302 return tecINTERNAL;
303 // LCOV_EXCL_STOP
304 }
305 if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet))
306 {
307 // LCOV_EXCL_START
308 JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of "
309 << toBase58(ammAccountID);
310 return tecINTERNAL;
311 // LCOV_EXCL_STOP
312 }
313
314 sb.erase(ammSle);
315 sb.erase(sleAMMRoot);
316
317 return tesSUCCESS;
318}
319
320void
322 ApplyView& view,
323 std::shared_ptr<SLE>& ammSle,
324 AccountID const& account,
325 Issue const& lptIssue,
326 std::uint16_t tfee)
327{
328 auto const& rules = view.rules();
329 // AMM creator gets the voting slot.
330 STArray voteSlots;
331 STObject voteEntry = STObject::makeInnerObject(sfVoteEntry);
332 if (tfee != 0)
333 voteEntry.setFieldU16(sfTradingFee, tfee);
334 voteEntry.setFieldU32(sfVoteWeight, VOTE_WEIGHT_SCALE_FACTOR);
335 voteEntry.setAccountID(sfAccount, account);
336 voteSlots.push_back(voteEntry);
337 ammSle->setFieldArray(sfVoteSlots, voteSlots);
338 // AMM creator gets the auction slot for free.
339 // AuctionSlot is created on AMMCreate and updated on AMMDeposit
340 // when AMM is in an empty state
341 if (rules.enabled(fixInnerObjTemplate) &&
342 !ammSle->isFieldPresent(sfAuctionSlot))
343 {
344 STObject auctionSlot = STObject::makeInnerObject(sfAuctionSlot);
345 ammSle->set(std::move(auctionSlot));
346 }
347 STObject& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
348 auctionSlot.setAccountID(sfAccount, account);
349 // current + sec in 24h
350 auto const expiration = std::chrono::duration_cast<std::chrono::seconds>(
352 .count() +
354 auctionSlot.setFieldU32(sfExpiration, expiration);
355 auctionSlot.setFieldAmount(sfPrice, STAmount{lptIssue, 0});
356 // Set the fee
357 if (tfee != 0)
358 ammSle->setFieldU16(sfTradingFee, tfee);
359 else if (ammSle->isFieldPresent(sfTradingFee))
360 ammSle->makeFieldAbsent(sfTradingFee); // LCOV_EXCL_LINE
361 if (auto const dfee = tfee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION)
362 auctionSlot.setFieldU16(sfDiscountedFee, dfee);
363 else if (auctionSlot.isFieldPresent(sfDiscountedFee))
364 auctionSlot.makeFieldAbsent(sfDiscountedFee); // LCOV_EXCL_LINE
365}
366
367Expected<bool, TER>
369 ReadView const& view,
370 Issue const& ammIssue,
371 AccountID const& lpAccount)
372{
373 // Liquidity Provider (LP) must have one LPToken trustline
374 std::uint8_t nLPTokenTrustLines = 0;
375 // There are at most two IOU trustlines. One or both could be to the LP
376 // if LP is the issuer, or a different account if LP is not an issuer.
377 // For instance, if AMM has two tokens USD and EUR and LP is not the issuer
378 // of the tokens then the trustlines are between AMM account and the issuer.
379 std::uint8_t nIOUTrustLines = 0;
380 // There is only one AMM object
381 bool hasAMM = false;
382 // AMM LP has at most three trustlines and only one AMM object must exist.
383 // If there are more than five objects then it's either an error or
384 // there are more than one LP. Ten pages should be sufficient to include
385 // five objects.
386 std::uint8_t limit = 10;
387 auto const root = keylet::ownerDir(ammIssue.account);
388 auto currentIndex = root;
389
390 // Iterate over AMM owner directory objects.
391 while (limit-- >= 1)
392 {
393 auto const ownerDir = view.read(currentIndex);
394 if (!ownerDir)
395 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
396 for (auto const& key : ownerDir->getFieldV256(sfIndexes))
397 {
398 auto const sle = view.read(keylet::child(key));
399 if (!sle)
400 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
401 // Only one AMM object
402 if (sle->getFieldU16(sfLedgerEntryType) == ltAMM)
403 {
404 if (hasAMM)
405 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
406 hasAMM = true;
407 continue;
408 }
409 if (sle->getFieldU16(sfLedgerEntryType) != ltRIPPLE_STATE)
410 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
411 auto const lowLimit = sle->getFieldAmount(sfLowLimit);
412 auto const highLimit = sle->getFieldAmount(sfHighLimit);
413 auto const isLPTrustline = lowLimit.getIssuer() == lpAccount ||
414 highLimit.getIssuer() == lpAccount;
415 auto const isLPTokenTrustline =
416 lowLimit.issue() == ammIssue || highLimit.issue() == ammIssue;
417
418 // Liquidity Provider trustline
419 if (isLPTrustline)
420 {
421 // LPToken trustline
422 if (isLPTokenTrustline)
423 {
424 if (++nLPTokenTrustLines > 1)
425 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
426 }
427 else if (++nIOUTrustLines > 2)
428 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
429 }
430 // Another Liquidity Provider LPToken trustline
431 else if (isLPTokenTrustline)
432 return false;
433 else if (++nIOUTrustLines > 2)
434 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
435 }
436 auto const uNodeNext = ownerDir->getFieldU64(sfIndexNext);
437 if (uNodeNext == 0)
438 {
439 if (nLPTokenTrustLines != 1 || nIOUTrustLines == 0 ||
440 nIOUTrustLines > 2)
441 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
442 return true;
443 }
444 currentIndex = keylet::page(root, uNodeNext);
445 }
446 return Unexpected<TER>(tecINTERNAL); // LCOV_EXCL_LINE
447}
448
449Expected<bool, TER>
451 Sandbox& sb,
452 STAmount const& lpTokens,
453 std::shared_ptr<SLE>& ammSle,
454 AccountID const& account)
455{
456 if (auto const res = isOnlyLiquidityProvider(sb, lpTokens.issue(), account);
457 !res)
458 return Unexpected<TER>(res.error());
459 else if (res.value())
460 {
462 lpTokens,
463 ammSle->getFieldAmount(sfLPTokenBalance),
464 Number{1, -3}))
465 {
466 ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
467 sb.update(ammSle);
468 }
469 else
470 {
472 }
473 }
474 return true;
475}
476
477} // namespace ripple
A generic endpoint for log messages.
Definition Journal.h:41
Stream error() const
Definition Journal.h:327
Stream debug() const
Definition Journal.h:309
Stream trace() const
Severity stream access functions.
Definition Journal.h:303
Writeable view to a ledger, for applying a transaction.
Definition ApplyView.h:124
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.
A currency issued by an account.
Definition Issue.h:14
AccountID account
Definition Issue.h:17
Currency currency
Definition Issue.h:16
A view into a ledger.
Definition ReadView.h:32
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual STAmount balanceHook(AccountID const &account, AccountID const &issuer, STAmount const &amount) const
Definition ReadView.h:159
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:111
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:569
Issue const & issue() const
Definition STAmount.h:477
std::string getFullText() const override
Definition STAmount.cpp:654
void push_back(STObject const &object)
Definition STArray.h:193
AccountID getAccountID(SField const &field) const
Definition STObject.cpp:638
void makeFieldAbsent(SField const &field)
Definition STObject.cpp:551
void setFieldU16(SField const &field, std::uint16_t)
Definition STObject.cpp:732
void setFieldAmount(SField const &field, STAmount const &)
Definition STObject.cpp:792
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:465
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:76
void setAccountID(SField const &field, AccountID const &)
Definition STObject.cpp:774
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:738
STBase const & peekAtField(SField const &field) const
Definition STObject.cpp:410
Discardable, editable view to a ledger.
Definition Sandbox.h:16
void erase(std::shared_ptr< SLE > const &sle) override
Remove a peeked SLE.
void update(std::shared_ptr< SLE > const &sle) override
Indicate changes to a peeked SLE.
bool exists(Keylet const &k) const override
Determine if a state item exists.
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
T is_same_v
T make_optional(T... args)
T make_pair(T... args)
T make_tuple(T... args)
Keylet child(uint256 const &key) noexcept
Any item that can be in an owner dir.
Definition Indexes.cpp:171
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition Indexes.cpp:427
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:225
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:361
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition Indexes.cpp:355
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:95
FreezeHandling
Controls the treatment of frozen account balances.
Definition View.h:58
std::uint32_t constexpr TOTAL_TIME_SLOT_SECS
Definition AMMCore.h:15
bool isXRP(AccountID const &c)
Definition AccountID.h:71
Currency ammLPTCurrency(Currency const &cur1, Currency const &cur2)
Calculate Liquidity Provider Token (LPT) Currency.
Definition AMMCore.cpp:24
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
Definition AMMUtils.cpp:160
TER deleteAMMAccount(Sandbox &view, Issue const &asset, Issue const &asset2, beast::Journal j)
Delete trustlines to AMM.
Definition AMMUtils.cpp:264
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.
Definition AMMUtils.cpp:368
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.
Expected< bool, TER > verifyAndAdjustLPTokenBalance(Sandbox &sb, STAmount const &lpTokens, std::shared_ptr< SLE > &ammSle, AccountID const &account)
Due to rounding, the LPTokenBalance of the last LP might not match the LP's trustline balance.
Definition AMMUtils.cpp:450
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition View.cpp:228
void initializeFeeAuctionVote(ApplyView &view, std::shared_ptr< SLE > &ammSle, AccountID const &account, Issue const &lptIssue, std::uint16_t tfee)
Initialize Auction and Voting slots and set the trading/discounted fee.
Definition AMMUtils.cpp:321
STAmount ammLPHolds(ReadView const &view, Currency const &cur1, Currency const &cur2, AccountID const &ammAccount, AccountID const &lpAccount, beast::Journal const j)
Get the balance of LP tokens.
Definition AMMUtils.cpp:94
std::pair< STAmount, STAmount > ammPoolHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue1, Issue const &issue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool balances.
Definition AMMUtils.cpp:12
static TER deleteAMMTrustLines(Sandbox &sb, AccountID const &ammAccountID, std::uint16_t maxTrustlinesToDelete, beast::Journal j)
Definition AMMUtils.cpp:218
@ tecINTERNAL
Definition TER.h:292
@ tecAMM_INVALID_TOKENS
Definition TER.h:313
std::uint32_t constexpr VOTE_WEIGHT_SCALE_FACTOR
Definition AMMCore.h:26
@ tesSUCCESS
Definition TER.h:226
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue > > const &pair=std::nullopt)
Definition AMMCore.cpp:61
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:368
Expected< std::tuple< STAmount, STAmount, STAmount >, TER > ammHolds(ReadView const &view, SLE const &ammSle, std::optional< Issue > const &optIssue1, std::optional< Issue > const &optIssue2, FreezeHandling freezeHandling, beast::Journal const j)
Get AMM pool and LP token balances.
Definition AMMUtils.cpp:28
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
LedgerEntryType
Identifiers for on-ledger objects.
STAmount ammAccountHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue)
Returns total amount held by AMM for the given token.
Definition AMMUtils.cpp:192
Number root(Number f, unsigned d)
Definition Number.cpp:617
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition View.cpp:2787
std::uint16_t constexpr maxDeletableAMMTrustLines
The maximum number of trustlines to delete as part of AMM account deletion cleanup.
Definition Protocol.h:131
TERSubset< CanCvtToTER > TER
Definition TER.h:630
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:110
std::uint32_t constexpr AUCTION_SLOT_DISCOUNTED_FEE_FRACTION
Definition AMMCore.h:19
NetClock::time_point parentCloseTime
T time_since_epoch(T... args)