rippled
AMMInfo.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/ledger/LedgerMaster.h>
20 #include <ripple/app/misc/AMMUtils.h>
21 #include <ripple/json/json_value.h>
22 #include <ripple/ledger/ReadView.h>
23 #include <ripple/net/RPCErr.h>
24 #include <ripple/protocol/AMMCore.h>
25 #include <ripple/protocol/Issue.h>
26 #include <ripple/rpc/Context.h>
27 #include <ripple/rpc/impl/RPCHelpers.h>
28 #include <grpcpp/support/status.h>
29 
30 namespace ripple {
31 
34 {
35  std::string strIdent(v.asString());
36  AccountID accountID;
37 
38  if (auto jv = RPC::accountFromString(accountID, strIdent))
39  {
40  for (auto it = jv.begin(); it != jv.end(); ++it)
41  result[it.memberName()] = (*it);
42 
43  return std::nullopt;
44  }
45  return std::optional<AccountID>(accountID);
46 }
47 
48 Expected<Issue, error_code_i>
50 {
51  try
52  {
53  return issueFromJson(v);
54  }
55  catch (std::runtime_error const& ex)
56  {
57  JLOG(j.debug()) << "getIssue " << ex.what();
58  }
60 }
61 
64 {
65  // 2000-01-01 00:00:00 UTC is 946684800s from 1970-01-01 00:00:00 UTC
66  using namespace std::chrono;
67  return date::format(
68  "%Y-%Om-%dT%H:%M:%OS%z",
69  date::sys_time<system_clock::duration>(
70  system_clock::time_point{tp.time_since_epoch() + epoch_offset}));
71 }
72 
75 {
76  auto const& params(context.params);
77  Json::Value result;
78 
80  result = RPC::lookupLedger(ledger, context);
81  if (!ledger)
82  return result;
83 
84  struct ValuesFromContextParams
85  {
86  std::optional<AccountID> accountID;
87  Issue issue1;
88  Issue issue2;
90  };
91 
92  auto getValuesFromContextParams =
94  std::optional<AccountID> accountID;
95  std::optional<Issue> issue1;
96  std::optional<Issue> issue2;
98 
99  if ((params.isMember(jss::asset) != params.isMember(jss::asset2)) ||
100  (params.isMember(jss::asset) == params.isMember(jss::amm_account)))
102 
103  if (params.isMember(jss::asset))
104  {
105  if (auto const i = getIssue(params[jss::asset], context.j))
106  issue1 = *i;
107  else
108  return Unexpected(i.error());
109  }
110 
111  if (params.isMember(jss::asset2))
112  {
113  if (auto const i = getIssue(params[jss::asset2], context.j))
114  issue2 = *i;
115  else
116  return Unexpected(i.error());
117  }
118 
119  if (params.isMember(jss::amm_account))
120  {
121  auto const id = getAccount(params[jss::amm_account], result);
122  if (!id)
124  auto const sle = ledger->read(keylet::account(*id));
125  if (!sle)
127  ammID = sle->getFieldH256(sfAMMID);
128  }
129 
130  assert(
131  (issue1.has_value() == issue2.has_value()) &&
132  (issue1.has_value() != ammID.has_value()));
133 
134  if (params.isMember(jss::account))
135  {
136  accountID = getAccount(params[jss::account], result);
137  if (!accountID || !ledger->read(keylet::account(*accountID)))
139  }
140 
141  auto const ammKeylet = [&]() {
142  if (issue1 && issue2)
143  return keylet::amm(*issue1, *issue2);
144  assert(ammID);
145  return keylet::amm(*ammID);
146  }();
147  auto const amm = ledger->read(ammKeylet);
148  if (!amm)
150  if (!issue1 && !issue2)
151  {
152  issue1 = (*amm)[sfAsset];
153  issue2 = (*amm)[sfAsset2];
154  }
155 
156  return ValuesFromContextParams{
157  accountID, *issue1, *issue2, std::move(amm)};
158  };
159 
160  auto const r = getValuesFromContextParams();
161  if (!r)
162  {
163  RPC::inject_error(r.error(), result);
164  return result;
165  }
166 
167  auto const& [accountID, issue1, issue2, amm] = *r;
168 
169  auto const ammAccountID = amm->getAccountID(sfAccount);
170 
171  // provide funds if frozen, specify asset_frozen flag
172  auto const [asset1Balance, asset2Balance] = ammPoolHolds(
173  *ledger,
174  ammAccountID,
175  issue1,
176  issue2,
177  FreezeHandling::fhIGNORE_FREEZE,
178  context.j);
179  auto const lptAMMBalance = accountID
180  ? ammLPHolds(*ledger, *amm, *accountID, context.j)
181  : (*amm)[sfLPTokenBalance];
182 
183  Json::Value ammResult;
184  asset1Balance.setJson(ammResult[jss::amount]);
185  asset2Balance.setJson(ammResult[jss::amount2]);
186  lptAMMBalance.setJson(ammResult[jss::lp_token]);
187  ammResult[jss::trading_fee] = (*amm)[sfTradingFee];
188  ammResult[jss::account] = to_string(ammAccountID);
189  Json::Value voteSlots(Json::arrayValue);
190  if (amm->isFieldPresent(sfVoteSlots))
191  {
192  for (auto const& voteEntry : amm->getFieldArray(sfVoteSlots))
193  {
194  Json::Value vote;
195  vote[jss::account] = to_string(voteEntry.getAccountID(sfAccount));
196  vote[jss::trading_fee] = voteEntry[sfTradingFee];
197  vote[jss::vote_weight] = voteEntry[sfVoteWeight];
198  voteSlots.append(std::move(vote));
199  }
200  }
201  if (voteSlots.size() > 0)
202  ammResult[jss::vote_slots] = std::move(voteSlots);
203  if (amm->isFieldPresent(sfAuctionSlot))
204  {
205  auto const& auctionSlot =
206  static_cast<STObject const&>(amm->peekAtField(sfAuctionSlot));
207  if (auctionSlot.isFieldPresent(sfAccount))
208  {
209  Json::Value auction;
210  auto const timeSlot = ammAuctionTimeSlot(
211  ledger->info().parentCloseTime.time_since_epoch().count(),
212  auctionSlot);
213  auction[jss::time_interval] =
214  timeSlot ? *timeSlot : AUCTION_SLOT_TIME_INTERVALS;
215  auctionSlot[sfPrice].setJson(auction[jss::price]);
216  auction[jss::discounted_fee] = auctionSlot[sfDiscountedFee];
217  auction[jss::account] =
218  to_string(auctionSlot.getAccountID(sfAccount));
219  auction[jss::expiration] = to_iso8601(NetClock::time_point{
220  NetClock::duration{auctionSlot[sfExpiration]}});
221  if (auctionSlot.isFieldPresent(sfAuthAccounts))
222  {
223  Json::Value auth;
224  for (auto const& acct :
225  auctionSlot.getFieldArray(sfAuthAccounts))
226  {
227  Json::Value jv;
228  jv[jss::account] = to_string(acct.getAccountID(sfAccount));
229  auth.append(jv);
230  }
231  auction[jss::auth_accounts] = auth;
232  }
233  ammResult[jss::auction_slot] = std::move(auction);
234  }
235  }
236 
237  if (!isXRP(asset1Balance))
238  ammResult[jss::asset_frozen] =
239  isFrozen(*ledger, ammAccountID, issue1.currency, issue1.account);
240  if (!isXRP(asset2Balance))
241  ammResult[jss::asset2_frozen] =
242  isFrozen(*ledger, ammAccountID, issue2.currency, issue2.account);
243 
244  result[jss::amm] = std::move(ammResult);
245  if (!result.isMember(jss::ledger_index) &&
246  !result.isMember(jss::ledger_hash))
247  result[jss::ledger_current_index] = ledger->info().seq;
248  result[jss::validated] = context.ledgerMaster.isValidated(*ledger);
249 
250  return result;
251 }
252 
253 } // namespace ripple
ripple::ReadView::info
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
std::optional::has_value
T has_value(T... args)
ripple::RPC::JsonContext
Definition: Context.h:53
ripple::sfDiscountedFee
const SF_UINT16 sfDiscountedFee
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
ripple::sfAsset
const SF_ISSUE sfAsset
std::string
STL class.
std::shared_ptr
STL class.
ripple::rpcINVALID_PARAMS
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
ripple::AUCTION_SLOT_TIME_INTERVALS
constexpr std::uint16_t AUCTION_SLOT_TIME_INTERVALS
Definition: AMMCore.h:35
ripple::sfVoteSlots
const SField sfVoteSlots
Json::arrayValue
@ arrayValue
array value (ordered list)
Definition: json_value.h:42
ripple::sfLPTokenBalance
const SF_AMOUNT sfLPTokenBalance
ripple::keylet::amm
Keylet amm(Issue const &issue1, Issue const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:383
ripple::Unexpected
Unexpected(E(&)[N]) -> Unexpected< E const * >
std::chrono::duration
ripple::RPC::Context::ledgerMaster
LedgerMaster & ledgerMaster
Definition: Context.h:45
ripple::LedgerHeader::seq
LedgerIndex seq
Definition: LedgerHeader.h:41
ripple::sfPrice
const SF_AMOUNT sfPrice
ripple::sfVoteWeight
const SF_UINT32 sfVoteWeight
ripple::RPC::lookupLedger
Status lookupLedger(std::shared_ptr< ReadView const > &ledger, JsonContext &context, Json::Value &result)
Look up a ledger from a request and fill a Json::Result with the data representing a ledger.
Definition: RPCHelpers.cpp:623
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::to_iso8601
std::string to_iso8601(NetClock::time_point tp)
Definition: AMMInfo.cpp:63
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::RPC::Context::j
const beast::Journal j
Definition: Context.h:41
ripple::epoch_offset
constexpr static std::chrono::seconds epoch_offset
Clock for measuring the network time.
Definition: chrono.h:56
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
Json::Value::append
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:882
ripple::sfAsset2
const SF_ISSUE sfAsset2
ripple::Expected
Definition: Expected.h:132
ripple::getIssue
Issue getIssue(T const &amt)
Definition: AmountConversions.h:156
ripple::keylet::account
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:142
ripple::sfAuthAccounts
const SField sfAuthAccounts
ripple::rpcACT_NOT_FOUND
@ rpcACT_NOT_FOUND
Definition: ErrorCodes.h:70
std::chrono::time_point
Json::Value::size
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:706
std::runtime_error
STL class.
ripple::isXRP
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::issueFromJson
Issue issueFromJson(Json::Value const &v)
Definition: Issue.cpp:78
ripple::sfAMMID
const SF_UINT256 sfAMMID
ripple::ReadView::read
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
ripple::getAccount
std::optional< AccountID > getAccount(Json::Value const &v, Json::Value &result)
Definition: AMMInfo.cpp:33
ripple::doAMMInfo
Json::Value doAMMInfo(RPC::JsonContext &context)
Definition: AMMInfo.cpp:74
ripple::STObject
Definition: STObject.h:53
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::rpcACT_MALFORMED
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:90
ripple::LedgerMaster::isValidated
bool isValidated(ReadView const &ledger)
Definition: LedgerMaster.cpp:602
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
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
ripple::sfAccount
const SF_ACCOUNT sfAccount
ripple::rpcISSUE_MALFORMED
@ rpcISSUE_MALFORMED
Definition: ErrorCodes.h:146
ripple::sfAuctionSlot
const SField sfAuctionSlot
ripple::RPC::JsonContext::params
Json::Value params
Definition: Context.h:64
ripple::RPC::inject_error
void inject_error(error_code_i code, JsonValue &json)
Add or update the json update to reflect the error code.
Definition: ErrorCodes.h:214
ripple::isFrozen
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:203
ripple::RPC::accountFromString
Json::Value accountFromString(AccountID &result, std::string const &strIdent, bool bStrict)
Definition: RPCHelpers.cpp:87
std::runtime_error::what
T what(T... args)
ripple::ammAuctionTimeSlot
std::optional< std::uint8_t > ammAuctionTimeSlot(std::uint64_t current, STObject const &auctionSlot)
Get time slot of the auction slot.
Definition: AMMCore.cpp:107
Json::Value
Represents a JSON value.
Definition: json_value.h:145
Json::Value::asString
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:469
std::chrono