rippled
Loading...
Searching...
No Matches
BookChanges.h
1#ifndef XRPL_RPC_BOOKCHANGES_H_INCLUDED
2#define XRPL_RPC_BOOKCHANGES_H_INCLUDED
3
4#include <xrpl/json/json_value.h>
5#include <xrpl/protocol/LedgerFormats.h>
6#include <xrpl/protocol/STAmount.h>
7#include <xrpl/protocol/STObject.h>
8#include <xrpl/protocol/TxFormats.h>
9#include <xrpl/protocol/jss.h>
10
11#include <memory>
12
13namespace Json {
14class Value;
15}
16
17namespace ripple {
18
19class ReadView;
20class Transaction;
21class TxMeta;
22class STTx;
23
24namespace RPC {
25
26template <class L>
29{
33 STAmount, // side A volume
34 STAmount, // side B volume
35 STAmount, // high rate
36 STAmount, // low rate
37 STAmount, // open rate
38 STAmount, // close rate
39 std::optional<uint256>>> // optional: domain id
40 tally;
41
42 for (auto& tx : lpAccepted->txs)
43 {
44 if (!tx.first || !tx.second ||
45 !tx.first->isFieldPresent(sfTransactionType))
46 continue;
47
48 std::optional<uint32_t> offerCancel;
49 uint16_t tt = tx.first->getFieldU16(sfTransactionType);
50 switch (tt)
51 {
52 case ttOFFER_CANCEL:
53 case ttOFFER_CREATE: {
54 if (tx.first->isFieldPresent(sfOfferSequence))
55 offerCancel = tx.first->getFieldU32(sfOfferSequence);
56 break;
57 }
58 // in future if any other ways emerge to cancel an offer
59 // this switch makes them easy to add
60 default:
61 break;
62 }
63
64 for (auto const& node : tx.second->getFieldArray(sfAffectedNodes))
65 {
66 SField const& metaType = node.getFName();
67 uint16_t nodeType = node.getFieldU16(sfLedgerEntryType);
68
69 // we only care about ltOFFER objects being modified or
70 // deleted
71 if (nodeType != ltOFFER || metaType == sfCreatedNode)
72 continue;
73
74 // if either FF or PF are missing we can't compute
75 // but generally these are cancelled rather than crossed
76 // so skipping them is consistent
77 if (!node.isFieldPresent(sfFinalFields) ||
78 !node.isFieldPresent(sfPreviousFields))
79 continue;
80
81 auto const& ffBase = node.peekAtField(sfFinalFields);
82 auto const& finalFields = ffBase.template downcast<STObject>();
83 auto const& pfBase = node.peekAtField(sfPreviousFields);
84 auto const& previousFields = pfBase.template downcast<STObject>();
85
86 // defensive case that should never be hit
87 if (!finalFields.isFieldPresent(sfTakerGets) ||
88 !finalFields.isFieldPresent(sfTakerPays) ||
89 !previousFields.isFieldPresent(sfTakerGets) ||
90 !previousFields.isFieldPresent(sfTakerPays))
91 continue;
92
93 // filter out any offers deleted by explicit offer cancels
94 if (metaType == sfDeletedNode && offerCancel &&
95 finalFields.getFieldU32(sfSequence) == *offerCancel)
96 continue;
97
98 // compute the difference in gets and pays actually
99 // affected onto the offer
100 STAmount deltaGets = finalFields.getFieldAmount(sfTakerGets) -
101 previousFields.getFieldAmount(sfTakerGets);
102 STAmount deltaPays = finalFields.getFieldAmount(sfTakerPays) -
103 previousFields.getFieldAmount(sfTakerPays);
104
105 std::string g{to_string(deltaGets.issue())};
106 std::string p{to_string(deltaPays.issue())};
107
108 bool const noswap =
109 isXRP(deltaGets) ? true : (isXRP(deltaPays) ? false : (g < p));
110
111 STAmount first = noswap ? deltaGets : deltaPays;
112 STAmount second = noswap ? deltaPays : deltaGets;
113
114 // defensively programmed, should (probably) never happen
115 if (second == beast::zero)
116 continue;
117
118 STAmount rate = divide(first, second, noIssue());
119
120 if (first < beast::zero)
121 first = -first;
122
123 if (second < beast::zero)
124 second = -second;
125
127 if (noswap)
128 ss << g << "|" << p;
129 else
130 ss << p << "|" << g;
131
132 std::optional<uint256> domain = finalFields[~sfDomainID];
133
134 std::string key{ss.str()};
135
136 if (tally.find(key) == tally.end())
137 tally[key] = {
138 first, // side A vol
139 second, // side B vol
140 rate, // high
141 rate, // low
142 rate, // open
143 rate, // close
144 domain};
145 else
146 {
147 // increment volume
148 auto& entry = tally[key];
149
150 std::get<0>(entry) += first; // side A vol
151 std::get<1>(entry) += second; // side B vol
152
153 if (std::get<2>(entry) < rate) // high
154 std::get<2>(entry) = rate;
155
156 if (std::get<3>(entry) > rate) // low
157 std::get<3>(entry) = rate;
158
159 std::get<5>(entry) = rate; // close
160 std::get<6>(entry) = domain; // domain
161 }
162 }
163 }
164
166 jvObj[jss::type] = "bookChanges";
167
168 // retrieve validated information from LedgerHeader class
169 jvObj[jss::validated] = lpAccepted->info().validated;
170 jvObj[jss::ledger_index] = lpAccepted->info().seq;
171 jvObj[jss::ledger_hash] = to_string(lpAccepted->info().hash);
172 jvObj[jss::ledger_time] = Json::Value::UInt(
173 lpAccepted->info().closeTime.time_since_epoch().count());
174
175 jvObj[jss::changes] = Json::arrayValue;
176
177 for (auto const& entry : tally)
178 {
179 Json::Value& inner = jvObj[jss::changes].append(Json::objectValue);
180
181 STAmount volA = std::get<0>(entry.second);
182 STAmount volB = std::get<1>(entry.second);
183
184 inner[jss::currency_a] =
185 (isXRP(volA) ? "XRP_drops" : to_string(volA.issue()));
186 inner[jss::currency_b] =
187 (isXRP(volB) ? "XRP_drops" : to_string(volB.issue()));
188
189 inner[jss::volume_a] =
190 (isXRP(volA) ? to_string(volA.xrp()) : to_string(volA.iou()));
191 inner[jss::volume_b] =
192 (isXRP(volB) ? to_string(volB.xrp()) : to_string(volB.iou()));
193
194 inner[jss::high] = to_string(std::get<2>(entry.second).iou());
195 inner[jss::low] = to_string(std::get<3>(entry.second).iou());
196 inner[jss::open] = to_string(std::get<4>(entry.second).iou());
197 inner[jss::close] = to_string(std::get<5>(entry.second).iou());
198
199 std::optional<uint256> const domain = std::get<6>(entry.second);
200 if (domain)
201 inner[jss::domain] = to_string(*domain);
202 }
203
204 return jvObj;
205}
206
207} // namespace RPC
208} // namespace ripple
209
210#endif
Represents a JSON value.
Definition json_value.h:130
Json::UInt UInt
Definition json_value.h:137
Value & append(Value const &value)
Append value to array at the end.
Identifies fields.
Definition SField.h:127
IOUAmount iou() const
Definition STAmount.cpp:280
XRPAmount xrp() const
Definition STAmount.cpp:264
Issue const & issue() const
Definition STAmount.h:477
T is_same_v
JSON (JavaScript Object Notation).
Definition json_errors.h:6
@ arrayValue
array value (ordered list)
Definition json_value.h:25
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Json::Value computeBookChanges(std::shared_ptr< L const > const &lpAccepted)
Definition BookChanges.h:28
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:74
bool isXRP(AccountID const &c)
Definition AccountID.h:71
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:104
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
T str(T... args)