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());
501 testcase(
"Mint invalid");
503 using namespace test::jtx;
505 Env env{*
this, features};
506 Account
const alice{
"alice"};
507 Account
const minter{
"minter"};
512 env.fund(XRP(200), alice, minter);
519 env(pay(env.master, alice, XRP(1000)));
526 env(token::mint(alice, 0u),
531 env(token::mint(alice, 0u), txflags(0x00008000), ter(
temINVALID_FLAG));
535 env(token::mint(alice, 0u),
540 env(token::mint(alice, 0u),
546 env(token::mint(alice, 0u), token::issuer(alice), ter(
temMALFORMED));
549 env(token::mint(alice, 0u), token::uri(
""), ter(
temMALFORMED));
552 env(token::mint(alice, 0u),
560 env(token::mint(alice, 0u),
561 token::issuer(Account(
"demon")),
568 env(token::mint(minter, 0u),
569 token::issuer(alice),
577 testcase(
"Burn invalid");
579 using namespace test::jtx;
581 Env env{*
this, features};
582 Account
const alice{
"alice"};
583 Account
const buyer{
"buyer"};
584 Account
const minter{
"minter"};
585 Account
const gw(
"gw");
586 IOU
const gwAUD(gw[
"AUD"]);
591 env.fund(XRP(250), alice, buyer, minter, gw);
605 env(token::burn(alice, nftAlice0ID),
612 env(token::burn(alice, nftAlice0ID),
622 env(token::burn(alice, token::getID(alice, 0, 1)), ter(
tecNO_ENTRY));
636 testcase(
"Invalid NFT offer create");
638 using namespace test::jtx;
640 Env env{*
this, features};
641 Account
const alice{
"alice"};
642 Account
const buyer{
"buyer"};
643 Account
const gw(
"gw");
644 IOU
const gwAUD(gw[
"AUD"]);
649 env.fund(XRP(250), alice, buyer, gw);
655 env(token::mint(alice, 0u),
667 uint256 nftNoXferID = token::getNextID(env, alice, 0);
668 env(token::mint(alice, 0));
681 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
688 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
695 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
702 env(token::createOffer(buyer, nftXrpOnlyID, buyer[
"USD"](1)),
704 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](0)),
706 env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
712 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](1)),
713 token::expiration(0),
720 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
726 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
734 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
741 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
742 token::destination(alice),
749 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
750 token::destination(Account(
"demon")),
760 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
768 env(token::createOffer(buyer, token::getID(alice, 0, 1), XRP(1000)),
775 env(token::createOffer(alice, token::getID(alice, 0, 1), XRP(1000)),
782 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
788 env(trust(buyer, gwAUD(1000)));
794 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
806 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
817 env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
827 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
836 env(trust(buyer, gwAUD(1000)));
839 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
849 env(pay(gw, buyer, gwAUD(999)));
854 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
861 env(pay(env.master, buyer, XRP(50) + drops(119)));
864 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
871 env(pay(env.master, buyer, drops(11)));
876 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
886 testcase(
"Invalid NFT offer cancel");
888 using namespace test::jtx;
890 Env env{*
this, features};
891 Account
const alice{
"alice"};
892 Account
const buyer{
"buyer"};
893 Account
const gw(
"gw");
894 IOU
const gwAUD(gw[
"AUD"]);
896 env.fund(XRP(1000), alice, buyer, gw);
907 uint256 const buyerOfferIndex =
909 env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
919 env(token::cancelOffer(buyer, {buyerOfferIndex}),
926 env(token::cancelOffer(buyer, {buyerOfferIndex}),
946 env(token::cancelOffer(buyer, offers), ter(
temMALFORMED));
952 env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
966 env(pay(env.master, gw, XRP(5000)));
970 env(offer(gw, XRP(i), gwAUD(1)));
977 env(check::create(gw, env.master, XRP(300)));
984 env(check::cancel(gw, gwCheckId));
998 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1006 testcase(
"Invalid NFT offer accept");
1008 using namespace test::jtx;
1010 Env env{*
this, features};
1011 Account
const alice{
"alice"};
1012 Account
const buyer{
"buyer"};
1013 Account
const gw(
"gw");
1014 IOU
const gwAUD(gw[
"AUD"]);
1016 env.fund(XRP(1000), alice, buyer, gw);
1032 uint256 nftNoXferID = token::getNextID(env, alice, 0);
1033 env(token::mint(alice, 0));
1038 uint256 const plainOfferIndex =
1040 env(token::createOffer(alice, nftAlice0ID, XRP(10)),
1047 env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1052 uint256 const xrpOnlyOfferIndex =
1054 env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1059 uint256 const noXferOfferIndex =
1061 env(token::createOffer(alice, nftNoXferID, XRP(30)),
1067 uint256 const aliceExpOfferIndex =
1069 env(token::createOffer(alice, nftNoXferID, XRP(40)),
1079 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1086 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1087 txflags(0x00008000),
1094 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1103 Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1113 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1122 env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1123 token::brokerFee(gwAUD(0)),
1133 env(token::acceptBuyOffer(buyer, missingOfferIndex),
1139 env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1144 env(token::acceptSellOffer(buyer, missingOfferIndex),
1150 env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1159 env(trust(alice, gwAUD(1000)));
1160 env(trust(buyer, gwAUD(1000)));
1162 env(pay(gw, buyer, gwAUD(30)));
1171 uint256 const buyerOfferIndex =
1173 env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1174 token::owner(alice));
1179 env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1185 env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1192 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1198 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1204 uint256 const buyerOfferIndex =
1206 env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1207 token::owner(alice));
1213 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1214 token::brokerFee(XRP(40)),
1220 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1221 token::brokerFee(gwAUD(31)),
1228 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1229 token::brokerFee(gwAUD(1.5)),
1235 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1243 uint256 const buyerOfferIndex =
1245 env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1246 token::owner(alice));
1251 env(token::acceptBuyOffer(buyer, plainOfferIndex),
1257 env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1263 env(pay(buyer, gw, gwAUD(30)));
1265 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1266 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1275 env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1278 env(token::acceptSellOffer(gw, offerIndex));
1282 env(pay(gw, buyer, gwAUD(30)));
1286 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1292 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1300 uint256 const buyerOfferIndex =
1302 env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1303 token::owner(alice));
1308 env(token::acceptSellOffer(alice, buyerOfferIndex),
1314 env(token::acceptSellOffer(alice, plainOfferIndex),
1321 env(token::acceptSellOffer(buyer, plainOfferIndex),
1332 env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1335 env(token::acceptSellOffer(alice, offerIndex));
1339 env(pay(buyer, gw, gwAUD(30)));
1341 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1342 env(token::acceptSellOffer(buyer, audOfferIndex),
1359 testcase(
"Mint flagBurnable");
1361 using namespace test::jtx;
1363 Env env{*
this, features};
1364 Account
const alice{
"alice"};
1365 Account
const buyer{
"buyer"};
1366 Account
const minter1{
"minter1"};
1367 Account
const minter2{
"minter2"};
1369 env.fund(XRP(1000), alice, buyer, minter1, minter2);
1374 env(token::setMinter(alice, minter1));
1381 auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1383 uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1384 env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1389 env(token::createOffer(minter1, nftID, XRP(0)),
1393 env(token::acceptSellOffer(buyer, offerIndex));
1401 uint256 const noBurnID = nftToBuyer(0);
1402 env(token::burn(alice, noBurnID),
1403 token::owner(buyer),
1406 env(token::burn(minter1, noBurnID),
1407 token::owner(buyer),
1410 env(token::burn(minter2, noBurnID),
1411 token::owner(buyer),
1416 env(token::burn(buyer, noBurnID), token::owner(buyer));
1423 env(token::burn(minter2, burnableID),
1424 token::owner(buyer),
1429 env(token::burn(alice, burnableID), token::owner(buyer));
1437 env(token::burn(buyer, burnableID));
1445 env(token::burn(buyer, burnableID), token::owner(buyer));
1455 env(token::setMinter(alice, minter2));
1460 env(token::burn(minter1, burnableID),
1461 token::owner(buyer),
1467 env(token::burn(minter2, burnableID), token::owner(buyer));
1477 testcase(
"Mint flagOnlyXRP");
1479 using namespace test::jtx;
1481 Env env{*
this, features};
1482 Account
const alice{
"alice"};
1483 Account
const buyer{
"buyer"};
1484 Account
const gw(
"gw");
1485 IOU
const gwAUD(gw[
"AUD"]);
1488 env.fund(XRP(1000), alice, buyer, gw);
1490 env(trust(alice, gwAUD(1000)));
1491 env(trust(buyer, gwAUD(1000)));
1493 env(pay(gw, buyer, gwAUD(100)));
1503 uint256 const aliceOfferIndex =
1505 env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1511 uint256 const buyerOfferIndex =
1513 env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1514 token::owner(alice));
1519 env(token::cancelOffer(alice, {aliceOfferIndex}));
1520 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1526 env(token::burn(alice, nftIOUsOkayID));
1539 env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1546 env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1547 token::owner(alice),
1554 env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1560 env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1561 token::owner(alice));
1571 testcase(
"Mint flagCreateTrustLines");
1573 using namespace test::jtx;
1575 Env env{*
this, features};
1576 Account
const alice{
"alice"};
1577 Account
const becky{
"becky"};
1578 Account
const cheri{
"cheri"};
1579 Account
const gw(
"gw");
1580 IOU
const gwAUD(gw[
"AUD"]);
1581 IOU
const gwCAD(gw[
"CAD"]);
1582 IOU
const gwEUR(gw[
"EUR"]);
1584 env.fund(XRP(1000), alice, becky, cheri, gw);
1588 env(trust(becky, gwAUD(1000)));
1589 env(trust(cheri, gwAUD(1000)));
1590 env(trust(becky, gwCAD(1000)));
1591 env(trust(cheri, gwCAD(1000)));
1592 env(trust(becky, gwEUR(1000)));
1593 env(trust(cheri, gwEUR(1000)));
1595 env(pay(gw, becky, gwAUD(500)));
1596 env(pay(gw, becky, gwCAD(500)));
1597 env(pay(gw, becky, gwEUR(500)));
1598 env(pay(gw, cheri, gwAUD(500)));
1599 env(pay(gw, cheri, gwCAD(500)));
1606 uint256 const nftNoAutoTrustID{
1608 env(token::mint(alice, 0u),
1609 token::xferFee(xferFee),
1614 uint256 const beckyBuyOfferIndex =
1616 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1617 token::owner(alice));
1619 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1623 TER const createOfferTER =
1625 uint256 const beckyOfferIndex =
1627 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1629 ter(createOfferTER));
1633 uint256 const cheriOfferIndex =
1635 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1636 token::owner(becky),
1637 ter(createOfferTER));
1641 env(token::cancelOffer(becky, {beckyOfferIndex}));
1642 env(token::cancelOffer(cheri, {cheriOfferIndex}));
1650 uint256 const nftAutoTrustID{token::getNextID(
1652 env(token::mint(alice, 0u),
1653 token::xferFee(transferFee),
1658 uint256 const beckyBuyOfferIndex =
1660 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1661 token::owner(alice));
1663 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1667 uint256 const beckySellOfferIndex =
1669 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1672 env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1676 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1679 uint256 const beckyBuyBackOfferIndex =
1681 env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1682 token::owner(cheri));
1684 env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1688 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1689 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1695 uint256 const nftNoAutoTrustID{
1697 env(token::mint(alice, 0u),
1698 token::xferFee(transferFee),
1703 uint256 const aliceSellOfferIndex =
1705 env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1708 env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1714 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1717 env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1721 uint256 const cheriSellOfferIndex =
1723 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1726 env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1732 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1740 testcase(
"Mint flagTransferable");
1742 using namespace test::jtx;
1744 Env env{*
this, features};
1746 Account
const alice{
"alice"};
1747 Account
const becky{
"becky"};
1748 Account
const minter{
"minter"};
1750 env.fund(XRP(1000), alice, becky, minter);
1756 uint256 const nftAliceNoTransferID{
1757 token::getNextID(env, alice, 0u)};
1758 env(token::mint(alice, 0u), token::xferFee(0));
1764 env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1765 token::owner(alice),
1769 uint256 const aliceSellOfferIndex =
1771 env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1774 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1780 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1789 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1791 token::destination(alice),
1799 uint256 const aliceBuyOfferIndex =
1801 env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1802 token::owner(becky));
1804 env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1810 env(token::burn(alice, nftAliceNoTransferID));
1817 env(token::setMinter(alice, minter));
1821 uint256 const nftMinterNoTransferID{
1822 token::getNextID(env, alice, 0u)};
1823 env(token::mint(minter), token::issuer(alice));
1829 env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1830 token::owner(minter),
1836 env(token::clearMinter(alice));
1841 env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1849 for (
int i = 0; i < 10; ++i)
1852 env(token::setMinter(alice, minter));
1858 uint256 const minterSellOfferIndex =
1860 env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1867 env(token::clearMinter(alice));
1873 env(token::acceptSellOffer(becky, minterSellOfferIndex));
1879 env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1887 env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1888 token::owner(becky),
1895 uint256 const aliceBuyOfferIndex =
1897 env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1898 token::owner(becky));
1904 for (
int i = 0; i < 10; ++i)
1907 env(token::setMinter(alice, minter));
1912 uint256 const minterBuyOfferIndex =
1914 env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1915 token::owner(becky));
1921 env(token::clearMinter(alice));
1927 env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1935 env(token::burn(minter, nftMinterNoTransferID), ter(
tesSUCCESS));
1937 env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
1954 uint256 const aliceSellOfferIndex =
1956 env(token::createOffer(alice, nftAliceID, XRP(20)),
1961 uint256 const beckyBuyOfferIndex =
1963 env(token::createOffer(becky, nftAliceID, XRP(21)),
1964 token::owner(alice));
1969 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1975 uint256 const beckySellOfferIndex =
1977 env(token::createOffer(becky, nftAliceID, XRP(22)),
1985 env(token::acceptSellOffer(minter, beckySellOfferIndex));
1992 uint256 const minterSellOfferIndex =
1994 env(token::createOffer(minter, nftAliceID, XRP(23)),
2002 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2010 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2018 env(token::burn(becky, nftAliceID));
2030 testcase(
"Mint transferFee");
2032 using namespace test::jtx;
2034 Env env{*
this, features};
2036 Account
const alice{
"alice"};
2037 Account
const becky{
"becky"};
2038 Account
const carol{
"carol"};
2039 Account
const minter{
"minter"};
2040 Account
const gw{
"gw"};
2041 IOU
const gwXAU(gw[
"XAU"]);
2043 env.fund(XRP(1000), alice, becky, carol, minter, gw);
2046 env(trust(alice, gwXAU(2000)));
2047 env(trust(becky, gwXAU(2000)));
2048 env(trust(carol, gwXAU(2000)));
2049 env(trust(minter, gwXAU(2000)));
2051 env(pay(gw, alice, gwXAU(1000)));
2052 env(pay(gw, becky, gwXAU(1000)));
2053 env(pay(gw, carol, gwXAU(1000)));
2054 env(pay(gw, minter, gwXAU(1000)));
2059 env(token::setMinter(alice, minter));
2076 uint256 const beckyBuyOfferIndex =
2078 env(token::createOffer(becky, nftID, gwXAU(10)),
2079 token::owner(alice));
2081 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2082 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2084 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2086 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2087 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2090 uint256 const beckySellOfferIndex =
2092 env(token::createOffer(becky, nftID, gwXAU(10)),
2095 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2097 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2098 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2099 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2102 uint256 const minterBuyOfferIndex =
2104 env(token::createOffer(minter, nftID, gwXAU(10)),
2105 token::owner(carol));
2107 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2109 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2110 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2111 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2112 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2116 uint256 const minterSellOfferIndex =
2118 env(token::createOffer(minter, nftID, gwXAU(10)),
2121 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2123 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2124 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2125 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2126 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2129 env(token::burn(alice, nftID));
2142 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2146 uint256 const beckyBuyOfferIndex =
2148 env(token::createOffer(becky, nftID, gwXAU(10)),
2149 token::owner(alice));
2151 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2152 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2154 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2156 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2157 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2160 uint256 const beckySellOfferIndex =
2162 env(token::createOffer(becky, nftID, gwXAU(10)),
2165 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2168 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2169 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2170 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2173 uint256 const minterBuyOfferIndex =
2175 env(token::createOffer(minter, nftID, gwXAU(10)),
2176 token::owner(carol));
2178 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2181 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2182 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2183 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2184 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2188 uint256 const minterSellOfferIndex =
2190 env(token::createOffer(minter, nftID, gwXAU(10)),
2193 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2195 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2196 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2197 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2198 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2202 env(pay(alice, becky, gwXAU(0.0001)));
2203 env(pay(alice, carol, gwXAU(0.0001)));
2206 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2207 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2208 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2209 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2212 env(token::burn(alice, nftID));
2223 env(token::mint(alice),
2230 uint256 const nftID = token::getNextID(
2232 env(token::mint(alice),
2238 uint256 const beckyBuyOfferIndex =
2240 env(token::createOffer(becky, nftID, gwXAU(10)),
2241 token::owner(alice));
2243 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2244 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2246 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2248 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2249 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2252 uint256 const beckySellOfferIndex =
2254 env(token::createOffer(becky, nftID, gwXAU(100)),
2257 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2260 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2261 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2262 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2265 uint256 const carolBuyOfferIndex =
2267 env(token::createOffer(carol, nftID, gwXAU(10)),
2268 token::owner(minter));
2270 env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2273 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2274 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2275 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2276 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2280 uint256 const carolSellOfferIndex =
2282 env(token::createOffer(carol, nftID, gwXAU(10)),
2285 env(token::acceptSellOffer(alice, carolSellOfferIndex));
2288 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2289 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2290 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2291 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2294 env(pay(alice, minter, gwXAU(55)));
2295 env(pay(becky, minter, gwXAU(40)));
2297 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2298 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2299 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2300 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2303 env(token::burn(alice, nftID));
2317 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2323 STAmount aliceBalance = env.balance(alice);
2324 STAmount minterBalance = env.balance(minter);
2325 uint256 const minterBuyOfferIndex =
2327 env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2329 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2331 aliceBalance += XRP(1) - fee;
2332 minterBalance -= XRP(1) + fee;
2333 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2334 BEAST_EXPECT(env.balance(minter) == minterBalance);
2338 STAmount carolBalance = env.balance(carol);
2339 uint256 const minterSellOfferIndex =
2341 env(token::createOffer(minter, nftID, drops(99999)),
2344 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2346 minterBalance += drops(99999) - fee;
2347 carolBalance -= drops(99999) + fee;
2348 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2349 BEAST_EXPECT(env.balance(minter) == minterBalance);
2350 BEAST_EXPECT(env.balance(carol) == carolBalance);
2354 STAmount beckyBalance = env.balance(becky);
2355 uint256 const beckyBuyOfferIndex =
2357 env(token::createOffer(becky, nftID, drops(100000)),
2358 token::owner(carol));
2360 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2362 carolBalance += drops(99999) - fee;
2363 beckyBalance -= drops(100000) + fee;
2364 aliceBalance += drops(1);
2366 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2367 BEAST_EXPECT(env.balance(minter) == minterBalance);
2368 BEAST_EXPECT(env.balance(carol) == carolBalance);
2369 BEAST_EXPECT(env.balance(becky) == beckyBalance);
2378 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2384 env(pay(alice, gw, env.balance(alice, gwXAU)));
2385 env(pay(minter, gw, env.balance(minter, gwXAU)));
2386 env(pay(becky, gw, env.balance(becky, gwXAU)));
2391 env(pay(gw, alice, startXAUBalance));
2392 env(pay(gw, minter, startXAUBalance));
2393 env(pay(gw, becky, startXAUBalance));
2402 STAmount aliceBalance = env.balance(alice, gwXAU);
2403 STAmount minterBalance = env.balance(minter, gwXAU);
2404 uint256 const minterBuyOfferIndex =
2406 env(token::createOffer(minter, nftID, tinyXAU),
2407 token::owner(alice));
2409 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2411 aliceBalance += tinyXAU;
2412 minterBalance -= tinyXAU;
2413 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2414 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2417 STAmount carolBalance = env.balance(carol, gwXAU);
2418 uint256 const minterSellOfferIndex =
2420 env(token::createOffer(minter, nftID, tinyXAU),
2423 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2426 minterBalance += tinyXAU;
2427 carolBalance -= tinyXAU;
2429 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2430 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2431 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2438 STAmount beckyBalance = env.balance(becky, gwXAU);
2439 uint256 const beckyBuyOfferIndex =
2441 env(token::createOffer(becky, nftID, cheapNFT),
2442 token::owner(carol));
2444 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2447 aliceBalance += tinyXAU;
2448 beckyBalance -= cheapNFT;
2449 carolBalance += cheapNFT - tinyXAU;
2450 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2451 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2452 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2453 BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2461 testcase(
"Mint taxon");
2463 using namespace test::jtx;
2465 Env env{*
this, features};
2467 Account
const alice{
"alice"};
2468 Account
const becky{
"becky"};
2470 env.fund(XRP(1000), alice, becky);
2479 uint256 const nftID = token::getNextID(env, alice, 0u);
2486 uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2494 for (
int i = 0; i < 10; ++i)
2504 ss <<
"Taxon recovery failed from nftID "
2505 <<
to_string(nftID) <<
". Expected: " << taxon
2506 <<
"; got: " << gotTaxon;
2511 uint256 const nftAliceID = token::getID(
2514 rand_int<std::uint32_t>(),
2515 rand_int<std::uint16_t>(),
2516 rand_int<std::uint16_t>());
2517 check(taxon, nftAliceID);
2519 uint256 const nftBeckyID = token::getID(
2522 rand_int<std::uint32_t>(),
2523 rand_int<std::uint16_t>(),
2524 rand_int<std::uint16_t>());
2525 check(taxon, nftBeckyID);
2537 testcase(
"Mint URI");
2539 using namespace test::jtx;
2541 Env env{*
this, features};
2543 Account
const alice{
"alice"};
2544 Account
const becky{
"becky"};
2546 env.fund(XRP(10000), alice, becky);
2552 auto randURI = []() {
2574 : uri(std::move(uri_)), taxon(taxon_)
2582 entries.
emplace_back(randURI(), rand_int<std::uint32_t>());
2585 for (Entry
const& entry : entries)
2587 if (entry.uri.empty())
2589 env(token::mint(alice, entry.taxon));
2593 env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2601 params[jss::account] = alice.human();
2602 params[jss::type] =
"state";
2603 return env.rpc(
"json",
"account_nfts",
to_string(params));
2607 Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2608 if (!BEAST_EXPECT(nfts.
size() == entries.size()))
2621 return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2626 Entry
const& entry = entries[i];
2629 if (entry.uri.empty())
2644 testcase(
"Create offer destination");
2646 using namespace test::jtx;
2648 Env env{*
this, features};
2650 Account
const issuer{
"issuer"};
2651 Account
const minter{
"minter"};
2652 Account
const buyer{
"buyer"};
2653 Account
const broker{
"broker"};
2655 env.fund(XRP(1000), issuer, minter, buyer, broker);
2659 env(token::setMinter(issuer, minter));
2664 env(token::mint(minter, 0),
2665 token::issuer(issuer),
2672 uint256 const offerMinterToIssuer =
2674 env(token::createOffer(minter, nftokenID, drops(1)),
2675 token::destination(issuer),
2678 uint256 const offerMinterToBuyer =
2680 env(token::createOffer(minter, nftokenID, drops(1)),
2681 token::destination(buyer),
2685 env(token::createOffer(issuer, nftokenID, drops(1)),
2686 token::owner(minter),
2687 token::destination(minter),
2702 env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2704 env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2713 env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2714 env(token::cancelOffer(minter, {offerMinterToIssuer}));
2724 uint256 const offerMinterToBuyer =
2726 env(token::createOffer(minter, nftokenID, drops(1)),
2727 token::destination(buyer),
2736 env(token::acceptSellOffer(issuer, offerMinterToBuyer),
2744 env(token::acceptSellOffer(buyer, offerMinterToBuyer));
2753 env(token::createOffer(minter, nftokenID, drops(1)),
2754 token::owner(buyer),
2755 token::destination(buyer),
2763 uint256 const offerMinterToBuyer =
2765 env(token::createOffer(minter, nftokenID, drops(1)),
2766 token::owner(buyer));
2773 env(token::acceptBuyOffer(buyer, offerMinterToBuyer));
2783 uint256 const offerMinterToBroker =
2785 env(token::createOffer(minter, nftokenID, drops(1)),
2786 token::destination(broker),
2789 uint256 const offerBuyerToMinter =
2791 env(token::createOffer(buyer, nftokenID, drops(1)),
2792 token::owner(minter));
2801 env(token::brokerOffers(
2802 issuer, offerBuyerToMinter, offerMinterToBroker),
2811 env(token::brokerOffers(
2812 broker, offerBuyerToMinter, offerMinterToBroker));
2823 uint256 const offerBuyerToMinter =
2825 env(token::createOffer(buyer, nftokenID, drops(1)),
2826 token::destination(minter),
2829 uint256 const offerMinterToBuyer =
2831 env(token::createOffer(minter, nftokenID, drops(1)),
2832 token::owner(buyer));
2834 uint256 const offerIssuerToBuyer =
2836 env(token::createOffer(issuer, nftokenID, drops(1)),
2837 token::owner(buyer));
2845 env(token::brokerOffers(
2846 broker, offerIssuerToBuyer, offerBuyerToMinter),
2854 env(token::brokerOffers(
2855 broker, offerMinterToBuyer, offerBuyerToMinter));
2867 testcase(
"Create offer expiration");
2869 using namespace test::jtx;
2871 Env env{*
this, features};
2873 Account
const issuer{
"issuer"};
2874 Account
const minter{
"minter"};
2875 Account
const buyer{
"buyer"};
2877 env.fund(XRP(1000), issuer, minter, buyer);
2881 env(token::setMinter(issuer, minter));
2886 env(token::mint(minter, 0),
2887 token::issuer(issuer),
2893 env(token::mint(minter, 0),
2894 token::issuer(issuer),
2903 uint256 const offerMinterToIssuer =
2905 env(token::createOffer(minter, nftokenID0, drops(1)),
2906 token::destination(issuer),
2907 token::expiration(expiration),
2910 uint256 const offerMinterToAnyone =
2912 env(token::createOffer(minter, nftokenID0, drops(1)),
2913 token::expiration(expiration),
2916 uint256 const offerIssuerToMinter =
2918 env(token::createOffer(issuer, nftokenID0, drops(1)),
2919 token::owner(minter),
2920 token::expiration(expiration));
2922 uint256 const offerBuyerToMinter =
2924 env(token::createOffer(buyer, nftokenID0, drops(1)),
2925 token::owner(minter),
2926 token::expiration(expiration));
2938 env(token::cancelOffer(issuer, {offerMinterToAnyone}),
2940 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2943 BEAST_EXPECT(
lastClose(env) < expiration);
2949 env(token::cancelOffer(minter, {offerMinterToAnyone}));
2953 env(token::cancelOffer(issuer, {offerMinterToIssuer}));
2964 env(token::cancelOffer(issuer, {offerBuyerToMinter}));
2965 env(token::cancelOffer(buyer, {offerIssuerToMinter}));
2980 env(token::createOffer(minter, nftokenID0, drops(1)),
2981 token::expiration(expiration),
2986 env(token::createOffer(minter, nftokenID1, drops(1)),
2987 token::expiration(expiration),
2990 BEAST_EXPECT(
lastClose(env) < expiration);
2996 env(token::acceptSellOffer(buyer, offer0));
3007 env(token::acceptSellOffer(buyer, offer1), ter(
tecEXPIRED));
3008 env(token::acceptSellOffer(issuer, offer1), ter(
tecEXPIRED));
3017 env(token::cancelOffer(issuer, {offer1}));
3027 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3029 token::destination(minter));
3031 env(token::acceptSellOffer(minter, offerSellBack));
3045 env(token::createOffer(buyer, nftokenID0, drops(1)),
3046 token::owner(minter),
3047 token::expiration(expiration));
3050 env(token::createOffer(buyer, nftokenID1, drops(1)),
3051 token::owner(minter),
3052 token::expiration(expiration));
3054 BEAST_EXPECT(
lastClose(env) < expiration);
3060 env(token::acceptBuyOffer(minter, offer0));
3071 env(token::acceptBuyOffer(minter, offer1), ter(
tecEXPIRED));
3072 env(token::acceptBuyOffer(issuer, offer1), ter(
tecEXPIRED));
3081 env(token::cancelOffer(issuer, {offer1}));
3091 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3093 token::destination(minter));
3095 env(token::acceptSellOffer(minter, offerSellBack));
3110 env(token::createOffer(minter, nftokenID0, drops(1)),
3111 token::expiration(expiration),
3116 env(token::createOffer(minter, nftokenID1, drops(1)),
3117 token::expiration(expiration),
3122 env(token::createOffer(buyer, nftokenID0, drops(1)),
3123 token::owner(minter));
3127 env(token::createOffer(buyer, nftokenID1, drops(1)),
3128 token::owner(minter));
3131 BEAST_EXPECT(
lastClose(env) < expiration);
3137 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3148 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3158 env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3168 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3170 token::destination(minter));
3172 env(token::acceptSellOffer(minter, offerSellBack));
3187 env(token::createOffer(minter, nftokenID0, drops(1)),
3192 env(token::createOffer(minter, nftokenID1, drops(1)),
3197 env(token::createOffer(buyer, nftokenID0, drops(1)),
3198 token::expiration(expiration),
3199 token::owner(minter));
3203 env(token::createOffer(buyer, nftokenID1, drops(1)),
3204 token::expiration(expiration),
3205 token::owner(minter));
3208 BEAST_EXPECT(
lastClose(env) < expiration);
3214 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3225 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3235 env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3245 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3247 token::destination(minter));
3249 env(token::acceptSellOffer(minter, offerSellBack));
3265 env(token::createOffer(minter, nftokenID0, drops(1)),
3266 token::expiration(expiration),
3271 env(token::createOffer(minter, nftokenID1, drops(1)),
3272 token::expiration(expiration),
3277 env(token::createOffer(buyer, nftokenID0, drops(1)),
3278 token::expiration(expiration),
3279 token::owner(minter));
3283 env(token::createOffer(buyer, nftokenID1, drops(1)),
3284 token::expiration(expiration),
3285 token::owner(minter));
3288 BEAST_EXPECT(
lastClose(env) < expiration);
3294 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3305 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3315 env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3325 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3327 token::destination(minter));
3329 env(token::acceptSellOffer(minter, offerSellBack));
3341 testcase(
"Cancel offers");
3343 using namespace test::jtx;
3345 Env env{*
this, features};
3347 Account
const alice(
"alice");
3348 Account
const becky(
"becky");
3349 Account
const minter(
"minter");
3350 env.fund(XRP(50000), alice, becky, minter);
3354 env(token::setMinter(alice, minter));
3363 uint256 const expiredOfferIndex =
3366 env(token::createOffer(alice, nftokenID, XRP(1000)),
3368 token::expiration(
lastClose(env) + 13));
3373 env(token::cancelOffer(becky, {expiredOfferIndex}),
3381 env(token::cancelOffer(becky, {expiredOfferIndex}));
3387 uint256 const dest1OfferIndex =
3390 env(token::createOffer(alice, nftokenID, XRP(1000)),
3391 token::destination(becky),
3397 env(token::cancelOffer(minter, {dest1OfferIndex}),
3402 env(token::cancelOffer(becky, {dest1OfferIndex}));
3407 uint256 const dest2OfferIndex =
3410 env(token::createOffer(alice, nftokenID, XRP(1000)),
3411 token::destination(becky),
3416 env(token::cancelOffer(alice, {dest2OfferIndex}));
3423 uint256 const mintersNFTokenID =
3425 env(token::mint(minter, 0),
3426 token::issuer(alice),
3430 uint256 const minterOfferIndex =
3433 env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3439 env(token::cancelOffer(alice, {minterOfferIndex}),
3441 env(token::cancelOffer(becky, {minterOfferIndex}),
3446 env(token::cancelOffer(minter, {minterOfferIndex}));
3455 testcase(
"Cancel too many offers");
3457 using namespace test::jtx;
3459 Env env{*
this, features};
3474 Account
const alice(
"alice");
3475 env.fund(XRP(1000), alice);
3484 Account
const offerAcct(
3486 env.fund(XRP(1000), nftAcct, offerAcct);
3491 env(token::mint(nftAcct, 0),
3498 env(token::createOffer(offerAcct, nftokenID, drops(1)),
3499 token::owner(nftAcct),
3508 for (
uint256 const& offerIndex : offerIndexes)
3515 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3519 env(token::cancelOffer(alice, {offerIndexes.back()}));
3524 offerIndexes.pop_back();
3530 env(token::mint(alice, 0),
3536 env(token::createOffer(alice, nftokenID, drops(1)),
3545 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3549 env(token::burn(alice, nftokenID));
3555 offerIndexes.pop_back();
3560 env(token::cancelOffer(alice, offerIndexes));
3564 for (
uint256 const& offerIndex : offerIndexes)
3574 testcase(
"Brokered NFT offer accept");
3576 using namespace test::jtx;
3578 Env env{*
this, features};
3586 Account
const issuer{
"issuer"};
3587 Account
const minter{
"minter"};
3588 Account
const buyer{
"buyer"};
3589 Account
const broker{
"broker"};
3590 Account
const gw{
"gw"};
3591 IOU
const gwXAU(gw[
"XAU"]);
3593 env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3596 env(trust(issuer, gwXAU(2000)));
3597 env(trust(minter, gwXAU(2000)));
3598 env(trust(buyer, gwXAU(2000)));
3599 env(trust(broker, gwXAU(2000)));
3602 env(token::setMinter(issuer, minter));
3606 auto checkOwnerCountIsOne =
3611 for (Account
const& acct : accounts)
3617 ss <<
"Account " << acct.human()
3618 <<
" expected ownerCount == 1. Got " <<
ownerCount;
3619 fail(ss.
str(), __FILE__, line);
3625 auto mintNFT = [&env, &issuer, &minter](
std::uint16_t xferFee = 0) {
3628 env(token::mint(minter, 0),
3629 token::issuer(issuer),
3630 token::xferFee(xferFee),
3642 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3644 uint256 const nftID = mintNFT();
3647 uint256 const minterOfferIndex =
3649 env(token::createOffer(minter, nftID, XRP(0)),
3657 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3660 auto const minterBalance = env.balance(minter);
3661 auto const buyerBalance = env.balance(buyer);
3662 auto const brokerBalance = env.balance(broker);
3663 auto const issuerBalance = env.balance(issuer);
3666 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3671 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3672 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3673 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3674 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3677 env(token::burn(buyer, nftID));
3687 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3689 uint256 const nftID = mintNFT();
3692 uint256 const minterOfferIndex =
3694 env(token::createOffer(minter, nftID, XRP(0)),
3702 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3706 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3707 token::brokerFee(XRP(1.1)),
3711 auto const minterBalance = env.balance(minter);
3712 auto const buyerBalance = env.balance(buyer);
3713 auto const brokerBalance = env.balance(broker);
3714 auto const issuerBalance = env.balance(issuer);
3717 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3718 token::brokerFee(XRP(0.5)));
3723 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3724 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3726 env.balance(broker) == brokerBalance + XRP(0.5) - drops(10));
3727 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3730 env(token::burn(buyer, nftID));
3740 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3745 uint256 const minterOfferIndex =
3747 env(token::createOffer(minter, nftID, XRP(0)),
3755 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3758 auto const minterBalance = env.balance(minter);
3759 auto const buyerBalance = env.balance(buyer);
3760 auto const brokerBalance = env.balance(broker);
3761 auto const issuerBalance = env.balance(issuer);
3764 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3769 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3770 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3771 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3772 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
3775 env(token::burn(buyer, nftID));
3785 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3790 uint256 const minterOfferIndex =
3792 env(token::createOffer(minter, nftID, XRP(0)),
3800 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3803 auto const minterBalance = env.balance(minter);
3804 auto const buyerBalance = env.balance(buyer);
3805 auto const brokerBalance = env.balance(broker);
3806 auto const issuerBalance = env.balance(issuer);
3809 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3810 token::brokerFee(XRP(0.75)));
3816 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
3817 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3819 env.balance(broker) == brokerBalance + XRP(0.75) - drops(10));
3820 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
3823 env(token::burn(buyer, nftID));
3828 auto setXAUBalance_1000 =
3829 [
this, &gw, &gwXAU, &env](
3833 for (Account
const& acct : accounts)
3835 static const auto xau1000 = gwXAU(1000);
3836 auto const balance = env.balance(acct, gwXAU);
3837 if (balance < xau1000)
3839 env(pay(gw, acct, xau1000 - balance));
3842 else if (balance > xau1000)
3844 env(pay(acct, gw, balance - xau1000));
3847 if (env.balance(acct, gwXAU) != xau1000)
3850 ss <<
"Unable to set " << acct.human()
3851 <<
" account balance to gwXAU(1000)";
3852 this->fail(ss.
str(), __FILE__, line);
3860 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3861 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
3863 uint256 const nftID = mintNFT();
3866 uint256 const minterOfferIndex =
3868 env(token::createOffer(minter, nftID, gwXAU(1000)),
3876 env(token::createOffer(buyer, nftID, gwXAU(1001)),
3877 token::owner(minter));
3881 env(token::brokerOffers(
3882 broker, buyOfferIndex, minterOfferIndex),
3888 env(token::cancelOffer(buyer, {buyOfferIndex}));
3895 env(token::createOffer(buyer, nftID, gwXAU(999)),
3896 token::owner(minter));
3900 env(token::brokerOffers(
3901 broker, buyOfferIndex, minterOfferIndex),
3907 env(token::cancelOffer(buyer, {buyOfferIndex}));
3914 env(token::createOffer(buyer, nftID, gwXAU(1000)),
3915 token::owner(minter));
3919 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3920 token::brokerFee(gwXAU(0.1)),
3925 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3932 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
3933 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
3934 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
3935 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
3938 env(token::burn(buyer, nftID));
3945 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3946 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
3951 uint256 const minterOfferIndex =
3953 env(token::createOffer(minter, nftID, gwXAU(900)),
3960 env(token::createOffer(buyer, nftID, gwXAU(1001)),
3961 token::owner(minter));
3965 env(token::brokerOffers(
3966 broker, buyOfferIndex, minterOfferIndex),
3972 env(token::cancelOffer(buyer, {buyOfferIndex}));
3979 env(token::createOffer(buyer, nftID, gwXAU(899)),
3980 token::owner(minter));
3984 env(token::brokerOffers(
3985 broker, buyOfferIndex, minterOfferIndex),
3991 env(token::cancelOffer(buyer, {buyOfferIndex}));
3997 env(token::createOffer(buyer, nftID, gwXAU(1000)),
3998 token::owner(minter));
4003 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4004 token::brokerFee(gwXAU(101)),
4010 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4011 token::brokerFee(gwXAU(100)));
4018 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4019 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4020 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4021 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4024 env(token::burn(buyer, nftID));
4031 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4032 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4037 uint256 const minterOfferIndex =
4039 env(token::createOffer(minter, nftID, gwXAU(900)),
4046 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4047 token::owner(minter));
4053 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4054 token::brokerFee(gwXAU(50)));
4061 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4062 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4063 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4064 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4067 env(token::burn(buyer, nftID));
4076 testcase(
"NFToken transactions with tickets");
4078 using namespace test::jtx;
4080 Env env{*
this, features};
4082 Account
const issuer{
"issuer"};
4083 Account
const buyer{
"buyer"};
4084 env.fund(XRP(10000), issuer, buyer);
4091 env(ticket::create(issuer, 10));
4097 env(ticket::create(buyer, 10));
4105 env(token::mint(issuer, 0u),
4107 ticket::use(issuerTicketSeq++));
4115 env(token::createOffer(buyer, nftId, XRP(1)),
4116 token::owner(issuer),
4117 ticket::use(buyerTicketSeq++));
4123 env(token::cancelOffer(buyer, {offerIndex0}),
4124 ticket::use(buyerTicketSeq++));
4131 env(token::createOffer(buyer, nftId, XRP(2)),
4132 token::owner(issuer),
4133 ticket::use(buyerTicketSeq++));
4139 env(token::acceptBuyOffer(issuer, offerIndex1),
4140 ticket::use(issuerTicketSeq++));
4147 env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4154 BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4155 BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4166 testcase(
"NFToken delete account");
4168 using namespace test::jtx;
4170 Env env{*
this, features};
4172 Account
const issuer{
"issuer"};
4173 Account
const minter{
"minter"};
4174 Account
const becky{
"becky"};
4175 Account
const carla{
"carla"};
4176 Account
const daria{
"daria"};
4178 env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4182 for (
int i = 0; i < 300; ++i)
4185 env(token::setMinter(issuer, minter));
4189 env(token::mint(minter, 0u),
4190 token::issuer(issuer),
4203 for (
int i = 0; i < 15; ++i)
4207 env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4210 uint256 const carlaOfferIndex =
4212 env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4217 env(acctdelete(becky, daria), fee(XRP(50)));
4221 env(token::acceptBuyOffer(minter, carlaOfferIndex));
4226 env(acctdelete(minter, daria), fee(XRP(50)));
4238 for (
int i = 0; i < 15; ++i)
4243 env(token::burn(carla, nftId));
4246 env(acctdelete(issuer, daria), fee(XRP(50)));
4247 env(acctdelete(carla, daria), fee(XRP(50)));
4282 using namespace test::jtx;
4283 auto const sa = supported_amendments();