20 #include <ripple/app/tx/impl/details/NFTokenUtils.h>
21 #include <ripple/protocol/Feature.h>
22 #include <ripple/protocol/jss.h>
36 if (
auto const sleAcct = env.
le(acct))
46 params[jss::account] = acct.
human();
47 params[jss::type] =
"state";
49 return nfts[jss::result][jss::account_nfts].
size();
59 size_t const tokenCancelCount)
61 using namespace test::jtx;
64 env(token::mint(owner, 0),
69 offerIndexes.
reserve(tokenCancelCount);
71 for (uint32_t i = 0; i < tokenCancelCount; ++i)
75 env(token::createOffer(owner, nftokenID, drops(1)),
87 testcase(
"Burn random");
89 using namespace test::jtx;
91 Env env{*
this, features};
99 AcctStat(
char const* name) : acct(name)
108 AcctStat alice{
"alice"};
109 AcctStat becky{
"becky"};
110 AcctStat minter{
"minter"};
112 env.fund(XRP(10000), alice, becky, minter);
116 env(token::setMinter(alice, minter));
137 alice.nfts.reserve(105);
138 while (alice.nfts.size() < 105)
141 alice.nfts.push_back(token::getNextID(
143 env(token::mint(alice),
145 token::xferFee(xferFee));
149 minter.nfts.reserve(105);
150 while (minter.nfts.size() < 105)
153 minter.nfts.push_back(token::getNextID(
155 env(token::mint(minter),
157 token::xferFee(xferFee),
158 token::issuer(alice));
164 becky.nfts.reserve(70);
166 auto aliceIter = alice.nfts.begin();
167 auto minterIter = minter.nfts.begin();
168 while (becky.nfts.size() < 70)
171 auto xferNFT = [&env, &becky](AcctStat& acct,
auto& iter) {
174 env(token::createOffer(acct, *iter, XRP(0)),
177 env(token::acceptSellOffer(becky, offerIndex));
179 becky.nfts.push_back(*iter);
180 iter = acct.nfts.erase(iter);
183 xferNFT(alice, aliceIter);
184 xferNFT(minter, minterIter);
186 BEAST_EXPECT(aliceIter == alice.nfts.end());
187 BEAST_EXPECT(minterIter == minter.nfts.end());
191 BEAST_EXPECT(
nftCount(env, alice.acct) == 70);
192 BEAST_EXPECT(
nftCount(env, becky.acct) == 70);
193 BEAST_EXPECT(
nftCount(env, minter.acct) == 70);
198 [&env](AcctStat& owner, AcctStat& other1, AcctStat& other2) {
202 env(token::createOffer(owner, nft, drops(1)),
204 token::destination(other1));
205 env(token::createOffer(owner, nft, drops(1)),
207 token::destination(other2));
211 env(token::createOffer(other1, nft, drops(1)),
212 token::owner(owner));
213 env(token::createOffer(other2, nft, drops(1)),
214 token::owner(owner));
217 env(token::createOffer(other2, nft, drops(2)),
218 token::owner(owner));
219 env(token::createOffer(other1, nft, drops(2)),
220 token::owner(owner));
224 addOffers(alice, becky, minter);
225 addOffers(becky, minter, alice);
226 addOffers(minter, alice, becky);
234 AcctStat*
const stats[3] = {&alice, &becky, &minter};
238 while (stats[0]->nfts.size() > 0 || stats[1]->nfts.size() > 0 ||
239 stats[2]->nfts.size() > 0)
243 AcctStat& owner = *(stats[acctDist(engine)]);
244 if (owner.nfts.empty())
249 0lu, owner.nfts.size() - 1);
250 auto nftIter = owner.nfts.begin() + nftDist(engine);
252 owner.nfts.erase(nftIter);
257 AcctStat& burner = owner.acct == becky.acct
258 ? *(stats[acctDist(engine)])
259 : mintDist(engine) ? alice : minter;
261 if (owner.acct == burner.acct)
262 env(token::burn(burner, nft));
264 env(token::burn(burner, nft), token::owner(owner));
269 BEAST_EXPECT(
nftCount(env, alice.acct) == alice.nfts.size());
270 BEAST_EXPECT(
nftCount(env, becky.acct) == becky.nfts.size());
271 BEAST_EXPECT(
nftCount(env, minter.acct) == minter.nfts.size());
273 BEAST_EXPECT(
nftCount(env, alice.acct) == 0);
274 BEAST_EXPECT(
nftCount(env, becky.acct) == 0);
275 BEAST_EXPECT(
nftCount(env, minter.acct) == 0);
291 testcase(
"Burn sequential");
293 using namespace test::jtx;
295 Account
const alice{
"alice"};
297 Env env{*
this, features};
298 env.fund(XRP(1000), alice);
309 [[maybe_unused]]
auto printNFTPages = [&env](Volume vol) {
311 jvParams[jss::ledger_index] =
"current";
312 jvParams[jss::binary] =
false;
317 boost::lexical_cast<std::string>(jvParams));
321 !jrr[jss::result].
isMember(jss::state))
339 std::cout << tokenCount <<
" NFTokens in page "
367 auto genPackedTokens = [
this, &env, &alice](
380 auto internalTaxon = [&env](
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));
434 BEAST_EXPECT(pageCount == 3);
442 genPackedTokens(nfts);
443 BEAST_EXPECT(
nftCount(env, alice) == 96);
446 for (
uint256 const& nft : nfts)
448 env(token::burn(alice, {nft}));
451 BEAST_EXPECT(
nftCount(env, alice) == 0);
455 auto checkNoTokenPages = [
this, &env]() {
457 jvParams[jss::ledger_index] =
"current";
458 jvParams[jss::binary] =
false;
463 boost::lexical_cast<std::string>(jvParams));
478 genPackedTokens(nfts);
479 BEAST_EXPECT(
nftCount(env, alice) == 96);
483 for (
uint256 const& nft : nfts)
485 env(token::burn(alice, {nft}));
488 BEAST_EXPECT(
nftCount(env, alice) == 0);
495 genPackedTokens(nfts);
496 BEAST_EXPECT(
nftCount(env, alice) == 96);
501 env(token::burn(alice, nfts[i]));
504 nfts.erase(nfts.begin() + 32, nfts.begin() + 64);
505 BEAST_EXPECT(
nftCount(env, alice) == 64);
509 for (
uint256 const& nft : nfts)
511 env(token::burn(alice, {nft}));
514 BEAST_EXPECT(
nftCount(env, alice) == 0);
522 testcase(
"Burn too many offers");
524 using namespace test::jtx;
530 Env env{*
this, features};
532 Account
const alice(
"alice");
533 Account
const becky(
"becky");
534 env.fund(XRP(1000), alice, becky);
550 env(token::mint(alice, 0),
560 env.fund(XRP(1000), acct);
565 env(token::createOffer(acct, nftokenID, drops(1)),
566 token::owner(alice));
571 for (
uint256 const& offerIndex : offerIndexes)
577 uint256 const beckyOfferIndex =
579 env(token::createOffer(becky, nftokenID, drops(1)),
580 token::owner(alice));
583 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
587 for (
int i = 0; i < 10; ++i)
592 env(token::cancelOffer(becky, {beckyOfferIndex}));
595 uint256 const aliceOfferIndex =
597 env(token::createOffer(alice, nftokenID, drops(1)),
601 env(token::burn(alice, nftokenID), ter(
tefTOO_BIG));
605 env(token::cancelOffer(alice, {aliceOfferIndex}));
608 env(token::burn(alice, nftokenID));
612 for (
uint256 const& offerIndex : offerIndexes)
628 Env env{*
this, features};
630 Account
const alice(
"alice");
631 Account
const becky(
"becky");
632 env.fund(XRP(100000), alice, becky);
643 for (
uint256 const& offerIndex : offerIndexes)
649 uint256 const beckyOfferIndex =
651 env(token::createOffer(becky, nftokenID, drops(1)),
652 token::owner(alice));
656 env(token::burn(alice, nftokenID));
661 for (
uint256 const& offerIndex : offerIndexes)
679 Env env{*
this, features};
681 Account
const alice(
"alice");
682 Account
const becky(
"becky");
683 env.fund(XRP(100000), alice, becky);
694 for (
uint256 const& offerIndex : offerIndexes)
700 env(token::burn(alice, nftokenID));
703 uint32_t offerDeletedCount = 0;
705 for (
uint256 const& offerIndex : offerIndexes)
724 Env env{*
this, features};
726 Account
const alice(
"alice");
727 Account
const becky(
"becky");
728 env.fund(XRP(100000), alice, becky);
740 for (
uint256 const& offerIndex : offerIndexes)
746 env(token::createOffer(becky, nftokenID, drops(1)),
747 token::owner(alice));
749 env(token::createOffer(becky, nftokenID, drops(1)),
750 token::owner(alice));
754 env(token::burn(alice, nftokenID));
759 for (
uint256 const& offerIndex : offerIndexes)
785 using namespace test::jtx;