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