22#include <xrpld/app/tx/detail/NFTokenUtils.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/jss.h>
38 params[jss::account] = acct.
human();
39 params[jss::type] =
"state";
41 return nfts[jss::result][jss::account_nfts].
size();
51 size_t const tokenCancelCount)
53 using namespace test::jtx;
56 env(token::mint(owner, 0),
61 offerIndexes.
reserve(tokenCancelCount);
63 for (uint32_t i = 0; i < tokenCancelCount; ++i)
67 env(token::createOffer(owner, nftokenID, drops(1)),
88 jvParams[jss::ledger_index] =
"current";
89 jvParams[jss::binary] =
false;
94 boost::lexical_cast<std::string>(jvParams));
98 !jrr[jss::result].
isMember(jss::state))
111 if (state[i].isMember(sfNFTokens.jsonName) &&
112 state[i][sfNFTokens.jsonName].
isArray())
115 state[i][sfNFTokens.jsonName].
size();
116 std::cout << tokenCount <<
" NFtokens in page "
127 << state[i][sfNFTokens.jsonName][0u]
133 << state[i][sfNFTokens.jsonName][tokenCount - 1]
148 using namespace test::jtx;
150 Env env{*
this, features};
158 AcctStat(
char const* name) : acct(name)
167 AcctStat alice{
"alice"};
168 AcctStat becky{
"becky"};
169 AcctStat minter{
"minter"};
171 env.fund(XRP(10000), alice, becky, minter);
175 env(token::setMinter(alice, minter));
196 alice.nfts.reserve(105);
197 while (alice.nfts.size() < 105)
200 alice.nfts.push_back(token::getNextID(
202 env(token::mint(alice),
204 token::xferFee(xferFee));
208 minter.nfts.reserve(105);
209 while (minter.nfts.size() < 105)
212 minter.nfts.push_back(token::getNextID(
214 env(token::mint(minter),
216 token::xferFee(xferFee),
217 token::issuer(alice));
223 becky.nfts.reserve(70);
225 auto aliceIter = alice.nfts.begin();
226 auto minterIter = minter.nfts.begin();
227 while (becky.nfts.size() < 70)
230 auto xferNFT = [&env, &becky](AcctStat& acct,
auto& iter) {
233 env(token::createOffer(acct, *iter, XRP(0)),
236 env(token::acceptSellOffer(becky, offerIndex));
238 becky.nfts.push_back(*iter);
239 iter = acct.nfts.erase(iter);
242 xferNFT(alice, aliceIter);
243 xferNFT(minter, minterIter);
245 BEAST_EXPECT(aliceIter == alice.nfts.end());
246 BEAST_EXPECT(minterIter == minter.nfts.end());
250 BEAST_EXPECT(
nftCount(env, alice.acct) == 70);
251 BEAST_EXPECT(
nftCount(env, becky.acct) == 70);
252 BEAST_EXPECT(
nftCount(env, minter.acct) == 70);
257 [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) {
261 env(token::createOffer(owner, nft, drops(1)),
263 token::destination(other1));
264 env(token::createOffer(owner, nft, drops(1)),
266 token::destination(other2));
270 env(token::createOffer(other1, nft, drops(1)),
271 token::owner(owner));
272 env(token::createOffer(other2, nft, drops(1)),
273 token::owner(owner));
276 env(token::createOffer(other2, nft, drops(2)),
277 token::owner(owner));
278 env(token::createOffer(other1, nft, drops(2)),
279 token::owner(owner));
283 addOffers(alice, becky, minter);
284 addOffers(becky, minter, alice);
285 addOffers(minter, alice, becky);
286 BEAST_EXPECT(ownerCount(env, alice) == 424);
287 BEAST_EXPECT(ownerCount(env, becky) == 424);
288 BEAST_EXPECT(ownerCount(env, minter) == 424);
293 AcctStat*
const stats[3] = {&alice, &becky, &minter};
297 while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 ||
298 stats[2]->nfts.size() > 0)
302 AcctStat& owner = *(stats[acctDist(engine)]);
303 if (owner.nfts.empty())
308 0lu, owner.nfts.size() - 1);
309 auto nftIter = owner.nfts.begin() + nftDist(engine);
311 owner.nfts.erase(nftIter);
316 AcctStat& burner = owner.acct == becky.acct
317 ? *(stats[acctDist(engine)])
318 : mintDist(engine) ? alice
321 if (owner.acct == burner.acct)
322 env(token::burn(burner, nft));
324 env(token::burn(burner, nft), token::owner(owner));
329 BEAST_EXPECT(
nftCount(env, alice.acct) == alice.nfts.size());
330 BEAST_EXPECT(
nftCount(env, becky.acct) == becky.nfts.size());
331 BEAST_EXPECT(
nftCount(env, minter.acct) == minter.nfts.size());
333 BEAST_EXPECT(
nftCount(env, alice.acct) == 0);
334 BEAST_EXPECT(
nftCount(env, becky.acct) == 0);
335 BEAST_EXPECT(
nftCount(env, minter.acct) == 0);
339 BEAST_EXPECT(ownerCount(env, alice) == 0);
340 BEAST_EXPECT(ownerCount(env, becky) == 0);
341 BEAST_EXPECT(ownerCount(env, minter) == 0);
353 using namespace test::jtx;
355 Account
const alice{
"alice"};
357 Env env{*
this, features};
358 env.fund(XRP(1000), alice);
362 auto genPackedTokens = [
this, &env, &alice]() {
374 auto internalTaxon = [&env](
378 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
382 if (env.current()->rules().enabled(fixNFTokenRemint))
383 tokenSeq += env.le(acct)
384 ->at(~sfFirstNFTokenSequence)
385 .value_or(env.seq(acct));
399 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
400 uint32_t
const extTaxon = internalTaxon(alice, intTaxon);
401 nfts.
push_back(token::getNextID(env, alice, extTaxon));
402 env(token::mint(alice, extTaxon));
413 jvParams[jss::ledger_index] =
"current";
414 jvParams[jss::binary] =
false;
419 boost::lexical_cast<std::string>(jvParams));
426 if (state[i].isMember(sfNFTokens.jsonName) &&
427 state[i][sfNFTokens.jsonName].
isArray())
430 state[i][sfNFTokens.jsonName].
size() == 32);
436 BEAST_EXPECT(pageCount == 3);
445 BEAST_EXPECT(
nftCount(env, alice) == 96);
446 BEAST_EXPECT(ownerCount(env, alice) == 3);
448 for (
uint256 const& nft : nfts)
450 env(token::burn(alice, {nft}));
453 BEAST_EXPECT(
nftCount(env, alice) == 0);
454 BEAST_EXPECT(ownerCount(env, alice) == 0);
458 auto checkNoTokenPages = [
this, &env]() {
460 jvParams[jss::ledger_index] =
"current";
461 jvParams[jss::binary] =
false;
466 boost::lexical_cast<std::string>(jvParams));
472 BEAST_EXPECT(!state[i].isMember(sfNFTokens.jsonName));
482 BEAST_EXPECT(
nftCount(env, alice) == 96);
483 BEAST_EXPECT(ownerCount(env, alice) == 3);
488 if (!BEAST_EXPECT(lastNFTokenPage))
491 uint256 const middleNFTokenPageIndex =
492 lastNFTokenPage->at(sfPreviousPageMin);
495 if (!BEAST_EXPECT(middleNFTokenPage))
498 uint256 const firstNFTokenPageIndex =
499 middleNFTokenPage->at(sfPreviousPageMin);
502 if (!BEAST_EXPECT(firstNFTokenPage))
506 for (
int i = 0; i < 31; ++i)
508 env(token::burn(alice, {nfts.
back()}));
516 if (!BEAST_EXPECT(lastNFTokenPage))
520 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
521 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
522 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
525 env(token::burn(alice, {nfts.
back()}));
529 if (features[fixNFTokenPageLinks])
536 BEAST_EXPECT(lastNFTokenPage);
538 lastNFTokenPage->at(~sfPreviousPageMin) ==
539 firstNFTokenPageIndex);
540 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
542 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
547 BEAST_EXPECT(!middleNFTokenPage);
553 BEAST_EXPECT(firstNFTokenPage);
555 !firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
557 firstNFTokenPage->at(~sfNextPageMin) ==
558 lastNFTokenPage->key());
560 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
568 BEAST_EXPECT(!lastNFTokenPage);
574 if (!BEAST_EXPECT(middleNFTokenPage))
577 middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
578 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
582 while (!nfts.
empty())
584 env(token::burn(alice, {nfts.
back()}));
588 BEAST_EXPECT(
nftCount(env, alice) == 0);
589 BEAST_EXPECT(ownerCount(env, alice) == 0);
597 BEAST_EXPECT(
nftCount(env, alice) == 96);
598 BEAST_EXPECT(ownerCount(env, alice) == 3);
603 if (!BEAST_EXPECT(lastNFTokenPage))
606 uint256 const middleNFTokenPageIndex =
607 lastNFTokenPage->at(sfPreviousPageMin);
610 if (!BEAST_EXPECT(middleNFTokenPage))
613 uint256 const firstNFTokenPageIndex =
614 middleNFTokenPage->at(sfPreviousPageMin);
617 if (!BEAST_EXPECT(firstNFTokenPage))
622 env(token::burn(alice, nfts[i]));
626 BEAST_EXPECT(
nftCount(env, alice) == 64);
627 BEAST_EXPECT(ownerCount(env, alice) == 2);
633 BEAST_EXPECT(!middleNFTokenPage);
636 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
638 lastNFTokenPage->getFieldH256(sfPreviousPageMin) ==
639 firstNFTokenPageIndex);
644 firstNFTokenPage->getFieldH256(sfNextPageMin) ==
646 BEAST_EXPECT(!firstNFTokenPage->isFieldPresent(sfPreviousPageMin));
649 for (
uint256 const& nft : nfts)
651 env(token::burn(alice, {nft}));
654 BEAST_EXPECT(
nftCount(env, alice) == 0);
655 BEAST_EXPECT(ownerCount(env, alice) == 0);
663 BEAST_EXPECT(
nftCount(env, alice) == 96);
664 BEAST_EXPECT(ownerCount(env, alice) == 3);
669 if (!BEAST_EXPECT(lastNFTokenPage))
672 uint256 const middleNFTokenPageIndex =
673 lastNFTokenPage->at(sfPreviousPageMin);
676 if (!BEAST_EXPECT(middleNFTokenPage))
679 uint256 const firstNFTokenPageIndex =
680 middleNFTokenPage->at(sfPreviousPageMin);
683 if (!BEAST_EXPECT(firstNFTokenPage))
688 for (
int i = 0; i < 32; ++i)
690 env(token::burn(alice, {nfts.
back()}));
698 BEAST_EXPECT(!firstNFTokenPage);
703 if (!BEAST_EXPECT(middleNFTokenPage))
705 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
706 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfNextPageMin));
709 if (!BEAST_EXPECT(lastNFTokenPage))
711 BEAST_EXPECT(lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
712 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
716 for (
int i = 0; i < 32; ++i)
718 env(token::burn(alice, {nfts.
back()}));
723 if (features[fixNFTokenPageLinks])
730 BEAST_EXPECT(lastNFTokenPage);
732 !lastNFTokenPage->isFieldPresent(sfPreviousPageMin));
733 BEAST_EXPECT(!lastNFTokenPage->isFieldPresent(sfNextPageMin));
735 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 32);
740 BEAST_EXPECT(!middleNFTokenPage);
745 BEAST_EXPECT(!firstNFTokenPage);
753 BEAST_EXPECT(!lastNFTokenPage);
759 if (!BEAST_EXPECT(middleNFTokenPage))
762 !middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
763 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
767 while (!nfts.
empty())
769 env(token::burn(alice, {nfts.
back()}));
773 BEAST_EXPECT(
nftCount(env, alice) == 0);
774 BEAST_EXPECT(ownerCount(env, alice) == 0);
778 if (features[fixNFTokenPageLinks])
792 BEAST_EXPECT(
nftCount(env, alice) == 96);
793 BEAST_EXPECT(ownerCount(env, alice) == 3);
796 for (
int i = 0; i < 31; ++i)
798 env(token::burn(alice, {nfts.
back()}));
814 env.current()->fees().base,
819 auto lastNFTokenPage =
821 if (!BEAST_EXPECT(lastNFTokenPage))
824 lastNFTokenPage->getFieldArray(sfNFTokens).size() == 1);
827 ac.view().erase(lastNFTokenPage);
831 for (
TER const& terExpect :
834 terActual = ac.checkInvariants(terActual,
XRPAmount{});
835 BEAST_EXPECT(terExpect == terActual);
837 sink.messages().str().starts_with(
"Invariant failed:"));
841 sink.messages().str().find(
842 "Last NFT page deleted with non-empty directory") !=
858 env.current()->fees().base,
863 auto lastNFTokenPage =
867 lastNFTokenPage->getFieldH256(sfPreviousPageMin)));
868 BEAST_EXPECT(middleNFTokenPage);
872 middleNFTokenPage->makeFieldAbsent(sfNextPageMin);
873 ac.view().update(middleNFTokenPage);
877 for (
TER const& terExpect :
880 terActual = ac.checkInvariants(terActual,
XRPAmount{});
881 BEAST_EXPECT(terExpect == terActual);
883 sink.messages().str().starts_with(
"Invariant failed:"));
887 sink.messages().str().find(
"Lost NextMinPage link") !=
900 using namespace test::jtx;
904 if (!features[fixNonFungibleTokensV1_2])
906 Env env{*
this, features};
908 Account
const alice(
"alice");
909 Account
const becky(
"becky");
910 env.fund(XRP(1000), alice, becky);
926 env(token::mint(alice, 0),
936 env.fund(XRP(1000), acct);
941 env(token::createOffer(acct, nftokenID, drops(1)),
942 token::owner(alice));
947 for (
uint256 const& offerIndex : offerIndexes)
953 uint256 const beckyOfferIndex =
955 env(token::createOffer(becky, nftokenID, drops(1)),
956 token::owner(alice));
959 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
963 for (
int i = 0; i < 10; ++i)
968 env(token::cancelOffer(becky, {beckyOfferIndex}));
971 uint256 const aliceOfferIndex =
973 env(token::createOffer(alice, nftokenID, drops(1)),
977 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
981 env(token::cancelOffer(alice, {aliceOfferIndex}));
984 env(token::burn(alice, nftokenID));
988 for (
uint256 const& offerIndex : offerIndexes)
994 BEAST_EXPECT(ownerCount(env, alice) == 0);
995 BEAST_EXPECT(ownerCount(env, becky) == 0);
1002 if (features[fixNonFungibleTokensV1_2])
1004 Env env{*
this, features};
1006 Account
const alice(
"alice");
1007 Account
const becky(
"becky");
1008 env.fund(XRP(100000), alice, becky);
1019 for (
uint256 const& offerIndex : offerIndexes)
1025 uint256 const beckyOfferIndex =
1027 env(token::createOffer(becky, nftokenID, drops(1)),
1028 token::owner(alice));
1032 env(token::burn(alice, nftokenID));
1037 for (
uint256 const& offerIndex : offerIndexes)
1047 BEAST_EXPECT(ownerCount(env, alice) == 0);
1048 BEAST_EXPECT(ownerCount(env, becky) == 0);
1053 if (features[fixNonFungibleTokensV1_2])
1055 Env env{*
this, features};
1057 Account
const alice(
"alice");
1058 Account
const becky(
"becky");
1059 env.fund(XRP(100000), alice, becky);
1070 for (
uint256 const& offerIndex : offerIndexes)
1076 env(token::burn(alice, nftokenID));
1079 uint32_t offerDeletedCount = 0;
1081 for (
uint256 const& offerIndex : offerIndexes)
1084 offerDeletedCount++;
1093 BEAST_EXPECT(ownerCount(env, alice) == 1);
1098 if (features[fixNonFungibleTokensV1_2])
1100 Env env{*
this, features};
1102 Account
const alice(
"alice");
1103 Account
const becky(
"becky");
1104 env.fund(XRP(100000), alice, becky);
1116 for (
uint256 const& offerIndex : offerIndexes)
1122 env(token::createOffer(becky, nftokenID, drops(1)),
1123 token::owner(alice));
1125 env(token::createOffer(becky, nftokenID, drops(1)),
1126 token::owner(alice));
1130 env(token::burn(alice, nftokenID));
1135 for (
uint256 const& offerIndex : offerIndexes)
1142 BEAST_EXPECT(ownerCount(env, alice) == 0);
1145 BEAST_EXPECT(ownerCount(env, becky) == 1);
1154 if (features[fixNFTokenPageLinks])
1162 using namespace test::jtx;
1164 Account
const alice{
"alice"};
1165 Account
const minter{
"minter"};
1167 Env env{*
this, features};
1168 env.fund(XRP(1000), alice, minter);
1172 auto genPackedTokens = [
this, &env, &alice, &minter]() {
1184 auto internalTaxon = [&env](
1185 Account
const& acct,
1188 env.le(acct)->at(~sfMintedNFTokens).value_or(0);
1192 if (env.current()->rules().enabled(fixNFTokenRemint))
1193 tokenSeq += env.le(acct)
1194 ->at(~sfFirstNFTokenSequence)
1195 .value_or(env.seq(acct));
1209 std::uint32_t const intTaxon = (i / 16) + (i & 0b10000 ? 2 : 0);
1210 uint32_t
const extTaxon = internalTaxon(minter, intTaxon);
1217 uint256 const minterOfferIndex =
1219 env(token::createOffer(minter, nfts.
back(), XRP(0)),
1224 env(token::acceptSellOffer(alice, minterOfferIndex));
1235 jvParams[jss::ledger_index] =
"current";
1236 jvParams[jss::binary] =
false;
1241 boost::lexical_cast<std::string>(jvParams));
1243 Json::Value& state = jrr[jss::result][jss::state];
1248 if (state[i].isMember(sfNFTokens.jsonName) &&
1249 state[i][sfNFTokens.jsonName].
isArray())
1252 state[i][sfNFTokens.jsonName].
size() == 32);
1258 BEAST_EXPECT(pageCount == 3);
1265 BEAST_EXPECT(
nftCount(env, alice) == 96);
1266 BEAST_EXPECT(ownerCount(env, alice) == 3);
1271 if (!BEAST_EXPECT(lastNFTokenPage))
1274 uint256 const middleNFTokenPageIndex =
1275 lastNFTokenPage->at(sfPreviousPageMin);
1278 if (!BEAST_EXPECT(middleNFTokenPage))
1281 uint256 const firstNFTokenPageIndex =
1282 middleNFTokenPage->at(sfPreviousPageMin);
1283 auto firstNFTokenPage = env.le(
1285 if (!BEAST_EXPECT(firstNFTokenPage))
1290 for (
int i = 0; i < 32; ++i)
1296 uint256 const aliceOfferIndex =
1298 env(token::createOffer(alice, last32NFTs.
back(), XRP(0)),
1303 env(token::acceptSellOffer(minter, aliceOfferIndex));
1311 BEAST_EXPECT(!lastNFTokenPage);
1312 BEAST_EXPECT(ownerCount(env, alice) == 2);
1318 if (!BEAST_EXPECT(middleNFTokenPage))
1320 BEAST_EXPECT(middleNFTokenPage->isFieldPresent(sfPreviousPageMin));
1321 BEAST_EXPECT(!middleNFTokenPage->isFieldPresent(sfNextPageMin));
1324 auto const acctDelFee{drops(env.current()->fees().increment)};
1325 env(acctdelete(alice, minter),
1331 for (
uint256 nftID : last32NFTs)
1334 uint256 const minterOfferIndex =
1336 env(token::createOffer(minter, nftID, XRP(0)),
1341 env(token::acceptSellOffer(alice, minterOfferIndex));
1344 BEAST_EXPECT(ownerCount(env, alice) == 3);
1352 params[jss::account] = alice.human();
1353 return env.rpc(
"json",
"account_objects",
to_string(params));
1355 BEAST_EXPECT(!acctObjs.
isMember(jss::marker));
1357 acctObjs[jss::result][jss::account_objects].size() == 2);
1364 params[jss::account] = alice.human();
1365 params[jss::type] =
"state";
1366 return env.rpc(
"json",
"account_nfts",
to_string(params));
1368 BEAST_EXPECT(!aliceNFTs.
isMember(jss::marker));
1370 aliceNFTs[jss::result][jss::account_nfts].size() == 64);
1387 using namespace test::jtx;
1389 static FeatureBitset const fixNFTV1_2{fixNonFungibleTokensV1_2};
1392 static FeatureBitset const fixNFTPageLinks{fixNFTokenPageLinks};
1395 all - fixNFTV1_2 - fixNFTDir - fixNFTRemint - fixNFTPageLinks,
1396 all - fixNFTV1_2 - fixNFTRemint - fixNFTPageLinks,
1397 all - fixNFTRemint - fixNFTPageLinks,
1398 all - fixNFTPageLinks,
1402 if (BEAST_EXPECT(instance < feats.size()))
1406 BEAST_EXPECT(!last || instance == feats.size() - 1);
1457BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnBaseUtil, tx,
ripple, 3);
1458BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnWOfixFungTokens, tx,
ripple, 3);
1459BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnWOFixTokenRemint, tx,
ripple, 3);
1460BEAST_DEFINE_TESTSUITE_PRIO(NFTokenBurnWOFixNFTPageLinks, tx,
ripple, 3);
1461BEAST_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(char const *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