21#include <xrpld/app/tx/detail/NFTokenUtils.h>
22#include <xrpl/protocol/Feature.h>
23#include <xrpl/protocol/jss.h>
36 params[jss::account] = acct.
human();
37 params[jss::type] =
"state";
39 return nfts[jss::result][jss::account_nfts].
size();
49 size_t const tokenCancelCount)
51 using namespace test::jtx;
54 env(token::mint(owner, 0),
59 offerIndexes.
reserve(tokenCancelCount);
61 for (uint32_t i = 0; i < tokenCancelCount; ++i)
65 env(token::createOffer(owner, nftokenID, drops(1)),
86 jvParams[jss::ledger_index] =
"current";
87 jvParams[jss::binary] =
false;
92 boost::lexical_cast<std::string>(jvParams));
96 !jrr[jss::result].
isMember(jss::state))
109 if (state[i].isMember(sfNFTokens.jsonName) &&
110 state[i][sfNFTokens.jsonName].
isArray())
113 state[i][sfNFTokens.jsonName].
size();
114 std::cout << tokenCount <<
" NFtokens in page "
125 << state[i][sfNFTokens.jsonName][0u]
131 << state[i][sfNFTokens.jsonName][tokenCount - 1]
146 using namespace test::jtx;
148 Env env{*
this, features};
156 AcctStat(
char const* name) : acct(name)
165 AcctStat alice{
"alice"};
166 AcctStat becky{
"becky"};
167 AcctStat minter{
"minter"};
169 env.fund(XRP(10000), alice, becky, minter);
173 env(token::setMinter(alice, minter));
194 alice.nfts.reserve(105);
195 while (alice.nfts.size() < 105)
198 alice.nfts.push_back(token::getNextID(
200 env(token::mint(alice),
202 token::xferFee(xferFee));
206 minter.nfts.reserve(105);
207 while (minter.nfts.size() < 105)
210 minter.nfts.push_back(token::getNextID(
212 env(token::mint(minter),
214 token::xferFee(xferFee),
215 token::issuer(alice));
221 becky.nfts.reserve(70);
223 auto aliceIter = alice.nfts.begin();
224 auto minterIter = minter.nfts.begin();
225 while (becky.nfts.size() < 70)
228 auto xferNFT = [&env, &becky](AcctStat& acct,
auto& iter) {
231 env(token::createOffer(acct, *iter, XRP(0)),
234 env(token::acceptSellOffer(becky, offerIndex));
236 becky.nfts.push_back(*iter);
237 iter = acct.nfts.erase(iter);
240 xferNFT(alice, aliceIter);
241 xferNFT(minter, minterIter);
243 BEAST_EXPECT(aliceIter == alice.nfts.end());
244 BEAST_EXPECT(minterIter == minter.nfts.end());
248 BEAST_EXPECT(
nftCount(env, alice.acct) == 70);
249 BEAST_EXPECT(
nftCount(env, becky.acct) == 70);
250 BEAST_EXPECT(
nftCount(env, minter.acct) == 70);
255 [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) {
259 env(token::createOffer(owner, nft, drops(1)),
261 token::destination(other1));
262 env(token::createOffer(owner, nft, drops(1)),
264 token::destination(other2));
268 env(token::createOffer(other1, nft, drops(1)),
269 token::owner(owner));
270 env(token::createOffer(other2, nft, drops(1)),
271 token::owner(owner));
274 env(token::createOffer(other2, nft, drops(2)),
275 token::owner(owner));
276 env(token::createOffer(other1, nft, drops(2)),
277 token::owner(owner));
281 addOffers(alice, becky, minter);
282 addOffers(becky, minter, alice);
283 addOffers(minter, alice, becky);
284 BEAST_EXPECT(ownerCount(env, alice) == 424);
285 BEAST_EXPECT(ownerCount(env, becky) == 424);
286 BEAST_EXPECT(ownerCount(env, minter) == 424);
291 AcctStat*
const stats[3] = {&alice, &becky, &minter};
295 while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 ||
296 stats[2]->nfts.size() > 0)
300 AcctStat& owner = *(stats[acctDist(engine)]);
301 if (owner.nfts.empty())
306 0lu, owner.nfts.size() - 1);
307 auto nftIter = owner.nfts.begin() + nftDist(engine);
309 owner.nfts.erase(nftIter);
314 AcctStat& burner = owner.acct == becky.acct
315 ? *(stats[acctDist(engine)])
316 : mintDist(engine) ? alice
319 if (owner.acct == burner.acct)
320 env(token::burn(burner, nft));
322 env(token::burn(burner, nft), token::owner(owner));
327 BEAST_EXPECT(
nftCount(env, alice.acct) == alice.nfts.size());
328 BEAST_EXPECT(
nftCount(env, becky.acct) == becky.nfts.size());
329 BEAST_EXPECT(
nftCount(env, minter.acct) == minter.nfts.size());
331 BEAST_EXPECT(
nftCount(env, alice.acct) == 0);
332 BEAST_EXPECT(
nftCount(env, becky.acct) == 0);
333 BEAST_EXPECT(
nftCount(env, minter.acct) == 0);
337 BEAST_EXPECT(ownerCount(env, alice) == 0);
338 BEAST_EXPECT(ownerCount(env, becky) == 0);
339 BEAST_EXPECT(ownerCount(env, minter) == 0);
351 using namespace test::jtx;
353 Account
const alice{
"alice"};
355 Env env{*
this, features};
356 env.fund(XRP(1000), alice);
360 auto genPackedTokens = [
this, &env, &alice]() {
372 auto internalTaxon = [&env](
376 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
380 if (env.current()->rules().enabled(fixNFTokenRemint))
381 tokenSeq += env.le(acct)
382 ->at(~sfFirstNFTokenSequence)
383 .value_or(env.seq(acct));
397 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
398 uint32_t
const extTaxon = internalTaxon(alice, intTaxon);
399 nfts.
push_back(token::getNextID(env, alice, extTaxon));
400 env(token::mint(alice, extTaxon));
411 jvParams[jss::ledger_index] =
"current";
412 jvParams[jss::binary] =
false;
417 boost::lexical_cast<std::string>(jvParams));
424 if (state[i].isMember(sfNFTokens.jsonName) &&
425 state[i][sfNFTokens.jsonName].
isArray())
428 state[i][sfNFTokens.jsonName].
size() == 32);
434 BEAST_EXPECT(pageCount == 3);
443 BEAST_EXPECT(
nftCount(env, alice) == 96);
444 BEAST_EXPECT(ownerCount(env, alice) == 3);
446 for (
uint256 const& nft : nfts)
448 env(token::burn(alice, {nft}));
451 BEAST_EXPECT(
nftCount(env, alice) == 0);
452 BEAST_EXPECT(ownerCount(env, alice) == 0);
456 auto checkNoTokenPages = [
this, &env]() {
458 jvParams[jss::ledger_index] =
"current";
459 jvParams[jss::binary] =
false;
464 boost::lexical_cast<std::string>(jvParams));
470 BEAST_EXPECT(!state[i].isMember(sfNFTokens.jsonName));
480 BEAST_EXPECT(
nftCount(env, alice) == 96);
481 BEAST_EXPECT(ownerCount(env, alice) == 3);
486 if (!BEAST_EXPECT(lastNFTokenPage))
489 uint256 const middleNFTokenPageIndex =
490 lastNFTokenPage->at(sfPreviousPageMin);
493 if (!BEAST_EXPECT(middleNFTokenPage))
496 uint256 const firstNFTokenPageIndex =
497 middleNFTokenPage->at(sfPreviousPageMin);
500 if (!BEAST_EXPECT(firstNFTokenPage))
504 for (
int i = 0; i < 31; ++i)
506 env(token::burn(alice, {nfts.
back()}));
514 if (!BEAST_EXPECT(lastNFTokenPage))
518 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
519 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
520 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
523 env(token::burn(alice, {nfts.
back()}));
527 if (features[fixNFTokenPageLinks])
534 BEAST_EXPECT(lastNFTokenPage);
536 lastNFTokenPage->at(~sfPreviousPageMin) ==
537 firstNFTokenPageIndex);
538 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
540 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
545 BEAST_EXPECT(!middleNFTokenPage);
551 BEAST_EXPECT(firstNFTokenPage);
553 !firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
555 firstNFTokenPage->at(~sfNextPageMin) ==
556 lastNFTokenPage->key());
558 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
566 BEAST_EXPECT(!lastNFTokenPage);
572 if (!BEAST_EXPECT(middleNFTokenPage))
575 middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
576 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
580 while (!nfts.
empty())
582 env(token::burn(alice, {nfts.
back()}));
586 BEAST_EXPECT(
nftCount(env, alice) == 0);
587 BEAST_EXPECT(ownerCount(env, alice) == 0);
595 BEAST_EXPECT(
nftCount(env, alice) == 96);
596 BEAST_EXPECT(ownerCount(env, alice) == 3);
601 if (!BEAST_EXPECT(lastNFTokenPage))
604 uint256 const middleNFTokenPageIndex =
605 lastNFTokenPage->at(sfPreviousPageMin);
608 if (!BEAST_EXPECT(middleNFTokenPage))
611 uint256 const firstNFTokenPageIndex =
612 middleNFTokenPage->at(sfPreviousPageMin);
615 if (!BEAST_EXPECT(firstNFTokenPage))
620 env(token::burn(alice, nfts[i]));
624 BEAST_EXPECT(
nftCount(env, alice) == 64);
625 BEAST_EXPECT(ownerCount(env, alice) == 2);
631 BEAST_EXPECT(!middleNFTokenPage);
634 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
636 lastNFTokenPage->getFieldH256(sfPreviousPageMin) ==
637 firstNFTokenPageIndex);
642 firstNFTokenPage->getFieldH256(sfNextPageMin) ==
644 BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
647 for (
uint256 const& nft : nfts)
649 env(token::burn(alice, {nft}));
652 BEAST_EXPECT(
nftCount(env, alice) == 0);
653 BEAST_EXPECT(ownerCount(env, alice) == 0);
661 BEAST_EXPECT(
nftCount(env, alice) == 96);
662 BEAST_EXPECT(ownerCount(env, alice) == 3);
667 if (!BEAST_EXPECT(lastNFTokenPage))
670 uint256 const middleNFTokenPageIndex =
671 lastNFTokenPage->at(sfPreviousPageMin);
674 if (!BEAST_EXPECT(middleNFTokenPage))
677 uint256 const firstNFTokenPageIndex =
678 middleNFTokenPage->at(sfPreviousPageMin);
681 if (!BEAST_EXPECT(firstNFTokenPage))
686 for (
int i = 0; i < 32; ++i)
688 env(token::burn(alice, {nfts.
back()}));
696 BEAST_EXPECT(!firstNFTokenPage);
701 if (!BEAST_EXPECT(middleNFTokenPage))
703 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
704 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfNextPageMin));
707 if (!BEAST_EXPECT(lastNFTokenPage))
709 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
710 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
714 for (
int i = 0; i < 32; ++i)
716 env(token::burn(alice, {nfts.
back()}));
721 if (features[fixNFTokenPageLinks])
728 BEAST_EXPECT(lastNFTokenPage);
730 !lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
731 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
733 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
738 BEAST_EXPECT(!middleNFTokenPage);
743 BEAST_EXPECT(!firstNFTokenPage);
751 BEAST_EXPECT(!lastNFTokenPage);
757 if (!BEAST_EXPECT(middleNFTokenPage))
760 !middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
761 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
765 while (!nfts.
empty())
767 env(token::burn(alice, {nfts.
back()}));
771 BEAST_EXPECT(
nftCount(env, alice) == 0);
772 BEAST_EXPECT(ownerCount(env, alice) == 0);
776 if (features[fixNFTokenPageLinks])
790 BEAST_EXPECT(
nftCount(env, alice) == 96);
791 BEAST_EXPECT(ownerCount(env, alice) == 3);
794 for (
int i = 0; i < 31; ++i)
796 env(token::burn(alice, {nfts.
back()}));
812 env.current()->fees().base,
817 auto lastNFTokenPage =
819 if (!BEAST_EXPECT(lastNFTokenPage))
822 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
825 ac.view().erase(lastNFTokenPage);
829 for (
TER const& terExpect :
832 terActual = ac.checkInvariants(terActual,
XRPAmount{});
833 BEAST_EXPECT(terExpect == terActual);
835 sink.messages().str().starts_with(
"Invariant failed:"));
839 sink.messages().str().find(
840 "Last NFT page deleted with non-empty directory") !=
856 env.current()->fees().base,
861 auto lastNFTokenPage =
865 lastNFTokenPage->getFieldH256(sfPreviousPageMin)));
866 BEAST_EXPECT(middleNFTokenPage);
870 middleNFTokenPage->makeFieldAbsent(sfNextPageMin);
871 ac.view().update(middleNFTokenPage);
875 for (
TER const& terExpect :
878 terActual = ac.checkInvariants(terActual,
XRPAmount{});
879 BEAST_EXPECT(terExpect == terActual);
881 sink.messages().str().starts_with(
"Invariant failed:"));
885 sink.messages().str().find(
"Lost NextMinPage link") !=
898 using namespace test::jtx;
902 if (!features[fixNonFungibleTokensV1_2])
904 Env env{*
this, features};
906 Account
const alice(
"alice");
907 Account
const becky(
"becky");
908 env.fund(XRP(1000), alice, becky);
924 env(token::mint(alice, 0),
934 env.fund(XRP(1000), acct);
939 env(token::createOffer(acct, nftokenID, drops(1)),
940 token::owner(alice));
945 for (
uint256 const& offerIndex : offerIndexes)
951 uint256 const beckyOfferIndex =
953 env(token::createOffer(becky, nftokenID, drops(1)),
954 token::owner(alice));
957 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
961 for (
int i = 0; i < 10; ++i)
966 env(token::cancelOffer(becky, {beckyOfferIndex}));
969 uint256 const aliceOfferIndex =
971 env(token::createOffer(alice, nftokenID, drops(1)),
975 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
979 env(token::cancelOffer(alice, {aliceOfferIndex}));
982 env(token::burn(alice, nftokenID));
986 for (
uint256 const& offerIndex : offerIndexes)
992 BEAST_EXPECT(ownerCount(env, alice) == 0);
993 BEAST_EXPECT(ownerCount(env, becky) == 0);
1000 if (features[fixNonFungibleTokensV1_2])
1002 Env env{*
this, features};
1004 Account
const alice(
"alice");
1005 Account
const becky(
"becky");
1006 env.fund(XRP(100000), alice, becky);
1017 for (
uint256 const& offerIndex : offerIndexes)
1023 uint256 const beckyOfferIndex =
1025 env(token::createOffer(becky, nftokenID, drops(1)),
1026 token::owner(alice));
1030 env(token::burn(alice, nftokenID));
1035 for (
uint256 const& offerIndex : offerIndexes)
1045 BEAST_EXPECT(ownerCount(env, alice) == 0);
1046 BEAST_EXPECT(ownerCount(env, becky) == 0);
1051 if (features[fixNonFungibleTokensV1_2])
1053 Env env{*
this, features};
1055 Account
const alice(
"alice");
1056 Account
const becky(
"becky");
1057 env.fund(XRP(100000), alice, becky);
1068 for (
uint256 const& offerIndex : offerIndexes)
1074 env(token::burn(alice, nftokenID));
1077 uint32_t offerDeletedCount = 0;
1079 for (
uint256 const& offerIndex : offerIndexes)
1082 offerDeletedCount++;
1091 BEAST_EXPECT(ownerCount(env, alice) == 1);
1096 if (features[fixNonFungibleTokensV1_2])
1098 Env env{*
this, features};
1100 Account
const alice(
"alice");
1101 Account
const becky(
"becky");
1102 env.fund(XRP(100000), alice, becky);
1114 for (
uint256 const& offerIndex : offerIndexes)
1120 env(token::createOffer(becky, nftokenID, drops(1)),
1121 token::owner(alice));
1123 env(token::createOffer(becky, nftokenID, drops(1)),
1124 token::owner(alice));
1128 env(token::burn(alice, nftokenID));
1133 for (
uint256 const& offerIndex : offerIndexes)
1140 BEAST_EXPECT(ownerCount(env, alice) == 0);
1143 BEAST_EXPECT(ownerCount(env, becky) == 1);
1152 if (features[fixNFTokenPageLinks])
1160 using namespace test::jtx;
1162 Account
const alice{
"alice"};
1163 Account
const minter{
"minter"};
1165 Env env{*
this, features};
1166 env.fund(XRP(1000), alice, minter);
1170 auto genPackedTokens = [
this, &env, &alice, &minter]() {
1182 auto internalTaxon = [&env](
1183 Account
const& acct,
1186 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
1190 if (env.current()->rules().enabled(fixNFTokenRemint))
1191 tokenSeq += env.le(acct)
1192 ->at(~sfFirstNFTokenSequence)
1193 .value_or(env.seq(acct));
1207 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
1208 uint32_t
const extTaxon = internalTaxon(minter, intTaxon);
1215 uint256 const minterOfferIndex =
1217 env(token::createOffer(minter, nfts.
back(), XRP(0)),
1222 env(token::acceptSellOffer(alice, minterOfferIndex));
1233 jvParams[jss::ledger_index] =
"current";
1234 jvParams[jss::binary] =
false;
1239 boost::lexical_cast<std::string>(jvParams));
1241 Json::Value& state = jrr[jss::result][jss::state];
1246 if (state[i].isMember(sfNFTokens.jsonName) &&
1247 state[i][sfNFTokens.jsonName].
isArray())
1250 state[i][sfNFTokens.jsonName].
size() == 32);
1256 BEAST_EXPECT(pageCount == 3);
1263 BEAST_EXPECT(
nftCount(env, alice) == 96);
1264 BEAST_EXPECT(ownerCount(env, alice) == 3);
1269 if (!BEAST_EXPECT(lastNFTokenPage))
1272 uint256 const middleNFTokenPageIndex =
1273 lastNFTokenPage->at(sfPreviousPageMin);
1276 if (!BEAST_EXPECT(middleNFTokenPage))
1279 uint256 const firstNFTokenPageIndex =
1280 middleNFTokenPage->at(sfPreviousPageMin);
1281 auto firstNFTokenPage = env.le(
1283 if (!BEAST_EXPECT(firstNFTokenPage))
1288 for (
int i = 0; i < 32; ++i)
1294 uint256 const aliceOfferIndex =
1296 env(token::createOffer(alice, last32NFTs.
back(), XRP(0)),
1301 env(token::acceptSellOffer(minter, aliceOfferIndex));
1309 BEAST_EXPECT(!lastNFTokenPage);
1310 BEAST_EXPECT(ownerCount(env, alice) == 2);
1316 if (!BEAST_EXPECT(middleNFTokenPage))
1318 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
1319 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
1322 auto const acctDelFee{drops(env.current()->fees().increment)};
1323 env(acctdelete(alice, minter),
1329 for (
uint256 nftID : last32NFTs)
1332 uint256 const minterOfferIndex =
1334 env(token::createOffer(minter, nftID, XRP(0)),
1339 env(token::acceptSellOffer(alice, minterOfferIndex));
1342 BEAST_EXPECT(ownerCount(env, alice) == 3);
1350 params[jss::account] = alice.human();
1351 return env.rpc(
"json",
"account_objects",
to_string(params));
1353 BEAST_EXPECT(!acctObjs.
isMember(jss::marker));
1355 acctObjs[jss::result][jss::account_objects].size() == 2);
1362 params[jss::account] = alice.human();
1363 params[jss::type] =
"state";
1364 return env.rpc(
"json",
"account_nfts",
to_string(params));
1366 BEAST_EXPECT(!aliceNFTs.
isMember(jss::marker));
1368 aliceNFTs[jss::result][jss::account_nfts].size() == 64);
1385 using namespace test::jtx;
1387 static FeatureBitset const fixNFTV1_2{fixNonFungibleTokensV1_2};
1390 static FeatureBitset const fixNFTPageLinks{fixNFTokenPageLinks};
1393 all - fixNFTV1_2 - fixNFTDir - fixNFTRemint - fixNFTPageLinks,
1394 all - fixNFTV1_2 - fixNFTRemint - fixNFTPageLinks,
1395 all - fixNFTRemint - fixNFTPageLinks,
1396 all - fixNFTPageLinks,
1400 if (BEAST_EXPECT(instance < feats.size()))
1404 BEAST_EXPECT(!last || instance == feats.size() - 1);
1455BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnBaseUtil, tx,
ripple, 3);
1456BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnWOfixFungTokens, tx,
ripple, 3);
1457BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnWOFixTokenRemint, tx,
ripple, 3);
1458BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnWOFixNFTPageLinks, tx,
ripple, 3);
1459BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnAllFeatures, tx,
ripple, 3);
UInt size() const
Number of values in array or object.
std::string toStyledString() const
std::string asString() const
Returns the unquoted string value.
bool isMember(const char *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
testcase_t testcase
Memberspace for declaring test cases.
State information when applying a tx.
void run() override
Runs the suite.
static std::uint32_t nftCount(test::jtx::Env &env, test::jtx::Account const &acct)
uint256 createNftAndOffers(test::jtx::Env &env, test::jtx::Account const &owner, std::vector< uint256 > &offerIndexes, size_t const tokenCancelCount)
void exerciseBrokenLinks(FeatureBitset features)
void run(std::uint32_t instance, bool last=false)
void run() override
Runs the suite.
void testWithFeats(FeatureBitset features)
void testBurnTooManyOffers(FeatureBitset features)
void printNFTPages(test::jtx::Env &env, Volume vol)
void testBurnRandom(FeatureBitset features)
void testBurnSequential(FeatureBitset features)
void run() override
Runs the suite.
void run() override
Runs the suite.
void run() override
Runs the suite.
Writable ledger view that accumulates state and tx changes.
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Keylet nftpage(Keylet const &k, uint256 const &token)
Keylet nftpage_min(AccountID const &owner)
NFT page keylets.
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Taxon cipheredTaxon(std::uint32_t tokenSeq, Taxon taxon)
Taxon toTaxon(std::uint32_t i)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t const tfSellNFToken
std::size_t constexpr maxTokenOfferCancelCount
The maximum number of token offers that can be canceled at once.
std::uint16_t constexpr maxTransferFee
The maximum token transfer fee allowed.
std::size_t constexpr maxDeletableTokenOfferEntries
The maximum number of offers in an offer directory for NFT to be burnable.
constexpr std::uint32_t const tfBurnable
std::size_t constexpr maxTokenURILength
The maximum length of a URI inside an NFT.
std::string to_string(base_uint< Bits, Tag > const &a)
TERSubset< CanCvtToTER > TER
constexpr std::uint32_t const tfTransferable