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  assert(
142  !view.rules().enabled(fixInnerObjTemplate) ||
143  ammSle.isFieldPresent(sfAuctionSlot));
144  if (ammSle.isFieldPresent(sfAuctionSlot))
145  {
146  auto const& auctionSlot =
147  static_cast<STObject const&>(ammSle.peekAtField(sfAuctionSlot));
148  // Not expired
149  if (auto const expiration = auctionSlot[~sfExpiration];
150  duration_cast<seconds>(
152  .count() < expiration)
153  {
154  if (auctionSlot[~sfAccount] == account)
155  return auctionSlot[sfDiscountedFee];
156  if (auctionSlot.isFieldPresent(sfAuthAccounts))
157  {
158  for (auto const& acct :
159  auctionSlot.getFieldArray(sfAuthAccounts))
160  if (acct[~sfAccount] == account)
161  return auctionSlot[sfDiscountedFee];
162  }
163  }
164  }
165  return ammSle[sfTradingFee];
166 }
167 
168 STAmount
170  ReadView const& view,
171  AccountID const& ammAccountID,
172  Issue const& issue)
173 {
174  if (isXRP(issue))
175  {
176  if (auto const sle = view.read(keylet::account(ammAccountID)))
177  return (*sle)[sfBalance];
178  }
179  else if (auto const sle = view.read(
180  keylet::line(ammAccountID, issue.account, issue.currency));
181  sle &&
182  !isFrozen(view, ammAccountID, issue.currency, issue.account))
183  {
184  auto amount = (*sle)[sfBalance];
185  if (ammAccountID > issue.account)
186  amount.negate();
187  amount.setIssuer(issue.account);
188  return amount;
189  }
190 
191  return STAmount{issue};
192 }
193 
194 static TER
196  Sandbox& sb,
197  AccountID const& ammAccountID,
198  std::uint16_t maxTrustlinesToDelete,
199  beast::Journal j)
200 {
201  return cleanupOnAccountDelete(
202  sb,
204  [&](LedgerEntryType nodeType,
205  uint256 const&,
207  // Skip AMM
208  if (nodeType == LedgerEntryType::ltAMM)
209  return {tesSUCCESS, SkipEntry::Yes};
210  // Should only have the trustlines
211  if (nodeType != LedgerEntryType::ltRIPPLE_STATE)
212  {
213  JLOG(j.error())
214  << "deleteAMMTrustLines: deleting non-trustline "
215  << nodeType;
216  return {tecINTERNAL, SkipEntry::No};
217  }
218 
219  // Trustlines must have zero balance
220  if (sleItem->getFieldAmount(sfBalance) != beast::zero)
221  {
222  JLOG(j.error())
223  << "deleteAMMTrustLines: deleting trustline with "
224  "non-zero balance.";
225  return {tecINTERNAL, SkipEntry::No};
226  }
227 
228  return {
229  deleteAMMTrustLine(sb, sleItem, ammAccountID, j),
230  SkipEntry::No};
231  },
232  j,
233  maxTrustlinesToDelete);
234 }
235 
236 TER
238  Sandbox& sb,
239  Issue const& asset,
240  Issue const& asset2,
241  beast::Journal j)
242 {
243  auto ammSle = sb.peek(keylet::amm(asset, asset2));
244  if (!ammSle)
245  {
246  JLOG(j.error()) << "deleteAMMAccount: AMM object does not exist "
247  << asset << " " << asset2;
248  return tecINTERNAL;
249  }
250 
251  auto const ammAccountID = (*ammSle)[sfAccount];
252  auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
253  if (!sleAMMRoot)
254  {
255  JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist "
257  return tecINTERNAL;
258  }
259 
260  if (auto const ter =
262  ter != tesSUCCESS)
263  return ter;
264 
265  auto const ownerDirKeylet = keylet::ownerDir(ammAccountID);
266  if (!sb.dirRemove(
267  ownerDirKeylet, (*ammSle)[sfOwnerNode], ammSle->key(), false))
268  {
269  JLOG(j.error()) << "deleteAMMAccount: failed to remove dir link";
270  return tecINTERNAL;
271  }
272  if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet))
273  {
274  JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of "
276  return tecINTERNAL;
277  }
278 
279  sb.erase(ammSle);
280  sb.erase(sleAMMRoot);
281 
282  return tesSUCCESS;
283 }
284 
285 void
287  ApplyView& view,
288  std::shared_ptr<SLE>& ammSle,
289  AccountID const& account,
290  Issue const& lptIssue,
291  std::uint16_t tfee)
292 {
293  auto const& rules = view.rules();
294  // AMM creator gets the voting slot.
295  STArray voteSlots;
296  STObject voteEntry = STObject::makeInnerObject(sfVoteEntry, rules);
297  if (tfee != 0)
298  voteEntry.setFieldU16(sfTradingFee, tfee);
300  voteEntry.setAccountID(sfAccount, account);
301  voteSlots.push_back(voteEntry);
302  ammSle->setFieldArray(sfVoteSlots, voteSlots);
303  // AMM creator gets the auction slot for free.
304  // AuctionSlot is created on AMMCreate and updated on AMMDeposit
305  // when AMM is in an empty state
306  if (rules.enabled(fixInnerObjTemplate) &&
307  !ammSle->isFieldPresent(sfAuctionSlot))
308  {
309  STObject auctionSlot = STObject::makeInnerObject(sfAuctionSlot, rules);
310  ammSle->set(std::move(auctionSlot));
311  }
312  STObject& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
313  auctionSlot.setAccountID(sfAccount, account);
314  // current + sec in 24h
315  auto const expiration = std::chrono::duration_cast<std::chrono::seconds>(
317  .count() +
319  auctionSlot.setFieldU32(sfExpiration, expiration);
320  auctionSlot.setFieldAmount(sfPrice, STAmount{lptIssue, 0});
321  // Set the fee
322  if (tfee != 0)
323  ammSle->setFieldU16(sfTradingFee, tfee);
324  else if (ammSle->isFieldPresent(sfTradingFee))
325  ammSle->makeFieldAbsent(sfTradingFee);
326  if (auto const dfee = tfee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION)
327  auctionSlot.setFieldU16(sfDiscountedFee, dfee);
328  else if (auctionSlot.isFieldPresent(sfDiscountedFee))
329  auctionSlot.makeFieldAbsent(sfDiscountedFee);
330 }
331 
332 } // 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:388
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:710
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:286
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:529
ripple::STLedgerEntry
Definition: STLedgerEntry.h:30
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
ripple::sfAsset
const SF_ISSUE sfAsset
ripple::Rules::enabled
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition: Rules.cpp:94
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:674
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:746
ripple::STObject::makeInnerObject
static STObject makeInnerObject(SField const &name, Rules const &rules)
Definition: STObject.cpp:63
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:142
ripple::STObject::setFieldAmount
void setFieldAmount(SField const &field, STAmount const &)
Definition: STObject.cpp:728
ripple::STObject::getAccountID
AccountID getAccountID(SField const &field) const
Definition: STObject.cpp:604
ripple::tecAMM_INVALID_TOKENS
@ tecAMM_INVALID_TOKENS
Definition: TER.h:311
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:607
ripple::STAmount
Definition: STAmount.h:46
beast::Journal::error
Stream error() const
Definition: Journal.h:332
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:290
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:54
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:454
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::ReadView::rules
virtual Rules const & rules() const =0
Returns the tx processing rules.
ripple::deleteAMMTrustLines
static TER deleteAMMTrustLines(Sandbox &sb, AccountID const &ammAccountID, std::uint16_t maxTrustlinesToDelete, beast::Journal j)
Definition: AMMUtils.cpp:195
ripple::STObject::isFieldPresent
bool isFieldPresent(SField const &field) const
Definition: STObject.cpp:443
ripple::deleteAMMAccount
TER deleteAMMAccount(Sandbox &view, Issue const &asset, Issue const &asset2, beast::Journal j)
Delete trustlines to AMM.
Definition: AMMUtils.cpp:237
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::fixInnerObjTemplate
const uint256 fixInnerObjTemplate
ripple::STObject::set
void set(const SOTemplate &)
Definition: STObject.cpp:115
ripple::STObject::setFieldU32
void setFieldU32(SField const &field, std::uint32_t)
Definition: STObject.cpp:680
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:169
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:235
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:618
ripple::Issue::account
AccountID account
Definition: Issue.h:39
std::chrono