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&,
203  std::shared_ptr<SLE>& sleItem) -> TER {
204  // Should only have the trustlines
205  if (nodeType != LedgerEntryType::ltRIPPLE_STATE)
206  {
207  JLOG(j.error())
208  << "deleteAMMTrustLines: deleting non-trustline "
209  << nodeType;
210  return tecINTERNAL;
211  }
212 
213  // Trustlines must have zero balance
214  if (sleItem->getFieldAmount(sfBalance) != beast::zero)
215  {
216  JLOG(j.error())
217  << "deleteAMMTrustLines: deleting trustline with "
218  "non-zero balance.";
219  return tecINTERNAL;
220  }
221 
222  return deleteAMMTrustLine(sb, sleItem, ammAccountID, j);
223  },
224  j,
225  maxTrustlinesToDelete);
226 }
227 
228 TER
230  Sandbox& sb,
231  Issue const& asset,
232  Issue const& asset2,
233  beast::Journal j)
234 {
235  auto ammSle = sb.peek(keylet::amm(asset, asset2));
236  if (!ammSle)
237  {
238  JLOG(j.error()) << "deleteAMMAccount: AMM object does not exist "
239  << asset << " " << asset2;
240  return tecINTERNAL;
241  }
242 
243  auto const ammAccountID = (*ammSle)[sfAccount];
244  auto sleAMMRoot = sb.peek(keylet::account(ammAccountID));
245  if (!sleAMMRoot)
246  {
247  JLOG(j.error()) << "deleteAMMAccount: AMM account does not exist "
249  return tecINTERNAL;
250  }
251 
252  if (auto const ter =
254  ter != tesSUCCESS)
255  return ter;
256 
257  auto const ownerDirKeylet = keylet::ownerDir(ammAccountID);
258  if (sb.exists(ownerDirKeylet) && !sb.emptyDirDelete(ownerDirKeylet))
259  {
260  JLOG(j.error()) << "deleteAMMAccount: cannot delete root dir node of "
262  return tecINTERNAL;
263  }
264 
265  sb.erase(ammSle);
266  sb.erase(sleAMMRoot);
267 
268  return tesSUCCESS;
269 }
270 
271 void
273  ApplyView& view,
274  std::shared_ptr<SLE>& ammSle,
275  AccountID const& account,
276  Issue const& lptIssue,
277  std::uint16_t tfee)
278 {
279  // AMM creator gets the voting slot.
280  STArray voteSlots;
281  STObject voteEntry{sfVoteEntry};
282  if (tfee != 0)
283  voteEntry.setFieldU16(sfTradingFee, tfee);
284  voteEntry.setFieldU32(sfVoteWeight, VOTE_WEIGHT_SCALE_FACTOR);
285  voteEntry.setAccountID(sfAccount, account);
286  voteSlots.push_back(voteEntry);
287  ammSle->setFieldArray(sfVoteSlots, voteSlots);
288  // AMM creator gets the auction slot for free.
289  auto& auctionSlot = ammSle->peekFieldObject(sfAuctionSlot);
290  auctionSlot.setAccountID(sfAccount, account);
291  // current + sec in 24h
292  auto const expiration = std::chrono::duration_cast<std::chrono::seconds>(
294  .count() +
296  auctionSlot.setFieldU32(sfExpiration, expiration);
297  auctionSlot.setFieldAmount(sfPrice, STAmount{lptIssue, 0});
298  // Set the fee
299  if (tfee != 0)
300  ammSle->setFieldU16(sfTradingFee, tfee);
301  else if (ammSle->isFieldPresent(sfTradingFee))
302  ammSle->makeFieldAbsent(sfTradingFee);
303  if (auto const dfee = tfee / AUCTION_SLOT_DISCOUNTED_FEE_FRACTION)
304  auctionSlot.setFieldU16(sfDiscountedFee, dfee);
305  else if (auctionSlot.isFieldPresent(sfDiscountedFee))
306  auctionSlot.makeFieldAbsent(sfDiscountedFee);
307 }
308 
309 } // 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:304
ripple::STObject::setAccountID
void setAccountID(SField const &field, AccountID const &)
Definition: STObject.cpp:689
ripple::STAmount::negate
void negate()
Definition: STAmount.h:402
ripple::issues
bool issues(DebtDirection dir)
Definition: Steps.h:50
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:272
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::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:375
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:101
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:78
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::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::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:134
ripple::STObject::getAccountID
AccountID getAccountID(SField const &field) const
Definition: STObject.cpp:589
ripple::tecAMM_INVALID_TOKENS
@ tecAMM_INVALID_TOKENS
Definition: TER.h:302
ripple::TERSubset< CanCvtToTER >
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:580
ripple::STAmount
Definition: STAmount.h:45
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::tecINTERNAL
@ tecINTERNAL
Definition: TER.h:281
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:89
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:194
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
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::cleanupOnAccountDelete
TER cleanupOnAccountDelete(ApplyView &view, Keylet const &ownerDirKeylet, std::function< TER(LedgerEntryType, uint256 const &, std::shared_ptr< SLE > &)> deleter, beast::Journal j, std::optional< uint16_t > maxNodesToDelete)
Definition: View.cpp:1531
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:229
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::sfBalance
const SF_AMOUNT sfBalance
std::optional
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
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:226
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