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} // namespace Json
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 const 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 const 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) ||
85 !finalFields.isFieldPresent(sfTakerPays) ||
86 !previousFields.isFieldPresent(sfTakerGets) ||
87 !previousFields.isFieldPresent(sfTakerPays))
88 continue;
89
90 // filter out any offers deleted by explicit offer cancels
91 if (metaType == sfDeletedNode && offerCancel &&
92 finalFields.getFieldU32(sfSequence) == *offerCancel)
93 continue;
94
95 // compute the difference in gets and pays actually
96 // affected onto the offer
97 STAmount const deltaGets = finalFields.getFieldAmount(sfTakerGets) -
98 previousFields.getFieldAmount(sfTakerGets);
99 STAmount const deltaPays = finalFields.getFieldAmount(sfTakerPays) -
100 previousFields.getFieldAmount(sfTakerPays);
101
102 std::string const g{to_string(deltaGets.issue())};
103 std::string const p{to_string(deltaPays.issue())};
104
105 bool const noswap = isXRP(deltaGets) ? true : (isXRP(deltaPays) ? false : (g < p));
106
107 STAmount first = noswap ? deltaGets : deltaPays;
108 STAmount second = noswap ? deltaPays : deltaGets;
109
110 // defensively programmed, should (probably) never happen
111 if (second == beast::zero)
112 continue;
113
114 STAmount const rate = divide(first, second, noIssue());
115
116 if (first < beast::zero)
117 first = -first;
118
119 if (second < beast::zero)
120 second = -second;
121
123 if (noswap)
124 ss << g << "|" << p;
125 else
126 ss << p << "|" << g;
127
128 std::optional<uint256> const domain = finalFields[~sfDomainID];
129
130 std::string const key{ss.str()};
131
132 if (tally.find(key) == tally.end())
133 tally[key] = {
134 first, // side A vol
135 second, // side B vol
136 rate, // high
137 rate, // low
138 rate, // open
139 rate, // close
140 domain};
141 else
142 {
143 // increment volume
144 auto& entry = tally[key];
145
146 std::get<0>(entry) += first; // side A vol
147 std::get<1>(entry) += second; // side B vol
148
149 if (std::get<2>(entry) < rate) // high
150 std::get<2>(entry) = rate;
151
152 if (std::get<3>(entry) > rate) // low
153 std::get<3>(entry) = rate;
154
155 std::get<5>(entry) = rate; // close
156 std::get<6>(entry) = domain; // domain
157 }
158 }
159 }
160
162 jvObj[jss::type] = "bookChanges";
163
164 // retrieve validated information from LedgerHeader class
165 jvObj[jss::validated] = lpAccepted->header().validated;
166 jvObj[jss::ledger_index] = lpAccepted->header().seq;
167 jvObj[jss::ledger_hash] = to_string(lpAccepted->header().hash);
168 jvObj[jss::ledger_time] =
169 Json::Value::UInt(lpAccepted->header().closeTime.time_since_epoch().count());
170
171 jvObj[jss::changes] = Json::arrayValue;
172
173 for (auto const& entry : tally)
174 {
175 Json::Value& inner = jvObj[jss::changes].append(Json::objectValue);
176
177 STAmount const volA = std::get<0>(entry.second);
178 STAmount const volB = std::get<1>(entry.second);
179
180 inner[jss::currency_a] = (isXRP(volA) ? "XRP_drops" : to_string(volA.issue()));
181 inner[jss::currency_b] = (isXRP(volB) ? "XRP_drops" : to_string(volB.issue()));
182
183 inner[jss::volume_a] = (isXRP(volA) ? to_string(volA.xrp()) : to_string(volA.iou()));
184 inner[jss::volume_b] = (isXRP(volB) ? to_string(volB.xrp()) : to_string(volB.iou()));
185
186 inner[jss::high] = to_string(std::get<2>(entry.second).iou());
187 inner[jss::low] = to_string(std::get<3>(entry.second).iou());
188 inner[jss::open] = to_string(std::get<4>(entry.second).iou());
189 inner[jss::close] = to_string(std::get<5>(entry.second).iou());
190
191 std::optional<uint256> const domain = std::get<6>(entry.second);
192 if (domain)
193 inner[jss::domain] = to_string(*domain);
194 }
195
196 return jvObj;
197}
198
199} // namespace RPC
200} // 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:470
IOUAmount iou() const
Definition STAmount.cpp:276
XRPAmount xrp() const
Definition STAmount.cpp:261
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:602
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:105
T str(T... args)