151 using namespace std::chrono_literals;
154 auto baseFee = env.
current()->fees().base.drops();
161 stream[jss::streams].append(
"transactions");
162 auto jv = wsc->invoke(
"subscribe", stream);
163 if (wsc->version() == 2)
165 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
166 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
167 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
169 BEAST_EXPECT(jv[jss::status] ==
"success");
177 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
178 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"]
180 == Account(
"alice").human() &&
181 jv[jss::transaction][jss::TransactionType]
183 jv[jss::transaction][jss::DeliverMax]
184 == std::to_string(10000000000 + baseFee) &&
185 jv[jss::transaction][jss::Fee]
186 == std::to_string(baseFee) &&
187 jv[jss::transaction][jss::Sequence]
192 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
193 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"][
"FinalFields"]
194 [jss::Account] == Account(
"alice").human();
201 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
202 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"]
204 == Account(
"bob").human() &&
205 jv[jss::transaction][jss::TransactionType]
207 jv[jss::transaction][jss::DeliverMax]
208 == std::to_string(10000000000 + baseFee) &&
209 jv[jss::transaction][jss::Fee]
210 == std::to_string(baseFee) &&
211 jv[jss::transaction][jss::Sequence]
216 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
217 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"][
"FinalFields"]
218 [jss::Account] == Account(
"bob").human();
224 auto jv = wsc->invoke(
"unsubscribe", stream);
225 if (wsc->version() == 2)
227 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
228 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
229 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
231 BEAST_EXPECT(jv[jss::status] ==
"success");
238 stream[jss::accounts].append(
Account(
"alice").human());
239 auto jv = wsc->invoke(
"subscribe", stream);
240 if (wsc->version() == 2)
242 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
243 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
244 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
246 BEAST_EXPECT(jv[jss::status] ==
"success");
253 BEAST_EXPECT(!wsc->getMsg(10ms));
260 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
261 return jv[jss::meta][
"AffectedNodes"][1u][
"ModifiedNode"][
"FinalFields"]
262 [jss::Account] == Account(
"alice").human();
265 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
266 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"][
"NewFields"][
"LowLimit"]
267 [jss::issuer] == Account(
"alice").human();
272 auto jv = wsc->invoke(
"unsubscribe", stream);
273 if (wsc->version() == 2)
275 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
276 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
277 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
279 BEAST_EXPECT(jv[jss::status] ==
"success");
399 auto& cfg = env.app().config();
400 if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
402 auto const parsedseed = parseBase58<Seed>(cfg.section(SECTION_VALIDATION_SEED).values()[0]);
403 if (BEAST_EXPECT(parsedseed); not parsedseed.has_value())
417 stream[jss::streams].append(
"validations");
418 auto jv = wsc->invoke(
"subscribe", stream);
419 if (wsc->version() == 2)
421 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
422 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
423 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
425 BEAST_EXPECT(jv[jss::status] ==
"success");
430 auto validValidationFields = [&env, &valPublicKey](
Json::Value const& jv) {
431 if (jv[jss::type] !=
"validationReceived")
434 if (jv[jss::validation_public_key].asString() != valPublicKey)
437 if (jv[jss::ledger_hash] !=
to_string(env.closed()->header().hash))
440 if (jv[jss::ledger_index] !=
std::to_string(env.closed()->header().seq))
446 if (jv[jss::full] !=
true)
449 if (jv.isMember(jss::load_fee))
452 if (!jv.isMember(jss::signature))
455 if (!jv.isMember(jss::signing_time))
458 if (!jv.isMember(jss::cookie))
461 if (!jv.isMember(jss::validated_hash))
464 uint32_t
const netID = env.app().getNetworkIDService().getNetworkID();
465 if (!jv.isMember(jss::network_id) || jv[jss::network_id] != netID)
469 bool const isFlagLedger = (env.closed()->header().seq + 1) % 256 == 0;
485 while (env.closed()->header().seq < 300)
487 BEAST_EXPECT(env.syncClose());
488 using namespace std::chrono_literals;
489 BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
494 auto jv = wsc->invoke(
"unsubscribe", stream);
495 if (wsc->version() == 2)
497 BEAST_EXPECT(jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
498 BEAST_EXPECT(jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
499 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
501 BEAST_EXPECT(jv[jss::status] ==
"success");
537 auto const method = subscribe ?
"subscribe" :
"unsubscribe";
538 testcase <<
"Error cases for " << method;
544 auto jr = env.rpc(
"json", method,
"{}")[jss::result];
545 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
546 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
551 jv[jss::url] =
"not-a-url";
552 jv[jss::username] =
"admin";
553 jv[jss::password] =
"password";
554 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
557 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
558 BEAST_EXPECT(jr[jss::error_message] ==
"Failed to parse url.");
566 jv[jss::url] =
"ftp://scheme.not.supported.tld";
567 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
570 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
571 BEAST_EXPECT(jr[jss::error_message] ==
"Only http and https is supported.");
578 jv[jss::url] =
"no-url";
579 auto jr = env_nonadmin.rpc(
"json", method,
to_string(jv))[jss::result];
580 BEAST_EXPECT(jr[jss::error] ==
"noPermission");
581 BEAST_EXPECT(jr[jss::error_message] ==
"You don't have permission for this command.");
593 for (
auto const& f : {jss::accounts_proposed, jss::accounts})
595 for (
auto const& nonArray : nonArrays)
599 auto jr = wsc->invoke(method, jv)[jss::result];
600 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
601 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
607 auto jr = wsc->invoke(method, jv)[jss::result];
608 BEAST_EXPECT(jr[jss::error] ==
"actMalformed");
609 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
613 for (
auto const& nonArray : nonArrays)
616 jv[jss::books] = nonArray;
617 auto jr = wsc->invoke(method, jv)[jss::result];
618 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
619 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
625 jv[jss::books][0u] = 1;
626 auto jr = wsc->invoke(method, jv)[jss::result];
627 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
628 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
637 auto jr = wsc->invoke(method, jv)[jss::result];
638 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
639 BEAST_EXPECT(jr[jss::error_message] ==
"Source currency is malformed.");
648 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"ZZZZ";
649 auto jr = wsc->invoke(method, jv)[jss::result];
650 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
651 BEAST_EXPECT(jr[jss::error_message] ==
"Source currency is malformed.");
660 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
661 jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
662 auto jr = wsc->invoke(method, jv)[jss::result];
663 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
664 BEAST_EXPECT(jr[jss::error_message] ==
"Source issuer is malformed.");
673 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
674 jv[jss::books][0u][jss::taker_pays][jss::issuer] =
Account{
"gateway"}.
human() +
"DEAD";
675 auto jr = wsc->invoke(method, jv)[jss::result];
676 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
677 BEAST_EXPECT(jr[jss::error_message] ==
"Source issuer is malformed.");
684 jv[jss::books][0u][jss::taker_pays] =
687 auto jr = wsc->invoke(method, jv)[jss::result];
690 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
692 jr[jss::error_message] ==
"Destination amount/currency/issuer is malformed.");
699 jv[jss::books][0u][jss::taker_pays] =
701 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"ZZZZ";
702 auto jr = wsc->invoke(method, jv)[jss::result];
705 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
707 jr[jss::error_message] ==
"Destination amount/currency/issuer is malformed.");
714 jv[jss::books][0u][jss::taker_pays] =
716 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
717 jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
718 auto jr = wsc->invoke(method, jv)[jss::result];
719 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
720 BEAST_EXPECT(jr[jss::error_message] ==
"Destination issuer is malformed.");
727 jv[jss::books][0u][jss::taker_pays] =
729 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
730 jv[jss::books][0u][jss::taker_gets][jss::issuer] =
Account{
"gateway"}.
human() +
"DEAD";
731 auto jr = wsc->invoke(method, jv)[jss::result];
732 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
733 BEAST_EXPECT(jr[jss::error_message] ==
"Destination issuer is malformed.");
740 jv[jss::books][0u][jss::taker_pays] =
742 jv[jss::books][0u][jss::taker_gets] =
744 auto jr = wsc->invoke(method, jv)[jss::result];
745 BEAST_EXPECT(jr[jss::error] ==
"badMarket");
746 BEAST_EXPECT(jr[jss::error_message] ==
"No such market.");
749 for (
auto const& nonArray : nonArrays)
752 jv[jss::streams] = nonArray;
753 auto jr = wsc->invoke(method, jv)[jss::result];
754 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
755 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
761 jv[jss::streams][0u] = 1;
762 auto jr = wsc->invoke(method, jv)[jss::result];
763 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
764 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
770 jv[jss::streams][0u] =
"not_a_stream";
771 auto jr = wsc->invoke(method, jv)[jss::result];
772 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
773 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
782 using namespace std::chrono_literals;
795 auto goodSubRPC = [](
Json::Value const& subReply) ->
bool {
796 return subReply.isMember(jss::result) && subReply[jss::result].isMember(jss::status) &&
797 subReply[jss::result][jss::status] == jss::success;
810 bool first_flag =
false;
812 for (
int i = 0; i < numReplies; ++i)
815 auto reply = wsc.
getMsg(timeout);
819 if (r.isMember(jss::account_history_tx_index))
820 idx = r[jss::account_history_tx_index].asInt();
821 if (r.isMember(jss::account_history_tx_first))
823 bool const boundary = r.isMember(jss::account_history_boundary);
824 int const ledger_idx = r[jss::ledger_index].asInt();
825 if (r.isMember(jss::transaction) && r[jss::transaction].isMember(jss::hash))
827 auto t{r[jss::transaction]};
828 v.emplace_back(idx, t[jss::hash].asString(), boundary, ledger_idx);
832 return {
false, first_flag};
835 return {
true, first_flag};
842 auto sendPayments = [
this](
851 for (
int i = 0; i < newTxns; ++i)
853 auto& from = (i % 2 == 0) ? a : b;
854 auto& to = (i % 2 == 0) ? b : a;
860 for (
int i = 0; i < ledgersToClose; ++i)
870 auto hashCompare = [](IdxHashVec
const& accountVec,
871 IdxHashVec
const& txHistoryVec,
872 bool sizeCompare) ->
bool {
873 if (accountVec.empty() || txHistoryVec.empty())
875 if (sizeCompare && accountVec.size() != (txHistoryVec.size()))
879 for (
auto const& tx : txHistoryVec)
885 if (i >= accountVec.size())
888 if (it == txHistoryMap.
end())
893 auto firstHistoryIndex = getHistoryIndex(0);
894 if (!firstHistoryIndex)
896 for (
std::size_t i = 1; i < accountVec.size(); ++i)
898 if (
auto idx = getHistoryIndex(i); !idx || *idx != *firstHistoryIndex + i)
934 auto checkBoundary = [](IdxHashVec
const& vec,
bool ) {
935 size_t const num_tx = vec.size();
936 for (
size_t i = 0; i < num_tx; ++i)
938 auto [idx, hash, boundary, ledger] = vec[i];
939 if ((i + 1 == num_tx || ledger !=
std::get<3>(vec[i + 1])) != boundary)
958 request[jss::account_history_tx_stream][jss::account] = alice.
human();
959 auto jv = wscTxHistory->invoke(
"subscribe", request);
960 if (!BEAST_EXPECT(goodSubRPC(jv)))
963 jv = wscTxHistory->invoke(
"subscribe", request);
964 BEAST_EXPECT(!goodSubRPC(jv));
969 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
true;
970 jv = wscTxHistory->invoke(
"unsubscribe", request);
971 if (!BEAST_EXPECT(goodSubRPC(jv)))
974 sendPayments(env, env.
master, alice, 1, 1, 123456);
977 auto r = getTxHash(*wscTxHistory, vec, 1);
978 if (!BEAST_EXPECT(r.first && r.second))
984 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
false;
985 jv = wscTxHistory->invoke(
"unsubscribe", request);
986 BEAST_EXPECT(goodSubRPC(jv));
988 sendPayments(env, env.
master, alice, 1, 1);
989 r = getTxHash(*wscTxHistory, vec, 1, 10ms);
990 BEAST_EXPECT(!r.first);
1001 request[jss::account_history_tx_stream][jss::account] =
1002 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1003 auto jv = wscTxHistory->invoke(
"subscribe", request);
1004 if (!BEAST_EXPECT(goodSubRPC(jv)))
1006 IdxHashVec genesisFullHistoryVec;
1008 if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1, 10ms).first))
1015 sendPayments(env, env.
master, bob, 1, 1, 654321);
1017 auto r = getTxHash(*wscTxHistory, genesisFullHistoryVec, 1);
1018 if (!BEAST_EXPECT(r.first && r.second))
1021 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1022 jv = wscTxHistory->invoke(
"subscribe", request);
1023 if (!BEAST_EXPECT(goodSubRPC(jv)))
1025 IdxHashVec bobFullHistoryVec;
1027 r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
1028 if (!BEAST_EXPECT(r.first && r.second))
1036 jv = wscTxHistory->invoke(
"unsubscribe", request);
1037 if (!BEAST_EXPECT(goodSubRPC(jv)))
1039 request[jss::account_history_tx_stream][jss::account] =
1040 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1041 jv = wscTxHistory->invoke(
"unsubscribe", request);
1042 BEAST_EXPECT(goodSubRPC(jv));
1048 sendPayments(env, env.
master, bob, 30, 300);
1050 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1051 jv = wscTxHistory->invoke(
"subscribe", request);
1053 bobFullHistoryVec.
clear();
1054 BEAST_EXPECT(getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second);
1055 jv = wscTxHistory->invoke(
"unsubscribe", request);
1057 request[jss::account_history_tx_stream][jss::account] =
1058 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1059 jv = wscTxHistory->invoke(
"subscribe", request);
1060 genesisFullHistoryVec.
clear();
1062 BEAST_EXPECT(getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
1063 jv = wscTxHistory->invoke(
"unsubscribe", request);
1079 env.
fund(
XRP(222222), accounts);
1085 stream[jss::accounts].append(alice.
human());
1086 auto jv = wscAccount->invoke(
"subscribe", stream);
1088 sendPayments(env, alice, bob, 5, 1);
1089 sendPayments(env, alice, bob, 5, 1);
1090 IdxHashVec accountVec;
1091 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1097 request[jss::account_history_tx_stream][jss::account] = alice.
human();
1098 jv = wscTxHistory->invoke(
"subscribe", request);
1101 IdxHashVec txHistoryVec;
1102 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1104 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1109 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1114 IdxHashVec initFundTxns;
1115 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, initFundTxns, 10).second) ||
1116 !BEAST_EXPECT(checkBoundary(initFundTxns,
false)))
1121 sendPayments(env, alice, bob, 10, 1);
1122 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1124 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1126 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1131 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1134 wscTxHistory->invoke(
"unsubscribe", request);
1135 wscAccount->invoke(
"unsubscribe", stream);
1144 auto const USD_a = alice[
"USD"];
1147 env.
fund(
XRP(333333), accounts);
1148 env.
trust(USD_a(20000), carol);
1151 auto mixedPayments = [&]() ->
int {
1152 sendPayments(env, alice, carol, 1, 0);
1153 env(
pay(alice, carol, USD_a(100)));
1161 request[jss::account_history_tx_stream][jss::account] = carol.
human();
1163 auto jv = ws->invoke(
"subscribe", request);
1168 getTxHash(*ws, tempVec, 100, 1000ms);
1171 auto count = mixedPayments();
1173 if (!BEAST_EXPECT(getTxHash(*ws, vec1, count).first))
1175 ws->invoke(
"unsubscribe", request);
1184 env.
fund(
XRP(444444), accounts);
1188 auto oneRound = [&](
int numPayments) {
1189 return sendPayments(env, alice, carol, numPayments, 300);
1195 request[jss::account_history_tx_stream][jss::account] = carol.
human();
1197 auto jv = wscLong->invoke(
"subscribe", request);
1202 getTxHash(*wscLong, tempVec, 100, 1000ms);
1206 for (
int kk = 2; kk < 10; ++kk)
1208 auto count = oneRound(kk);
1210 if (!BEAST_EXPECT(getTxHash(*wscLong, vec1, count).first))
1215 auto jv = wscShort->invoke(
"subscribe", request);
1217 if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first))
1219 if (!BEAST_EXPECT(hashCompare(vec1, vec2,
true)))
1221 wscShort->invoke(
"unsubscribe", request);
1289 testcase(
"Test synthetic fields from Subscribe response");
1291 using namespace test::jtx;
1292 using namespace std::chrono_literals;
1296 Account const broker{
"broker"};
1299 env.fund(
XRP(10000), alice, bob, broker);
1300 BEAST_EXPECT(env.syncClose());
1305 stream[jss::streams].append(
"transactions");
1306 auto jv = wsc->invoke(
"subscribe", stream);
1311 auto verifyNFTokenID = [&](
uint256 const& actualNftID) {
1312 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1314 BEAST_EXPECT(nftID.parseHex(jv[jss::meta][jss::nftoken_id].asString()));
1315 return nftID == actualNftID;
1322 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1323 std::vector<uint256> metaIDs;
1325 jv[jss::meta][jss::nftoken_ids].begin(),
1326 jv[jss::meta][jss::nftoken_ids].end(),
1327 std::back_inserter(metaIDs),
1328 [this](Json::Value id) {
1330 BEAST_EXPECT(nftID.parseHex(id.asString()));
1334 std::sort(metaIDs.begin(), metaIDs.end());
1335 std::sort(actualNftIDs.begin(), actualNftIDs.end());
1338 BEAST_EXPECT(metaIDs.size() == actualNftIDs.size());
1342 for (
size_t i = 0; i < metaIDs.size(); ++i)
1343 BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
1350 auto verifyNFTokenOfferID = [&](
uint256 const& offerID) {
1351 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1352 uint256 metaOfferID;
1353 BEAST_EXPECT(metaOfferID.parseHex(jv[jss::meta][jss::offer_id].asString()));
1354 return metaOfferID == offerID;
1363 env(
token::mint(alice, 0u), txflags(tfTransferable));
1364 BEAST_EXPECT(env.syncClose());
1365 verifyNFTokenID(nftId1);
1368 env(
token::mint(alice, 0u), txflags(tfTransferable));
1369 BEAST_EXPECT(env.syncClose());
1370 verifyNFTokenID(nftId2);
1377 BEAST_EXPECT(env.syncClose());
1378 verifyNFTokenOfferID(aliceOfferIndex1);
1382 BEAST_EXPECT(env.syncClose());
1383 verifyNFTokenOfferID(aliceOfferIndex2);
1389 BEAST_EXPECT(env.syncClose());
1390 verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
1396 BEAST_EXPECT(env.syncClose());
1397 verifyNFTokenOfferID(bobBuyOfferIndex);
1402 BEAST_EXPECT(env.syncClose());
1403 verifyNFTokenID(nftId1);
1410 env(
token::mint(alice, 0u), txflags(tfTransferable));
1411 BEAST_EXPECT(env.syncClose());
1412 verifyNFTokenID(nftId);
1417 token::destination(broker),
1418 txflags(tfSellNFToken));
1419 BEAST_EXPECT(env.syncClose());
1420 verifyNFTokenOfferID(offerAliceToBroker);
1425 BEAST_EXPECT(env.syncClose());
1426 verifyNFTokenOfferID(offerBobToBroker);
1430 BEAST_EXPECT(env.syncClose());
1431 verifyNFTokenID(nftId);
1439 env(
token::mint(alice, 0u), txflags(tfTransferable));
1440 BEAST_EXPECT(env.syncClose());
1441 verifyNFTokenID(nftId);
1446 BEAST_EXPECT(env.syncClose());
1447 verifyNFTokenOfferID(aliceOfferIndex1);
1451 BEAST_EXPECT(env.syncClose());
1452 verifyNFTokenOfferID(aliceOfferIndex2);
1457 BEAST_EXPECT(env.syncClose());
1458 verifyNFTokenIDsInCancelOffer({nftId});
1461 if (features[featureNFTokenMintOffer])
1465 BEAST_EXPECT(env.syncClose());
1466 verifyNFTokenOfferID(aliceMintWithOfferIndex1);