160 using namespace std::chrono_literals;
163 auto baseFee = env.
current()->fees().base.drops();
170 stream[jss::streams].append(
"transactions");
171 auto jv = wsc->invoke(
"subscribe", stream);
172 if (wsc->version() == 2)
175 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
177 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
178 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
180 BEAST_EXPECT(jv[jss::status] ==
"success");
188 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
189 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
190 [
"NewFields"][jss::Account]
191 == Account(
"alice").human() &&
192 jv[jss::transaction][jss::TransactionType]
194 jv[jss::transaction][jss::DeliverMax]
195 == std::to_string(10000000000 + baseFee) &&
196 jv[jss::transaction][jss::Fee]
197 == std::to_string(baseFee) &&
198 jv[jss::transaction][jss::Sequence]
203 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
204 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
205 [
"FinalFields"][jss::Account] ==
206 Account(
"alice").human();
213 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
214 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
215 [
"NewFields"][jss::Account]
216 == Account(
"bob").human() &&
217 jv[jss::transaction][jss::TransactionType]
219 jv[jss::transaction][jss::DeliverMax]
220 == std::to_string(10000000000 + baseFee) &&
221 jv[jss::transaction][jss::Fee]
222 == std::to_string(baseFee) &&
223 jv[jss::transaction][jss::Sequence]
228 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
229 return jv[jss::meta][
"AffectedNodes"][0u][
"ModifiedNode"]
230 [
"FinalFields"][jss::Account] ==
231 Account(
"bob").human();
237 auto jv = wsc->invoke(
"unsubscribe", stream);
238 if (wsc->version() == 2)
241 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
243 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 stream[jss::accounts].append(
Account(
"alice").human());
254 auto jv = wsc->invoke(
"subscribe", stream);
255 if (wsc->version() == 2)
258 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
260 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
261 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
263 BEAST_EXPECT(jv[jss::status] ==
"success");
270 BEAST_EXPECT(!wsc->getMsg(10ms));
277 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
278 return jv[jss::meta][
"AffectedNodes"][1u][
"ModifiedNode"]
279 [
"FinalFields"][jss::Account] ==
280 Account(
"alice").human();
283 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
284 return jv[jss::meta][
"AffectedNodes"][1u][
"CreatedNode"]
285 [
"NewFields"][
"LowLimit"][jss::issuer] ==
286 Account(
"alice").human();
291 auto jv = wsc->invoke(
"unsubscribe", stream);
292 if (wsc->version() == 2)
295 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
297 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
298 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
300 BEAST_EXPECT(jv[jss::status] ==
"success");
428 auto& cfg = env.app().config();
429 if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
431 auto const parsedseed =
432 parseBase58<Seed>(cfg.section(SECTION_VALIDATION_SEED).values()[0]);
433 if (!BEAST_EXPECT(parsedseed))
448 stream[jss::streams].append(
"validations");
449 auto jv = wsc->invoke(
"subscribe", stream);
450 if (wsc->version() == 2)
453 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
455 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
456 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
458 BEAST_EXPECT(jv[jss::status] ==
"success");
463 auto validValidationFields = [&env, &valPublicKey](
465 if (jv[jss::type] !=
"validationReceived")
468 if (jv[jss::validation_public_key].asString() != valPublicKey)
471 if (jv[jss::ledger_hash] !=
475 if (jv[jss::ledger_index] !=
482 if (jv[jss::full] !=
true)
485 if (jv.isMember(jss::load_fee))
488 if (!jv.isMember(jss::signature))
491 if (!jv.isMember(jss::signing_time))
494 if (!jv.isMember(jss::cookie))
497 if (!jv.isMember(jss::validated_hash))
500 uint32_t netID = env.app().config().NETWORK_ID;
501 if (!jv.isMember(jss::network_id) ||
502 jv[jss::network_id] != netID)
507 (env.closed()->info().seq + 1) % 256 == 0;
523 while (env.closed()->info().seq < 300)
526 using namespace std::chrono_literals;
527 BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
532 auto jv = wsc->invoke(
"unsubscribe", stream);
533 if (wsc->version() == 2)
536 jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] ==
"2.0");
538 jv.isMember(jss::ripplerpc) && jv[jss::ripplerpc] ==
"2.0");
539 BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
541 BEAST_EXPECT(jv[jss::status] ==
"success");
577 auto const method = subscribe ?
"subscribe" :
"unsubscribe";
578 testcase <<
"Error cases for " << method;
584 auto jr = env.rpc(
"json", method,
"{}")[jss::result];
585 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
586 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
591 jv[jss::url] =
"not-a-url";
592 jv[jss::username] =
"admin";
593 jv[jss::password] =
"password";
594 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
597 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
598 BEAST_EXPECT(jr[jss::error_message] ==
"Failed to parse url.");
606 jv[jss::url] =
"ftp://scheme.not.supported.tld";
607 auto jr = env.rpc(
"json", method,
to_string(jv))[jss::result];
610 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
612 jr[jss::error_message] ==
613 "Only http and https is supported.");
620 jv[jss::url] =
"no-url";
622 env_nonadmin.rpc(
"json", method,
to_string(jv))[jss::result];
623 BEAST_EXPECT(jr[jss::error] ==
"noPermission");
625 jr[jss::error_message] ==
626 "You don't have permission for this command.");
638 for (
auto const& f : {jss::accounts_proposed, jss::accounts})
640 for (
auto const& nonArray : nonArrays)
644 auto jr = wsc->invoke(method, jv)[jss::result];
645 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
646 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
652 auto jr = wsc->invoke(method, jv)[jss::result];
653 BEAST_EXPECT(jr[jss::error] ==
"actMalformed");
654 BEAST_EXPECT(jr[jss::error_message] ==
"Account malformed.");
658 for (
auto const& nonArray : nonArrays)
661 jv[jss::books] = nonArray;
662 auto jr = wsc->invoke(method, jv)[jss::result];
663 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
664 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
670 jv[jss::books][0u] = 1;
671 auto jr = wsc->invoke(method, jv)[jss::result];
672 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
673 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
682 auto jr = wsc->invoke(method, jv)[jss::result];
683 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
685 jr[jss::error_message] ==
"Source currency is malformed.");
694 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"ZZZZ";
695 auto jr = wsc->invoke(method, jv)[jss::result];
696 BEAST_EXPECT(jr[jss::error] ==
"srcCurMalformed");
698 jr[jss::error_message] ==
"Source currency is malformed.");
707 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
708 jv[jss::books][0u][jss::taker_pays][jss::issuer] = 1;
709 auto jr = wsc->invoke(method, jv)[jss::result];
710 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
712 jr[jss::error_message] ==
"Source issuer is malformed.");
721 jv[jss::books][0u][jss::taker_pays][jss::currency] =
"USD";
722 jv[jss::books][0u][jss::taker_pays][jss::issuer] =
724 auto jr = wsc->invoke(method, jv)[jss::result];
725 BEAST_EXPECT(jr[jss::error] ==
"srcIsrMalformed");
727 jr[jss::error_message] ==
"Source issuer is malformed.");
734 jv[jss::books][0u][jss::taker_pays] =
735 Account{
"gateway"}[
"USD"](1).value().getJson(
738 auto jr = wsc->invoke(method, jv)[jss::result];
741 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
743 jr[jss::error_message] ==
744 "Destination amount/currency/issuer is malformed.");
751 jv[jss::books][0u][jss::taker_pays] =
752 Account{
"gateway"}[
"USD"](1).value().getJson(
754 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"ZZZZ";
755 auto jr = wsc->invoke(method, jv)[jss::result];
758 BEAST_EXPECT(jr[jss::error] ==
"dstAmtMalformed");
760 jr[jss::error_message] ==
761 "Destination amount/currency/issuer is malformed.");
768 jv[jss::books][0u][jss::taker_pays] =
769 Account{
"gateway"}[
"USD"](1).value().getJson(
771 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
772 jv[jss::books][0u][jss::taker_gets][jss::issuer] = 1;
773 auto jr = wsc->invoke(method, jv)[jss::result];
774 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
776 jr[jss::error_message] ==
"Destination issuer is malformed.");
783 jv[jss::books][0u][jss::taker_pays] =
784 Account{
"gateway"}[
"USD"](1).value().getJson(
786 jv[jss::books][0u][jss::taker_gets][jss::currency] =
"USD";
787 jv[jss::books][0u][jss::taker_gets][jss::issuer] =
789 auto jr = wsc->invoke(method, jv)[jss::result];
790 BEAST_EXPECT(jr[jss::error] ==
"dstIsrMalformed");
792 jr[jss::error_message] ==
"Destination issuer is malformed.");
799 jv[jss::books][0u][jss::taker_pays] =
800 Account{
"gateway"}[
"USD"](1).value().getJson(
802 jv[jss::books][0u][jss::taker_gets] =
803 Account{
"gateway"}[
"USD"](1).value().getJson(
805 auto jr = wsc->invoke(method, jv)[jss::result];
806 BEAST_EXPECT(jr[jss::error] ==
"badMarket");
807 BEAST_EXPECT(jr[jss::error_message] ==
"No such market.");
810 for (
auto const& nonArray : nonArrays)
813 jv[jss::streams] = nonArray;
814 auto jr = wsc->invoke(method, jv)[jss::result];
815 BEAST_EXPECT(jr[jss::error] ==
"invalidParams");
816 BEAST_EXPECT(jr[jss::error_message] ==
"Invalid parameters.");
822 jv[jss::streams][0u] = 1;
823 auto jr = wsc->invoke(method, jv)[jss::result];
824 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
825 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
831 jv[jss::streams][0u] =
"not_a_stream";
832 auto jr = wsc->invoke(method, jv)[jss::result];
833 BEAST_EXPECT(jr[jss::error] ==
"malformedStream");
834 BEAST_EXPECT(jr[jss::error_message] ==
"Stream malformed.");
843 using namespace std::chrono_literals;
856 auto goodSubRPC = [](
Json::Value const& subReply) ->
bool {
857 return subReply.isMember(jss::result) &&
858 subReply[jss::result].isMember(jss::status) &&
859 subReply[jss::result][jss::status] == jss::success;
870 bool first_flag =
false;
872 for (
int i = 0; i < numReplies; ++i)
875 auto reply = wsc.
getMsg(100ms);
879 if (r.isMember(jss::account_history_tx_index))
880 idx = r[jss::account_history_tx_index].asInt();
881 if (r.isMember(jss::account_history_tx_first))
883 bool boundary = r.isMember(jss::account_history_boundary);
884 int ledger_idx = r[jss::ledger_index].asInt();
885 if (r.isMember(jss::transaction) &&
886 r[jss::transaction].isMember(jss::hash))
888 auto t{r[jss::transaction]};
890 idx, t[jss::hash].asString(), boundary, ledger_idx);
894 return {
false, first_flag};
897 return {
true, first_flag};
904 auto sendPayments = [](
Env& env,
912 for (
int i = 0; i < newTxns; ++i)
914 auto& from = (i % 2 == 0) ? a : b;
915 auto& to = (i % 2 == 0) ? b : a;
922 for (
int i = 0; i < ledgersToClose; ++i)
932 auto hashCompare = [](IdxHashVec
const& accountVec,
933 IdxHashVec
const& txHistoryVec,
934 bool sizeCompare) ->
bool {
935 if (accountVec.empty() || txHistoryVec.empty())
937 if (sizeCompare && accountVec.size() != (txHistoryVec.size()))
941 for (
auto const& tx : txHistoryVec)
947 if (i >= accountVec.size())
950 if (it == txHistoryMap.
end())
955 auto firstHistoryIndex = getHistoryIndex(0);
956 if (!firstHistoryIndex)
958 for (
std::size_t i = 1; i < accountVec.size(); ++i)
960 if (
auto idx = getHistoryIndex(i);
961 !idx || *idx != *firstHistoryIndex + i)
997 auto checkBoundary = [](IdxHashVec
const& vec,
bool ) {
998 size_t num_tx = vec.size();
999 for (
size_t i = 0; i < num_tx; ++i)
1001 auto [idx, hash, boundary, ledger] = vec[i];
1002 if ((i + 1 == num_tx || ledger !=
std::get<3>(vec[i + 1])) !=
1022 request[jss::account_history_tx_stream][jss::account] =
1024 auto jv = wscTxHistory->invoke(
"subscribe", request);
1025 if (!BEAST_EXPECT(goodSubRPC(jv)))
1028 jv = wscTxHistory->invoke(
"subscribe", request);
1029 BEAST_EXPECT(!goodSubRPC(jv));
1034 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
1036 jv = wscTxHistory->invoke(
"unsubscribe", request);
1037 if (!BEAST_EXPECT(goodSubRPC(jv)))
1040 sendPayments(env, env.
master, alice, 1, 1, 123456);
1043 auto r = getTxHash(*wscTxHistory, vec, 1);
1044 if (!BEAST_EXPECT(r.first && r.second))
1050 request[jss::account_history_tx_stream][jss::stop_history_tx_only] =
1052 jv = wscTxHistory->invoke(
"unsubscribe", request);
1053 BEAST_EXPECT(goodSubRPC(jv));
1055 sendPayments(env, env.
master, alice, 1, 1);
1056 r = getTxHash(*wscTxHistory, vec, 1);
1057 BEAST_EXPECT(!r.first);
1068 request[jss::account_history_tx_stream][jss::account] =
1069 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1070 auto jv = wscTxHistory->invoke(
"subscribe", request);
1071 if (!BEAST_EXPECT(goodSubRPC(jv)))
1073 IdxHashVec genesisFullHistoryVec;
1075 !getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first))
1082 sendPayments(env, env.
master, bob, 1, 1, 654321);
1084 auto r = getTxHash(*wscTxHistory, genesisFullHistoryVec, 1);
1085 if (!BEAST_EXPECT(r.first && r.second))
1088 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1089 jv = wscTxHistory->invoke(
"subscribe", request);
1090 if (!BEAST_EXPECT(goodSubRPC(jv)))
1092 IdxHashVec bobFullHistoryVec;
1093 r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
1094 if (!BEAST_EXPECT(r.first && r.second))
1103 jv = wscTxHistory->invoke(
"unsubscribe", request);
1104 if (!BEAST_EXPECT(goodSubRPC(jv)))
1106 request[jss::account_history_tx_stream][jss::account] =
1107 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1108 jv = wscTxHistory->invoke(
"unsubscribe", request);
1109 BEAST_EXPECT(goodSubRPC(jv));
1115 sendPayments(env, env.
master, bob, 30, 300);
1117 request[jss::account_history_tx_stream][jss::account] = bob.
human();
1118 jv = wscTxHistory->invoke(
"subscribe", request);
1120 bobFullHistoryVec.
clear();
1122 getTxHash(*wscTxHistory, bobFullHistoryVec, 31).second);
1123 jv = wscTxHistory->invoke(
"unsubscribe", request);
1125 request[jss::account_history_tx_stream][jss::account] =
1126 "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
1127 jv = wscTxHistory->invoke(
"subscribe", request);
1128 genesisFullHistoryVec.
clear();
1130 getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
1131 jv = wscTxHistory->invoke(
"unsubscribe", request);
1148 env.
fund(
XRP(222222), accounts);
1154 stream[jss::accounts].append(alice.
human());
1155 auto jv = wscAccount->invoke(
"subscribe", stream);
1157 sendPayments(env, alice, bob, 5, 1);
1158 sendPayments(env, alice, bob, 5, 1);
1159 IdxHashVec accountVec;
1160 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1166 request[jss::account_history_tx_stream][jss::account] =
1168 jv = wscTxHistory->invoke(
"subscribe", request);
1171 IdxHashVec txHistoryVec;
1172 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1174 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1179 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1184 IdxHashVec initFundTxns;
1186 getTxHash(*wscTxHistory, initFundTxns, 10).second) ||
1187 !BEAST_EXPECT(checkBoundary(initFundTxns,
false)))
1192 sendPayments(env, alice, bob, 10, 1);
1193 if (!BEAST_EXPECT(getTxHash(*wscAccount, accountVec, 10).first))
1195 if (!BEAST_EXPECT(getTxHash(*wscTxHistory, txHistoryVec, 10).first))
1197 if (!BEAST_EXPECT(hashCompare(accountVec, txHistoryVec,
true)))
1202 if (!BEAST_EXPECT(checkBoundary(txHistoryVec,
false)))
1205 wscTxHistory->invoke(
"unsubscribe", request);
1206 wscAccount->invoke(
"unsubscribe", stream);
1215 auto const USD_a = alice[
"USD"];
1218 env.
fund(
XRP(333333), accounts);
1219 env.
trust(USD_a(20000), carol);
1222 auto mixedPayments = [&]() ->
int {
1223 sendPayments(env, alice, carol, 1, 0);
1224 env(
pay(alice, carol, USD_a(100)));
1232 request[jss::account_history_tx_stream][jss::account] =
1235 auto jv = ws->invoke(
"subscribe", request);
1239 getTxHash(*ws, tempVec, 100);
1242 auto count = mixedPayments();
1244 if (!BEAST_EXPECT(getTxHash(*ws, vec1, count).first))
1246 ws->invoke(
"unsubscribe", request);
1255 env.
fund(
XRP(444444), accounts);
1259 auto oneRound = [&](
int numPayments) {
1260 return sendPayments(env, alice, carol, numPayments, 300);
1266 request[jss::account_history_tx_stream][jss::account] =
1269 auto jv = wscLong->invoke(
"subscribe", request);
1273 getTxHash(*wscLong, tempVec, 100);
1277 for (
int kk = 2; kk < 10; ++kk)
1279 auto count = oneRound(kk);
1281 if (!BEAST_EXPECT(getTxHash(*wscLong, vec1, count).first))
1286 auto jv = wscShort->invoke(
"subscribe", request);
1288 if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first))
1290 if (!BEAST_EXPECT(hashCompare(vec1, vec2,
true)))
1292 wscShort->invoke(
"unsubscribe", request);
1367 testcase(
"Test synthetic fields from Subscribe response");
1369 using namespace test::jtx;
1370 using namespace std::chrono_literals;
1374 Account const broker{
"broker"};
1376 Env env{*
this, features};
1377 env.fund(
XRP(10000), alice, bob, broker);
1383 stream[jss::streams].append(
"transactions");
1384 auto jv = wsc->invoke(
"subscribe", stream);
1389 auto verifyNFTokenID = [&](
uint256 const& actualNftID) {
1390 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1393 nftID.parseHex(jv[jss::meta][jss::nftoken_id].asString()));
1394 return nftID == actualNftID;
1400 auto verifyNFTokenIDsInCancelOffer =
1402 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1403 std::vector<uint256> metaIDs;
1405 jv[jss::meta][jss::nftoken_ids].begin(),
1406 jv[jss::meta][jss::nftoken_ids].end(),
1407 std::back_inserter(metaIDs),
1408 [this](Json::Value id) {
1410 BEAST_EXPECT(nftID.parseHex(id.asString()));
1414 std::sort(metaIDs.begin(), metaIDs.end());
1415 std::sort(actualNftIDs.begin(), actualNftIDs.end());
1418 BEAST_EXPECT(metaIDs.size() == actualNftIDs.size());
1422 for (
size_t i = 0; i < metaIDs.size(); ++i)
1423 BEAST_EXPECT(metaIDs[i] == actualNftIDs[i]);
1430 auto verifyNFTokenOfferID = [&](
uint256 const& offerID) {
1431 BEAST_EXPECT(wsc->findMsg(5s, [&](
auto const& jv) {
1432 uint256 metaOfferID;
1433 BEAST_EXPECT(metaOfferID.parseHex(
1434 jv[jss::meta][jss::offer_id].asString()));
1435 return metaOfferID == offerID;
1447 verifyNFTokenID(nftId1);
1453 verifyNFTokenID(nftId2);
1458 uint256 const aliceOfferIndex1 =
1463 verifyNFTokenOfferID(aliceOfferIndex1);
1465 uint256 const aliceOfferIndex2 =
1470 verifyNFTokenOfferID(aliceOfferIndex2);
1476 alice, {aliceOfferIndex1, aliceOfferIndex2}));
1478 verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
1482 auto const bobBuyOfferIndex =
1486 verifyNFTokenOfferID(bobBuyOfferIndex);
1492 verifyNFTokenID(nftId1);
1502 verifyNFTokenID(nftId);
1505 uint256 const offerAliceToBroker =
1508 token::destination(broker),
1511 verifyNFTokenOfferID(offerAliceToBroker);
1514 uint256 const offerBobToBroker =
1518 verifyNFTokenOfferID(offerBobToBroker);
1522 broker, offerBobToBroker, offerAliceToBroker));
1524 verifyNFTokenID(nftId);
1535 verifyNFTokenID(nftId);
1538 uint256 const aliceOfferIndex1 =
1543 verifyNFTokenOfferID(aliceOfferIndex1);
1545 uint256 const aliceOfferIndex2 =
1550 verifyNFTokenOfferID(aliceOfferIndex2);
1555 alice, {aliceOfferIndex1, aliceOfferIndex2}));
1557 verifyNFTokenIDsInCancelOffer({nftId});
1560 if (features[featureNFTokenMintOffer])
1562 uint256 const aliceMintWithOfferIndex1 =
1566 verifyNFTokenOfferID(aliceMintWithOfferIndex1);