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/misc/AMMUtils.h>
20 #include <ripple/json/json_value.h>
21 #include <ripple/ledger/ReadView.h>
22 #include <ripple/net/RPCErr.h>
23 #include <ripple/protocol/AMMCore.h>
24 #include <ripple/protocol/Issue.h>
25 #include <ripple/rpc/Context.h>
26 #include <ripple/rpc/impl/RPCHelpers.h>
27 #include <grpcpp/support/status.h>
28 
29 namespace ripple {
30 
33 {
34  std::string strIdent(v.asString());
35  AccountID accountID;
36 
37  if (auto jv = RPC::accountFromString(accountID, strIdent))
38  {
39  for (auto it = jv.begin(); it != jv.end(); ++it)
40  result[it.memberName()] = (*it);
41 
42  return std::nullopt;
43  }
44  return std::optional<AccountID>(accountID);
45 }
46 
47 Expected<Issue, error_code_i>
49 {
50  try
51  {
52  return issueFromJson(v);
53  }
54  catch (std::runtime_error const& ex)
55  {
56  JLOG(j.debug()) << "getIssue " << ex.what();
57  }
59 }
60 
63 {
64  // 2000-01-01 00:00:00 UTC is 946684800s from 1970-01-01 00:00:00 UTC
65  using namespace std::chrono;
66  return date::format(
67  "%Y-%Om-%dT%H:%M:%OS%z",
68  date::sys_time<system_clock::duration>(
69  system_clock::time_point{tp.time_since_epoch() + 946684800s}));
70 }
71 
74 {
75  auto const& params(context.params);
76  Json::Value result;
77 
79  result = RPC::lookupLedger(ledger, context);
80  if (!ledger)
81  return result;
82 
83  struct ValuesFromContextParams
84  {
85  std::optional<AccountID> accountID;
86  Issue issue1;
87  Issue issue2;
89  };
90 
91  auto getValuesFromContextParams =
93  std::optional<AccountID> accountID;
94  std::optional<Issue> issue1;
95  std::optional<Issue> issue2;
97 
98  if ((params.isMember(jss::asset) != params.isMember(jss::asset2)) ||
99  (params.isMember(jss::asset) == params.isMember(jss::amm_account)))
101 
102  if (params.isMember(jss::asset))
103  {
104  if (auto const i = getIssue(params[jss::asset], context.j))
105  issue1 = *i;
106  else
107  return Unexpected(i.error());
108  }
109 
110  if (params.isMember(jss::asset2))
111  {
112  if (auto const i = getIssue(params[jss::asset2], context.j))
113  issue2 = *i;
114  else
115  return Unexpected(i.error());
116  }
117 
118  if (params.isMember(jss::amm_account))
119  {
120  auto const id = getAccount(params[jss::amm_account], result);
121  if (!id)
123  auto const sle = ledger->read(keylet::account(*id));
124  if (!sle)
126  ammID = sle->getFieldH256(sfAMMID);
127  }
128 
129  assert(
130  (issue1.has_value() == issue2.has_value()) &&
131  (issue1.has_value() != ammID.has_value()));
132 
133  if (params.isMember(jss::account))
134  {
135  accountID = getAccount(params[jss::account], result);
136  if (!accountID || !ledger->read(keylet::account(*accountID)))
138  }
139 
140  auto const ammKeylet = [&]() {
141  if (issue1 && issue2)
142  return keylet::amm(*issue1, *issue2);
143  assert(ammID);
144  return keylet::amm(*ammID);
145  }();
146  auto const amm = ledger->read(ammKeylet);
147  if (!amm)
149  if (!issue1 && !issue2)
150  {
151  issue1 = (*amm)[sfAsset];
152  issue2 = (*amm)[sfAsset2];
153  }
154 
155  return ValuesFromContextParams{
156  accountID, *issue1, *issue2, std::move(amm)};
157  };
158 
159  auto const r = getValuesFromContextParams();
160  if (!r)
161  {
162  RPC::inject_error(r.error(), result);
163  return result;
164  }
165 
166  auto const& [accountID, issue1, issue2, amm] = *r;
167 
168  auto const ammAccountID = amm->getAccountID(sfAccount);
169 
170  // provide funds if frozen, specify asset_frozen flag
171  auto const [asset1Balance, asset2Balance] = ammPoolHolds(
172  *ledger,
173  ammAccountID,
174  issue1,
175  issue2,
176  FreezeHandling::fhIGNORE_FREEZE,
177  context.j);
178  auto const lptAMMBalance = accountID
179  ? ammLPHolds(*ledger, *amm, *accountID, context.j)
180  : (*amm)[sfLPTokenBalance];
181 
182  Json::Value ammResult;
183  asset1Balance.setJson(ammResult[jss::amount]);
184  asset2Balance.setJson(ammResult[jss::amount2]);
185  lptAMMBalance.setJson(ammResult[jss::lp_token]);
186  ammResult[jss::trading_fee] = (*amm)[sfTradingFee];
187  ammResult[jss::account] = to_string(ammAccountID);
188  Json::Value voteSlots(Json::arrayValue);
189  if (amm->isFieldPresent(sfVoteSlots))
190  {
191  for (auto const& voteEntry : amm->getFieldArray(sfVoteSlots))
192  {
193  Json::Value vote;
194  vote[jss::account] = to_string(voteEntry.getAccountID(sfAccount));
195  vote[jss::trading_fee] = voteEntry[sfTradingFee];
196  vote[jss::vote_weight] = voteEntry[sfVoteWeight];
197  voteSlots.append(std::move(vote));
198  }
199  }
200  if (voteSlots.size() > 0)
201  ammResult[jss::vote_slots] = std::move(voteSlots);
202  if (amm->isFieldPresent(sfAuctionSlot))
203  {
204  auto const& auctionSlot =
205  static_cast<STObject const&>(amm->peekAtField(sfAuctionSlot));
206  if (auctionSlot.isFieldPresent(sfAccount))
207  {
208  Json::Value auction;
209  auto const timeSlot = ammAuctionTimeSlot(
210  ledger->info().parentCloseTime.time_since_epoch().count(),
211  auctionSlot);
212  auction[jss::time_interval] =
213  timeSlot ? *timeSlot : AUCTION_SLOT_TIME_INTERVALS;
214  auctionSlot[sfPrice].setJson(auction[jss::price]);
215  auction[jss::discounted_fee] = auctionSlot[sfDiscountedFee];
216  auction[jss::account] =
217  to_string(auctionSlot.getAccountID(sfAccount));
218  auction[jss::expiration] = to_iso8601(NetClock::time_point{
219  NetClock::duration{auctionSlot[sfExpiration]}});
220  if (auctionSlot.isFieldPresent(sfAuthAccounts))
221  {
222  Json::Value auth;
223  for (auto const& acct :
224  auctionSlot.getFieldArray(sfAuthAccounts))
225  {
226  Json::Value jv;
227  jv[jss::account] = to_string(acct.getAccountID(sfAccount));
228  auth.append(jv);
229  }
230  auction[jss::auth_accounts] = auth;
231  }
232  ammResult[jss::auction_slot] = std::move(auction);
233  }
234  }
235 
236  if (!isXRP(asset1Balance))
237  ammResult[jss::asset_frozen] =
238  isFrozen(*ledger, ammAccountID, issue1.currency, issue1.account);
239  if (!isXRP(asset2Balance))
240  ammResult[jss::asset2_frozen] =
241  isFrozen(*ledger, ammAccountID, issue2.currency, issue2.account);
242 
243  result[jss::amm] = std::move(ammResult);
244  if (!result.isMember(jss::ledger_index) &&
245  !result.isMember(jss::ledger_hash))
246  result[jss::ledger_current_index] = ledger->info().seq;
247  result[jss::validated] =
248  RPC::isValidated(context.ledgerMaster, *ledger, context.app);
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:375
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:676
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:62
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::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:134
ripple::sfAuthAccounts
const SField sfAuthAccounts
ripple::rpcACT_NOT_FOUND
@ rpcACT_NOT_FOUND
Definition: ErrorCodes.h:70
ripple::RPC::Context::app
Application & app
Definition: Context.h:42
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:89
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:77
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:32
ripple::doAMMInfo
Json::Value doAMMInfo(RPC::JsonContext &context)
Definition: AMMInfo.cpp:73
ripple::STObject
Definition: STObject.h:52
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::RPC::isValidated
bool isValidated(LedgerMaster &ledgerMaster, ReadView const &ledger, Application &app)
Definition: RPCHelpers.cpp:604
ripple::rpcACT_MALFORMED
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:90
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: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
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