19#include <test/jtx/WSClient.h>
21#include <xrpld/rpc/detail/Tuning.h>
23#include <xrpl/beast/unit_test.h>
24#include <xrpl/protocol/Indexes.h>
25#include <xrpl/protocol/LedgerFormats.h>
26#include <xrpl/protocol/TxFlags.h>
27#include <xrpl/protocol/jss.h>
45 auto key = view->succ(uBookBase, uBookEnd);
48 auto sleOfferDir = view->read(
keylet::page(key.value()));
50 unsigned int bookEntry;
52 *view, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex);
54 dir =
to_string(sleOffer->getFieldH256(sfBookDirectory));
64 using namespace std::chrono_literals;
68 auto USD =
Account(
"alice")[
"USD"];
77 j[jss::snapshot] =
true;
78 j[jss::taker_gets][jss::currency] =
"XRP";
79 j[jss::taker_pays][jss::currency] =
"USD";
80 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
83 auto jv = wsc->invoke(
"subscribe", books);
84 if (wsc->version() == 2)
87 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
89 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
90 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
92 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
95 jv[jss::result].isMember(jss::offers) &&
96 jv[jss::result][jss::offers].size() == 0);
97 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
98 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
103 env(
offer(
"alice",
XRP(700), USD(100)),
108 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
109 auto const& t = jv[jss::transaction];
110 return t[jss::TransactionType] == jss::OfferCreate &&
112 USD(100).value().getJson(JsonOptions::none) &&
114 XRP(700).value().getJson(JsonOptions::none);
122 BEAST_EXPECT(!wsc->getMsg(10ms));
126 auto jv = wsc->invoke(
"unsubscribe", books);
127 BEAST_EXPECT(jv[jss::status] ==
"success");
128 if (wsc->version() == 2)
131 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
133 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
134 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
141 testcase(
"One Side Offers In Book");
142 using namespace std::chrono_literals;
146 auto USD =
Account(
"alice")[
"USD"];
162 j[jss::snapshot] =
true;
163 j[jss::taker_gets][jss::currency] =
"XRP";
164 j[jss::taker_pays][jss::currency] =
"USD";
165 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
168 auto jv = wsc->invoke(
"subscribe", books);
169 if (wsc->version() == 2)
172 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
174 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
175 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
177 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
180 jv[jss::result].isMember(jss::offers) &&
181 jv[jss::result][jss::offers].size() == 1);
183 jv[jss::result][jss::offers][0u][jss::TakerGets] ==
186 jv[jss::result][jss::offers][0u][jss::TakerPays] ==
188 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
189 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
194 env(
offer(
"alice",
XRP(700), USD(100)),
199 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
200 auto const& t = jv[jss::transaction];
201 return t[jss::TransactionType] == jss::OfferCreate &&
203 USD(100).value().getJson(JsonOptions::none) &&
205 XRP(700).value().getJson(JsonOptions::none);
213 BEAST_EXPECT(!wsc->getMsg(10ms));
217 auto jv = wsc->invoke(
"unsubscribe", books);
218 BEAST_EXPECT(jv[jss::status] ==
"success");
219 if (wsc->version() == 2)
222 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
224 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
225 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
233 using namespace std::chrono_literals;
237 auto USD =
Account(
"alice")[
"USD"];
246 j[jss::snapshot] =
true;
248 j[jss::taker_gets][jss::currency] =
"XRP";
249 j[jss::taker_pays][jss::currency] =
"USD";
250 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
253 auto jv = wsc->invoke(
"subscribe", books);
254 if (wsc->version() == 2)
257 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
259 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
260 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
262 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
265 jv[jss::result].isMember(jss::asks) &&
266 jv[jss::result][jss::asks].size() == 0);
268 jv[jss::result].isMember(jss::bids) &&
269 jv[jss::result][jss::bids].size() == 0);
270 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
275 env(
offer(
"alice",
XRP(700), USD(100)),
280 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
281 auto const& t = jv[jss::transaction];
282 return t[jss::TransactionType] == jss::OfferCreate &&
284 USD(100).value().getJson(JsonOptions::none) &&
286 XRP(700).value().getJson(JsonOptions::none);
296 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
297 auto const& t = jv[jss::transaction];
298 return t[jss::TransactionType] == jss::OfferCreate &&
300 XRP(75).value().getJson(JsonOptions::none) &&
302 USD(100).value().getJson(JsonOptions::none);
307 auto jv = wsc->invoke(
"unsubscribe", books);
308 BEAST_EXPECT(jv[jss::status] ==
"success");
309 if (wsc->version() == 2)
312 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
314 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
315 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
322 testcase(
"Both Sides Offers In Book");
323 using namespace std::chrono_literals;
327 auto USD =
Account(
"alice")[
"USD"];
343 j[jss::snapshot] =
true;
345 j[jss::taker_gets][jss::currency] =
"XRP";
346 j[jss::taker_pays][jss::currency] =
"USD";
347 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
350 auto jv = wsc->invoke(
"subscribe", books);
351 if (wsc->version() == 2)
354 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
356 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
357 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
359 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
362 jv[jss::result].isMember(jss::asks) &&
363 jv[jss::result][jss::asks].size() == 1);
365 jv[jss::result].isMember(jss::bids) &&
366 jv[jss::result][jss::bids].size() == 1);
368 jv[jss::result][jss::asks][0u][jss::TakerGets] ==
371 jv[jss::result][jss::asks][0u][jss::TakerPays] ==
374 jv[jss::result][jss::bids][0u][jss::TakerGets] ==
377 jv[jss::result][jss::bids][0u][jss::TakerPays] ==
379 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
384 env(
offer(
"alice",
XRP(700), USD(100)),
389 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
390 auto const& t = jv[jss::transaction];
391 return t[jss::TransactionType] == jss::OfferCreate &&
393 USD(100).value().getJson(JsonOptions::none) &&
395 XRP(700).value().getJson(JsonOptions::none);
405 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
406 auto const& t = jv[jss::transaction];
407 return t[jss::TransactionType] == jss::OfferCreate &&
409 XRP(75).value().getJson(JsonOptions::none) &&
411 USD(100).value().getJson(JsonOptions::none);
416 auto jv = wsc->invoke(
"unsubscribe", books);
417 BEAST_EXPECT(jv[jss::status] ==
"success");
418 if (wsc->version() == 2)
421 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
423 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
424 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
431 testcase(
"Multiple Books, One Side Empty");
432 using namespace std::chrono_literals;
436 auto USD =
Account(
"alice")[
"USD"];
437 auto CNY =
Account(
"alice")[
"CNY"];
438 auto JPY =
Account(
"alice")[
"JPY"];
447 j[jss::snapshot] =
true;
448 j[jss::taker_gets][jss::currency] =
"XRP";
449 j[jss::taker_pays][jss::currency] =
"USD";
450 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
454 j[jss::snapshot] =
true;
455 j[jss::taker_gets][jss::currency] =
"CNY";
456 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
457 j[jss::taker_pays][jss::currency] =
"JPY";
458 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
461 auto jv = wsc->invoke(
"subscribe", books);
462 if (wsc->version() == 2)
465 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
467 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
468 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
470 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
473 jv[jss::result].isMember(jss::offers) &&
474 jv[jss::result][jss::offers].size() == 0);
475 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
476 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
481 env(
offer(
"alice",
XRP(700), USD(100)),
486 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
487 auto const& t = jv[jss::transaction];
488 return t[jss::TransactionType] == jss::OfferCreate &&
490 USD(100).value().getJson(JsonOptions::none) &&
492 XRP(700).value().getJson(JsonOptions::none);
500 BEAST_EXPECT(!wsc->getMsg(10ms));
505 env(
offer(
"alice", CNY(700), JPY(100)),
510 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
511 auto const& t = jv[jss::transaction];
512 return t[jss::TransactionType] == jss::OfferCreate &&
514 JPY(100).value().getJson(JsonOptions::none) &&
516 CNY(700).value().getJson(JsonOptions::none);
524 BEAST_EXPECT(!wsc->getMsg(10ms));
528 auto jv = wsc->invoke(
"unsubscribe", books);
529 BEAST_EXPECT(jv[jss::status] ==
"success");
530 if (wsc->version() == 2)
533 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
535 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
536 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
543 testcase(
"Multiple Books, One Side Offers In Book");
544 using namespace std::chrono_literals;
548 auto USD =
Account(
"alice")[
"USD"];
549 auto CNY =
Account(
"alice")[
"CNY"];
550 auto JPY =
Account(
"alice")[
"JPY"];
572 j[jss::snapshot] =
true;
573 j[jss::taker_gets][jss::currency] =
"XRP";
574 j[jss::taker_pays][jss::currency] =
"USD";
575 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
579 j[jss::snapshot] =
true;
580 j[jss::taker_gets][jss::currency] =
"CNY";
581 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
582 j[jss::taker_pays][jss::currency] =
"JPY";
583 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
586 auto jv = wsc->invoke(
"subscribe", books);
587 if (wsc->version() == 2)
590 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
592 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
593 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
595 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
598 jv[jss::result].isMember(jss::offers) &&
599 jv[jss::result][jss::offers].size() == 2);
601 jv[jss::result][jss::offers][0u][jss::TakerGets] ==
604 jv[jss::result][jss::offers][0u][jss::TakerPays] ==
607 jv[jss::result][jss::offers][1u][jss::TakerGets] ==
610 jv[jss::result][jss::offers][1u][jss::TakerPays] ==
612 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
613 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
618 env(
offer(
"alice",
XRP(700), USD(100)),
623 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
624 auto const& t = jv[jss::transaction];
625 return t[jss::TransactionType] == jss::OfferCreate &&
627 USD(100).value().getJson(JsonOptions::none) &&
629 XRP(700).value().getJson(JsonOptions::none);
637 BEAST_EXPECT(!wsc->getMsg(10ms));
642 env(
offer(
"alice", CNY(700), JPY(100)),
647 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
648 auto const& t = jv[jss::transaction];
649 return t[jss::TransactionType] == jss::OfferCreate &&
651 JPY(100).value().getJson(JsonOptions::none) &&
653 CNY(700).value().getJson(JsonOptions::none);
661 BEAST_EXPECT(!wsc->getMsg(10ms));
665 auto jv = wsc->invoke(
"unsubscribe", books);
666 BEAST_EXPECT(jv[jss::status] ==
"success");
667 if (wsc->version() == 2)
670 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
672 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
673 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
680 testcase(
"Multiple Books, Both Sides Empty Book");
681 using namespace std::chrono_literals;
685 auto USD =
Account(
"alice")[
"USD"];
686 auto CNY =
Account(
"alice")[
"CNY"];
687 auto JPY =
Account(
"alice")[
"JPY"];
696 j[jss::snapshot] =
true;
698 j[jss::taker_gets][jss::currency] =
"XRP";
699 j[jss::taker_pays][jss::currency] =
"USD";
700 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
704 j[jss::snapshot] =
true;
706 j[jss::taker_gets][jss::currency] =
"CNY";
707 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
708 j[jss::taker_pays][jss::currency] =
"JPY";
709 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
712 auto jv = wsc->invoke(
"subscribe", books);
713 if (wsc->version() == 2)
716 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
718 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
719 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
721 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
724 jv[jss::result].isMember(jss::asks) &&
725 jv[jss::result][jss::asks].size() == 0);
727 jv[jss::result].isMember(jss::bids) &&
728 jv[jss::result][jss::bids].size() == 0);
729 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
734 env(
offer(
"alice",
XRP(700), USD(100)),
739 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
740 auto const& t = jv[jss::transaction];
741 return t[jss::TransactionType] == jss::OfferCreate &&
743 USD(100).value().getJson(JsonOptions::none) &&
745 XRP(700).value().getJson(JsonOptions::none);
755 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
756 auto const& t = jv[jss::transaction];
757 return t[jss::TransactionType] == jss::OfferCreate &&
759 XRP(75).value().getJson(JsonOptions::none) &&
761 USD(100).value().getJson(JsonOptions::none);
767 env(
offer(
"alice", CNY(700), JPY(100)),
772 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
773 auto const& t = jv[jss::transaction];
774 return t[jss::TransactionType] == jss::OfferCreate &&
776 JPY(100).value().getJson(JsonOptions::none) &&
778 CNY(700).value().getJson(JsonOptions::none);
788 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
789 auto const& t = jv[jss::transaction];
790 return t[jss::TransactionType] == jss::OfferCreate &&
792 CNY(75).value().getJson(JsonOptions::none) &&
794 JPY(100).value().getJson(JsonOptions::none);
799 auto jv = wsc->invoke(
"unsubscribe", books);
800 BEAST_EXPECT(jv[jss::status] ==
"success");
801 if (wsc->version() == 2)
804 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
806 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
807 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
814 testcase(
"Multiple Books, Both Sides Offers In Book");
815 using namespace std::chrono_literals;
819 auto USD =
Account(
"alice")[
"USD"];
820 auto CNY =
Account(
"alice")[
"CNY"];
821 auto JPY =
Account(
"alice")[
"JPY"];
843 j[jss::snapshot] =
true;
845 j[jss::taker_gets][jss::currency] =
"XRP";
846 j[jss::taker_pays][jss::currency] =
"USD";
847 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
852 j[jss::snapshot] =
true;
854 j[jss::taker_gets][jss::currency] =
"CNY";
855 j[jss::taker_gets][jss::issuer] =
Account(
"alice").
human();
856 j[jss::taker_pays][jss::currency] =
"JPY";
857 j[jss::taker_pays][jss::issuer] =
Account(
"alice").
human();
860 auto jv = wsc->invoke(
"subscribe", books);
861 if (wsc->version() == 2)
864 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
866 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
867 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
869 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
872 jv[jss::result].isMember(jss::asks) &&
873 jv[jss::result][jss::asks].size() == 2);
875 jv[jss::result].isMember(jss::bids) &&
876 jv[jss::result][jss::bids].size() == 2);
878 jv[jss::result][jss::asks][0u][jss::TakerGets] ==
881 jv[jss::result][jss::asks][0u][jss::TakerPays] ==
884 jv[jss::result][jss::asks][1u][jss::TakerGets] ==
887 jv[jss::result][jss::asks][1u][jss::TakerPays] ==
890 jv[jss::result][jss::bids][0u][jss::TakerGets] ==
893 jv[jss::result][jss::bids][0u][jss::TakerPays] ==
896 jv[jss::result][jss::bids][1u][jss::TakerGets] ==
899 jv[jss::result][jss::bids][1u][jss::TakerPays] ==
901 BEAST_EXPECT(!jv[jss::result].isMember(jss::offers));
906 env(
offer(
"alice",
XRP(700), USD(100)),
911 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
912 auto const& t = jv[jss::transaction];
913 return t[jss::TransactionType] == jss::OfferCreate &&
915 USD(100).value().getJson(JsonOptions::none) &&
917 XRP(700).value().getJson(JsonOptions::none);
927 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
928 auto const& t = jv[jss::transaction];
929 return t[jss::TransactionType] == jss::OfferCreate &&
931 XRP(75).value().getJson(JsonOptions::none) &&
933 USD(100).value().getJson(JsonOptions::none);
939 env(
offer(
"alice", CNY(700), JPY(100)),
944 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
945 auto const& t = jv[jss::transaction];
946 return t[jss::TransactionType] == jss::OfferCreate &&
948 JPY(100).value().getJson(JsonOptions::none) &&
950 CNY(700).value().getJson(JsonOptions::none);
960 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
961 auto const& t = jv[jss::transaction];
962 return t[jss::TransactionType] == jss::OfferCreate &&
964 CNY(75).value().getJson(JsonOptions::none) &&
966 JPY(100).value().getJson(JsonOptions::none);
971 auto jv = wsc->invoke(
"unsubscribe", books);
972 BEAST_EXPECT(jv[jss::status] ==
"success");
973 if (wsc->version() == 2)
976 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
978 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
979 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
993 env.
fund(
XRP(20000), alice, bob, gw);
995 auto USD = gw[
"USD"];
1002 j[jss::snapshot] =
true;
1003 j[jss::taker_gets][jss::currency] =
"XRP";
1004 j[jss::taker_pays][jss::currency] =
"USD";
1005 j[jss::taker_pays][jss::issuer] = gw.human();
1008 auto jv = wsc->invoke(
"subscribe", books);
1009 if (wsc->version() == 2)
1012 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1014 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1015 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1017 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1020 jv[jss::result].isMember(jss::offers) &&
1021 jv[jss::result][jss::offers].size() == 0);
1022 BEAST_EXPECT(!jv[jss::result].isMember(jss::asks));
1023 BEAST_EXPECT(!jv[jss::result].isMember(jss::bids));
1028 env.
trust(USD(1000), alice);
1029 env.
trust(USD(1000), bob);
1030 env(
pay(gw, alice, USD(100)));
1031 env(
pay(gw, bob, USD(50)));
1032 env(
offer(alice,
XRP(4000), USD(10)));
1037 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1038 jvParams[jss::ledger_index] =
"validated";
1039 jvParams[jss::taker_gets][jss::currency] =
"USD";
1040 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1042 auto jv = wsc->invoke(
"book_offers", jvParams);
1043 if (wsc->version() == 2)
1046 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1048 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1049 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1051 auto jrr = jv[jss::result];
1053 BEAST_EXPECT(jrr[jss::offers].isArray());
1054 BEAST_EXPECT(jrr[jss::offers].size() == 1);
1055 auto const jrOffer = jrr[jss::offers][0u];
1056 BEAST_EXPECT(jrOffer[sfAccount.fieldName] == alice.human());
1058 jrOffer[sfBookDirectory.fieldName] ==
1060 BEAST_EXPECT(jrOffer[sfBookNode.fieldName] ==
"0");
1061 BEAST_EXPECT(jrOffer[jss::Flags] == 0);
1062 BEAST_EXPECT(jrOffer[sfLedgerEntryType.fieldName] == jss::Offer);
1063 BEAST_EXPECT(jrOffer[sfOwnerNode.fieldName] ==
"0");
1064 BEAST_EXPECT(jrOffer[sfSequence.fieldName] == 5);
1066 jrOffer[jss::TakerGets] ==
1069 jrOffer[jss::TakerPays] ==
1071 BEAST_EXPECT(jrOffer[jss::owner_funds] ==
"100");
1072 BEAST_EXPECT(jrOffer[jss::quality] ==
"400000000");
1074 using namespace std::chrono_literals;
1075 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jval) {
1076 auto const& t = jval[jss::transaction];
1077 return t[jss::TransactionType] == jss::OfferCreate &&
1078 t[jss::TakerGets] ==
1079 USD(10).value().getJson(JsonOptions::none) &&
1080 t[jss::owner_funds] ==
"100" &&
1081 t[jss::TakerPays] ==
1082 XRP(4000).value().getJson(JsonOptions::none);
1085 env(
offer(bob,
XRP(2000), USD(5)));
1088 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jval) {
1089 auto const& t = jval[jss::transaction];
1090 return t[jss::TransactionType] == jss::OfferCreate &&
1091 t[jss::TakerGets] ==
1092 USD(5).value().getJson(JsonOptions::none) &&
1093 t[jss::owner_funds] ==
"50" &&
1094 t[jss::TakerPays] ==
1095 XRP(2000).value().getJson(JsonOptions::none);
1098 jv = wsc->invoke(
"book_offers", jvParams);
1099 if (wsc->version() == 2)
1102 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1104 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1105 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1107 jrr = jv[jss::result];
1109 BEAST_EXPECT(jrr[jss::offers].isArray());
1110 BEAST_EXPECT(jrr[jss::offers].size() == 2);
1111 auto const jrNextOffer = jrr[jss::offers][1u];
1112 BEAST_EXPECT(jrNextOffer[sfAccount.fieldName] == bob.human());
1114 jrNextOffer[sfBookDirectory.fieldName] ==
1116 BEAST_EXPECT(jrNextOffer[sfBookNode.fieldName] ==
"0");
1117 BEAST_EXPECT(jrNextOffer[jss::Flags] == 0);
1118 BEAST_EXPECT(jrNextOffer[sfLedgerEntryType.fieldName] == jss::Offer);
1119 BEAST_EXPECT(jrNextOffer[sfOwnerNode.fieldName] ==
"0");
1120 BEAST_EXPECT(jrNextOffer[sfSequence.fieldName] == 5);
1122 jrNextOffer[jss::TakerGets] ==
1125 jrNextOffer[jss::TakerPays] ==
1127 BEAST_EXPECT(jrNextOffer[jss::owner_funds] ==
"50");
1128 BEAST_EXPECT(jrNextOffer[jss::quality] ==
"400000000");
1130 jv = wsc->invoke(
"unsubscribe", books);
1131 if (wsc->version() == 2)
1134 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
1136 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
1137 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
1139 BEAST_EXPECT(jv[jss::status] ==
"success");
1150 auto maybeJv = wsc->getMsg(timeout);
1155 if (!(*maybeJv).isMember(jss::transaction))
1157 auto const& t = (*maybeJv)[jss::transaction];
1158 if (t[jss::TransactionType] != jss::OfferCreate ||
1163 return wsc->getMsg(timeout) == std::nullopt;
1169 testcase(
"Crossing single book offer");
1175 using namespace jtx;
1182 auto const gw =
Account(
"gateway");
1183 auto const alice =
Account(
"alice");
1184 auto const bob =
Account(
"bob");
1185 auto const charlie =
Account(
"charlie");
1186 auto const USD = gw[
"USD"];
1188 env.
fund(
XRP(1000000), gw, alice, bob, charlie);
1191 env(
trust(alice, USD(500)));
1192 env(
trust(bob, USD(500)));
1195 env(
pay(gw, alice, USD(500)));
1196 env(
pay(gw, bob, USD(500)));
1200 env(
offer(alice,
XRP(500), USD(500)));
1201 env(
offer(bob,
XRP(500), USD(500)));
1211 j[jss::snapshot] =
false;
1212 j[jss::taker_gets][jss::currency] =
"XRP";
1213 j[jss::taker_pays][jss::currency] =
"USD";
1214 j[jss::taker_pays][jss::issuer] = gw.human();
1217 auto jv = wsc->invoke(
"subscribe", books);
1218 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1223 env(
offer(charlie, USD(1000),
XRP(1000)));
1226 using namespace std::chrono_literals;
1230 auto jv = wsc->invoke(
"unsubscribe", books);
1231 BEAST_EXPECT(jv[jss::status] ==
"success");
1237 testcase(
"Crossing multi-book offer");
1243 using namespace jtx;
1252 auto const gw =
Account(
"gateway");
1253 auto const alice =
Account(
"alice");
1254 auto const bob =
Account(
"bob");
1255 auto const charlie =
Account(
"charlie");
1256 auto const USD = gw[
"USD"];
1257 auto const EUR = gw[
"EUR"];
1259 env.
fund(
XRP(1000000), gw, alice, bob, charlie);
1262 for (
auto const& account : {alice, bob, charlie})
1264 for (
auto const& iou : {USD, EUR})
1266 env(
trust(account, iou(1)));
1271 env(
pay(gw, alice, USD(1)));
1272 env(
pay(gw, charlie, EUR(1)));
1275 env(
offer(alice,
XRP(100), USD(1)));
1287 j[jss::snapshot] =
false;
1288 j[jss::taker_gets][jss::currency] =
"XRP";
1289 j[jss::taker_pays][jss::currency] =
"USD";
1290 j[jss::taker_pays][jss::issuer] = gw.human();
1295 j[jss::snapshot] =
false;
1296 j[jss::taker_gets][jss::currency] =
"EUR";
1297 j[jss::taker_gets][jss::issuer] = gw.human();
1298 j[jss::taker_pays][jss::currency] =
"XRP";
1301 auto jv = wsc->invoke(
"subscribe", books);
1302 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1307 env(
offer(charlie, USD(1), EUR(1)));
1309 using namespace std::chrono_literals;
1313 auto jv = wsc->invoke(
"unsubscribe", books);
1314 BEAST_EXPECT(jv[jss::status] ==
"success");
1321 using namespace jtx;
1325 env.
fund(
XRP(10000), alice, gw);
1327 auto USD = gw[
"USD"];
1331 jvParams[jss::ledger_index] = 10u;
1332 auto const jrr = env.
rpc(
1333 "json",
"book_offers",
to_string(jvParams))[jss::result];
1334 BEAST_EXPECT(jrr[jss::error] ==
"lgrNotFound");
1335 BEAST_EXPECT(jrr[jss::error_message] ==
"ledgerNotFound");
1340 jvParams[jss::ledger_index] =
"validated";
1341 auto const jrr = env.
rpc(
1342 "json",
"book_offers",
to_string(jvParams))[jss::result];
1343 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1345 jrr[jss::error_message] ==
"Missing field 'taker_pays'.");
1350 jvParams[jss::ledger_index] =
"validated";
1352 auto const jrr = env.
rpc(
1353 "json",
"book_offers",
to_string(jvParams))[jss::result];
1354 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1356 jrr[jss::error_message] ==
"Missing field 'taker_gets'.");
1361 jvParams[jss::ledger_index] =
"validated";
1362 jvParams[jss::taker_pays] =
"not an object";
1364 auto const jrr = env.
rpc(
1365 "json",
"book_offers",
to_string(jvParams))[jss::result];
1366 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1368 jrr[jss::error_message] ==
1369 "Invalid field 'taker_pays', not object.");
1374 jvParams[jss::ledger_index] =
"validated";
1376 jvParams[jss::taker_gets] =
"not an object";
1377 auto const jrr = env.
rpc(
1378 "json",
"book_offers",
to_string(jvParams))[jss::result];
1379 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1381 jrr[jss::error_message] ==
1382 "Invalid field 'taker_gets', not object.");
1387 jvParams[jss::ledger_index] =
"validated";
1390 auto const jrr = env.
rpc(
1391 "json",
"book_offers",
to_string(jvParams))[jss::result];
1392 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1394 jrr[jss::error_message] ==
1395 "Missing field 'taker_pays.currency'.");
1400 jvParams[jss::ledger_index] =
"validated";
1401 jvParams[jss::taker_pays][jss::currency] = 1;
1403 auto const jrr = env.
rpc(
1404 "json",
"book_offers",
to_string(jvParams))[jss::result];
1405 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1407 jrr[jss::error_message] ==
1408 "Invalid field 'taker_pays.currency', not string.");
1413 jvParams[jss::ledger_index] =
"validated";
1414 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1416 auto const jrr = env.
rpc(
1417 "json",
"book_offers",
to_string(jvParams))[jss::result];
1418 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1420 jrr[jss::error_message] ==
1421 "Missing field 'taker_gets.currency'.");
1426 jvParams[jss::ledger_index] =
"validated";
1427 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1428 jvParams[jss::taker_gets][jss::currency] = 1;
1429 auto const jrr = env.
rpc(
1430 "json",
"book_offers",
to_string(jvParams))[jss::result];
1431 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1433 jrr[jss::error_message] ==
1434 "Invalid field 'taker_gets.currency', not string.");
1439 jvParams[jss::ledger_index] =
"validated";
1440 jvParams[jss::taker_pays][jss::currency] =
"NOT_VALID";
1441 jvParams[jss::taker_gets][jss::currency] =
"XRP";
1442 auto const jrr = env.
rpc(
1443 "json",
"book_offers",
to_string(jvParams))[jss::result];
1444 BEAST_EXPECT(jrr[jss::error] ==
"srcCurMalformed");
1446 jrr[jss::error_message] ==
1447 "Invalid field 'taker_pays.currency', bad currency.");
1452 jvParams[jss::ledger_index] =
"validated";
1453 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1454 jvParams[jss::taker_gets][jss::currency] =
"NOT_VALID";
1455 auto const jrr = env.
rpc(
1456 "json",
"book_offers",
to_string(jvParams))[jss::result];
1457 BEAST_EXPECT(jrr[jss::error] ==
"dstAmtMalformed");
1459 jrr[jss::error_message] ==
1460 "Invalid field 'taker_gets.currency', bad currency.");
1465 jvParams[jss::ledger_index] =
"validated";
1466 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1467 jvParams[jss::taker_gets][jss::currency] =
"USD";
1468 jvParams[jss::taker_gets][jss::issuer] = 1;
1469 auto const jrr = env.
rpc(
1470 "json",
"book_offers",
to_string(jvParams))[jss::result];
1471 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1473 jrr[jss::error_message] ==
1474 "Invalid field 'taker_gets.issuer', not string.");
1479 jvParams[jss::ledger_index] =
"validated";
1480 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1481 jvParams[jss::taker_pays][jss::issuer] = 1;
1482 jvParams[jss::taker_gets][jss::currency] =
"USD";
1483 auto const jrr = env.
rpc(
1484 "json",
"book_offers",
to_string(jvParams))[jss::result];
1485 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1487 jrr[jss::error_message] ==
1488 "Invalid field 'taker_pays.issuer', not string.");
1493 jvParams[jss::ledger_index] =
"validated";
1494 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1495 jvParams[jss::taker_pays][jss::issuer] = gw.human() +
"DEAD";
1496 jvParams[jss::taker_gets][jss::currency] =
"USD";
1497 auto const jrr = env.
rpc(
1498 "json",
"book_offers",
to_string(jvParams))[jss::result];
1499 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1501 jrr[jss::error_message] ==
1502 "Invalid field 'taker_pays.issuer', bad issuer.");
1507 jvParams[jss::ledger_index] =
"validated";
1508 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1510 jvParams[jss::taker_gets][jss::currency] =
"USD";
1511 auto const jrr = env.
rpc(
1512 "json",
"book_offers",
to_string(jvParams))[jss::result];
1513 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1515 jrr[jss::error_message] ==
1516 "Invalid field 'taker_pays.issuer', bad issuer account one.");
1521 jvParams[jss::ledger_index] =
"validated";
1522 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1523 jvParams[jss::taker_gets][jss::currency] =
"USD";
1524 jvParams[jss::taker_gets][jss::issuer] = gw.human() +
"DEAD";
1525 auto const jrr = env.
rpc(
1526 "json",
"book_offers",
to_string(jvParams))[jss::result];
1527 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1529 jrr[jss::error_message] ==
1530 "Invalid field 'taker_gets.issuer', bad issuer.");
1535 jvParams[jss::ledger_index] =
"validated";
1536 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1537 jvParams[jss::taker_gets][jss::currency] =
"USD";
1539 auto const jrr = env.
rpc(
1540 "json",
"book_offers",
to_string(jvParams))[jss::result];
1541 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1543 jrr[jss::error_message] ==
1544 "Invalid field 'taker_gets.issuer', bad issuer account one.");
1549 jvParams[jss::ledger_index] =
"validated";
1550 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1551 jvParams[jss::taker_pays][jss::issuer] = alice.human();
1552 jvParams[jss::taker_gets][jss::currency] =
"USD";
1553 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1554 auto const jrr = env.
rpc(
1555 "json",
"book_offers",
to_string(jvParams))[jss::result];
1556 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1558 jrr[jss::error_message] ==
1559 "Unneeded field 'taker_pays.issuer' "
1560 "for XRP currency specification.");
1565 jvParams[jss::ledger_index] =
"validated";
1566 jvParams[jss::taker_pays][jss::currency] =
"USD";
1568 jvParams[jss::taker_gets][jss::currency] =
"USD";
1569 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1570 auto const jrr = env.
rpc(
1571 "json",
"book_offers",
to_string(jvParams))[jss::result];
1572 BEAST_EXPECT(jrr[jss::error] ==
"srcIsrMalformed");
1574 jrr[jss::error_message] ==
1575 "Invalid field 'taker_pays.issuer', expected non-XRP issuer.");
1580 jvParams[jss::ledger_index] =
"validated";
1581 jvParams[jss::taker] = 1;
1582 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1583 jvParams[jss::taker_gets][jss::currency] =
"USD";
1584 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1585 auto const jrr = env.
rpc(
1586 "json",
"book_offers",
to_string(jvParams))[jss::result];
1587 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1589 jrr[jss::error_message] ==
1590 "Invalid field 'taker', not string.");
1595 jvParams[jss::ledger_index] =
"validated";
1596 jvParams[jss::taker] = env.
master.
human() +
"DEAD";
1597 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1598 jvParams[jss::taker_gets][jss::currency] =
"USD";
1599 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1600 auto const jrr = env.
rpc(
1601 "json",
"book_offers",
to_string(jvParams))[jss::result];
1602 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1603 BEAST_EXPECT(jrr[jss::error_message] ==
"Invalid field 'taker'.");
1608 jvParams[jss::ledger_index] =
"validated";
1610 jvParams[jss::taker_pays][jss::currency] =
"USD";
1611 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1612 jvParams[jss::taker_gets][jss::currency] =
"USD";
1613 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1614 auto const jrr = env.
rpc(
1615 "json",
"book_offers",
to_string(jvParams))[jss::result];
1616 BEAST_EXPECT(jrr[jss::error] ==
"badMarket");
1617 BEAST_EXPECT(jrr[jss::error_message] ==
"No such market.");
1622 jvParams[jss::ledger_index] =
"validated";
1624 jvParams[jss::limit] =
"0";
1625 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1626 jvParams[jss::taker_gets][jss::currency] =
"USD";
1627 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1628 auto const jrr = env.
rpc(
1629 "json",
"book_offers",
to_string(jvParams))[jss::result];
1630 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1632 jrr[jss::error_message] ==
1633 "Invalid field 'limit', not unsigned integer.");
1638 jvParams[jss::ledger_index] =
"validated";
1639 jvParams[jss::taker_pays][jss::currency] =
"USD";
1640 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1641 jvParams[jss::taker_gets][jss::currency] =
"USD";
1642 auto const jrr = env.
rpc(
1643 "json",
"book_offers",
to_string(jvParams))[jss::result];
1644 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1646 jrr[jss::error_message] ==
1647 "Invalid field 'taker_gets.issuer', "
1648 "expected non-XRP issuer.");
1653 jvParams[jss::ledger_index] =
"validated";
1654 jvParams[jss::taker_pays][jss::currency] =
"USD";
1655 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1656 jvParams[jss::taker_gets][jss::currency] =
"XRP";
1657 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1658 auto const jrr = env.
rpc(
1659 "json",
"book_offers",
to_string(jvParams))[jss::result];
1660 BEAST_EXPECT(jrr[jss::error] ==
"dstIsrMalformed");
1662 jrr[jss::error_message] ==
1663 "Unneeded field 'taker_gets.issuer' "
1664 "for XRP currency specification.");
1668 jvParams[jss::ledger_index] =
"validated";
1669 jvParams[jss::taker_pays][jss::currency] =
"USD";
1670 jvParams[jss::taker_pays][jss::issuer] = gw.human();
1671 jvParams[jss::taker_gets][jss::currency] =
"EUR";
1672 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1673 jvParams[jss::domain] =
"badString";
1674 auto const jrr = env.
rpc(
1675 "json",
"book_offers",
to_string(jvParams))[jss::result];
1676 BEAST_EXPECT(jrr[jss::error] ==
"domainMalformed");
1677 BEAST_EXPECT(jrr[jss::error_message] ==
"Unable to parse domain.");
1685 using namespace jtx;
1688 env.fund(
XRP(200000), gw);
1693 auto USD = gw[
"USD"];
1696 env(
offer(gw,
XRP(50 + 1 * i), USD(1.0 + 0.1 * i)));
1702 jvParams[jss::limit] = 1;
1703 jvParams[jss::ledger_index] =
"validated";
1704 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1705 jvParams[jss::taker_gets][jss::currency] =
"USD";
1706 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1708 env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1709 BEAST_EXPECT(jrr[jss::offers].isArray());
1710 BEAST_EXPECT(jrr[jss::offers].size() == (asAdmin ? 1u : 0u));
1713 jvParams[jss::limit] = 0u;
1714 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1715 BEAST_EXPECT(jrr[jss::offers].isArray());
1716 BEAST_EXPECT(jrr[jss::offers].size() == 0u);
1719 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1720 BEAST_EXPECT(jrr[jss::offers].isArray());
1722 jrr[jss::offers].size() ==
1726 jrr = env.rpc(
"json",
"book_offers",
to_string(jvParams))[jss::result];
1727 BEAST_EXPECT(jrr[jss::offers].isArray());
1729 jrr[jss::offers].size() ==
1737 using namespace jtx;
1741 featureCredentials | featurePermissionedDEX};
1745 auto const alice = permDex.
alice;
1746 auto const bob = permDex.
bob;
1747 auto const carol = permDex.
carol;
1748 auto const domainID = permDex.
domainID;
1749 auto const gw = permDex.
gw;
1750 auto const USD = permDex.
USD;
1757 auto checkBookOffers = [&](
Json::Value const& jrr) {
1758 BEAST_EXPECT(jrr[jss::offers].isArray());
1759 BEAST_EXPECT(jrr[jss::offers].size() == 1);
1760 auto const jrOffer = jrr[jss::offers][0u];
1761 BEAST_EXPECT(jrOffer[sfAccount.fieldName] == alice.human());
1763 jrOffer[sfBookDirectory.fieldName] ==
1765 BEAST_EXPECT(jrOffer[sfBookNode.fieldName] ==
"0");
1766 BEAST_EXPECT(jrOffer[jss::Flags] == 0);
1767 BEAST_EXPECT(jrOffer[sfLedgerEntryType.fieldName] == jss::Offer);
1768 BEAST_EXPECT(jrOffer[sfOwnerNode.fieldName] ==
"0");
1770 jrOffer[jss::TakerGets] ==
1773 jrOffer[jss::TakerPays] ==
1776 jrOffer[sfDomainID.jsonName].asString() ==
to_string(domainID));
1783 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1784 jvParams[jss::ledger_index] =
"validated";
1785 jvParams[jss::taker_gets][jss::currency] =
"USD";
1786 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1788 auto jv = wsc->invoke(
"book_offers", jvParams);
1789 auto jrr = jv[jss::result];
1790 BEAST_EXPECT(jrr[jss::offers].isArray());
1791 BEAST_EXPECT(jrr[jss::offers].size() == 0);
1796 jv[jss::result].isMember(jss::offers) &&
1797 jv[jss::result][jss::offers].size() == 1);
1799 jv[jss::result][jss::offers][0u][jss::TakerGets] ==
1802 jv[jss::result][jss::offers][0u][jss::TakerPays] ==
1805 jv[jss::result][jss::offers][0u][sfDomainID.jsonName]
1813 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1814 jvParams[jss::ledger_index] =
"validated";
1815 jvParams[jss::taker_gets][jss::currency] =
"USD";
1816 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1817 jvParams[jss::domain] =
to_string(domainID);
1819 auto jv = wsc->invoke(
"book_offers", jvParams);
1820 auto jrr = jv[jss::result];
1821 checkBookOffers(jrr);
1830 j[jss::snapshot] =
true;
1831 j[jss::taker_pays][jss::currency] =
"XRP";
1832 j[jss::taker_gets][jss::currency] =
"USD";
1833 j[jss::taker_gets][jss::issuer] = gw.human();
1837 auto jv = wsc->invoke(
"subscribe", books);
1838 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1849 j[jss::snapshot] =
true;
1850 j[jss::taker_pays][jss::currency] =
"XRP";
1851 j[jss::taker_gets][jss::currency] =
"USD";
1852 j[jss::taker_gets][jss::issuer] = gw.human();
1855 auto jv = wsc->invoke(
"subscribe", books);
1856 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1859 jv[jss::result].isMember(jss::offers) &&
1860 jv[jss::result][jss::offers].size() == 0);
1868 using namespace jtx;
1872 featureCredentials | featurePermissionedDEX};
1876 auto const alice = permDex.
alice;
1877 auto const bob = permDex.
bob;
1878 auto const carol = permDex.
carol;
1879 auto const domainID = permDex.
domainID;
1880 auto const gw = permDex.
gw;
1881 auto const USD = permDex.
USD;
1885 env(
offer(alice,
XRP(10), USD(10)),
1890 auto checkBookOffers = [&](
Json::Value const& jrr) {
1891 BEAST_EXPECT(jrr[jss::offers].isArray());
1892 BEAST_EXPECT(jrr[jss::offers].size() == 1);
1893 auto const jrOffer = jrr[jss::offers][0u];
1894 BEAST_EXPECT(jrOffer[sfAccount.fieldName] == alice.human());
1896 jrOffer[sfBookDirectory.fieldName] ==
1898 BEAST_EXPECT(jrOffer[sfBookNode.fieldName] ==
"0");
1899 BEAST_EXPECT(jrOffer[jss::Flags] ==
lsfHybrid);
1900 BEAST_EXPECT(jrOffer[sfLedgerEntryType.fieldName] == jss::Offer);
1901 BEAST_EXPECT(jrOffer[sfOwnerNode.fieldName] ==
"0");
1903 jrOffer[jss::TakerGets] ==
1906 jrOffer[jss::TakerPays] ==
1909 jrOffer[sfDomainID.jsonName].asString() ==
to_string(domainID));
1910 BEAST_EXPECT(jrOffer[sfAdditionalBooks.jsonName].size() == 1);
1917 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1918 jvParams[jss::ledger_index] =
"validated";
1919 jvParams[jss::taker_gets][jss::currency] =
"USD";
1920 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1922 auto jv = wsc->invoke(
"book_offers", jvParams);
1923 auto jrr = jv[jss::result];
1924 checkBookOffers(jrr);
1929 jv[jss::result].isMember(jss::offers) &&
1930 jv[jss::result][jss::offers].size() == 1);
1932 jv[jss::result][jss::offers][0u][jss::TakerGets] ==
1935 jv[jss::result][jss::offers][0u][jss::TakerPays] ==
1938 jv[jss::result][jss::offers][0u][sfDomainID.jsonName]
1946 jvParams[jss::taker_pays][jss::currency] =
"XRP";
1947 jvParams[jss::ledger_index] =
"validated";
1948 jvParams[jss::taker_gets][jss::currency] =
"USD";
1949 jvParams[jss::taker_gets][jss::issuer] = gw.human();
1950 jvParams[jss::domain] =
to_string(domainID);
1952 auto jv = wsc->invoke(
"book_offers", jvParams);
1953 auto jrr = jv[jss::result];
1954 checkBookOffers(jrr);
1963 j[jss::snapshot] =
true;
1964 j[jss::taker_pays][jss::currency] =
"XRP";
1965 j[jss::taker_gets][jss::currency] =
"USD";
1966 j[jss::taker_gets][jss::issuer] = gw.human();
1970 auto jv = wsc->invoke(
"subscribe", books);
1971 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
1976 auto unsubJv = wsc->invoke(
"unsubscribe", books);
1977 if (wsc->version() == 2)
1978 BEAST_EXPECT(unsubJv[jss::status] ==
"success");
1987 j[jss::snapshot] =
true;
1988 j[jss::taker_pays][jss::currency] =
"XRP";
1989 j[jss::taker_gets][jss::currency] =
"USD";
1990 j[jss::taker_gets][jss::issuer] = gw.human();
1993 auto jv = wsc->invoke(
"subscribe", books);
1994 if (!BEAST_EXPECT(jv[jss::status] ==
"success"))
Value & append(Value const &value)
Append value to array at the end.
testcase_t testcase
Memberspace for declaring test cases.
virtual Config & config()=0
A currency issued by an account.
Json::Value getJson(JsonOptions=JsonOptions::none) const override
void testMultipleBooksBothSidesOffersInBook()
void testMultipleBooksBothSidesEmptyBook()
void testBothSidesOffersInBook()
void testCrossingMultiBookOffer()
void testTrackDomainOffer()
std::string getBookDir(jtx::Env &env, Issue const &in, Issue const &out, std::optional< uint256 > const &domain=std::nullopt)
void testOneSideOffersInBook()
void testBothSidesEmptyBook()
void testMultipleBooksOneSideEmptyBook()
void testBookOfferLimits(bool asAdmin)
void testOneSideEmptyBook()
void testBookOfferErrors()
void testMultipleBooksOneSideOffersInBook()
void run() override
Runs the suite.
void testCrossingSingleBookOffer()
static bool offerOnlyOnceInStream(std::unique_ptr< WSClient > const &wsc, std::chrono::milliseconds const &timeout, jtx::PrettyAmount const &takerGets, jtx::PrettyAmount const &takerPays)
void testTrackHybridOffer()
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
void require(Args const &... args)
Check a set of requirements.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Match the number of items in the account's owner directory.
Check a set of conditions.
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
static LimitRange constexpr bookOffers
Limits for the book_offers command.
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
owner_count< ltOFFER > offers
Match the number of offers in the account's owner directory.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
std::unique_ptr< Config > no_admin(std::unique_ptr< Config >)
adjust config so no admin ports are enabled
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
FeatureBitset testable_amendments()
Json::Value rate(Account const &account, double multiplier)
Set a transfer rate.
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
XRP_t const XRP
Converts to XRP Issue or STAmount.
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
AccountID const & noAccount()
A placeholder for empty accounts.
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
bool cdirFirst(ReadView const &view, uint256 const &root, std::shared_ptr< SLE const > &page, unsigned int &index, uint256 &entry)
Returns the first entry in the directory, advancing the index.
AccountID const & xrpAccount()
Compute AccountID from public key.
constexpr std::uint32_t tfHybrid
uint256 getQualityNext(uint256 const &uBase)
std::string to_string(base_uint< Bits, Tag > const &a)
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
uint256 getBookBase(Book const &book)
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...
STAmount const & value() const