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