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>
39 if (
auto const sleAcct = env.
le(acct))
49 if (
auto const sleIssuer = env.
le(issuer))
59 if (
auto const sleIssuer = env.
le(issuer))
69 params[jss::account] = acct.
human();
70 params[jss::type] =
"state";
72 return nfts[jss::result][jss::account_nfts].
size();
80 if (
auto const sleAcct = env.
le(acct))
89 return env.
current()->info().parentCloseTime.time_since_epoch().count();
97 using namespace test::jtx;
105 Account
const& master = env.master;
111 uint256 const nftId{token::getNextID(env, master, 0u)};
126 env(token::createOffer(master, nftId, XRP(10)), ter(
temDISABLED));
132 env(token::cancelOffer(master, {offerIndex}), ter(
temDISABLED));
138 env(token::acceptBuyOffer(master, offerIndex), ter(
temDISABLED));
147 Env env{*
this, features};
148 Account
const& master = env.master;
154 uint256 const nftId0{token::getNextID(env, env.master, 0u)};
155 env(token::mint(env.master, 0u));
161 env(token::burn(env.master, nftId0));
175 Account
const alice{
"alice"};
176 env.fund(XRP(10000), alice);
178 uint256 const aliceOfferIndex =
180 env(token::createOffer(alice, nftId1, XRP(1000)),
181 token::owner(master));
192 env(token::acceptBuyOffer(master, aliceOfferIndex));
209 testcase(
"Mint reserve");
211 using namespace test::jtx;
213 Env env{*
this, features};
214 Account
const alice{
"alice"};
215 Account
const minter{
"minter"};
220 env.fund(XRP(200), alice, minter);
222 BEAST_EXPECT(env.balance(alice) == XRP(200));
223 BEAST_EXPECT(env.balance(minter) == XRP(200));
235 env(pay(env.master, alice, XRP(50) + drops(9)));
240 auto checkAliceOwnerMintedBurned = [&env,
this, &alice](
253 ss <<
"Wrong " << type <<
" count. Found: " << found
254 <<
"; Expected: " << exp;
255 fail(ss.
str(), __FILE__, line);
258 oneCheck(
"owner",
ownerCount(env, alice), owners);
259 oneCheck(
"minted",
mintedCount(env, alice), minted);
260 oneCheck(
"burned",
burnedCount(env, alice), burned);
266 checkAliceOwnerMintedBurned(0, 0, 0, __LINE__);
269 env(pay(env.master, alice, drops(11)));
273 env(token::mint(alice));
275 checkAliceOwnerMintedBurned(1, 1, 0, __LINE__);
279 for (
int i = 1; i < 32; ++i)
281 env(token::mint(alice));
282 checkAliceOwnerMintedBurned(1, i + 1, 0, __LINE__);
289 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
292 env(pay(env.master, alice, XRP(50) + drops(329)));
298 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
301 env(pay(env.master, alice, drops(11)));
305 env(token::mint(alice));
307 checkAliceOwnerMintedBurned(2, 33, 0, __LINE__);
314 env(token::burn(alice, token::getID(alice, 0, seq++)));
316 checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__);
320 env(token::burn(alice, token::getID(alice, 197, 5)), ter(
tecNO_ENTRY));
322 checkAliceOwnerMintedBurned(0, 33, 33, __LINE__);
326 env(token::setMinter(alice, minter));
333 auto checkMintersOwnerMintedBurned = [&env,
this, &alice, &minter](
341 auto oneCheck = [
this](
351 ss <<
"Wrong " << type <<
" count. Found: " << found
352 <<
"; Expected: " << exp;
353 fail(ss.
str(), __FILE__, line);
356 oneCheck(
"alice owner",
ownerCount(env, alice), aliceOwners, line);
358 "alice minted",
mintedCount(env, alice), aliceMinted, line);
360 "alice burned",
burnedCount(env, alice), aliceBurned, line);
362 "minter owner",
ownerCount(env, minter), minterOwners, line);
364 "minter minted",
mintedCount(env, minter), minterMinted, line);
366 "minter burned",
burnedCount(env, minter), minterBurned, line);
372 env(pay(env.master, minter, XRP(50) - drops(1)));
374 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
379 env(token::mint(minter),
380 token::issuer(alice),
384 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
387 env(pay(env.master, minter, drops(11)));
391 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
393 checkMintersOwnerMintedBurned(0, 34, nftSeq, 1, 0, 0, __LINE__);
397 for (
int i = 1; i < 32; ++i)
399 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
400 checkMintersOwnerMintedBurned(0, i + 34, nftSeq, 1, 0, 0, __LINE__);
404 env(pay(env.master, minter, XRP(50) + drops(319)));
409 env(token::mint(minter),
410 token::issuer(alice),
414 checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__);
417 env(pay(env.master, minter, drops(11)));
421 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
423 checkMintersOwnerMintedBurned(0, 66, nftSeq, 2, 0, 0, __LINE__);
428 env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
430 checkMintersOwnerMintedBurned(
431 0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__);
435 env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
437 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
440 env(token::burn(minter, token::getID(alice, 2009, 3)),
443 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
451 testcase(
"Mint max tokens");
453 using namespace test::jtx;
455 Account
const alice{
"alice"};
456 Env env{*
this, features};
457 env.fund(XRP(1000), alice);
466 uint256 const nftId0{token::getNextID(env, alice, 0u)};
467 env(token::mint(alice, 0u));
470 env(token::burn(alice, nftId0));
477 env.app().openLedger().modify(
486 auto replacement = std::make_shared<SLE>(*sle, sle->key());
492 view.rawReplace(replacement);
496 // See whether alice is at the boundary that causes an error.
497 env(token::mint(alice, 0u), ter(tesSUCCESS));
498 env(token::mint(alice, 0u), ter(tecMAX_SEQUENCE_REACHED));
502 testMintInvalid(FeatureBitset features)
504 // Explore many of the invalid ways to mint an NFT.
505 testcase("Mint invalid");
507 using namespace test::jtx;
509 Env env{*this, features};
510 Account const alice{"alice"};
511 Account const minter{"minter"};
513 // Fund alice and minter enough to exist, but not enough to meet
514 // the reserve for creating their first NFT. Account reserve for unit
515 // tests is 200 XRP, not 20.
516 env.fund(XRP(200), alice, minter);
519 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
522 // Fund alice enough to start minting NFTs.
523 env(pay(env.master, alice, XRP(1000)));
526 //----------------------------------------------------------------------
529 // Set a negative fee.
530 env(token::mint(alice, 0u),
531 fee(STAmount(10ull, true)),
534 // Set an invalid flag.
535 env(token::mint(alice, 0u), txflags(0x00008000), ter(temINVALID_FLAG));
537 // Can't
set a transfer fee
if the NFT does not have the tfTRANSFERABLE
539 env(token::mint(alice, 0u),
544 env(token::mint(alice, 0u),
550 env(token::mint(alice, 0u), token::issuer(alice), ter(
temMALFORMED));
553 env(token::mint(alice, 0u), token::uri(
""), ter(
temMALFORMED));
556 env(token::mint(alice, 0u),
564 env(token::mint(alice, 0u),
565 token::issuer(Account(
"demon")),
572 env(token::mint(minter, 0u),
573 token::issuer(alice),
581 testcase(
"Burn invalid");
583 using namespace test::jtx;
585 Env env{*
this, features};
586 Account
const alice{
"alice"};
587 Account
const buyer{
"buyer"};
588 Account
const minter{
"minter"};
589 Account
const gw(
"gw");
590 IOU
const gwAUD(gw[
"AUD"]);
595 env.fund(XRP(250), alice, buyer, minter, gw);
609 env(token::burn(alice, nftAlice0ID),
616 env(token::burn(alice, nftAlice0ID),
626 env(token::burn(alice, token::getID(alice, 0, 1)), ter(
tecNO_ENTRY));
640 testcase(
"Invalid NFT offer create");
642 using namespace test::jtx;
644 Env env{*
this, features};
645 Account
const alice{
"alice"};
646 Account
const buyer{
"buyer"};
647 Account
const gw(
"gw");
648 IOU
const gwAUD(gw[
"AUD"]);
653 env.fund(XRP(250), alice, buyer, gw);
659 env(token::mint(alice, 0u),
671 uint256 nftNoXferID = token::getNextID(env, alice, 0);
672 env(token::mint(alice, 0));
685 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
692 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
699 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
706 env(token::createOffer(buyer, nftXrpOnlyID, buyer[
"USD"](1)),
708 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](0)),
710 env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
716 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](1)),
717 token::expiration(0),
724 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
730 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
738 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
745 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
746 token::destination(alice),
753 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
754 token::destination(Account(
"demon")),
764 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
772 env(token::createOffer(buyer, token::getID(alice, 0, 1), XRP(1000)),
779 env(token::createOffer(alice, token::getID(alice, 0, 1), XRP(1000)),
786 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
792 env(trust(buyer, gwAUD(1000)));
798 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
810 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
821 env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
831 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
840 env(trust(buyer, gwAUD(1000)));
843 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
853 env(pay(gw, buyer, gwAUD(999)));
858 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
865 env(pay(env.master, buyer, XRP(50) + drops(119)));
868 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
875 env(pay(env.master, buyer, drops(11)));
880 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
890 testcase(
"Invalid NFT offer cancel");
892 using namespace test::jtx;
894 Env env{*
this, features};
895 Account
const alice{
"alice"};
896 Account
const buyer{
"buyer"};
897 Account
const gw(
"gw");
898 IOU
const gwAUD(gw[
"AUD"]);
900 env.fund(XRP(1000), alice, buyer, gw);
911 uint256 const buyerOfferIndex =
913 env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
923 env(token::cancelOffer(buyer, {buyerOfferIndex}),
930 env(token::cancelOffer(buyer, {buyerOfferIndex}),
950 env(token::cancelOffer(buyer, offers), ter(
temMALFORMED));
956 env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
970 env(pay(env.master, gw, XRP(5000)));
974 env(offer(gw, XRP(i), gwAUD(1)));
981 env(check::create(gw, env.master, XRP(300)));
988 env(check::cancel(gw, gwCheckId));
1002 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1010 testcase(
"Invalid NFT offer accept");
1012 using namespace test::jtx;
1014 Env env{*
this, features};
1015 Account
const alice{
"alice"};
1016 Account
const buyer{
"buyer"};
1017 Account
const gw(
"gw");
1018 IOU
const gwAUD(gw[
"AUD"]);
1020 env.fund(XRP(1000), alice, buyer, gw);
1036 uint256 nftNoXferID = token::getNextID(env, alice, 0);
1037 env(token::mint(alice, 0));
1042 uint256 const plainOfferIndex =
1044 env(token::createOffer(alice, nftAlice0ID, XRP(10)),
1051 env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1056 uint256 const xrpOnlyOfferIndex =
1058 env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1063 uint256 const noXferOfferIndex =
1065 env(token::createOffer(alice, nftNoXferID, XRP(30)),
1071 uint256 const aliceExpOfferIndex =
1073 env(token::createOffer(alice, nftNoXferID, XRP(40)),
1083 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1090 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1091 txflags(0x00008000),
1098 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1107 Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1117 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1126 env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1127 token::brokerFee(gwAUD(0)),
1136 env(token::acceptBuyOffer(buyer, beast::zero),
1143 env(token::acceptBuyOffer(buyer, missingOfferIndex),
1149 env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1154 env(token::acceptSellOffer(buyer, beast::zero),
1160 env(token::acceptSellOffer(buyer, missingOfferIndex),
1166 env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1175 env(trust(alice, gwAUD(1000)));
1176 env(trust(buyer, gwAUD(1000)));
1178 env(pay(gw, buyer, gwAUD(30)));
1187 uint256 const buyerOfferIndex =
1189 env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1190 token::owner(alice));
1195 env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1201 env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1208 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1214 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1220 uint256 const buyerOfferIndex =
1222 env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1223 token::owner(alice));
1229 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1230 token::brokerFee(XRP(40)),
1236 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1237 token::brokerFee(gwAUD(31)),
1244 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1245 token::brokerFee(gwAUD(1.5)),
1251 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1259 uint256 const buyerOfferIndex =
1261 env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1262 token::owner(alice));
1267 env(token::acceptBuyOffer(buyer, plainOfferIndex),
1273 env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1279 env(pay(buyer, gw, gwAUD(30)));
1281 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1282 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1291 env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1294 env(token::acceptSellOffer(gw, offerIndex));
1298 env(pay(gw, buyer, gwAUD(30)));
1302 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1308 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1316 uint256 const buyerOfferIndex =
1318 env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1319 token::owner(alice));
1324 env(token::acceptSellOffer(alice, buyerOfferIndex),
1330 env(token::acceptSellOffer(alice, plainOfferIndex),
1337 env(token::acceptSellOffer(buyer, plainOfferIndex),
1348 env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1351 env(token::acceptSellOffer(alice, offerIndex));
1355 env(pay(buyer, gw, gwAUD(30)));
1357 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1358 env(token::acceptSellOffer(buyer, audOfferIndex),
1375 testcase(
"Mint flagBurnable");
1377 using namespace test::jtx;
1379 Env env{*
this, features};
1380 Account
const alice{
"alice"};
1381 Account
const buyer{
"buyer"};
1382 Account
const minter1{
"minter1"};
1383 Account
const minter2{
"minter2"};
1385 env.fund(XRP(1000), alice, buyer, minter1, minter2);
1390 env(token::setMinter(alice, minter1));
1397 auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1399 uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1400 env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1405 env(token::createOffer(minter1, nftID, XRP(0)),
1409 env(token::acceptSellOffer(buyer, offerIndex));
1417 uint256 const noBurnID = nftToBuyer(0);
1418 env(token::burn(alice, noBurnID),
1419 token::owner(buyer),
1422 env(token::burn(minter1, noBurnID),
1423 token::owner(buyer),
1426 env(token::burn(minter2, noBurnID),
1427 token::owner(buyer),
1432 env(token::burn(buyer, noBurnID), token::owner(buyer));
1439 env(token::burn(minter2, burnableID),
1440 token::owner(buyer),
1445 env(token::burn(alice, burnableID), token::owner(buyer));
1453 env(token::burn(buyer, burnableID));
1461 env(token::burn(buyer, burnableID), token::owner(buyer));
1471 env(token::setMinter(alice, minter2));
1476 env(token::burn(minter1, burnableID),
1477 token::owner(buyer),
1483 env(token::burn(minter2, burnableID), token::owner(buyer));
1493 testcase(
"Mint flagOnlyXRP");
1495 using namespace test::jtx;
1497 Env env{*
this, features};
1498 Account
const alice{
"alice"};
1499 Account
const buyer{
"buyer"};
1500 Account
const gw(
"gw");
1501 IOU
const gwAUD(gw[
"AUD"]);
1504 env.fund(XRP(1000), alice, buyer, gw);
1506 env(trust(alice, gwAUD(1000)));
1507 env(trust(buyer, gwAUD(1000)));
1509 env(pay(gw, buyer, gwAUD(100)));
1519 uint256 const aliceOfferIndex =
1521 env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1527 uint256 const buyerOfferIndex =
1529 env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1530 token::owner(alice));
1535 env(token::cancelOffer(alice, {aliceOfferIndex}));
1536 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1542 env(token::burn(alice, nftIOUsOkayID));
1555 env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1562 env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1563 token::owner(alice),
1570 env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1576 env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1577 token::owner(alice));
1587 testcase(
"Mint flagCreateTrustLines");
1589 using namespace test::jtx;
1591 Account
const alice{
"alice"};
1592 Account
const becky{
"becky"};
1593 Account
const cheri{
"cheri"};
1594 Account
const gw(
"gw");
1595 IOU
const gwAUD(gw[
"AUD"]);
1596 IOU
const gwCAD(gw[
"CAD"]);
1597 IOU
const gwEUR(gw[
"EUR"]);
1602 for (
auto const& tweakedFeatures :
1606 Env env{*
this, tweakedFeatures};
1607 env.fund(XRP(1000), alice, becky, cheri, gw);
1611 env(trust(becky, gwAUD(1000)));
1612 env(trust(cheri, gwAUD(1000)));
1613 env(trust(becky, gwCAD(1000)));
1614 env(trust(cheri, gwCAD(1000)));
1615 env(trust(becky, gwEUR(1000)));
1616 env(trust(cheri, gwEUR(1000)));
1618 env(pay(gw, becky, gwAUD(500)));
1619 env(pay(gw, becky, gwCAD(500)));
1620 env(pay(gw, becky, gwEUR(500)));
1621 env(pay(gw, cheri, gwAUD(500)));
1622 env(pay(gw, cheri, gwCAD(500)));
1629 uint256 const nftNoAutoTrustID{
1631 env(token::mint(alice, 0u),
1632 token::xferFee(xferFee),
1637 uint256 const beckyBuyOfferIndex =
1639 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1640 token::owner(alice));
1642 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1646 TER const createOfferTER =
1648 uint256 const beckyOfferIndex =
1650 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1652 ter(createOfferTER));
1656 uint256 const cheriOfferIndex =
1658 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1659 token::owner(becky),
1660 ter(createOfferTER));
1664 env(token::cancelOffer(becky, {beckyOfferIndex}));
1665 env(token::cancelOffer(cheri, {cheriOfferIndex}));
1673 uint256 const nftAutoTrustID{token::getNextID(
1684 env(token::mint(alice, 0u),
1685 token::xferFee(transferFee),
1696 uint256 const beckyBuyOfferIndex =
1698 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1699 token::owner(alice));
1701 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1705 uint256 const beckySellOfferIndex =
1707 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1710 env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1714 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1717 uint256 const beckyBuyBackOfferIndex =
1719 env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1720 token::owner(cheri));
1722 env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1726 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1727 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1733 uint256 const nftNoAutoTrustID{token::getNextID(
1735 env(token::mint(alice, 0u),
1736 token::xferFee(transferFee),
1741 uint256 const aliceSellOfferIndex =
1743 env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1746 env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1752 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1755 env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1759 uint256 const cheriSellOfferIndex =
1761 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1764 env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1770 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1779 testcase(
"Mint flagTransferable");
1781 using namespace test::jtx;
1783 Env env{*
this, features};
1785 Account
const alice{
"alice"};
1786 Account
const becky{
"becky"};
1787 Account
const minter{
"minter"};
1789 env.fund(XRP(1000), alice, becky, minter);
1795 uint256 const nftAliceNoTransferID{
1796 token::getNextID(env, alice, 0u)};
1797 env(token::mint(alice, 0u), token::xferFee(0));
1803 env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1804 token::owner(alice),
1808 uint256 const aliceSellOfferIndex =
1810 env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1813 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1819 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1828 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1830 token::destination(alice),
1838 uint256 const aliceBuyOfferIndex =
1840 env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1841 token::owner(becky));
1843 env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1849 env(token::burn(alice, nftAliceNoTransferID));
1856 env(token::setMinter(alice, minter));
1860 uint256 const nftMinterNoTransferID{
1861 token::getNextID(env, alice, 0u)};
1862 env(token::mint(minter), token::issuer(alice));
1868 env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1869 token::owner(minter),
1875 env(token::clearMinter(alice));
1880 env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1888 for (
int i = 0; i < 10; ++i)
1891 env(token::setMinter(alice, minter));
1897 uint256 const minterSellOfferIndex =
1899 env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1906 env(token::clearMinter(alice));
1912 env(token::acceptSellOffer(becky, minterSellOfferIndex));
1918 env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1926 env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1927 token::owner(becky),
1934 uint256 const aliceBuyOfferIndex =
1936 env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1937 token::owner(becky));
1943 for (
int i = 0; i < 10; ++i)
1946 env(token::setMinter(alice, minter));
1951 uint256 const minterBuyOfferIndex =
1953 env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1954 token::owner(becky));
1960 env(token::clearMinter(alice));
1966 env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1974 env(token::burn(minter, nftMinterNoTransferID), ter(
tesSUCCESS));
1976 env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
1993 uint256 const aliceSellOfferIndex =
1995 env(token::createOffer(alice, nftAliceID, XRP(20)),
2000 uint256 const beckyBuyOfferIndex =
2002 env(token::createOffer(becky, nftAliceID, XRP(21)),
2003 token::owner(alice));
2008 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
2014 uint256 const beckySellOfferIndex =
2016 env(token::createOffer(becky, nftAliceID, XRP(22)),
2024 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2031 uint256 const minterSellOfferIndex =
2033 env(token::createOffer(minter, nftAliceID, XRP(23)),
2041 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2049 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2057 env(token::burn(becky, nftAliceID));
2069 testcase(
"Mint transferFee");
2071 using namespace test::jtx;
2073 Env env{*
this, features};
2075 Account
const alice{
"alice"};
2076 Account
const becky{
"becky"};
2077 Account
const carol{
"carol"};
2078 Account
const minter{
"minter"};
2079 Account
const gw{
"gw"};
2080 IOU
const gwXAU(gw[
"XAU"]);
2082 env.fund(XRP(1000), alice, becky, carol, minter, gw);
2085 env(trust(alice, gwXAU(2000)));
2086 env(trust(becky, gwXAU(2000)));
2087 env(trust(carol, gwXAU(2000)));
2088 env(trust(minter, gwXAU(2000)));
2090 env(pay(gw, alice, gwXAU(1000)));
2091 env(pay(gw, becky, gwXAU(1000)));
2092 env(pay(gw, carol, gwXAU(1000)));
2093 env(pay(gw, minter, gwXAU(1000)));
2098 env(token::setMinter(alice, minter));
2115 uint256 const beckyBuyOfferIndex =
2117 env(token::createOffer(becky, nftID, gwXAU(10)),
2118 token::owner(alice));
2120 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2121 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2123 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2125 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2126 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2129 uint256 const beckySellOfferIndex =
2131 env(token::createOffer(becky, nftID, gwXAU(10)),
2134 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2136 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2137 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2138 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2141 uint256 const minterBuyOfferIndex =
2143 env(token::createOffer(minter, nftID, gwXAU(10)),
2144 token::owner(carol));
2146 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2148 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2149 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2150 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2151 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2155 uint256 const minterSellOfferIndex =
2157 env(token::createOffer(minter, nftID, gwXAU(10)),
2160 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2162 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2163 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2164 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2165 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2168 env(token::burn(alice, nftID));
2181 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2185 uint256 const beckyBuyOfferIndex =
2187 env(token::createOffer(becky, nftID, gwXAU(10)),
2188 token::owner(alice));
2190 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2191 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2193 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2195 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2196 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2199 uint256 const beckySellOfferIndex =
2201 env(token::createOffer(becky, nftID, gwXAU(10)),
2204 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2207 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2208 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2209 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2212 uint256 const minterBuyOfferIndex =
2214 env(token::createOffer(minter, nftID, gwXAU(10)),
2215 token::owner(carol));
2217 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2220 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2221 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2222 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2223 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2227 uint256 const minterSellOfferIndex =
2229 env(token::createOffer(minter, nftID, gwXAU(10)),
2232 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2234 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2235 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2236 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2237 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2241 env(pay(alice, becky, gwXAU(0.0001)));
2242 env(pay(alice, carol, gwXAU(0.0001)));
2245 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2246 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2247 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2248 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2251 env(token::burn(alice, nftID));
2262 env(token::mint(alice),
2269 uint256 const nftID = token::getNextID(
2271 env(token::mint(alice),
2277 uint256 const beckyBuyOfferIndex =
2279 env(token::createOffer(becky, nftID, gwXAU(10)),
2280 token::owner(alice));
2282 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2283 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2285 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2287 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2288 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2291 uint256 const beckySellOfferIndex =
2293 env(token::createOffer(becky, nftID, gwXAU(100)),
2296 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2299 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2300 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2301 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2304 uint256 const carolBuyOfferIndex =
2306 env(token::createOffer(carol, nftID, gwXAU(10)),
2307 token::owner(minter));
2309 env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2312 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2313 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2314 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2315 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2319 uint256 const carolSellOfferIndex =
2321 env(token::createOffer(carol, nftID, gwXAU(10)),
2324 env(token::acceptSellOffer(alice, carolSellOfferIndex));
2327 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2328 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2329 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2330 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2333 env(pay(alice, minter, gwXAU(55)));
2334 env(pay(becky, minter, gwXAU(40)));
2336 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2337 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2338 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2339 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2342 env(token::burn(alice, nftID));
2352 for (
auto NumberSwitchOver : {
true})
2354 if (NumberSwitchOver)
2362 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2368 STAmount aliceBalance = env.balance(alice);
2369 STAmount minterBalance = env.balance(minter);
2370 uint256 const minterBuyOfferIndex =
2372 env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2374 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2376 aliceBalance += XRP(1) - fee;
2377 minterBalance -= XRP(1) + fee;
2378 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2379 BEAST_EXPECT(env.balance(minter) == minterBalance);
2383 auto pmt = NumberSwitchOver ? drops(50000) : drops(99999);
2384 STAmount carolBalance = env.balance(carol);
2385 uint256 const minterSellOfferIndex =
2387 env(token::createOffer(minter, nftID, pmt), txflags(
tfSellNFToken));
2389 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2391 minterBalance += pmt - fee;
2392 carolBalance -= pmt + fee;
2393 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2394 BEAST_EXPECT(env.balance(minter) == minterBalance);
2395 BEAST_EXPECT(env.balance(carol) == carolBalance);
2399 STAmount beckyBalance = env.balance(becky);
2400 uint256 const beckyBuyOfferIndex =
2402 pmt = NumberSwitchOver ? drops(50001) : drops(100000);
2403 env(token::createOffer(becky, nftID, pmt), token::owner(carol));
2405 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2407 carolBalance += pmt - drops(1) - fee;
2408 beckyBalance -= pmt + fee;
2409 aliceBalance += drops(1);
2411 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2412 BEAST_EXPECT(env.balance(minter) == minterBalance);
2413 BEAST_EXPECT(env.balance(carol) == carolBalance);
2414 BEAST_EXPECT(env.balance(becky) == beckyBalance);
2423 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2429 env(pay(alice, gw, env.balance(alice, gwXAU)));
2430 env(pay(minter, gw, env.balance(minter, gwXAU)));
2431 env(pay(becky, gw, env.balance(becky, gwXAU)));
2436 env(pay(gw, alice, startXAUBalance));
2437 env(pay(gw, minter, startXAUBalance));
2438 env(pay(gw, becky, startXAUBalance));
2447 STAmount aliceBalance = env.balance(alice, gwXAU);
2448 STAmount minterBalance = env.balance(minter, gwXAU);
2449 uint256 const minterBuyOfferIndex =
2451 env(token::createOffer(minter, nftID, tinyXAU),
2452 token::owner(alice));
2454 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2456 aliceBalance += tinyXAU;
2457 minterBalance -= tinyXAU;
2458 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2459 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2462 STAmount carolBalance = env.balance(carol, gwXAU);
2463 uint256 const minterSellOfferIndex =
2465 env(token::createOffer(minter, nftID, tinyXAU),
2468 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2471 minterBalance += tinyXAU;
2472 carolBalance -= tinyXAU;
2474 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2475 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2476 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2483 STAmount beckyBalance = env.balance(becky, gwXAU);
2484 uint256 const beckyBuyOfferIndex =
2486 env(token::createOffer(becky, nftID, cheapNFT),
2487 token::owner(carol));
2489 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2492 aliceBalance += tinyXAU;
2493 beckyBalance -= cheapNFT;
2494 carolBalance += cheapNFT - tinyXAU;
2495 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2496 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2497 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2498 BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2506 testcase(
"Mint taxon");
2508 using namespace test::jtx;
2510 Env env{*
this, features};
2512 Account
const alice{
"alice"};
2513 Account
const becky{
"becky"};
2515 env.fund(XRP(1000), alice, becky);
2524 uint256 const nftID = token::getNextID(env, alice, 0u);
2531 uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2539 for (
int i = 0; i < 10; ++i)
2549 ss <<
"Taxon recovery failed from nftID "
2550 <<
to_string(nftID) <<
". Expected: " << taxon
2551 <<
"; got: " << gotTaxon;
2556 uint256 const nftAliceID = token::getID(
2559 rand_int<std::uint32_t>(),
2560 rand_int<std::uint16_t>(),
2561 rand_int<std::uint16_t>());
2562 check(taxon, nftAliceID);
2564 uint256 const nftBeckyID = token::getID(
2567 rand_int<std::uint32_t>(),
2568 rand_int<std::uint16_t>(),
2569 rand_int<std::uint16_t>());
2570 check(taxon, nftBeckyID);
2582 testcase(
"Mint URI");
2584 using namespace test::jtx;
2586 Env env{*
this, features};
2588 Account
const alice{
"alice"};
2589 Account
const becky{
"becky"};
2591 env.fund(XRP(10000), alice, becky);
2597 auto randURI = []() {
2619 : uri(std::move(uri_)), taxon(taxon_)
2627 entries.
emplace_back(randURI(), rand_int<std::uint32_t>());
2630 for (Entry
const& entry : entries)
2632 if (entry.uri.empty())
2634 env(token::mint(alice, entry.taxon));
2638 env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2646 params[jss::account] = alice.human();
2647 params[jss::type] =
"state";
2648 return env.rpc(
"json",
"account_nfts",
to_string(params));
2652 Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2653 if (!BEAST_EXPECT(nfts.
size() == entries.size()))
2666 return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2671 Entry
const& entry = entries[i];
2674 if (entry.uri.empty())
2689 testcase(
"Create offer destination");
2691 using namespace test::jtx;
2693 Env env{*
this, features};
2695 Account
const issuer{
"issuer"};
2696 Account
const minter{
"minter"};
2697 Account
const buyer{
"buyer"};
2698 Account
const broker{
"broker"};
2700 env.fund(XRP(1000), issuer, minter, buyer, broker);
2704 env(token::setMinter(issuer, minter));
2709 env(token::mint(minter, 0),
2710 token::issuer(issuer),
2717 uint256 const offerMinterToIssuer =
2719 env(token::createOffer(minter, nftokenID, drops(1)),
2720 token::destination(issuer),
2723 uint256 const offerMinterToBuyer =
2725 env(token::createOffer(minter, nftokenID, drops(1)),
2726 token::destination(buyer),
2729 uint256 const offerIssuerToMinter =
2731 env(token::createOffer(issuer, nftokenID, drops(1)),
2732 token::owner(minter),
2733 token::destination(minter));
2735 uint256 const offerIssuerToBuyer =
2737 env(token::createOffer(issuer, nftokenID, drops(1)),
2738 token::owner(minter),
2739 token::destination(buyer));
2753 env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2755 env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2757 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2759 env(token::cancelOffer(minter, {offerIssuerToBuyer}),
2768 env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2769 env(token::cancelOffer(minter, {offerMinterToIssuer}));
2770 env(token::cancelOffer(buyer, {offerIssuerToBuyer}));
2771 env(token::cancelOffer(issuer, {offerIssuerToMinter}));
2781 uint256 const offerMinterSellsToBuyer =
2783 env(token::createOffer(minter, nftokenID, drops(1)),
2784 token::destination(buyer),
2793 env(token::acceptSellOffer(issuer, offerMinterSellsToBuyer),
2801 env(token::acceptSellOffer(buyer, offerMinterSellsToBuyer));
2811 uint256 const offerMinterBuysFromBuyer =
2813 env(token::createOffer(minter, nftokenID, drops(1)),
2814 token::owner(buyer),
2815 token::destination(buyer));
2823 env(token::acceptBuyOffer(issuer, offerMinterBuysFromBuyer),
2831 env(token::acceptBuyOffer(buyer, offerMinterBuysFromBuyer));
2840 uint256 const offerBuyerBuysFromMinter =
2842 env(token::createOffer(buyer, nftokenID, drops(1)),
2843 token::owner(minter),
2844 token::destination(broker));
2850 env(token::acceptBuyOffer(minter, offerBuyerBuysFromMinter),
2855 env(token::cancelOffer(buyer, {offerBuyerBuysFromMinter}));
2865 uint256 const offerMinterToBroker =
2867 env(token::createOffer(minter, nftokenID, drops(1)),
2868 token::destination(broker),
2871 uint256 const offerBuyerToMinter =
2873 env(token::createOffer(buyer, nftokenID, drops(1)),
2874 token::owner(minter));
2883 env(token::brokerOffers(
2884 issuer, offerBuyerToMinter, offerMinterToBroker),
2893 env(token::brokerOffers(
2894 broker, offerBuyerToMinter, offerMinterToBroker));
2905 uint256 const offerBuyerToMinter =
2907 env(token::createOffer(buyer, nftokenID, drops(1)),
2908 token::destination(minter),
2911 uint256 const offerMinterToBuyer =
2913 env(token::createOffer(minter, nftokenID, drops(1)),
2914 token::owner(buyer));
2916 uint256 const offerIssuerToBuyer =
2918 env(token::createOffer(issuer, nftokenID, drops(1)),
2919 token::owner(buyer));
2927 env(token::brokerOffers(
2928 broker, offerIssuerToBuyer, offerBuyerToMinter),
2936 env(token::brokerOffers(
2937 broker, offerMinterToBuyer, offerBuyerToMinter));
2944 env(token::cancelOffer(issuer, {offerIssuerToBuyer}));
2954 uint256 const offerMinterToBroker =
2956 env(token::createOffer(minter, nftokenID, drops(1)),
2957 token::destination(broker),
2960 uint256 const offerBuyerToBroker =
2962 env(token::createOffer(buyer, nftokenID, drops(1)),
2963 token::owner(minter),
2964 token::destination(broker));
2968 env(token::brokerOffers(
2969 issuer, offerBuyerToBroker, offerMinterToBroker),
2977 env(token::brokerOffers(
2978 broker, offerBuyerToBroker, offerMinterToBroker));
2989 testcase(
"Create offer destination disallow incoming");
2991 using namespace test::jtx;
2996 Account
const alice{
"alice"};
2997 env.fund(XRP(10000), alice);
3000 auto const sle = env.le(alice);
3001 uint32_t flags = sle->getFlags();
3007 Account
const issuer{
"issuer"};
3008 Account
const minter{
"minter"};
3009 Account
const buyer{
"buyer"};
3010 Account
const alice{
"alice"};
3012 env.fund(XRP(1000), issuer, minter, buyer, alice);
3014 env(token::setMinter(issuer, minter));
3019 env(token::mint(minter, 0),
3020 token::issuer(issuer),
3030 env(token::createOffer(minter, nftokenID, drops(1)),
3031 token::destination(buyer),
3049 env(token::createOffer(minter, nftokenID, drops(1)),
3050 token::destination(buyer),
3054 env(token::cancelOffer(minter, {offerIndex}));
3063 env(token::createOffer(minter, nftokenID, drops(1)),
3064 token::destination(buyer),
3071 env(token::cancelOffer(minter, {offerIndex}));
3083 env(token::createOffer(minter, nftokenID, drops(1)),
3084 token::destination(buyer),
3088 env(token::acceptSellOffer(buyer, offerIndex));
3100 env(token::createOffer(alice, nftokenID, drops(1)),
3101 token::owner(buyer),
3108 env(token::createOffer(minter, nftokenID, drops(1)),
3109 token::owner(buyer),
3119 testcase(
"Create offer expiration");
3121 using namespace test::jtx;
3123 Env env{*
this, features};
3125 Account
const issuer{
"issuer"};
3126 Account
const minter{
"minter"};
3127 Account
const buyer{
"buyer"};
3129 env.fund(XRP(1000), issuer, minter, buyer);
3133 env(token::setMinter(issuer, minter));
3138 env(token::mint(minter, 0),
3139 token::issuer(issuer),
3145 env(token::mint(minter, 0),
3146 token::issuer(issuer),
3155 uint256 const offerMinterToIssuer =
3157 env(token::createOffer(minter, nftokenID0, drops(1)),
3158 token::destination(issuer),
3159 token::expiration(expiration),
3162 uint256 const offerMinterToAnyone =
3164 env(token::createOffer(minter, nftokenID0, drops(1)),
3165 token::expiration(expiration),
3168 uint256 const offerIssuerToMinter =
3170 env(token::createOffer(issuer, nftokenID0, drops(1)),
3171 token::owner(minter),
3172 token::expiration(expiration));
3174 uint256 const offerBuyerToMinter =
3176 env(token::createOffer(buyer, nftokenID0, drops(1)),
3177 token::owner(minter),
3178 token::expiration(expiration));
3190 env(token::cancelOffer(issuer, {offerMinterToAnyone}),
3192 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
3195 BEAST_EXPECT(
lastClose(env) < expiration);
3201 env(token::cancelOffer(minter, {offerMinterToAnyone}));
3205 env(token::cancelOffer(issuer, {offerMinterToIssuer}));
3216 env(token::cancelOffer(issuer, {offerBuyerToMinter}));
3217 env(token::cancelOffer(buyer, {offerIssuerToMinter}));
3232 env(token::createOffer(minter, nftokenID0, drops(1)),
3233 token::expiration(expiration),
3238 env(token::createOffer(minter, nftokenID1, drops(1)),
3239 token::expiration(expiration),
3242 BEAST_EXPECT(
lastClose(env) < expiration);
3248 env(token::acceptSellOffer(buyer, offer0));
3259 env(token::acceptSellOffer(buyer, offer1), ter(
tecEXPIRED));
3260 env(token::acceptSellOffer(issuer, offer1), ter(
tecEXPIRED));
3269 env(token::cancelOffer(issuer, {offer1}));
3279 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3281 token::destination(minter));
3283 env(token::acceptSellOffer(minter, offerSellBack));
3297 env(token::createOffer(buyer, nftokenID0, drops(1)),
3298 token::owner(minter),
3299 token::expiration(expiration));
3302 env(token::createOffer(buyer, nftokenID1, drops(1)),
3303 token::owner(minter),
3304 token::expiration(expiration));
3306 BEAST_EXPECT(
lastClose(env) < expiration);
3312 env(token::acceptBuyOffer(minter, offer0));
3323 env(token::acceptBuyOffer(minter, offer1), ter(
tecEXPIRED));
3324 env(token::acceptBuyOffer(issuer, offer1), ter(
tecEXPIRED));
3333 env(token::cancelOffer(issuer, {offer1}));
3343 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3345 token::destination(minter));
3347 env(token::acceptSellOffer(minter, offerSellBack));
3362 env(token::createOffer(minter, nftokenID0, drops(1)),
3363 token::expiration(expiration),
3368 env(token::createOffer(minter, nftokenID1, drops(1)),
3369 token::expiration(expiration),
3374 env(token::createOffer(buyer, nftokenID0, drops(1)),
3375 token::owner(minter));
3379 env(token::createOffer(buyer, nftokenID1, drops(1)),
3380 token::owner(minter));
3383 BEAST_EXPECT(
lastClose(env) < expiration);
3389 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3400 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3410 env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3420 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3422 token::destination(minter));
3424 env(token::acceptSellOffer(minter, offerSellBack));
3439 env(token::createOffer(minter, nftokenID0, drops(1)),
3444 env(token::createOffer(minter, nftokenID1, drops(1)),
3449 env(token::createOffer(buyer, nftokenID0, drops(1)),
3450 token::expiration(expiration),
3451 token::owner(minter));
3455 env(token::createOffer(buyer, nftokenID1, drops(1)),
3456 token::expiration(expiration),
3457 token::owner(minter));
3460 BEAST_EXPECT(
lastClose(env) < expiration);
3466 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3477 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3487 env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3497 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3499 token::destination(minter));
3501 env(token::acceptSellOffer(minter, offerSellBack));
3517 env(token::createOffer(minter, nftokenID0, drops(1)),
3518 token::expiration(expiration),
3523 env(token::createOffer(minter, nftokenID1, drops(1)),
3524 token::expiration(expiration),
3529 env(token::createOffer(buyer, nftokenID0, drops(1)),
3530 token::expiration(expiration),
3531 token::owner(minter));
3535 env(token::createOffer(buyer, nftokenID1, drops(1)),
3536 token::expiration(expiration),
3537 token::owner(minter));
3540 BEAST_EXPECT(
lastClose(env) < expiration);
3546 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3557 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3567 env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3577 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3579 token::destination(minter));
3581 env(token::acceptSellOffer(minter, offerSellBack));
3593 testcase(
"Cancel offers");
3595 using namespace test::jtx;
3597 Env env{*
this, features};
3599 Account
const alice(
"alice");
3600 Account
const becky(
"becky");
3601 Account
const minter(
"minter");
3602 env.fund(XRP(50000), alice, becky, minter);
3606 env(token::setMinter(alice, minter));
3615 uint256 const expiredOfferIndex =
3618 env(token::createOffer(alice, nftokenID, XRP(1000)),
3620 token::expiration(
lastClose(env) + 13));
3625 env(token::cancelOffer(becky, {expiredOfferIndex}),
3633 env(token::cancelOffer(becky, {expiredOfferIndex}));
3639 uint256 const dest1OfferIndex =
3642 env(token::createOffer(alice, nftokenID, XRP(1000)),
3643 token::destination(becky),
3649 env(token::cancelOffer(minter, {dest1OfferIndex}),
3654 env(token::cancelOffer(becky, {dest1OfferIndex}));
3659 uint256 const dest2OfferIndex =
3662 env(token::createOffer(alice, nftokenID, XRP(1000)),
3663 token::destination(becky),
3668 env(token::cancelOffer(alice, {dest2OfferIndex}));
3675 uint256 const mintersNFTokenID =
3677 env(token::mint(minter, 0),
3678 token::issuer(alice),
3682 uint256 const minterOfferIndex =
3685 env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3691 env(token::cancelOffer(alice, {minterOfferIndex}),
3693 env(token::cancelOffer(becky, {minterOfferIndex}),
3698 env(token::cancelOffer(minter, {minterOfferIndex}));
3707 testcase(
"Cancel too many offers");
3709 using namespace test::jtx;
3711 Env env{*
this, features};
3726 Account
const alice(
"alice");
3727 env.fund(XRP(1000), alice);
3736 Account
const offerAcct(
3738 env.fund(XRP(1000), nftAcct, offerAcct);
3743 env(token::mint(nftAcct, 0),
3750 env(token::createOffer(offerAcct, nftokenID, drops(1)),
3751 token::owner(nftAcct),
3760 for (
uint256 const& offerIndex : offerIndexes)
3767 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3771 env(token::cancelOffer(alice, {offerIndexes.back()}));
3776 offerIndexes.pop_back();
3782 env(token::mint(alice, 0),
3788 env(token::createOffer(alice, nftokenID, drops(1)),
3797 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3801 env(token::burn(alice, nftokenID));
3807 offerIndexes.pop_back();
3812 env(token::cancelOffer(alice, offerIndexes));
3816 for (
uint256 const& offerIndex : offerIndexes)
3826 testcase(
"Brokered NFT offer accept");
3828 using namespace test::jtx;
3830 Env env{*
this, features};
3838 Account
const issuer{
"issuer"};
3839 Account
const minter{
"minter"};
3840 Account
const buyer{
"buyer"};
3841 Account
const broker{
"broker"};
3842 Account
const gw{
"gw"};
3843 IOU
const gwXAU(gw[
"XAU"]);
3845 env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3848 env(trust(issuer, gwXAU(2000)));
3849 env(trust(minter, gwXAU(2000)));
3850 env(trust(buyer, gwXAU(2000)));
3851 env(trust(broker, gwXAU(2000)));
3854 env(token::setMinter(issuer, minter));
3858 auto checkOwnerCountIsOne =
3863 for (Account
const& acct : accounts)
3869 ss <<
"Account " << acct.human()
3870 <<
" expected ownerCount == 1. Got " <<
ownerCount;
3871 fail(ss.
str(), __FILE__, line);
3877 auto mintNFT = [&env, &issuer, &minter](
std::uint16_t xferFee = 0) {
3880 env(token::mint(minter, 0),
3881 token::issuer(issuer),
3882 token::xferFee(xferFee),
3894 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3896 uint256 const nftID = mintNFT();
3899 uint256 const minterOfferIndex =
3901 env(token::createOffer(minter, nftID, XRP(0)),
3909 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3912 auto const minterBalance = env.balance(minter);
3913 auto const buyerBalance = env.balance(buyer);
3914 auto const brokerBalance = env.balance(broker);
3915 auto const issuerBalance = env.balance(issuer);
3918 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3923 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3924 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3925 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3926 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3929 env(token::burn(buyer, nftID));
3939 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3941 uint256 const nftID = mintNFT();
3944 uint256 const minterOfferIndex =
3946 env(token::createOffer(minter, nftID, XRP(0)),
3954 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3958 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3959 token::brokerFee(XRP(1.1)),
3963 auto const minterBalance = env.balance(minter);
3964 auto const buyerBalance = env.balance(buyer);
3965 auto const brokerBalance = env.balance(broker);
3966 auto const issuerBalance = env.balance(issuer);
3969 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3970 token::brokerFee(XRP(0.5)));
3975 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3976 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3978 env.balance(broker) == brokerBalance + XRP(0.5) - drops(10));
3979 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3982 env(token::burn(buyer, nftID));
3992 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3997 uint256 const minterOfferIndex =
3999 env(token::createOffer(minter, nftID, XRP(0)),
4007 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
4010 auto const minterBalance = env.balance(minter);
4011 auto const buyerBalance = env.balance(buyer);
4012 auto const brokerBalance = env.balance(broker);
4013 auto const issuerBalance = env.balance(issuer);
4016 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
4021 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4022 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4023 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
4024 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
4027 env(token::burn(buyer, nftID));
4037 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4042 uint256 const minterOfferIndex =
4044 env(token::createOffer(minter, nftID, XRP(0)),
4052 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
4055 auto const minterBalance = env.balance(minter);
4056 auto const buyerBalance = env.balance(buyer);
4057 auto const brokerBalance = env.balance(broker);
4058 auto const issuerBalance = env.balance(issuer);
4061 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4062 token::brokerFee(XRP(0.75)));
4068 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
4069 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4071 env.balance(broker) == brokerBalance + XRP(0.75) - drops(10));
4072 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
4075 env(token::burn(buyer, nftID));
4080 auto setXAUBalance_1000 =
4081 [
this, &gw, &gwXAU, &env](
4085 for (Account
const& acct : accounts)
4087 static const auto xau1000 = gwXAU(1000);
4088 auto const balance = env.balance(acct, gwXAU);
4089 if (balance < xau1000)
4091 env(pay(gw, acct, xau1000 - balance));
4094 else if (balance > xau1000)
4096 env(pay(acct, gw, balance - xau1000));
4099 if (env.balance(acct, gwXAU) != xau1000)
4102 ss <<
"Unable to set " << acct.human()
4103 <<
" account balance to gwXAU(1000)";
4104 this->fail(ss.
str(), __FILE__, line);
4112 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4113 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4115 uint256 const nftID = mintNFT();
4118 uint256 const minterOfferIndex =
4120 env(token::createOffer(minter, nftID, gwXAU(1000)),
4128 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4129 token::owner(minter));
4133 env(token::brokerOffers(
4134 broker, buyOfferIndex, minterOfferIndex),
4140 env(token::cancelOffer(buyer, {buyOfferIndex}));
4147 env(token::createOffer(buyer, nftID, gwXAU(999)),
4148 token::owner(minter));
4152 env(token::brokerOffers(
4153 broker, buyOfferIndex, minterOfferIndex),
4159 env(token::cancelOffer(buyer, {buyOfferIndex}));
4166 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4167 token::owner(minter));
4171 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4172 token::brokerFee(gwXAU(0.1)),
4177 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
4184 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4185 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
4186 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4187 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
4190 env(token::burn(buyer, nftID));
4197 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4198 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4203 uint256 const minterOfferIndex =
4205 env(token::createOffer(minter, nftID, gwXAU(900)),
4212 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4213 token::owner(minter));
4217 env(token::brokerOffers(
4218 broker, buyOfferIndex, minterOfferIndex),
4224 env(token::cancelOffer(buyer, {buyOfferIndex}));
4231 env(token::createOffer(buyer, nftID, gwXAU(899)),
4232 token::owner(minter));
4236 env(token::brokerOffers(
4237 broker, buyOfferIndex, minterOfferIndex),
4243 env(token::cancelOffer(buyer, {buyOfferIndex}));
4249 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4250 token::owner(minter));
4255 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4256 token::brokerFee(gwXAU(101)),
4262 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4263 token::brokerFee(gwXAU(100)));
4270 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4271 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4272 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4273 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4276 env(token::burn(buyer, nftID));
4283 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4284 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4289 uint256 const minterOfferIndex =
4291 env(token::createOffer(minter, nftID, gwXAU(900)),
4298 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4299 token::owner(minter));
4305 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4306 token::brokerFee(gwXAU(50)));
4313 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4314 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4315 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4316 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4319 env(token::burn(buyer, nftID));
4328 testcase(
"NFToken offer owner");
4330 using namespace test::jtx;
4332 Env env{*
this, features};
4334 Account
const issuer{
"issuer"};
4335 Account
const buyer1{
"buyer1"};
4336 Account
const buyer2{
"buyer2"};
4337 env.fund(XRP(10000), issuer, buyer1, buyer2);
4346 BEAST_EXPECT(
nftCount(env, issuer) == 1);
4347 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4348 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4351 uint256 const buyer1OfferIndex =
4353 env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4354 uint256 const buyer2OfferIndex =
4356 env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4366 env.rpc(
"json",
"nft_buy_offers",
to_string(params));
4368 if (buyOffers.
isMember(jss::result) &&
4369 buyOffers[jss::result].
isMember(jss::offers))
4370 return buyOffers[jss::result][jss::offers].
size();
4376 BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4379 env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4383 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4384 BEAST_EXPECT(
nftCount(env, buyer1) == 1);
4385 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4389 BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4393 env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4397 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4398 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4399 BEAST_EXPECT(
nftCount(env, buyer2) == 1);
4402 BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4409 testcase(
"NFToken transactions with tickets");
4411 using namespace test::jtx;
4413 Env env{*
this, features};
4415 Account
const issuer{
"issuer"};
4416 Account
const buyer{
"buyer"};
4417 env.fund(XRP(10000), issuer, buyer);
4424 env(ticket::create(issuer, 10));
4430 env(ticket::create(buyer, 10));
4438 env(token::mint(issuer, 0u),
4440 ticket::use(issuerTicketSeq++));
4448 env(token::createOffer(buyer, nftId, XRP(1)),
4449 token::owner(issuer),
4450 ticket::use(buyerTicketSeq++));
4456 env(token::cancelOffer(buyer, {offerIndex0}),
4457 ticket::use(buyerTicketSeq++));
4464 env(token::createOffer(buyer, nftId, XRP(2)),
4465 token::owner(issuer),
4466 ticket::use(buyerTicketSeq++));
4472 env(token::acceptBuyOffer(issuer, offerIndex1),
4473 ticket::use(issuerTicketSeq++));
4480 env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4487 BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4488 BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4499 testcase(
"NFToken delete account");
4501 using namespace test::jtx;
4503 Env env{*
this, features};
4505 Account
const issuer{
"issuer"};
4506 Account
const minter{
"minter"};
4507 Account
const becky{
"becky"};
4508 Account
const carla{
"carla"};
4509 Account
const daria{
"daria"};
4511 env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4515 for (
int i = 0; i < 300; ++i)
4518 env(token::setMinter(issuer, minter));
4522 env(token::mint(minter, 0u),
4523 token::issuer(issuer),
4536 for (
int i = 0; i < 15; ++i)
4540 env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4543 uint256 const carlaOfferIndex =
4545 env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4550 env(acctdelete(becky, daria), fee(XRP(50)));
4554 env(token::acceptBuyOffer(minter, carlaOfferIndex));
4559 env(acctdelete(minter, daria), fee(XRP(50)));
4571 for (
int i = 0; i < 15; ++i)
4576 env(token::burn(carla, nftId));
4579 env(acctdelete(issuer, daria), fee(XRP(50)));
4580 env(acctdelete(carla, daria), fee(XRP(50)));
4587 testcase(
"nft_buy_offers and nft_sell_offers");
4596 using namespace test::jtx;
4598 Env env{*
this, features};
4600 Account
const issuer{
"issuer"};
4601 Account
const buyer{
"buyer"};
4604 env.fund(XRP(1000000), issuer, buyer);
4613 auto checkOffers = [
this, &env, &nftID](
4614 char const* request,
4616 int expectMarkerCount,
4618 int markerCount = 0;
4625 Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4629 if (!marker.
empty())
4630 params[jss::marker] = marker;
4631 return env.rpc(
"json", request,
to_string(params));
4635 if (expectCount == 0)
4639 "expected \"result\"",
4644 nftOffers[jss::result].isMember(jss::error),
4645 "expected \"error\"",
4650 nftOffers[jss::result][jss::error].asString() ==
4652 "expected \"objectNotFound\"",
4663 "expected \"result\"",
4672 marker = result[jss::marker].
asString();
4677 "expected \"offers\"",
4683 allOffers.
append(someOffers[i]);
4686 }
while (!marker.
empty());
4690 allOffers.
size() == expectCount,
4691 "Unexpected returned offer count",
4695 markerCount == expectMarkerCount,
4696 "Unexpected marker count",
4706 globalFlags = offer[jss::flags].asInt();
4709 *globalFlags == offer[jss::flags].asInt(),
4710 "Inconsistent flags returned",
4716 offerIndexes.
insert(offer[jss::nft_offer_index].asString());
4717 amounts.
insert(offer[jss::amount].asString());
4721 offerIndexes.
size() == expectCount,
4722 "Duplicate indexes returned?",
4726 amounts.
size() == expectCount,
4727 "Duplicate amounts returned?",
4733 checkOffers(
"nft_sell_offers", 0,
false, __LINE__);
4737 auto makeSellOffers =
4738 [&env, &issuer, &nftID, &sellPrice](
STAmount const& limit) {
4741 while (sellPrice < limit)
4743 sellPrice += XRP(1);
4744 env(token::createOffer(issuer, nftID, sellPrice),
4746 if (++offerCount % 10 == 0)
4753 makeSellOffers(XRP(1));
4754 checkOffers(
"nft_sell_offers", 1, 0, __LINE__);
4757 makeSellOffers(XRP(250));
4758 checkOffers(
"nft_sell_offers", 250, 0, __LINE__);
4761 makeSellOffers(XRP(251));
4762 checkOffers(
"nft_sell_offers", 251, 1, __LINE__);
4765 makeSellOffers(XRP(500));
4766 checkOffers(
"nft_sell_offers", 500, 1, __LINE__);
4769 makeSellOffers(XRP(501));
4770 checkOffers(
"nft_sell_offers", 501, 2, __LINE__);
4773 checkOffers(
"nft_buy_offers", 0, 0, __LINE__);
4777 auto makeBuyOffers =
4778 [&env, &buyer, &issuer, &nftID, &buyPrice](
STAmount const& limit) {
4781 while (buyPrice < limit)
4784 env(token::createOffer(buyer, nftID, buyPrice),
4785 token::owner(issuer));
4786 if (++offerCount % 10 == 0)
4793 makeBuyOffers(XRP(1));
4794 checkOffers(
"nft_buy_offers", 1, 0, __LINE__);
4797 makeBuyOffers(XRP(250));
4798 checkOffers(
"nft_buy_offers", 250, 0, __LINE__);
4801 makeBuyOffers(XRP(251));
4802 checkOffers(
"nft_buy_offers", 251, 1, __LINE__);
4805 makeBuyOffers(XRP(500));
4806 checkOffers(
"nft_buy_offers", 500, 1, __LINE__);
4809 makeBuyOffers(XRP(501));
4810 checkOffers(
"nft_buy_offers", 501, 2, __LINE__);
4817 using namespace test::jtx;
4819 testcase(
"fixNFTokenNegOffer");
4821 Account
const issuer{
"issuer"};
4822 Account
const buyer{
"buyer"};
4823 Account
const gw{
"gw"};
4824 IOU
const gwXAU(gw[
"XAU"]);
4827 for (
auto const& tweakedFeatures :
4834 Env env{*
this, tweakedFeatures};
4836 env.fund(XRP(1000000), issuer, buyer, gw);
4839 env(trust(issuer, gwXAU(2000)));
4840 env(trust(buyer, gwXAU(2000)));
4843 env(pay(gw, issuer, gwXAU(1000)));
4844 env(pay(gw, buyer, gwXAU(1000)));
4864 uint256 const sellNegXrpOfferIndex =
4866 env(token::createOffer(issuer, nftID0, XRP(-2)),
4868 ter(offerCreateTER));
4871 uint256 const sellNegIouOfferIndex =
4873 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4875 ter(offerCreateTER));
4878 uint256 const buyNegXrpOfferIndex =
4880 env(token::createOffer(buyer, nftID0, XRP(-1)),
4881 token::owner(issuer),
4882 ter(offerCreateTER));
4885 uint256 const buyNegIouOfferIndex =
4887 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4888 token::owner(issuer),
4889 ter(offerCreateTER));
4901 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4902 ter(offerAcceptTER));
4904 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
4905 ter(offerAcceptTER));
4909 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
4910 ter(offerAcceptTER));
4912 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
4913 ter(offerAcceptTER));
4924 env(token::brokerOffers(
4925 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
4926 ter(offerAcceptTER));
4928 env(token::brokerOffers(
4929 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
4930 ter(offerAcceptTER));
4942 env.fund(XRP(1000000), issuer, buyer, gw);
4945 env(trust(issuer, gwXAU(2000)));
4946 env(trust(buyer, gwXAU(2000)));
4949 env(pay(gw, issuer, gwXAU(1000)));
4950 env(pay(gw, buyer, gwXAU(1000)));
4966 uint256 const sellNegXrpOfferIndex =
4968 env(token::createOffer(issuer, nftID0, XRP(-2)),
4972 uint256 const sellNegIouOfferIndex =
4974 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4978 uint256 const buyNegXrpOfferIndex =
4980 env(token::createOffer(buyer, nftID0, XRP(-1)),
4981 token::owner(issuer));
4984 uint256 const buyNegIouOfferIndex =
4986 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4987 token::owner(issuer));
4996 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4999 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
5004 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
5007 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5012 env(token::brokerOffers(
5013 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5016 env(token::brokerOffers(
5017 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5024 for (
auto const& tweakedFeatures :
5028 Env env{*
this, tweakedFeatures};
5030 env.fund(XRP(1000000), issuer, buyer);
5042 env(token::createOffer(buyer, nftID, drops(1)),
5043 token::owner(issuer),
5044 token::destination(issuer),
5045 ter(offerCreateTER));
5085 using namespace test::jtx;