52class BookChanges final {
54 BookChanges() =
delete;
62 [[nodiscard]]
static std::vector<BookChange>
63 compute(std::vector<data::TransactionAndMetadata>
const& transactions)
65 return HandlerImpl{}(transactions);
69 class HandlerImpl final {
70 std::map<std::string, BookChange> tally_;
71 std::optional<uint32_t> offerCancel_;
74 [[nodiscard]] std::vector<BookChange>
75 operator()(std::vector<data::TransactionAndMetadata>
const& transactions)
77 for (
auto const& tx : transactions)
81 std::vector<BookChange> changes;
83 std::make_move_iterator(std::begin(tally_)),
84 std::make_move_iterator(std::end(tally_)),
85 std::back_inserter(changes),
86 [](
auto obj) {
return obj.second; }
93 handleAffectedNode(ripple::STObject
const& node)
95 auto const& metaType = node.getFName();
96 auto const nodeType = node.getFieldU16(ripple::sfLedgerEntryType);
100 if (nodeType != ripple::ltOFFER || metaType == ripple::sfCreatedNode)
106 if (!node.isFieldPresent(ripple::sfFinalFields) ||
107 !node.isFieldPresent(ripple::sfPreviousFields))
110 auto const& finalFields =
111 node.peekAtField(ripple::sfFinalFields).downcast<ripple::STObject>();
112 auto const& previousFields =
113 node.peekAtField(ripple::sfPreviousFields).downcast<ripple::STObject>();
116 if (!finalFields.isFieldPresent(ripple::sfTakerGets) ||
117 !finalFields.isFieldPresent(ripple::sfTakerPays) ||
118 !previousFields.isFieldPresent(ripple::sfTakerGets) ||
119 !previousFields.isFieldPresent(ripple::sfTakerPays))
123 if (metaType == ripple::sfDeletedNode && offerCancel_ &&
124 finalFields.getFieldU32(ripple::sfSequence) == *offerCancel_)
129 auto const deltaGets = finalFields.getFieldAmount(ripple::sfTakerGets) -
130 previousFields.getFieldAmount(ripple::sfTakerGets);
131 auto const deltaPays = finalFields.getFieldAmount(ripple::sfTakerPays) -
132 previousFields.getFieldAmount(ripple::sfTakerPays);
134 transformAndStore(deltaGets, deltaPays, finalFields[~ripple::sfDomainID]);
139 ripple::STAmount
const& deltaGets,
140 ripple::STAmount
const& deltaPays,
141 std::optional<ripple::uint256>
const& domain
144 auto const g = to_string(deltaGets.issue());
145 auto const p = to_string(deltaPays.issue());
147 auto const noswap = [&]() {
148 if (isXRP(deltaGets))
150 return isXRP(deltaPays) ? false : (g < p);
153 auto first = noswap ? deltaGets : deltaPays;
154 auto second = noswap ? deltaPays : deltaGets;
157 if (second == beast::zero)
160 auto const rate = divide(first, second, ripple::noIssue());
162 if (first < beast::zero)
165 if (second < beast::zero)
168 auto const key = noswap ? (g +
'|' + p) : (p +
'|' + g);
169 if (tally_.contains(key)) {
170 auto& entry = tally_.at(key);
172 entry.sideAVolume += first;
173 entry.sideBVolume += second;
175 if (entry.highRate < rate)
176 entry.highRate = rate;
178 if (entry.lowRate > rate)
179 entry.lowRate = rate;
181 entry.closeRate = rate;
182 entry.domain = domain;
185 .sideAVolume = first,
186 .sideBVolume = second,
197 handleBookChange(data::TransactionAndMetadata
const& blob)
200 if (!tx || !meta || !tx->isFieldPresent(ripple::sfTransactionType))
203 offerCancel_ = shouldCancelOffer(tx);
204 for (
auto const& node : meta->getFieldArray(ripple::sfAffectedNodes))
205 handleAffectedNode(node);
208 static std::optional<uint32_t>
209 shouldCancelOffer(std::shared_ptr<ripple::STTx const>
const& tx)
211 switch (tx->getFieldU16(ripple::sfTransactionType)) {
214 case ripple::ttOFFER_CANCEL:
215 case ripple::ttOFFER_CREATE:
216 if (tx->isFieldPresent(ripple::sfOfferSequence))
217 return tx->getFieldU32(ripple::sfOfferSequence);
235 auto amountStr = [](ripple::STAmount
const& amount) -> std::string {
236 return isXRP(amount) ? to_string(amount.xrp()) : to_string(amount.iou());
239 auto currencyStr = [](ripple::STAmount
const& amount) -> std::string {
240 return isXRP(amount) ?
"XRP_drops" : to_string(amount.issue());
244 {JS(currency_a), currencyStr(change.sideAVolume)},
245 {JS(currency_b), currencyStr(change.sideBVolume)},
246 {JS(volume_a), amountStr(change.sideAVolume)},
247 {JS(volume_b), amountStr(change.sideBVolume)},
248 {JS(high), to_string(change.highRate.iou())},
249 {JS(low), to_string(change.lowRate.iou())},
250 {JS(open), to_string(change.openRate.iou())},
251 {JS(close), to_string(change.closeRate.iou())},
254 if (change.domain.has_value())
255 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:233