rippled
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 #include <ripple/app/misc/AMMUtils.h>
20 #include <ripple/basics/Log.h>
21 #include <ripple/ledger/Sandbox.h>
22 #include <ripple/protocol/AMMCore.h>
23 #include <ripple/protocol/STAccount.h>
24 #include <ripple/protocol/STObject.h>
25 
26 namespace ripple {
27 
30  ReadView const& view,
31  AccountID const& ammAccountID,
32  Issue const& issue1,
33  Issue const& issue2,
34  FreezeHandling freezeHandling,
35  beast::Journal const j)
36 {
37  auto const assetInBalance =
38  accountHolds(view, ammAccountID, issue1, freezeHandling, j);
39  auto const assetOutBalance =
40  accountHolds(view, ammAccountID, issue2, freezeHandling, j);
41  return std::make_pair(assetInBalance, assetOutBalance);
42 }
43 
44 Expected<std::tuple<STAmount, STAmount, STAmount>, TER>
46  ReadView const& view,
47  SLE const& ammSle,
48  std::optional<Issue> const& optIssue1,
49  std::optional<Issue> const& optIssue2,
50  FreezeHandling freezeHandling,
51  beast::Journal const j)
52 {
53  auto const issues = [&]() -> std::optional<std::pair<Issue, Issue>> {
54  auto const issue1 = ammSle[sfAsset];
55  auto const issue2 = ammSle[sfAsset2];
56  if (optIssue1 && optIssue2)
57  {
59  *optIssue1,
60  *optIssue2,
61  std::make_optional(std::make_pair(issue1, issue2))))
62  {
63  JLOG(j.debug()) << "ammHolds: Invalid optIssue1 or optIssue2 "
64  << *optIssue1 << " " << *optIssue2;
65  return std::nullopt;
66  }
67  return std::make_optional(std::make_pair(*optIssue1, *optIssue2));
68  }
69  auto const singleIssue =
70  [&issue1, &issue2, &j](
71  Issue checkIssue,
72  const char* label) -> std::optional<std::pair<Issue, Issue>> {
73  if (checkIssue == issue1)
74  return std::make_optional(std::make_pair(issue1, issue2));
75  else if (checkIssue == issue2)
76  return std::make_optional(std::make_pair(issue2, issue1));
77  JLOG(j.debug())
78  << "ammHolds: Invalid " << label << " " << checkIssue;
79  return std::nullopt;
80  };
81  if (optIssue1)
82  {
83  return singleIssue(*optIssue1, "optIssue1");
84  }
85  else if (optIssue2)
86  {
87  return singleIssue(*optIssue2, "optIssue2");
88  }
89  return std::make_optional(std::make_pair(issue1, issue2));
90  }();
91  if (!issues)
93  auto const [asset1, asset2] = ammPoolHolds(
94  view,
95  ammSle.getAccountID(sfAccount),
96  issues->first,
97  issues->second,
98  freezeHandling,
99  j);
100  return std::make_tuple(asset1, asset2, ammSle[sfLPTokenBalance]);
101 }
102 
103 STAmount
105  ReadView const& view,
106  Currency const& cur1,
107  Currency const& cur2,
108  AccountID const& ammAccount,
109  AccountID const& lpAccount,
110  beast::Journal const j)
111 {
112  return accountHolds(
113  view,
114  lpAccount,
115  ammLPTCurrency(cur1, cur2),
116  ammAccount,
117  FreezeHandling::fhZERO_IF_FROZEN,
118  j);
119 }
120 
121 STAmount
123  ReadView const& view,
124  SLE const& ammSle,
125  AccountID const& lpAccount,
126  beast::Journal const j)
127 {
128  return ammLPHolds(
129  view,
130  ammSle[sfAsset].currency,
131  ammSle[sfAsset2].currency,
132  ammSle[sfAccount],
133  lpAccount,
134  j);
135 }
136 
138 getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account)
139 {
140  using namespace std::chrono;
141  if (ammSle.isFieldPresent(sfAuctionSlot))
142  {
143  auto const& auctionSlot =
144  static_cast<STObject const&>(ammSle.peekAtField(sfAuctionSlot));
145  // Not expired
146  if (auto const expiration = auctionSlot[~sfExpiration];
147  duration_cast<seconds>(
149  .count() < expiration)
150  {
151  if (auctionSlot[~sfAccount] == account)
152  return auctionSlot[sfDiscountedFee];
153  if (auctionSlot.isFieldPresent(sfAuthAccounts))
154  {
155  for (auto const& acct :
156  auctionSlot.getFieldArray(sfAuthAccounts))
157  if (acct[~sfAccount] == account)
158  return auctionSlot[sfDiscountedFee];
159  }
160  }
161  }
162  return ammSle[sfTradingFee];
163 }
164 
165 STAmount
167  ReadView const& view,
168  AccountID const& ammAccountID,
169  Issue const& issue)
170 {
171  if (isXRP(issue))
172  {
173  if (auto const sle = view.read(keylet::account(ammAccountID)))
174  return (*sle)[sfBalance];
175  }
176  else if (auto const sle = view.read(
177  keylet::line(ammAccountID, issue.account, issue.currency));
178  sle &&
179  !isFrozen(view, ammAccountID, issue.currency, issue.account))
180  {
181  auto amount = (*sle)[sfBalance];
182  if (ammAccountID > issue.account)
183  amount.negate();
184  amount.setIssuer(issue.account);
185  return amount;
186  }
187 
188  return STAmount{issue};
189 }
190 
191 static TER
193  Sandbox& sb,
194  AccountID const& ammAccountID,
195  std::uint16_t maxTrustlinesToDelete,
196  beast::Journal j)
197 {
198  return cleanupOnAccountDelete(
199  sb,
201  [&](LedgerEntryType nodeType,
202  uint256 const&,
204  // Skip AMM
205  if (nodeType == LedgerEntryType::ltAMM)
206  return {tesSUCCESS, SkipEntry::Yes};
207  // Should only have the trustlines
208  if (nodeType != LedgerEntryType::ltRIPPLE_STATE)
209  {
210  JLOG(j.error())
211  << "deleteAMMTrustLines: deleting non-trustline "
212  << nodeType;
213  return {tecINTERNAL, SkipEntry::No};
214  }
215 
216  // Trustlines must have zero balance
217  if (sleItem->getFieldAmount(sfBalance) != beast::zero)
218  {
219  JLOG(j.error())
220  << "deleteAMMTrustLines: deleting trustline with "
221  "non-zero balance.";
222  return {tecINTERNAL, SkipEntry::No};
223  }
224 
225  return {
226  deleteAMMTrustLine(sb, sleItem, ammAccountID, j),
227  SkipEntry::No};
228  },
229  j,
230  maxTrustlinesToDelete);
231 }
232 
233 TER
235  Sandbox& sb,
236  Issue const& asset,
237  Issue const& asset2,
238  beast::Journal j)
239 {
240  auto ammSle = sb.peek(keylet::amm(asset, asset2));
241  if (!ammSle)
242  {
243  JLOG(j.error()) << "deleteAMMAccount: AMM object does not exist "
244  << asset << " " << asset2;
245  return tecINTERNAL;
246  }
247 
248  auto const ammAccountID = (*ammSle)[sfAccount];
249  auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
250  if (!sleAMMRoot)
251  {
252  JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist "
254  return tecINTERNAL;
255  }
256 
257  if (auto const ter =
259  ter != tesSUCCESS)
260  return ter;
261 
262  auto const ownerDirKeylet = keylet::ownerDir(ammAccountID);
263  if (!sb.dirRemove(
264  ownerDirKeylet, (*ammSle)[sfOwnerNode], ammSle->key(), false))
265  {
266  JLOG(j.error()) << "deleteAMMAccount: failed to remove dir link";
267  return tecINTERNAL;
268  }
269  if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet))
270  {
271  JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of "
273  return tecINTERNAL;
274  }
275 
276  sb.erase(ammSle);
277  sb.erase(sleAMMRoot);
278 
279  return tesSUCCESS;
280 }
281 
282 void
284  ApplyView& view,
285  std::shared_ptr<SLE>& ammSle,
286  AccountID const& account,
287  Issue const& lptIssue,
288  std::uint16_t tfee)
289 {
290  // AMM creator gets the voting slot.
291  STArray voteSlots;
292  STObject voteEntry{sfVoteEntry};
293  if (tfee != 0)
294  voteEntry.setFieldU16(sfTradingFee, tfee);
295  voteEntry.setFieldU32(sfVoteWeight, VOTE_WEIGHT_SCALE_FACTOR);
296  voteEntry.setAccountID(sfAccount, account);
297  voteSlots.push_back(voteEntry);
298  ammSle->setFieldArray(sfVoteSlots, voteSlots);
299  // AMM creator gets the auction slot for free.
300  auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
301  auctionSlot.setAccountID(sfAccount, account);
302  // current + sec in 24h
303  auto const expiration = std::chrono::duration_cast<std::chrono::seconds>(
305  .count() +
307  auctionSlot.setFieldU32(sfExpiration, expiration);
308  auctionSlot.setFieldAmount(sfPrice, STAmount{lptIssue, 0});
309  // Set the fee
310  if (tfee != 0)
311  ammSle->setFieldU16(sfTradingFee, tfee);
312  else if (ammSle->isFieldPresent(sfTradingFee))
313  ammSle->makeFieldAbsent(sfTradingFee);
314  if (auto const dfee = tfee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION)
315  auctionSlot.setFieldU16(sfDiscountedFee, dfee);
316  else if (auctionSlot.isFieldPresent(sfDiscountedFee))
317  auctionSlot.makeFieldAbsent(sfDiscountedFee);
318 }
319 
320 } // namespace ripple
ripple::ReadView::info
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
ripple::STObject::peekAtField
const STBase & peekAtField(SField const &field) const
Definition: STObject.cpp:373
ripple::keylet::ownerDir
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:312
ripple::STObject::setAccountID
void setAccountID(SField const &field, AccountID const &)
Definition: STObject.cpp:689
ripple::STAmount::negate
void negate()
Definition: STAmount.h:405
ripple::issues
bool issues(DebtDirection dir)
Definition: Steps.h:51
ripple::initializeFeeAuctionVote
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:283
std::make_tuple
T make_tuple(T... args)
ripple::sfDiscountedFee
const SF_UINT16 sfDiscountedFee
ripple::STObject::makeFieldAbsent
void makeFieldAbsent(SField const &field)
Definition: STObject.cpp:514
ripple::STLedgerEntry
Definition: STLedgerEntry.h:30
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
ripple::sfAsset
const SF_ISSUE sfAsset
std::shared_ptr
STL class.
ripple::sfOwnerNode
const SF_UINT64 sfOwnerNode
ripple::STObject::setFieldU16
void setFieldU16(SField const &field, std::uint16_t)
Definition: STObject.cpp:653
ripple::VOTE_WEIGHT_SCALE_FACTOR
constexpr std::uint32_t VOTE_WEIGHT_SCALE_FACTOR
Definition: AMMCore.h:45
ripple::ammHolds
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:45
ripple::sfVoteSlots
const SField sfVoteSlots
std::pair
ripple::sfVoteEntry
const SField sfVoteEntry
ripple::sfLPTokenBalance
const SF_AMOUNT sfLPTokenBalance
ripple::AUCTION_SLOT_DISCOUNTED_FEE_FRACTION
constexpr std::uint32_t AUCTION_SLOT_DISCOUNTED_FEE_FRACTION
Definition: AMMCore.h:38
ripple::detail::ApplyViewBase::exists
bool exists(Keylet const &k) const override
Determine if a state item exists.
Definition: ApplyViewBase.cpp:58
ripple::accountHolds
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition: View.cpp:226
ripple::keylet::amm
Keylet amm(Issue const &issue1, Issue const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:383
std::make_optional
T make_optional(T... args)
ripple::Unexpected
Unexpected(E(&)[N]) -> Unexpected< E const * >
ripple::Issue::currency
Currency currency
Definition: Issue.h:38
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:104
ripple::STArray::push_back
void push_back(STObject const &object)
Definition: STArray.h:212
ripple::maxDeletableAMMTrustLines
constexpr std::uint16_t maxDeletableAMMTrustLines
The maximum number of trustlines to delete as part of AMM account deletion cleanup.
Definition: Protocol.h:110
ripple::deleteAMMTrustLine
TER deleteAMMTrustLine(ApplyView &view, std::shared_ptr< SLE > sleState, std::optional< AccountID > const &ammAccountID, beast::Journal j)
Delete trustline to AMM.
Definition: View.cpp:1607
ripple::FreezeHandling
FreezeHandling
Controls the treatment of frozen account balances.
Definition: View.h:79
ripple::sfPrice
const SF_AMOUNT sfPrice
ripple::sfVoteWeight
const SF_UINT32 sfVoteWeight
ripple::ammAccountID
AccountID ammAccountID(std::uint16_t prefix, uint256 const &parentHash, uint256 const &ammID)
Calculate AMM account ID.
Definition: AMMCore.cpp:30
ripple::sfTradingFee
const SF_UINT16 sfTradingFee
ripple::ApplyView
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:134
ripple::ammPoolHolds
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:29
ripple::sfExpiration
const SF_UINT32 sfExpiration
ripple::ApplyView::dirRemove
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
Definition: ApplyView.cpp:189
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:82
std::chrono::time_point::time_since_epoch
T time_since_epoch(T... args)
ripple::LedgerHeader::parentCloseTime
NetClock::time_point parentCloseTime
Definition: LedgerHeader.h:42
ripple::SkipEntry::Yes
@ Yes
ripple::sfAsset2
const SF_ISSUE sfAsset2
ripple::STObject::setFieldArray
void setFieldArray(SField const &field, STArray const &v)
Definition: STObject.cpp:725
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:142
ripple::STObject::getAccountID
AccountID getAccountID(SField const &field) const
Definition: STObject.cpp:589
ripple::tecAMM_INVALID_TOKENS
@ tecAMM_INVALID_TOKENS
Definition: TER.h:312
ripple::sfAuthAccounts
const SField sfAuthAccounts
ripple::getTradingFee
std::uint16_t getTradingFee(ReadView const &view, SLE const &ammSle, AccountID const &account)
Get AMM trading fee for the given account.
Definition: AMMUtils.cpp:138
ripple::STArray
Definition: STArray.h:28
ripple::Sandbox
Discardable, editable view to a ledger.
Definition: Sandbox.h:34
ripple::TER
TERSubset< CanCvtToTER > TER
Definition: TER.h:608
ripple::STAmount
Definition: STAmount.h:46
beast::Journal::error
Stream error() const
Definition: Journal.h:332
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:291
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint16_t
ripple::invalidAMMAssetPair
NotTEC invalidAMMAssetPair(Issue const &issue1, Issue const &issue2, std::optional< std::pair< Issue, Issue >> const &pair=std::nullopt)
Definition: AMMCore.cpp:79
ripple::keylet::line
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:202
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::cleanupOnAccountDelete
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, EntryDeleter const &deleter, beast::Journal j, std::optional< uint16_t > maxNodesToDelete)
Definition: View.cpp:1531
ripple::detail::ApplyViewBase::erase
void erase(std::shared_ptr< SLE > const &sle) override
Remove a peeked SLE.
Definition: ApplyViewBase.cpp:134
ripple::STObject
Definition: STObject.h:52
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:54
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::STObject::peekFieldObject
STObject & peekFieldObject(SField const &field)
Definition: STObject.cpp:439
ripple::TOTAL_TIME_SLOT_SECS
constexpr std::uint32_t TOTAL_TIME_SLOT_SECS
Definition: AMMCore.h:34
ripple::LedgerEntryType
LedgerEntryType
Identifiers for on-ledger objects.
Definition: LedgerFormats.h:53
ripple::ApplyView::emptyDirDelete
bool emptyDirDelete(Keylet const &directory)
Remove the specified directory, if it is empty.
Definition: ApplyView.cpp:125
ripple::deleteAMMTrustLines
static TER deleteAMMTrustLines(Sandbox &sb, AccountID const &ammAccountID, std::uint16_t maxTrustlinesToDelete, beast::Journal j)
Definition: AMMUtils.cpp:192
ripple::STObject::isFieldPresent
bool isFieldPresent(SField const &field) const
Definition: STObject.cpp:428
ripple::deleteAMMAccount
TER deleteAMMAccount(Sandbox &view, Issue const &asset, Issue const &asset2, beast::Journal j)
Delete trustlines to AMM.
Definition: AMMUtils.cpp:234
ripple::ammLPHolds
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:104
ripple::SkipEntry::No
@ No
ripple::sfBalance
const SF_AMOUNT sfBalance
std::optional
beast::Journal::debug
Stream debug() const
Definition: Journal.h:314
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
std::make_pair
T make_pair(T... args)
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::detail::ApplyViewBase::peek
std::shared_ptr< SLE > peek(Keylet const &k) override
Prepare to modify the SLE associated with key.
Definition: ApplyViewBase.cpp:128
ripple::ammLPTCurrency
Currency ammLPTCurrency(Currency const &cur1, Currency const &cur2)
Calculate Liquidity Provider Token (LPT) Currency.
Definition: AMMCore.cpp:42
ripple::sfAuctionSlot
const SField sfAuctionSlot
ripple::ammAccountHolds
STAmount ammAccountHolds(ReadView const &view, AccountID const &ammAccountID, Issue const &issue)
Returns total amount held by AMM for the given token.
Definition: AMMUtils.cpp:166
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:236
ripple::isFrozen
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:203
ripple::STObject::getFieldAmount
STAmount const & getFieldAmount(SField const &field) const
Definition: STObject.cpp:603
ripple::Issue::account
AccountID account
Definition: Issue.h:39
std::chrono