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