71class BookChanges final {
73 BookChanges() =
delete;
81 [[nodiscard]]
static std::vector<BookChange>
82 compute(std::vector<data::TransactionAndMetadata>
const& transactions)
84 return HandlerImpl{}(transactions);
88 class HandlerImpl final {
89 std::map<std::string, BookChange> tally_;
90 std::optional<uint32_t> offerCancel_;
93 [[nodiscard]] std::vector<BookChange>
94 operator()(std::vector<data::TransactionAndMetadata>
const& transactions)
96 for (
auto const& tx : transactions)
100 std::vector<BookChange> changes;
102 std::make_move_iterator(std::begin(tally_)),
103 std::make_move_iterator(std::end(tally_)),
104 std::back_inserter(changes),
105 [](
auto obj) {
return obj.second; }
112 handleAffectedNode(ripple::STObject
const& node)
114 auto const& metaType = node.getFName();
115 auto const nodeType = node.getFieldU16(ripple::sfLedgerEntryType);
119 if (nodeType != ripple::ltOFFER || metaType == ripple::sfCreatedNode)
125 if (!node.isFieldPresent(ripple::sfFinalFields) ||
126 !node.isFieldPresent(ripple::sfPreviousFields))
129 auto const& finalFields =
130 node.peekAtField(ripple::sfFinalFields).downcast<ripple::STObject>();
131 auto const& previousFields =
132 node.peekAtField(ripple::sfPreviousFields).downcast<ripple::STObject>();
135 if (!finalFields.isFieldPresent(ripple::sfTakerGets) ||
136 !finalFields.isFieldPresent(ripple::sfTakerPays) ||
137 !previousFields.isFieldPresent(ripple::sfTakerGets) ||
138 !previousFields.isFieldPresent(ripple::sfTakerPays))
142 if (metaType == ripple::sfDeletedNode && offerCancel_ &&
143 finalFields.getFieldU32(ripple::sfSequence) == *offerCancel_)
148 auto const deltaGets = finalFields.getFieldAmount(ripple::sfTakerGets) -
149 previousFields.getFieldAmount(ripple::sfTakerGets);
150 auto const deltaPays = finalFields.getFieldAmount(ripple::sfTakerPays) -
151 previousFields.getFieldAmount(ripple::sfTakerPays);
153 transformAndStore(deltaGets, deltaPays, finalFields[~ripple::sfDomainID]);
158 ripple::STAmount
const& deltaGets,
159 ripple::STAmount
const& deltaPays,
160 std::optional<ripple::uint256>
const& domain
163 auto const g = to_string(deltaGets.issue());
164 auto const p = to_string(deltaPays.issue());
166 auto const noswap = [&]() {
167 if (isXRP(deltaGets))
169 return isXRP(deltaPays) ? false : (g < p);
172 auto first = noswap ? deltaGets : deltaPays;
173 auto second = noswap ? deltaPays : deltaGets;
176 if (second == beast::zero)
179 auto const rate = divide(first, second, ripple::noIssue());
181 if (first < beast::zero)
184 if (second < beast::zero)
187 auto const key = noswap ? (g +
'|' + p) : (p +
'|' + g);
188 if (tally_.contains(key)) {
189 auto& entry = tally_.at(key);
191 entry.sideAVolume += first;
192 entry.sideBVolume += second;
194 if (entry.highRate < rate)
195 entry.highRate = rate;
197 if (entry.lowRate > rate)
198 entry.lowRate = rate;
200 entry.closeRate = rate;
201 entry.domain = domain;
204 .sideAVolume = first,
205 .sideBVolume = second,
216 handleBookChange(data::TransactionAndMetadata
const& blob)
219 if (!tx || !meta || !tx->isFieldPresent(ripple::sfTransactionType))
222 offerCancel_ = shouldCancelOffer(tx);
223 for (
auto const& node : meta->getFieldArray(ripple::sfAffectedNodes))
224 handleAffectedNode(node);
227 static std::optional<uint32_t>
228 shouldCancelOffer(std::shared_ptr<ripple::STTx const>
const& tx)
230 switch (tx->getFieldU16(ripple::sfTransactionType)) {
233 case ripple::ttOFFER_CANCEL:
234 case ripple::ttOFFER_CREATE:
235 if (tx->isFieldPresent(ripple::sfOfferSequence))
236 return tx->getFieldU32(ripple::sfOfferSequence);
254 auto amountStr = [](ripple::STAmount
const& amount) -> std::string {
255 return isXRP(amount) ? to_string(amount.xrp()) : to_string(amount.iou());
258 auto currencyStr = [](ripple::STAmount
const& amount) -> std::string {
259 return isXRP(amount) ?
"XRP_drops" : to_string(amount.issue());
263 {JS(currency_a), currencyStr(change.sideAVolume)},
264 {JS(currency_b), currencyStr(change.sideBVolume)},
265 {JS(volume_a), amountStr(change.sideAVolume)},
266 {JS(volume_b), amountStr(change.sideBVolume)},
267 {JS(high), to_string(change.highRate.iou())},
268 {JS(low), to_string(change.lowRate.iou())},
269 {JS(open), to_string(change.openRate.iou())},
270 {JS(close), to_string(change.closeRate.iou())},
273 if (change.domain.has_value())
274 jv.as_object()[JS(domain)] = ripple::to_string(*change.domain);
void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, BookChange const &change)
Implementation of value_from for BookChange type.
Definition BookChangesHelper.hpp:252