rippled
Loading...
Searching...
No Matches
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
20#include <xrpld/app/ledger/LedgerMaster.h>
21#include <xrpld/app/misc/AMMUtils.h>
22#include <xrpld/ledger/ReadView.h>
23#include <xrpld/rpc/Context.h>
24#include <xrpld/rpc/detail/RPCHelpers.h>
25
26#include <xrpl/json/json_value.h>
27#include <xrpl/protocol/AMMCore.h>
28#include <xrpl/protocol/Issue.h>
29
30#include <grpcpp/support/status.h>
31
32namespace ripple {
33
36{
37 std::string strIdent(v.asString());
38 AccountID accountID;
39
40 if (auto jv = RPC::accountFromString(accountID, strIdent))
41 {
42 for (auto it = jv.begin(); it != jv.end(); ++it)
43 result[it.memberName()] = (*it);
44
45 return std::nullopt;
46 }
47 return std::optional<AccountID>(accountID);
48}
49
50Expected<Issue, error_code_i>
52{
53 try
54 {
55 return issueFromJson(v);
56 }
57 catch (std::runtime_error const& ex)
58 {
59 JLOG(j.debug()) << "getIssue " << ex.what();
60 }
62}
63
66{
67 // 2000-01-01 00:00:00 UTC is 946684800s from 1970-01-01 00:00:00 UTC
68 using namespace std::chrono;
69 return date::format(
70 "%Y-%Om-%dT%H:%M:%OS%z",
71 date::sys_time<system_clock::duration>(
72 system_clock::time_point{tp.time_since_epoch() + epoch_offset}));
73}
74
77{
78 auto const& params(context.params);
79 Json::Value result;
80
82 result = RPC::lookupLedger(ledger, context);
83 if (!ledger)
84 return result;
85
86 struct ValuesFromContextParams
87 {
89 Issue issue1;
90 Issue issue2;
92 };
93
94 auto getValuesFromContextParams =
100
101 constexpr auto invalid = [](Json::Value const& params) -> bool {
102 return (params.isMember(jss::asset) !=
103 params.isMember(jss::asset2)) ||
104 (params.isMember(jss::asset) ==
105 params.isMember(jss::amm_account));
106 };
107
108 // NOTE, identical check for apVersion >= 3 below
109 if (context.apiVersion < 3 && invalid(params))
111
112 if (params.isMember(jss::asset))
113 {
114 if (auto const i = getIssue(params[jss::asset], context.j))
115 issue1 = *i;
116 else
117 return Unexpected(i.error());
118 }
119
120 if (params.isMember(jss::asset2))
121 {
122 if (auto const i = getIssue(params[jss::asset2], context.j))
123 issue2 = *i;
124 else
125 return Unexpected(i.error());
126 }
127
128 if (params.isMember(jss::amm_account))
129 {
130 auto const id = getAccount(params[jss::amm_account], result);
131 if (!id)
133 auto const sle = ledger->read(keylet::account(*id));
134 if (!sle)
136 ammID = sle->getFieldH256(sfAMMID);
137 if (ammID->isZero())
139 }
140
141 if (params.isMember(jss::account))
142 {
143 accountID = getAccount(params[jss::account], result);
144 if (!accountID || !ledger->read(keylet::account(*accountID)))
146 }
147
148 // NOTE, identical check for apVersion < 3 above
149 if (context.apiVersion >= 3 && invalid(params))
151
152 XRPL_ASSERT(
153 (issue1.has_value() == issue2.has_value()) &&
154 (issue1.has_value() != ammID.has_value()),
155 "ripple::doAMMInfo : issue1 and issue2 do match");
156
157 auto const ammKeylet = [&]() {
158 if (issue1 && issue2)
159 return keylet::amm(*issue1, *issue2);
160 XRPL_ASSERT(ammID, "ripple::doAMMInfo::ammKeylet : ammID is set");
161 return keylet::amm(*ammID);
162 }();
163 auto const amm = ledger->read(ammKeylet);
164 if (!amm)
166 if (!issue1 && !issue2)
167 {
168 issue1 = (*amm)[sfAsset].get<Issue>();
169 issue2 = (*amm)[sfAsset2].get<Issue>();
170 }
171
172 return ValuesFromContextParams{
173 accountID, *issue1, *issue2, std::move(amm)};
174 };
175
176 auto const r = getValuesFromContextParams();
177 if (!r)
178 {
179 RPC::inject_error(r.error(), result);
180 return result;
181 }
182
183 auto const& [accountID, issue1, issue2, amm] = *r;
184
185 auto const ammAccountID = amm->getAccountID(sfAccount);
186
187 // provide funds if frozen, specify asset_frozen flag
188 auto const [asset1Balance, asset2Balance] = ammPoolHolds(
189 *ledger,
191 issue1,
192 issue2,
194 context.j);
195 auto const lptAMMBalance = accountID
196 ? ammLPHolds(*ledger, *amm, *accountID, context.j)
197 : (*amm)[sfLPTokenBalance];
198
199 Json::Value ammResult;
200 asset1Balance.setJson(ammResult[jss::amount]);
201 asset2Balance.setJson(ammResult[jss::amount2]);
202 lptAMMBalance.setJson(ammResult[jss::lp_token]);
203 ammResult[jss::trading_fee] = (*amm)[sfTradingFee];
204 ammResult[jss::account] = to_string(ammAccountID);
205 Json::Value voteSlots(Json::arrayValue);
206 if (amm->isFieldPresent(sfVoteSlots))
207 {
208 for (auto const& voteEntry : amm->getFieldArray(sfVoteSlots))
209 {
210 Json::Value vote;
211 vote[jss::account] = to_string(voteEntry.getAccountID(sfAccount));
212 vote[jss::trading_fee] = voteEntry[sfTradingFee];
213 vote[jss::vote_weight] = voteEntry[sfVoteWeight];
214 voteSlots.append(std::move(vote));
215 }
216 }
217 if (voteSlots.size() > 0)
218 ammResult[jss::vote_slots] = std::move(voteSlots);
219 XRPL_ASSERT(
220 !ledger->rules().enabled(fixInnerObjTemplate) ||
221 amm->isFieldPresent(sfAuctionSlot),
222 "ripple::doAMMInfo : auction slot is set");
223 if (amm->isFieldPresent(sfAuctionSlot))
224 {
225 auto const& auctionSlot =
226 static_cast<STObject const&>(amm->peekAtField(sfAuctionSlot));
227 if (auctionSlot.isFieldPresent(sfAccount))
228 {
229 Json::Value auction;
230 auto const timeSlot = ammAuctionTimeSlot(
231 ledger->info().parentCloseTime.time_since_epoch().count(),
232 auctionSlot);
233 auction[jss::time_interval] =
234 timeSlot ? *timeSlot : AUCTION_SLOT_TIME_INTERVALS;
235 auctionSlot[sfPrice].setJson(auction[jss::price]);
236 auction[jss::discounted_fee] = auctionSlot[sfDiscountedFee];
237 auction[jss::account] =
238 to_string(auctionSlot.getAccountID(sfAccount));
239 auction[jss::expiration] = to_iso8601(NetClock::time_point{
240 NetClock::duration{auctionSlot[sfExpiration]}});
241 if (auctionSlot.isFieldPresent(sfAuthAccounts))
242 {
243 Json::Value auth;
244 for (auto const& acct :
245 auctionSlot.getFieldArray(sfAuthAccounts))
246 {
247 Json::Value jv;
248 jv[jss::account] = to_string(acct.getAccountID(sfAccount));
249 auth.append(jv);
250 }
251 auction[jss::auth_accounts] = auth;
252 }
253 ammResult[jss::auction_slot] = std::move(auction);
254 }
255 }
256
257 if (!isXRP(asset1Balance))
258 ammResult[jss::asset_frozen] =
259 isFrozen(*ledger, ammAccountID, issue1.currency, issue1.account);
260 if (!isXRP(asset2Balance))
261 ammResult[jss::asset2_frozen] =
262 isFrozen(*ledger, ammAccountID, issue2.currency, issue2.account);
263
264 result[jss::amm] = std::move(ammResult);
265 if (!result.isMember(jss::ledger_index) &&
266 !result.isMember(jss::ledger_hash))
267 result[jss::ledger_current_index] = ledger->info().seq;
268 result[jss::validated] = context.ledgerMaster.isValidated(*ledger);
269
270 return result;
271}
272
273} // namespace ripple
Represents a JSON value.
Definition: json_value.h:148
UInt size() const
Number of values in array or object.
Definition: json_value.cpp:712
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:897
std::string asString() const
Returns the unquoted string value.
Definition: json_value.cpp:475
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:949
A generic endpoint for log messages.
Definition: Journal.h:60
Stream debug() const
Definition: Journal.h:328
A currency issued by an account.
Definition: Issue.h:36
bool isValidated(ReadView const &ledger)
@ arrayValue
array value (ordered list)
Definition: json_value.h:43
void inject_error(error_code_i code, JsonValue &json)
Add or update the json update to reflect the error code.
Definition: ErrorCodes.h:223
Json::Value accountFromString(AccountID &result, std::string const &strIdent, bool bStrict)
Definition: RPCHelpers.cpp:88
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:622
Keylet amm(Asset const &issue1, Asset const &issue2) noexcept
AMM entry.
Definition: Indexes.cpp:437
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:175
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ fhIGNORE_FREEZE
Definition: View.h:76
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
std::uint16_t constexpr AUCTION_SLOT_TIME_INTERVALS
Definition: AMMCore.h:35
std::optional< std::uint8_t > ammAuctionTimeSlot(std::uint64_t current, STObject const &auctionSlot)
Get time slot of the auction slot.
Definition: AMMCore.cpp:120
@ rpcACT_NOT_FOUND
Definition: ErrorCodes.h:70
@ rpcISSUE_MALFORMED
Definition: ErrorCodes.h:146
@ rpcACT_MALFORMED
Definition: ErrorCodes.h:90
@ rpcINVALID_PARAMS
Definition: ErrorCodes.h:84
Issue getIssue(T const &amt)
std::optional< AccountID > getAccount(Json::Value const &v, Json::Value &result)
Definition: AMMInfo.cpp:35
Json::Value doAMMInfo(RPC::JsonContext &context)
Definition: AMMInfo.cpp:76
bool isFrozen(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer)
Definition: View.cpp:239
AccountID ammAccountID(std::uint16_t prefix, uint256 const &parentHash, uint256 const &ammID)
Calculate AMM account ID.
Definition: AMMCore.cpp:43
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:112
Issue issueFromJson(Json::Value const &v)
Definition: Issue.cpp:95
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:30
@ invalid
Timely, but invalid signature.
static constexpr std::chrono::seconds epoch_offset
Clock for measuring the network time.
Definition: chrono.h:55
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
std::string to_iso8601(NetClock::time_point tp)
Definition: AMMInfo.cpp:65
T has_value(T... args)
unsigned int apiVersion
Definition: Context.h:49
beast::Journal const j
Definition: Context.h:40
LedgerMaster & ledgerMaster
Definition: Context.h:44
Json::Value params
Definition: Context.h:63
T time_since_epoch(T... args)
T what(T... args)