20 #include <ripple/app/tx/impl/details/NFTokenUtils.h>
21 #include <ripple/basics/random.h>
22 #include <ripple/protocol/Feature.h>
23 #include <ripple/protocol/jss.h>
37 if (
auto const sleAcct = env.
le(acct))
47 if (
auto const sleIssuer = env.
le(issuer))
57 if (
auto const sleIssuer = env.
le(issuer))
67 params[jss::account] = acct.
human();
68 params[jss::type] =
"state";
70 return nfts[jss::result][jss::account_nfts].
size();
78 if (
auto const sleAcct = env.
le(acct))
87 return env.
current()->info().parentCloseTime.time_since_epoch().count();
95 using namespace test::jtx;
100 Account
const& master = env.master;
106 uint256 const nftId{token::getNextID(env, master, 0u)};
121 env(token::createOffer(master, nftId, XRP(10)), ter(
temDISABLED));
127 env(token::cancelOffer(master, {offerIndex}), ter(
temDISABLED));
133 env(token::acceptBuyOffer(master, offerIndex), ter(
temDISABLED));
142 Env env{*
this, features};
143 Account
const& master = env.master;
149 uint256 const nftId0{token::getNextID(env, env.master, 0u)};
150 env(token::mint(env.master, 0u));
156 env(token::burn(env.master, nftId0));
170 Account
const alice{
"alice"};
171 env.fund(XRP(10000), alice);
173 uint256 const aliceOfferIndex =
175 env(token::createOffer(alice, nftId1, XRP(1000)),
176 token::owner(master));
187 env(token::acceptBuyOffer(master, aliceOfferIndex));
204 testcase(
"Mint reserve");
206 using namespace test::jtx;
208 Env env{*
this, features};
209 Account
const alice{
"alice"};
210 Account
const minter{
"minter"};
215 env.fund(XRP(200), alice, minter);
217 BEAST_EXPECT(env.balance(alice) == XRP(200));
218 BEAST_EXPECT(env.balance(minter) == XRP(200));
230 env(pay(env.master, alice, XRP(50) + drops(9)));
235 auto checkAliceOwnerMintedBurned = [&env,
this, &alice](
248 ss <<
"Wrong " << type <<
" count. Found: " << found
249 <<
"; Expected: " << exp;
250 fail(ss.
str(), __FILE__, line);
253 oneCheck(
"owner",
ownerCount(env, alice), owners);
254 oneCheck(
"minted",
mintedCount(env, alice), minted);
255 oneCheck(
"burned",
burnedCount(env, alice), burned);
261 checkAliceOwnerMintedBurned(0, 0, 0, __LINE__);
264 env(pay(env.master, alice, drops(11)));
268 env(token::mint(alice));
270 checkAliceOwnerMintedBurned(1, 1, 0, __LINE__);
274 for (
int i = 1; i < 32; ++i)
276 env(token::mint(alice));
277 checkAliceOwnerMintedBurned(1, i + 1, 0, __LINE__);
284 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
287 env(pay(env.master, alice, XRP(50) + drops(329)));
293 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
296 env(pay(env.master, alice, drops(11)));
300 env(token::mint(alice));
302 checkAliceOwnerMintedBurned(2, 33, 0, __LINE__);
309 env(token::burn(alice, token::getID(alice, 0, seq++)));
311 checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__);
315 env(token::burn(alice, token::getID(alice, 197, 5)), ter(
tecNO_ENTRY));
317 checkAliceOwnerMintedBurned(0, 33, 33, __LINE__);
321 env(token::setMinter(alice, minter));
328 auto checkMintersOwnerMintedBurned = [&env,
this, &alice, &minter](
336 auto oneCheck = [
this](
346 ss <<
"Wrong " << type <<
" count. Found: " << found
347 <<
"; Expected: " << exp;
348 fail(ss.
str(), __FILE__, line);
351 oneCheck(
"alice owner",
ownerCount(env, alice), aliceOwners, line);
353 "alice minted",
mintedCount(env, alice), aliceMinted, line);
355 "alice burned",
burnedCount(env, alice), aliceBurned, line);
357 "minter owner",
ownerCount(env, minter), minterOwners, line);
359 "minter minted",
mintedCount(env, minter), minterMinted, line);
361 "minter burned",
burnedCount(env, minter), minterBurned, line);
367 env(pay(env.master, minter, XRP(50) - drops(1)));
369 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
374 env(token::mint(minter),
375 token::issuer(alice),
379 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
382 env(pay(env.master, minter, drops(11)));
386 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
388 checkMintersOwnerMintedBurned(0, 34, nftSeq, 1, 0, 0, __LINE__);
392 for (
int i = 1; i < 32; ++i)
394 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
395 checkMintersOwnerMintedBurned(0, i + 34, nftSeq, 1, 0, 0, __LINE__);
399 env(pay(env.master, minter, XRP(50) + drops(319)));
404 env(token::mint(minter),
405 token::issuer(alice),
409 checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__);
412 env(pay(env.master, minter, drops(11)));
416 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
418 checkMintersOwnerMintedBurned(0, 66, nftSeq, 2, 0, 0, __LINE__);
423 env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
425 checkMintersOwnerMintedBurned(
426 0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__);
430 env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
432 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
435 env(token::burn(minter, token::getID(alice, 2009, 3)),
438 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
446 testcase(
"Mint max tokens");
448 using namespace test::jtx;
450 Account
const alice{
"alice"};
451 Env env{*
this, features};
452 env.fund(XRP(1000), alice);
461 uint256 const nftId0{token::getNextID(env, alice, 0u)};
462 env(token::mint(alice, 0u));
465 env(token::burn(alice, nftId0));
472 env.app().openLedger().modify(
481 auto replacement = std::make_shared<SLE>(*sle, sle->key());
487 view.rawReplace(replacement);
491 // See whether alice is at the boundary that causes an error.
492 env(token::mint(alice, 0u), ter(tesSUCCESS));
493 env(token::mint(alice, 0u), ter(tecMAX_SEQUENCE_REACHED));
497 testMintInvalid(FeatureBitset features)
499 // Explore many of the invalid ways to mint an NFT.
500 testcase("Mint invalid");
502 using namespace test::jtx;
504 Env env{*this, features};
505 Account const alice{"alice"};
506 Account const minter{"minter"};
508 // Fund alice and minter enough to exist, but not enough to meet
509 // the reserve for creating their first NFT. Account reserve for unit
510 // tests is 200 XRP, not 20.
511 env.fund(XRP(200), alice, minter);
514 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
517 // Fund alice enough to start minting NFTs.
518 env(pay(env.master, alice, XRP(1000)));
521 //----------------------------------------------------------------------
524 // Set a negative fee.
525 env(token::mint(alice, 0u),
526 fee(STAmount(10ull, true)),
529 // Set an invalid flag.
530 env(token::mint(alice, 0u), txflags(0x00008000), ter(temINVALID_FLAG));
532 // Can't
set a transfer fee
if the NFT does not have the tfTRANSFERABLE
534 env(token::mint(alice, 0u),
539 env(token::mint(alice, 0u),
545 env(token::mint(alice, 0u), token::issuer(alice), ter(
temMALFORMED));
548 env(token::mint(alice, 0u), token::uri(
""), ter(
temMALFORMED));
551 env(token::mint(alice, 0u),
559 env(token::mint(alice, 0u),
560 token::issuer(Account(
"demon")),
567 env(token::mint(minter, 0u),
568 token::issuer(alice),
576 testcase(
"Burn invalid");
578 using namespace test::jtx;
580 Env env{*
this, features};
581 Account
const alice{
"alice"};
582 Account
const buyer{
"buyer"};
583 Account
const minter{
"minter"};
584 Account
const gw(
"gw");
585 IOU
const gwAUD(gw[
"AUD"]);
590 env.fund(XRP(250), alice, buyer, minter, gw);
604 env(token::burn(alice, nftAlice0ID),
611 env(token::burn(alice, nftAlice0ID),
621 env(token::burn(alice, token::getID(alice, 0, 1)), ter(
tecNO_ENTRY));
635 testcase(
"Invalid NFT offer create");
637 using namespace test::jtx;
639 Env env{*
this, features};
640 Account
const alice{
"alice"};
641 Account
const buyer{
"buyer"};
642 Account
const gw(
"gw");
643 IOU
const gwAUD(gw[
"AUD"]);
648 env.fund(XRP(250), alice, buyer, gw);
654 env(token::mint(alice, 0u),
666 uint256 nftNoXferID = token::getNextID(env, alice, 0);
667 env(token::mint(alice, 0));
680 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
687 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
694 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
701 env(token::createOffer(buyer, nftXrpOnlyID, buyer[
"USD"](1)),
703 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](0)),
705 env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
711 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](1)),
712 token::expiration(0),
719 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
725 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
733 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
740 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
741 token::destination(alice),
748 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
749 token::destination(Account(
"demon")),
759 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
767 env(token::createOffer(buyer, token::getID(alice, 0, 1), XRP(1000)),
774 env(token::createOffer(alice, token::getID(alice, 0, 1), XRP(1000)),
781 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
787 env(trust(buyer, gwAUD(1000)));
793 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
805 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
816 env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
826 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
835 env(trust(buyer, gwAUD(1000)));
838 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
848 env(pay(gw, buyer, gwAUD(999)));
853 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
860 env(pay(env.master, buyer, XRP(50) + drops(119)));
863 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
870 env(pay(env.master, buyer, drops(11)));
875 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
885 testcase(
"Invalid NFT offer cancel");
887 using namespace test::jtx;
889 Env env{*
this, features};
890 Account
const alice{
"alice"};
891 Account
const buyer{
"buyer"};
892 Account
const gw(
"gw");
893 IOU
const gwAUD(gw[
"AUD"]);
895 env.fund(XRP(1000), alice, buyer, gw);
906 uint256 const buyerOfferIndex =
908 env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
918 env(token::cancelOffer(buyer, {buyerOfferIndex}),
925 env(token::cancelOffer(buyer, {buyerOfferIndex}),
945 env(token::cancelOffer(buyer, offers), ter(
temMALFORMED));
951 env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
965 env(pay(env.master, gw, XRP(5000)));
969 env(offer(gw, XRP(i), gwAUD(1)));
976 env(check::create(gw, env.master, XRP(300)));
983 env(check::cancel(gw, gwCheckId));
997 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1005 testcase(
"Invalid NFT offer accept");
1007 using namespace test::jtx;
1009 Env env{*
this, features};
1010 Account
const alice{
"alice"};
1011 Account
const buyer{
"buyer"};
1012 Account
const gw(
"gw");
1013 IOU
const gwAUD(gw[
"AUD"]);
1015 env.fund(XRP(1000), alice, buyer, gw);
1031 uint256 nftNoXferID = token::getNextID(env, alice, 0);
1032 env(token::mint(alice, 0));
1037 uint256 const plainOfferIndex =
1039 env(token::createOffer(alice, nftAlice0ID, XRP(10)),
1046 env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1051 uint256 const xrpOnlyOfferIndex =
1053 env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1058 uint256 const noXferOfferIndex =
1060 env(token::createOffer(alice, nftNoXferID, XRP(30)),
1066 uint256 const aliceExpOfferIndex =
1068 env(token::createOffer(alice, nftNoXferID, XRP(40)),
1078 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1085 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1086 txflags(0x00008000),
1093 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1102 Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1112 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1121 env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1122 token::brokerFee(gwAUD(0)),
1132 env(token::acceptBuyOffer(buyer, missingOfferIndex),
1138 env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1143 env(token::acceptSellOffer(buyer, missingOfferIndex),
1149 env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1158 env(trust(alice, gwAUD(1000)));
1159 env(trust(buyer, gwAUD(1000)));
1161 env(pay(gw, buyer, gwAUD(30)));
1170 uint256 const buyerOfferIndex =
1172 env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1173 token::owner(alice));
1178 env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1184 env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1191 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1197 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1203 uint256 const buyerOfferIndex =
1205 env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1206 token::owner(alice));
1212 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1213 token::brokerFee(XRP(40)),
1219 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1220 token::brokerFee(gwAUD(31)),
1227 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1228 token::brokerFee(gwAUD(1.5)),
1234 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1242 uint256 const buyerOfferIndex =
1244 env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1245 token::owner(alice));
1250 env(token::acceptBuyOffer(buyer, plainOfferIndex),
1256 env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1262 env(pay(buyer, gw, gwAUD(30)));
1264 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1265 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1274 env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1277 env(token::acceptSellOffer(gw, offerIndex));
1281 env(pay(gw, buyer, gwAUD(30)));
1285 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1291 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1299 uint256 const buyerOfferIndex =
1301 env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1302 token::owner(alice));
1307 env(token::acceptSellOffer(alice, buyerOfferIndex),
1313 env(token::acceptSellOffer(alice, plainOfferIndex),
1320 env(token::acceptSellOffer(buyer, plainOfferIndex),
1331 env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1334 env(token::acceptSellOffer(alice, offerIndex));
1338 env(pay(buyer, gw, gwAUD(30)));
1340 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1341 env(token::acceptSellOffer(buyer, audOfferIndex),
1358 testcase(
"Mint flagBurnable");
1360 using namespace test::jtx;
1362 Env env{*
this, features};
1363 Account
const alice{
"alice"};
1364 Account
const buyer{
"buyer"};
1365 Account
const minter1{
"minter1"};
1366 Account
const minter2{
"minter2"};
1368 env.fund(XRP(1000), alice, buyer, minter1, minter2);
1373 env(token::setMinter(alice, minter1));
1380 auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1382 uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1383 env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1388 env(token::createOffer(minter1, nftID, XRP(0)),
1392 env(token::acceptSellOffer(buyer, offerIndex));
1400 uint256 const noBurnID = nftToBuyer(0);
1401 env(token::burn(alice, noBurnID),
1402 token::owner(buyer),
1405 env(token::burn(minter1, noBurnID),
1406 token::owner(buyer),
1409 env(token::burn(minter2, noBurnID),
1410 token::owner(buyer),
1415 env(token::burn(buyer, noBurnID), token::owner(buyer));
1422 env(token::burn(minter2, burnableID),
1423 token::owner(buyer),
1428 env(token::burn(alice, burnableID), token::owner(buyer));
1436 env(token::burn(buyer, burnableID));
1444 env(token::burn(buyer, burnableID), token::owner(buyer));
1454 env(token::setMinter(alice, minter2));
1459 env(token::burn(minter1, burnableID),
1460 token::owner(buyer),
1466 env(token::burn(minter2, burnableID), token::owner(buyer));
1476 testcase(
"Mint flagOnlyXRP");
1478 using namespace test::jtx;
1480 Env env{*
this, features};
1481 Account
const alice{
"alice"};
1482 Account
const buyer{
"buyer"};
1483 Account
const gw(
"gw");
1484 IOU
const gwAUD(gw[
"AUD"]);
1487 env.fund(XRP(1000), alice, buyer, gw);
1489 env(trust(alice, gwAUD(1000)));
1490 env(trust(buyer, gwAUD(1000)));
1492 env(pay(gw, buyer, gwAUD(100)));
1502 uint256 const aliceOfferIndex =
1504 env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1510 uint256 const buyerOfferIndex =
1512 env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1513 token::owner(alice));
1518 env(token::cancelOffer(alice, {aliceOfferIndex}));
1519 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1525 env(token::burn(alice, nftIOUsOkayID));
1538 env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1545 env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1546 token::owner(alice),
1553 env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1559 env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1560 token::owner(alice));
1570 testcase(
"Mint flagCreateTrustLines");
1572 using namespace test::jtx;
1574 Env env{*
this, features};
1575 Account
const alice{
"alice"};
1576 Account
const becky{
"becky"};
1577 Account
const cheri{
"cheri"};
1578 Account
const gw(
"gw");
1579 IOU
const gwAUD(gw[
"AUD"]);
1580 IOU
const gwCAD(gw[
"CAD"]);
1581 IOU
const gwEUR(gw[
"EUR"]);
1583 env.fund(XRP(1000), alice, becky, cheri, gw);
1587 env(trust(becky, gwAUD(1000)));
1588 env(trust(cheri, gwAUD(1000)));
1589 env(trust(becky, gwCAD(1000)));
1590 env(trust(cheri, gwCAD(1000)));
1591 env(trust(becky, gwEUR(1000)));
1592 env(trust(cheri, gwEUR(1000)));
1594 env(pay(gw, becky, gwAUD(500)));
1595 env(pay(gw, becky, gwCAD(500)));
1596 env(pay(gw, becky, gwEUR(500)));
1597 env(pay(gw, cheri, gwAUD(500)));
1598 env(pay(gw, cheri, gwCAD(500)));
1605 uint256 const nftNoAutoTrustID{
1607 env(token::mint(alice, 0u),
1608 token::xferFee(xferFee),
1613 uint256 const beckyBuyOfferIndex =
1615 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1616 token::owner(alice));
1618 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1622 TER const createOfferTER =
1624 uint256 const beckyOfferIndex =
1626 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1628 ter(createOfferTER));
1632 uint256 const cheriOfferIndex =
1634 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1635 token::owner(becky),
1636 ter(createOfferTER));
1640 env(token::cancelOffer(becky, {beckyOfferIndex}));
1641 env(token::cancelOffer(cheri, {cheriOfferIndex}));
1649 uint256 const nftAutoTrustID{token::getNextID(
1651 env(token::mint(alice, 0u),
1652 token::xferFee(transferFee),
1657 uint256 const beckyBuyOfferIndex =
1659 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1660 token::owner(alice));
1662 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1666 uint256 const beckySellOfferIndex =
1668 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1671 env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1675 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1678 uint256 const beckyBuyBackOfferIndex =
1680 env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1681 token::owner(cheri));
1683 env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1687 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1688 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1694 uint256 const nftNoAutoTrustID{
1696 env(token::mint(alice, 0u),
1697 token::xferFee(transferFee),
1702 uint256 const aliceSellOfferIndex =
1704 env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1707 env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1713 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1716 env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1720 uint256 const cheriSellOfferIndex =
1722 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1725 env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1731 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1739 testcase(
"Mint flagTransferable");
1741 using namespace test::jtx;
1743 Env env{*
this, features};
1745 Account
const alice{
"alice"};
1746 Account
const becky{
"becky"};
1747 Account
const minter{
"minter"};
1749 env.fund(XRP(1000), alice, becky, minter);
1755 uint256 const nftAliceNoTransferID{
1756 token::getNextID(env, alice, 0u)};
1757 env(token::mint(alice, 0u), token::xferFee(0));
1763 env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1764 token::owner(alice),
1768 uint256 const aliceSellOfferIndex =
1770 env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1773 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1779 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1788 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1790 token::destination(alice),
1798 uint256 const aliceBuyOfferIndex =
1800 env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1801 token::owner(becky));
1803 env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1809 env(token::burn(alice, nftAliceNoTransferID));
1816 env(token::setMinter(alice, minter));
1820 uint256 const nftMinterNoTransferID{
1821 token::getNextID(env, alice, 0u)};
1822 env(token::mint(minter), token::issuer(alice));
1828 env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1829 token::owner(minter),
1835 env(token::clearMinter(alice));
1840 env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1848 for (
int i = 0; i < 10; ++i)
1851 env(token::setMinter(alice, minter));
1857 uint256 const minterSellOfferIndex =
1859 env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1866 env(token::clearMinter(alice));
1872 env(token::acceptSellOffer(becky, minterSellOfferIndex));
1878 env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1886 env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1887 token::owner(becky),
1894 uint256 const aliceBuyOfferIndex =
1896 env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1897 token::owner(becky));
1903 for (
int i = 0; i < 10; ++i)
1906 env(token::setMinter(alice, minter));
1911 uint256 const minterBuyOfferIndex =
1913 env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1914 token::owner(becky));
1920 env(token::clearMinter(alice));
1926 env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1934 env(token::burn(minter, nftMinterNoTransferID), ter(
tesSUCCESS));
1936 env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
1953 uint256 const aliceSellOfferIndex =
1955 env(token::createOffer(alice, nftAliceID, XRP(20)),
1960 uint256 const beckyBuyOfferIndex =
1962 env(token::createOffer(becky, nftAliceID, XRP(21)),
1963 token::owner(alice));
1968 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1974 uint256 const beckySellOfferIndex =
1976 env(token::createOffer(becky, nftAliceID, XRP(22)),
1984 env(token::acceptSellOffer(minter, beckySellOfferIndex));
1991 uint256 const minterSellOfferIndex =
1993 env(token::createOffer(minter, nftAliceID, XRP(23)),
2001 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2009 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2017 env(token::burn(becky, nftAliceID));
2029 testcase(
"Mint transferFee");
2031 using namespace test::jtx;
2033 Env env{*
this, features};
2035 Account
const alice{
"alice"};
2036 Account
const becky{
"becky"};
2037 Account
const carol{
"carol"};
2038 Account
const minter{
"minter"};
2039 Account
const gw{
"gw"};
2040 IOU
const gwXAU(gw[
"XAU"]);
2042 env.fund(XRP(1000), alice, becky, carol, minter, gw);
2045 env(trust(alice, gwXAU(2000)));
2046 env(trust(becky, gwXAU(2000)));
2047 env(trust(carol, gwXAU(2000)));
2048 env(trust(minter, gwXAU(2000)));
2050 env(pay(gw, alice, gwXAU(1000)));
2051 env(pay(gw, becky, gwXAU(1000)));
2052 env(pay(gw, carol, gwXAU(1000)));
2053 env(pay(gw, minter, gwXAU(1000)));
2058 env(token::setMinter(alice, minter));
2075 uint256 const beckyBuyOfferIndex =
2077 env(token::createOffer(becky, nftID, gwXAU(10)),
2078 token::owner(alice));
2080 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2081 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2083 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2085 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2086 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2089 uint256 const beckySellOfferIndex =
2091 env(token::createOffer(becky, nftID, gwXAU(10)),
2094 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2096 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2097 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2098 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2101 uint256 const minterBuyOfferIndex =
2103 env(token::createOffer(minter, nftID, gwXAU(10)),
2104 token::owner(carol));
2106 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2108 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2109 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2110 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2111 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2115 uint256 const minterSellOfferIndex =
2117 env(token::createOffer(minter, nftID, gwXAU(10)),
2120 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2122 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2123 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2124 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2125 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2128 env(token::burn(alice, nftID));
2141 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2145 uint256 const beckyBuyOfferIndex =
2147 env(token::createOffer(becky, nftID, gwXAU(10)),
2148 token::owner(alice));
2150 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2151 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2153 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2155 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2156 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2159 uint256 const beckySellOfferIndex =
2161 env(token::createOffer(becky, nftID, gwXAU(10)),
2164 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2167 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2168 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2169 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2172 uint256 const minterBuyOfferIndex =
2174 env(token::createOffer(minter, nftID, gwXAU(10)),
2175 token::owner(carol));
2177 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2180 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2181 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2182 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2183 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2187 uint256 const minterSellOfferIndex =
2189 env(token::createOffer(minter, nftID, gwXAU(10)),
2192 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2194 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2195 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2196 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2197 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2201 env(pay(alice, becky, gwXAU(0.0001)));
2202 env(pay(alice, carol, gwXAU(0.0001)));
2205 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2206 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2207 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2208 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2211 env(token::burn(alice, nftID));
2222 env(token::mint(alice),
2229 uint256 const nftID = token::getNextID(
2231 env(token::mint(alice),
2237 uint256 const beckyBuyOfferIndex =
2239 env(token::createOffer(becky, nftID, gwXAU(10)),
2240 token::owner(alice));
2242 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2243 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2245 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2247 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2248 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2251 uint256 const beckySellOfferIndex =
2253 env(token::createOffer(becky, nftID, gwXAU(100)),
2256 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2259 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2260 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2261 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2264 uint256 const carolBuyOfferIndex =
2266 env(token::createOffer(carol, nftID, gwXAU(10)),
2267 token::owner(minter));
2269 env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2272 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2273 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2274 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2275 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2279 uint256 const carolSellOfferIndex =
2281 env(token::createOffer(carol, nftID, gwXAU(10)),
2284 env(token::acceptSellOffer(alice, carolSellOfferIndex));
2287 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2288 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2289 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2290 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2293 env(pay(alice, minter, gwXAU(55)));
2294 env(pay(becky, minter, gwXAU(40)));
2296 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2297 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2298 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2299 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2302 env(token::burn(alice, nftID));
2316 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2322 STAmount aliceBalance = env.balance(alice);
2323 STAmount minterBalance = env.balance(minter);
2324 uint256 const minterBuyOfferIndex =
2326 env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2328 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2330 aliceBalance += XRP(1) - fee;
2331 minterBalance -= XRP(1) + fee;
2332 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2333 BEAST_EXPECT(env.balance(minter) == minterBalance);
2337 STAmount carolBalance = env.balance(carol);
2338 uint256 const minterSellOfferIndex =
2340 env(token::createOffer(minter, nftID, drops(99999)),
2343 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2345 minterBalance += drops(99999) - fee;
2346 carolBalance -= drops(99999) + fee;
2347 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2348 BEAST_EXPECT(env.balance(minter) == minterBalance);
2349 BEAST_EXPECT(env.balance(carol) == carolBalance);
2353 STAmount beckyBalance = env.balance(becky);
2354 uint256 const beckyBuyOfferIndex =
2356 env(token::createOffer(becky, nftID, drops(100000)),
2357 token::owner(carol));
2359 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2361 carolBalance += drops(99999) - fee;
2362 beckyBalance -= drops(100000) + fee;
2363 aliceBalance += drops(1);
2365 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2366 BEAST_EXPECT(env.balance(minter) == minterBalance);
2367 BEAST_EXPECT(env.balance(carol) == carolBalance);
2368 BEAST_EXPECT(env.balance(becky) == beckyBalance);
2377 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2383 env(pay(alice, gw, env.balance(alice, gwXAU)));
2384 env(pay(minter, gw, env.balance(minter, gwXAU)));
2385 env(pay(becky, gw, env.balance(becky, gwXAU)));
2390 env(pay(gw, alice, startXAUBalance));
2391 env(pay(gw, minter, startXAUBalance));
2392 env(pay(gw, becky, startXAUBalance));
2401 STAmount aliceBalance = env.balance(alice, gwXAU);
2402 STAmount minterBalance = env.balance(minter, gwXAU);
2403 uint256 const minterBuyOfferIndex =
2405 env(token::createOffer(minter, nftID, tinyXAU),
2406 token::owner(alice));
2408 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2410 aliceBalance += tinyXAU;
2411 minterBalance -= tinyXAU;
2412 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2413 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2416 STAmount carolBalance = env.balance(carol, gwXAU);
2417 uint256 const minterSellOfferIndex =
2419 env(token::createOffer(minter, nftID, tinyXAU),
2422 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2425 minterBalance += tinyXAU;
2426 carolBalance -= tinyXAU;
2428 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2429 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2430 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2437 STAmount beckyBalance = env.balance(becky, gwXAU);
2438 uint256 const beckyBuyOfferIndex =
2440 env(token::createOffer(becky, nftID, cheapNFT),
2441 token::owner(carol));
2443 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2446 aliceBalance += tinyXAU;
2447 beckyBalance -= cheapNFT;
2448 carolBalance += cheapNFT - tinyXAU;
2449 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2450 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2451 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2452 BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2460 testcase(
"Mint taxon");
2462 using namespace test::jtx;
2464 Env env{*
this, features};
2466 Account
const alice{
"alice"};
2467 Account
const becky{
"becky"};
2469 env.fund(XRP(1000), alice, becky);
2478 uint256 const nftID = token::getNextID(env, alice, 0u);
2485 uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2493 for (
int i = 0; i < 10; ++i)
2503 ss <<
"Taxon recovery failed from nftID "
2504 <<
to_string(nftID) <<
". Expected: " << taxon
2505 <<
"; got: " << gotTaxon;
2510 uint256 const nftAliceID = token::getID(
2513 rand_int<std::uint32_t>(),
2514 rand_int<std::uint16_t>(),
2515 rand_int<std::uint16_t>());
2516 check(taxon, nftAliceID);
2518 uint256 const nftBeckyID = token::getID(
2521 rand_int<std::uint32_t>(),
2522 rand_int<std::uint16_t>(),
2523 rand_int<std::uint16_t>());
2524 check(taxon, nftBeckyID);
2536 testcase(
"Mint URI");
2538 using namespace test::jtx;
2540 Env env{*
this, features};
2542 Account
const alice{
"alice"};
2543 Account
const becky{
"becky"};
2545 env.fund(XRP(10000), alice, becky);
2551 auto randURI = []() {
2573 : uri(std::move(uri_)), taxon(taxon_)
2581 entries.
emplace_back(randURI(), rand_int<std::uint32_t>());
2584 for (Entry
const& entry : entries)
2586 if (entry.uri.empty())
2588 env(token::mint(alice, entry.taxon));
2592 env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2600 params[jss::account] = alice.human();
2601 params[jss::type] =
"state";
2602 return env.rpc(
"json",
"account_nfts",
to_string(params));
2606 Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2607 if (!BEAST_EXPECT(nfts.
size() == entries.size()))
2620 return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2625 Entry
const& entry = entries[i];
2628 if (entry.uri.empty())
2643 testcase(
"Create offer destination");
2645 using namespace test::jtx;
2647 Env env{*
this, features};
2649 Account
const issuer{
"issuer"};
2650 Account
const minter{
"minter"};
2651 Account
const buyer{
"buyer"};
2652 Account
const broker{
"broker"};
2654 env.fund(XRP(1000), issuer, minter, buyer, broker);
2658 env(token::setMinter(issuer, minter));
2663 env(token::mint(minter, 0),
2664 token::issuer(issuer),
2671 uint256 const offerMinterToIssuer =
2673 env(token::createOffer(minter, nftokenID, drops(1)),
2674 token::destination(issuer),
2677 uint256 const offerMinterToBuyer =
2679 env(token::createOffer(minter, nftokenID, drops(1)),
2680 token::destination(buyer),
2684 env(token::createOffer(issuer, nftokenID, drops(1)),
2685 token::owner(minter),
2686 token::destination(minter),
2701 env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2703 env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2712 env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2713 env(token::cancelOffer(minter, {offerMinterToIssuer}));
2723 uint256 const offerMinterToBuyer =
2725 env(token::createOffer(minter, nftokenID, drops(1)),
2726 token::destination(buyer),
2735 env(token::acceptSellOffer(issuer, offerMinterToBuyer),
2743 env(token::acceptSellOffer(buyer, offerMinterToBuyer));
2752 env(token::createOffer(minter, nftokenID, drops(1)),
2753 token::owner(buyer),
2754 token::destination(buyer),
2762 uint256 const offerMinterToBuyer =
2764 env(token::createOffer(minter, nftokenID, drops(1)),
2765 token::owner(buyer));
2772 env(token::acceptBuyOffer(buyer, offerMinterToBuyer));
2782 uint256 const offerMinterToBroker =
2784 env(token::createOffer(minter, nftokenID, drops(1)),
2785 token::destination(broker),
2788 uint256 const offerBuyerToMinter =
2790 env(token::createOffer(buyer, nftokenID, drops(1)),
2791 token::owner(minter));
2800 env(token::brokerOffers(
2801 issuer, offerBuyerToMinter, offerMinterToBroker),
2810 env(token::brokerOffers(
2811 broker, offerBuyerToMinter, offerMinterToBroker));
2822 uint256 const offerBuyerToMinter =
2824 env(token::createOffer(buyer, nftokenID, drops(1)),
2825 token::destination(minter),
2828 uint256 const offerMinterToBuyer =
2830 env(token::createOffer(minter, nftokenID, drops(1)),
2831 token::owner(buyer));
2833 uint256 const offerIssuerToBuyer =
2835 env(token::createOffer(issuer, nftokenID, drops(1)),
2836 token::owner(buyer));
2844 env(token::brokerOffers(
2845 broker, offerIssuerToBuyer, offerBuyerToMinter),
2853 env(token::brokerOffers(
2854 broker, offerMinterToBuyer, offerBuyerToMinter));
2866 testcase(
"Create offer expiration");
2868 using namespace test::jtx;
2870 Env env{*
this, features};
2872 Account
const issuer{
"issuer"};
2873 Account
const minter{
"minter"};
2874 Account
const buyer{
"buyer"};
2876 env.fund(XRP(1000), issuer, minter, buyer);
2880 env(token::setMinter(issuer, minter));
2885 env(token::mint(minter, 0),
2886 token::issuer(issuer),
2892 env(token::mint(minter, 0),
2893 token::issuer(issuer),
2902 uint256 const offerMinterToIssuer =
2904 env(token::createOffer(minter, nftokenID0, drops(1)),
2905 token::destination(issuer),
2906 token::expiration(expiration),
2909 uint256 const offerMinterToAnyone =
2911 env(token::createOffer(minter, nftokenID0, drops(1)),
2912 token::expiration(expiration),
2915 uint256 const offerIssuerToMinter =
2917 env(token::createOffer(issuer, nftokenID0, drops(1)),
2918 token::owner(minter),
2919 token::expiration(expiration));
2921 uint256 const offerBuyerToMinter =
2923 env(token::createOffer(buyer, nftokenID0, drops(1)),
2924 token::owner(minter),
2925 token::expiration(expiration));
2937 env(token::cancelOffer(issuer, {offerMinterToAnyone}),
2939 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2942 BEAST_EXPECT(
lastClose(env) < expiration);
2948 env(token::cancelOffer(minter, {offerMinterToAnyone}));
2952 env(token::cancelOffer(issuer, {offerMinterToIssuer}));
2963 env(token::cancelOffer(issuer, {offerBuyerToMinter}));
2964 env(token::cancelOffer(buyer, {offerIssuerToMinter}));
2979 env(token::createOffer(minter, nftokenID0, drops(1)),
2980 token::expiration(expiration),
2985 env(token::createOffer(minter, nftokenID1, drops(1)),
2986 token::expiration(expiration),
2989 BEAST_EXPECT(
lastClose(env) < expiration);
2995 env(token::acceptSellOffer(buyer, offer0));
3006 env(token::acceptSellOffer(buyer, offer1), ter(
tecEXPIRED));
3007 env(token::acceptSellOffer(issuer, offer1), ter(
tecEXPIRED));
3016 env(token::cancelOffer(issuer, {offer1}));
3026 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3028 token::destination(minter));
3030 env(token::acceptSellOffer(minter, offerSellBack));
3044 env(token::createOffer(buyer, nftokenID0, drops(1)),
3045 token::owner(minter),
3046 token::expiration(expiration));
3049 env(token::createOffer(buyer, nftokenID1, drops(1)),
3050 token::owner(minter),
3051 token::expiration(expiration));
3053 BEAST_EXPECT(
lastClose(env) < expiration);
3059 env(token::acceptBuyOffer(minter, offer0));
3070 env(token::acceptBuyOffer(minter, offer1), ter(
tecEXPIRED));
3071 env(token::acceptBuyOffer(issuer, offer1), ter(
tecEXPIRED));
3080 env(token::cancelOffer(issuer, {offer1}));
3090 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3092 token::destination(minter));
3094 env(token::acceptSellOffer(minter, offerSellBack));
3109 env(token::createOffer(minter, nftokenID0, drops(1)),
3110 token::expiration(expiration),
3115 env(token::createOffer(minter, nftokenID1, drops(1)),
3116 token::expiration(expiration),
3121 env(token::createOffer(buyer, nftokenID0, drops(1)),
3122 token::owner(minter));
3126 env(token::createOffer(buyer, nftokenID1, drops(1)),
3127 token::owner(minter));
3130 BEAST_EXPECT(
lastClose(env) < expiration);
3136 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3147 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3157 env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3167 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3169 token::destination(minter));
3171 env(token::acceptSellOffer(minter, offerSellBack));
3186 env(token::createOffer(minter, nftokenID0, drops(1)),
3191 env(token::createOffer(minter, nftokenID1, drops(1)),
3196 env(token::createOffer(buyer, nftokenID0, drops(1)),
3197 token::expiration(expiration),
3198 token::owner(minter));
3202 env(token::createOffer(buyer, nftokenID1, drops(1)),
3203 token::expiration(expiration),
3204 token::owner(minter));
3207 BEAST_EXPECT(
lastClose(env) < expiration);
3213 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3224 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3234 env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3244 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3246 token::destination(minter));
3248 env(token::acceptSellOffer(minter, offerSellBack));
3264 env(token::createOffer(minter, nftokenID0, drops(1)),
3265 token::expiration(expiration),
3270 env(token::createOffer(minter, nftokenID1, drops(1)),
3271 token::expiration(expiration),
3276 env(token::createOffer(buyer, nftokenID0, drops(1)),
3277 token::expiration(expiration),
3278 token::owner(minter));
3282 env(token::createOffer(buyer, nftokenID1, drops(1)),
3283 token::expiration(expiration),
3284 token::owner(minter));
3287 BEAST_EXPECT(
lastClose(env) < expiration);
3293 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3304 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3314 env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3324 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3326 token::destination(minter));
3328 env(token::acceptSellOffer(minter, offerSellBack));
3340 testcase(
"Cancel offers");
3342 using namespace test::jtx;
3344 Env env{*
this, features};
3346 Account
const alice(
"alice");
3347 Account
const becky(
"becky");
3348 Account
const minter(
"minter");
3349 env.fund(XRP(50000), alice, becky, minter);
3353 env(token::setMinter(alice, minter));
3362 uint256 const expiredOfferIndex =
3365 env(token::createOffer(alice, nftokenID, XRP(1000)),
3367 token::expiration(
lastClose(env) + 13));
3372 env(token::cancelOffer(becky, {expiredOfferIndex}),
3380 env(token::cancelOffer(becky, {expiredOfferIndex}));
3386 uint256 const dest1OfferIndex =
3389 env(token::createOffer(alice, nftokenID, XRP(1000)),
3390 token::destination(becky),
3396 env(token::cancelOffer(minter, {dest1OfferIndex}),
3401 env(token::cancelOffer(becky, {dest1OfferIndex}));
3406 uint256 const dest2OfferIndex =
3409 env(token::createOffer(alice, nftokenID, XRP(1000)),
3410 token::destination(becky),
3415 env(token::cancelOffer(alice, {dest2OfferIndex}));
3422 uint256 const mintersNFTokenID =
3424 env(token::mint(minter, 0),
3425 token::issuer(alice),
3429 uint256 const minterOfferIndex =
3432 env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3438 env(token::cancelOffer(alice, {minterOfferIndex}),
3440 env(token::cancelOffer(becky, {minterOfferIndex}),
3445 env(token::cancelOffer(minter, {minterOfferIndex}));
3454 testcase(
"Cancel too many offers");
3456 using namespace test::jtx;
3458 Env env{*
this, features};
3473 Account
const alice(
"alice");
3474 env.fund(XRP(1000), alice);
3483 Account
const offerAcct(
3485 env.fund(XRP(1000), nftAcct, offerAcct);
3490 env(token::mint(nftAcct, 0),
3497 env(token::createOffer(offerAcct, nftokenID, drops(1)),
3498 token::owner(nftAcct),
3507 for (
uint256 const& offerIndex : offerIndexes)
3514 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3518 env(token::cancelOffer(alice, {offerIndexes.back()}));
3523 offerIndexes.pop_back();
3529 env(token::mint(alice, 0),
3535 env(token::createOffer(alice, nftokenID, drops(1)),
3544 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3548 env(token::burn(alice, nftokenID));
3554 offerIndexes.pop_back();
3559 env(token::cancelOffer(alice, offerIndexes));
3563 for (
uint256 const& offerIndex : offerIndexes)
3573 testcase(
"Brokered NFT offer accept");
3575 using namespace test::jtx;
3577 Env env{*
this, features};
3585 Account
const issuer{
"issuer"};
3586 Account
const minter{
"minter"};
3587 Account
const buyer{
"buyer"};
3588 Account
const broker{
"broker"};
3589 Account
const gw{
"gw"};
3590 IOU
const gwXAU(gw[
"XAU"]);
3592 env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3595 env(trust(issuer, gwXAU(2000)));
3596 env(trust(minter, gwXAU(2000)));
3597 env(trust(buyer, gwXAU(2000)));
3598 env(trust(broker, gwXAU(2000)));
3601 env(token::setMinter(issuer, minter));
3605 auto checkOwnerCountIsOne =
3610 for (Account
const& acct : accounts)
3616 ss <<
"Account " << acct.human()
3617 <<
" expected ownerCount == 1. Got " <<
ownerCount;
3618 fail(ss.
str(), __FILE__, line);
3624 auto mintNFT = [&env, &issuer, &minter](
std::uint16_t xferFee = 0) {
3627 env(token::mint(minter, 0),
3628 token::issuer(issuer),
3629 token::xferFee(xferFee),
3641 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3643 uint256 const nftID = mintNFT();
3646 uint256 const minterOfferIndex =
3648 env(token::createOffer(minter, nftID, XRP(0)),
3656 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3659 auto const minterBalance = env.balance(minter);
3660 auto const buyerBalance = env.balance(buyer);
3661 auto const brokerBalance = env.balance(broker);
3662 auto const issuerBalance = env.balance(issuer);
3665 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3670 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3671 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3672 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3673 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3676 env(token::burn(buyer, nftID));
3686 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3688 uint256 const nftID = mintNFT();
3691 uint256 const minterOfferIndex =
3693 env(token::createOffer(minter, nftID, XRP(0)),
3701 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3705 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3706 token::brokerFee(XRP(1.1)),
3710 auto const minterBalance = env.balance(minter);
3711 auto const buyerBalance = env.balance(buyer);
3712 auto const brokerBalance = env.balance(broker);
3713 auto const issuerBalance = env.balance(issuer);
3716 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3717 token::brokerFee(XRP(0.5)));
3722 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3723 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3725 env.balance(broker) == brokerBalance + XRP(0.5) - drops(10));
3726 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3729 env(token::burn(buyer, nftID));
3739 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3744 uint256 const minterOfferIndex =
3746 env(token::createOffer(minter, nftID, XRP(0)),
3754 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3757 auto const minterBalance = env.balance(minter);
3758 auto const buyerBalance = env.balance(buyer);
3759 auto const brokerBalance = env.balance(broker);
3760 auto const issuerBalance = env.balance(issuer);
3763 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3768 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3769 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3770 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3771 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
3774 env(token::burn(buyer, nftID));
3784 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3789 uint256 const minterOfferIndex =
3791 env(token::createOffer(minter, nftID, XRP(0)),
3799 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3802 auto const minterBalance = env.balance(minter);
3803 auto const buyerBalance = env.balance(buyer);
3804 auto const brokerBalance = env.balance(broker);
3805 auto const issuerBalance = env.balance(issuer);
3808 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3809 token::brokerFee(XRP(0.75)));
3815 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
3816 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3818 env.balance(broker) == brokerBalance + XRP(0.75) - drops(10));
3819 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
3822 env(token::burn(buyer, nftID));
3827 auto setXAUBalance_1000 =
3828 [
this, &gw, &gwXAU, &env](
3832 for (Account
const& acct : accounts)
3834 static const auto xau1000 = gwXAU(1000);
3835 auto const balance = env.balance(acct, gwXAU);
3836 if (balance < xau1000)
3838 env(pay(gw, acct, xau1000 - balance));
3841 else if (balance > xau1000)
3843 env(pay(acct, gw, balance - xau1000));
3846 if (env.balance(acct, gwXAU) != xau1000)
3849 ss <<
"Unable to set " << acct.human()
3850 <<
" account balance to gwXAU(1000)";
3851 this->fail(ss.
str(), __FILE__, line);
3859 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3860 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
3862 uint256 const nftID = mintNFT();
3865 uint256 const minterOfferIndex =
3867 env(token::createOffer(minter, nftID, gwXAU(1000)),
3875 env(token::createOffer(buyer, nftID, gwXAU(1001)),
3876 token::owner(minter));
3880 env(token::brokerOffers(
3881 broker, buyOfferIndex, minterOfferIndex),
3887 env(token::cancelOffer(buyer, {buyOfferIndex}));
3894 env(token::createOffer(buyer, nftID, gwXAU(999)),
3895 token::owner(minter));
3899 env(token::brokerOffers(
3900 broker, buyOfferIndex, minterOfferIndex),
3906 env(token::cancelOffer(buyer, {buyOfferIndex}));
3913 env(token::createOffer(buyer, nftID, gwXAU(1000)),
3914 token::owner(minter));
3918 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3919 token::brokerFee(gwXAU(0.1)),
3924 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3931 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
3932 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
3933 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
3934 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
3937 env(token::burn(buyer, nftID));
3944 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3945 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
3950 uint256 const minterOfferIndex =
3952 env(token::createOffer(minter, nftID, gwXAU(900)),
3959 env(token::createOffer(buyer, nftID, gwXAU(1001)),
3960 token::owner(minter));
3964 env(token::brokerOffers(
3965 broker, buyOfferIndex, minterOfferIndex),
3971 env(token::cancelOffer(buyer, {buyOfferIndex}));
3978 env(token::createOffer(buyer, nftID, gwXAU(899)),
3979 token::owner(minter));
3983 env(token::brokerOffers(
3984 broker, buyOfferIndex, minterOfferIndex),
3990 env(token::cancelOffer(buyer, {buyOfferIndex}));
3996 env(token::createOffer(buyer, nftID, gwXAU(1000)),
3997 token::owner(minter));
4002 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4003 token::brokerFee(gwXAU(101)),
4009 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4010 token::brokerFee(gwXAU(100)));
4017 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4018 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4019 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4020 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4023 env(token::burn(buyer, nftID));
4030 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4031 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4036 uint256 const minterOfferIndex =
4038 env(token::createOffer(minter, nftID, gwXAU(900)),
4045 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4046 token::owner(minter));
4052 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4053 token::brokerFee(gwXAU(50)));
4060 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4061 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4062 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4063 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4066 env(token::burn(buyer, nftID));
4075 testcase(
"NFToken offer owner");
4077 using namespace test::jtx;
4079 Env env{*
this, features};
4081 Account
const issuer{
"issuer"};
4082 Account
const buyer1{
"buyer1"};
4083 Account
const buyer2{
"buyer2"};
4084 env.fund(XRP(10000), issuer, buyer1, buyer2);
4093 BEAST_EXPECT(
nftCount(env, issuer) == 1);
4094 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4095 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4098 uint256 const buyer1OfferIndex =
4100 env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4101 uint256 const buyer2OfferIndex =
4103 env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4113 env.rpc(
"json",
"nft_buy_offers",
to_string(params));
4115 if (buyOffers.
isMember(jss::result) &&
4116 buyOffers[jss::result].
isMember(jss::offers))
4117 return buyOffers[jss::result][jss::offers].
size();
4123 BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4126 env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4130 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4131 BEAST_EXPECT(
nftCount(env, buyer1) == 1);
4132 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4136 BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4140 env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4144 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4145 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4146 BEAST_EXPECT(
nftCount(env, buyer2) == 1);
4149 BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4156 testcase(
"NFToken transactions with tickets");
4158 using namespace test::jtx;
4160 Env env{*
this, features};
4162 Account
const issuer{
"issuer"};
4163 Account
const buyer{
"buyer"};
4164 env.fund(XRP(10000), issuer, buyer);
4171 env(ticket::create(issuer, 10));
4177 env(ticket::create(buyer, 10));
4185 env(token::mint(issuer, 0u),
4187 ticket::use(issuerTicketSeq++));
4195 env(token::createOffer(buyer, nftId, XRP(1)),
4196 token::owner(issuer),
4197 ticket::use(buyerTicketSeq++));
4203 env(token::cancelOffer(buyer, {offerIndex0}),
4204 ticket::use(buyerTicketSeq++));
4211 env(token::createOffer(buyer, nftId, XRP(2)),
4212 token::owner(issuer),
4213 ticket::use(buyerTicketSeq++));
4219 env(token::acceptBuyOffer(issuer, offerIndex1),
4220 ticket::use(issuerTicketSeq++));
4227 env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4234 BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4235 BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4246 testcase(
"NFToken delete account");
4248 using namespace test::jtx;
4250 Env env{*
this, features};
4252 Account
const issuer{
"issuer"};
4253 Account
const minter{
"minter"};
4254 Account
const becky{
"becky"};
4255 Account
const carla{
"carla"};
4256 Account
const daria{
"daria"};
4258 env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4262 for (
int i = 0; i < 300; ++i)
4265 env(token::setMinter(issuer, minter));
4269 env(token::mint(minter, 0u),
4270 token::issuer(issuer),
4283 for (
int i = 0; i < 15; ++i)
4287 env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4290 uint256 const carlaOfferIndex =
4292 env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4297 env(acctdelete(becky, daria), fee(XRP(50)));
4301 env(token::acceptBuyOffer(minter, carlaOfferIndex));
4306 env(acctdelete(minter, daria), fee(XRP(50)));
4318 for (
int i = 0; i < 15; ++i)
4323 env(token::burn(carla, nftId));
4326 env(acctdelete(issuer, daria), fee(XRP(50)));
4327 env(acctdelete(carla, daria), fee(XRP(50)));
4334 testcase(
"nft_buy_offers and nft_sell_offers");
4343 using namespace test::jtx;
4345 Env env{*
this, features};
4347 Account
const issuer{
"issuer"};
4348 Account
const buyer{
"buyer"};
4351 env.fund(XRP(1000000), issuer, buyer);
4360 auto checkOffers = [
this, &env, &nftID](
4361 char const* request,
4363 int expectMarkerCount,
4365 int markerCount = 0;
4372 Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4376 if (!marker.
empty())
4377 params[jss::marker] = marker;
4378 return env.rpc(
"json", request,
to_string(params));
4382 if (expectCount == 0)
4386 "expected \"result\"",
4391 nftOffers[jss::result].isMember(jss::error),
4392 "expected \"error\"",
4397 nftOffers[jss::result][jss::error].asString() ==
4399 "expected \"objectNotFound\"",
4410 "expected \"result\"",
4419 marker = result[jss::marker].
asString();
4424 "expected \"offers\"",
4430 allOffers.
append(someOffers[i]);
4433 }
while (!marker.
empty());
4437 allOffers.
size() == expectCount,
4438 "Unexpected returned offer count",
4442 markerCount == expectMarkerCount,
4443 "Unexpected marker count",
4453 globalFlags = offer[jss::flags].asInt();
4456 *globalFlags == offer[jss::flags].asInt(),
4457 "Inconsistent flags returned",
4463 offerIndexes.
insert(offer[jss::nft_offer_index].asString());
4464 amounts.
insert(offer[jss::amount].asString());
4468 offerIndexes.
size() == expectCount,
4469 "Duplicate indexes returned?",
4473 amounts.
size() == expectCount,
4474 "Duplicate amounts returned?",
4480 checkOffers(
"nft_sell_offers", 0,
false, __LINE__);
4484 auto makeSellOffers =
4485 [&env, &issuer, &nftID, &sellPrice](
STAmount const& limit) {
4488 while (sellPrice < limit)
4490 sellPrice += XRP(1);
4491 env(token::createOffer(issuer, nftID, sellPrice),
4493 if (++offerCount % 10 == 0)
4500 makeSellOffers(XRP(1));
4501 checkOffers(
"nft_sell_offers", 1, 0, __LINE__);
4504 makeSellOffers(XRP(250));
4505 checkOffers(
"nft_sell_offers", 250, 0, __LINE__);
4508 makeSellOffers(XRP(251));
4509 checkOffers(
"nft_sell_offers", 251, 1, __LINE__);
4512 makeSellOffers(XRP(500));
4513 checkOffers(
"nft_sell_offers", 500, 1, __LINE__);
4516 makeSellOffers(XRP(501));
4517 checkOffers(
"nft_sell_offers", 501, 2, __LINE__);
4520 checkOffers(
"nft_buy_offers", 0, 0, __LINE__);
4524 auto makeBuyOffers =
4525 [&env, &buyer, &issuer, &nftID, &buyPrice](
STAmount const& limit) {
4528 while (buyPrice < limit)
4531 env(token::createOffer(buyer, nftID, buyPrice),
4532 token::owner(issuer));
4533 if (++offerCount % 10 == 0)
4540 makeBuyOffers(XRP(1));
4541 checkOffers(
"nft_buy_offers", 1, 0, __LINE__);
4544 makeBuyOffers(XRP(250));
4545 checkOffers(
"nft_buy_offers", 250, 0, __LINE__);
4548 makeBuyOffers(XRP(251));
4549 checkOffers(
"nft_buy_offers", 251, 1, __LINE__);
4552 makeBuyOffers(XRP(500));
4553 checkOffers(
"nft_buy_offers", 500, 1, __LINE__);
4556 makeBuyOffers(XRP(501));
4557 checkOffers(
"nft_buy_offers", 501, 2, __LINE__);
4593 using namespace test::jtx;