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));
236 env(pay(env.master, alice, XRP(50) + drops(9)));
241 auto checkAliceOwnerMintedBurned = [&env,
this, &alice](
254 ss <<
"Wrong " << type <<
" count. Found: " << found
255 <<
"; Expected: " << exp;
256 fail(ss.
str(), __FILE__, line);
259 oneCheck(
"owner",
ownerCount(env, alice), owners);
260 oneCheck(
"minted",
mintedCount(env, alice), minted);
261 oneCheck(
"burned",
burnedCount(env, alice), burned);
268 checkAliceOwnerMintedBurned(0, 0, 0, __LINE__);
271 env(pay(env.master, alice, drops(11)));
275 env(token::mint(alice));
277 checkAliceOwnerMintedBurned(1, 1, 0, __LINE__);
281 for (
int i = 1; i < 32; ++i)
283 env(token::mint(alice));
284 checkAliceOwnerMintedBurned(1, i + 1, 0, __LINE__);
291 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
294 env(pay(env.master, alice, XRP(50) + drops(329)));
301 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
304 env(pay(env.master, alice, drops(11)));
308 env(token::mint(alice));
310 checkAliceOwnerMintedBurned(2, 33, 0, __LINE__);
317 env(token::burn(alice, token::getID(env, alice, 0, seq++)));
319 checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__);
323 env(token::burn(alice, token::getID(env, alice, 197, 5)),
326 checkAliceOwnerMintedBurned(0, 33, 33, __LINE__);
331 env(token::setMinter(alice, minter));
338 auto checkMintersOwnerMintedBurned = [&env,
this, &alice, &minter](
346 auto oneCheck = [
this](
356 ss <<
"Wrong " << type <<
" count. Found: " << found
357 <<
"; Expected: " << exp;
358 fail(ss.
str(), __FILE__, line);
361 oneCheck(
"alice owner",
ownerCount(env, alice), aliceOwners, line);
363 "alice minted",
mintedCount(env, alice), aliceMinted, line);
365 "alice burned",
burnedCount(env, alice), aliceBurned, line);
367 "minter owner",
ownerCount(env, minter), minterOwners, line);
369 "minter minted",
mintedCount(env, minter), minterMinted, line);
371 "minter burned",
burnedCount(env, minter), minterBurned, line);
377 env(pay(env.master, minter, XRP(50) - drops(1)));
379 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
384 env(token::mint(minter),
385 token::issuer(alice),
389 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
392 env(pay(env.master, minter, drops(11)));
396 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
398 checkMintersOwnerMintedBurned(0, 34, nftSeq, 1, 0, 0, __LINE__);
402 for (
int i = 1; i < 32; ++i)
404 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
405 checkMintersOwnerMintedBurned(0, i + 34, nftSeq, 1, 0, 0, __LINE__);
410 env(pay(env.master, minter, XRP(50) + drops(319)));
415 env(token::mint(minter),
416 token::issuer(alice),
420 checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__);
423 env(pay(env.master, minter, drops(11)));
427 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
429 checkMintersOwnerMintedBurned(0, 66, nftSeq, 2, 0, 0, __LINE__);
434 env(token::burn(minter, token::getID(env, alice, 0, nftSeq++)));
436 checkMintersOwnerMintedBurned(
437 0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__);
442 env(token::burn(minter, token::getID(env, alice, 0, nftSeq++)));
444 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
447 env(token::burn(minter, token::getID(env, alice, 2009, 3)),
450 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
458 testcase(
"Mint max tokens");
460 using namespace test::jtx;
462 Account
const alice{
"alice"};
463 Env env{*
this, features};
464 env.fund(XRP(1000), alice);
473 uint256 const nftId0{token::getNextID(env, alice, 0u)};
474 env(token::mint(alice, 0u));
477 env(token::burn(alice, nftId0));
484 env.app().openLedger().modify(
493 auto replacement = std::make_shared<SLE>(*sle, sle->key());
506 (*replacement)[sfMintedNFTokens] = 0x0000'0000;
514 view.rawReplace(replacement);
518 // See whether alice is at the boundary that causes an error.
519 env(token::mint(alice, 0u), ter(tesSUCCESS));
520 env(token::mint(alice, 0u), ter(tecMAX_SEQUENCE_REACHED));
524 testMintInvalid(FeatureBitset features)
526 // Explore many of the invalid ways to mint an NFT.
527 testcase("Mint invalid");
529 using namespace test::jtx;
531 Env env{*this, features};
532 Account const alice{"alice"};
533 Account const minter{"minter"};
535 // Fund alice and minter enough to exist, but not enough to meet
536 // the reserve for creating their first NFT. Account reserve for unit
537 // tests is 200 XRP, not 20.
538 env.fund(XRP(200), alice, minter);
541 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
544 // Fund alice enough to start minting NFTs.
545 env(pay(env.master, alice, XRP(1000)));
548 //----------------------------------------------------------------------
551 // Set a negative fee.
552 env(token::mint(alice, 0u),
553 fee(STAmount(10ull, true)),
556 // Set an invalid flag.
557 env(token::mint(alice, 0u), txflags(0x00008000), ter(temINVALID_FLAG));
559 // Can't
set a transfer fee
if the NFT does not have the tfTRANSFERABLE
561 env(token::mint(alice, 0u),
566 env(token::mint(alice, 0u),
572 env(token::mint(alice, 0u), token::issuer(alice), ter(
temMALFORMED));
575 env(token::mint(alice, 0u), token::uri(
""), ter(
temMALFORMED));
578 env(token::mint(alice, 0u),
586 env(token::mint(alice, 0u),
587 token::issuer(Account(
"demon")),
594 env(token::mint(minter, 0u),
595 token::issuer(alice),
603 testcase(
"Burn invalid");
605 using namespace test::jtx;
607 Env env{*
this, features};
608 Account
const alice{
"alice"};
609 Account
const buyer{
"buyer"};
610 Account
const minter{
"minter"};
611 Account
const gw(
"gw");
612 IOU
const gwAUD(gw[
"AUD"]);
617 env.fund(XRP(250), alice, buyer, minter, gw);
631 env(token::burn(alice, nftAlice0ID),
638 env(token::burn(alice, nftAlice0ID),
648 env(token::burn(alice, token::getID(env, alice, 0, 1)),
663 testcase(
"Invalid NFT offer create");
665 using namespace test::jtx;
667 Env env{*
this, features};
668 Account
const alice{
"alice"};
669 Account
const buyer{
"buyer"};
670 Account
const gw(
"gw");
671 IOU
const gwAUD(gw[
"AUD"]);
676 env.fund(XRP(250), alice, buyer, gw);
682 env(token::mint(alice, 0u),
694 uint256 nftNoXferID = token::getNextID(env, alice, 0);
695 env(token::mint(alice, 0));
708 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
715 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
722 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
729 env(token::createOffer(buyer, nftXrpOnlyID, buyer[
"USD"](1)),
731 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](0)),
733 env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
739 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](1)),
740 token::expiration(0),
747 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
753 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
761 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
768 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
769 token::destination(alice),
776 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
777 token::destination(Account(
"demon")),
787 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
795 env(token::createOffer(
796 buyer, token::getID(env, alice, 0, 1), XRP(1000)),
803 env(token::createOffer(
804 alice, token::getID(env, alice, 0, 1), XRP(1000)),
811 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
817 env(trust(buyer, gwAUD(1000)));
823 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
835 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
846 env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
856 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
865 env(trust(buyer, gwAUD(1000)));
868 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
878 env(pay(gw, buyer, gwAUD(999)));
883 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
890 env(pay(env.master, buyer, XRP(50) + drops(119)));
893 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
900 env(pay(env.master, buyer, drops(11)));
905 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
915 testcase(
"Invalid NFT offer cancel");
917 using namespace test::jtx;
919 Env env{*
this, features};
920 Account
const alice{
"alice"};
921 Account
const buyer{
"buyer"};
922 Account
const gw(
"gw");
923 IOU
const gwAUD(gw[
"AUD"]);
925 env.fund(XRP(1000), alice, buyer, gw);
936 uint256 const buyerOfferIndex =
938 env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
948 env(token::cancelOffer(buyer, {buyerOfferIndex}),
955 env(token::cancelOffer(buyer, {buyerOfferIndex}),
975 env(token::cancelOffer(buyer, offers), ter(
temMALFORMED));
981 env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
995 env(pay(env.master, gw, XRP(5000)));
999 env(offer(gw, XRP(i), gwAUD(1)));
1006 env(check::create(gw, env.master, XRP(300)));
1013 env(check::cancel(gw, gwCheckId));
1027 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1035 testcase(
"Invalid NFT offer accept");
1037 using namespace test::jtx;
1039 Env env{*
this, features};
1040 Account
const alice{
"alice"};
1041 Account
const buyer{
"buyer"};
1042 Account
const gw(
"gw");
1043 IOU
const gwAUD(gw[
"AUD"]);
1045 env.fund(XRP(1000), alice, buyer, gw);
1061 uint256 nftNoXferID = token::getNextID(env, alice, 0);
1062 env(token::mint(alice, 0));
1067 uint256 const plainOfferIndex =
1069 env(token::createOffer(alice, nftAlice0ID, XRP(10)),
1076 env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1081 uint256 const xrpOnlyOfferIndex =
1083 env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1088 uint256 const noXferOfferIndex =
1090 env(token::createOffer(alice, nftNoXferID, XRP(30)),
1096 uint256 const aliceExpOfferIndex =
1098 env(token::createOffer(alice, nftNoXferID, XRP(40)),
1108 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1115 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1116 txflags(0x00008000),
1123 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1132 Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1142 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1151 env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1152 token::brokerFee(gwAUD(0)),
1161 env(token::acceptBuyOffer(buyer, beast::zero),
1168 env(token::acceptBuyOffer(buyer, missingOfferIndex),
1174 env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1179 env(token::acceptSellOffer(buyer, beast::zero),
1185 env(token::acceptSellOffer(buyer, missingOfferIndex),
1191 env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1200 env(trust(alice, gwAUD(1000)));
1201 env(trust(buyer, gwAUD(1000)));
1203 env(pay(gw, buyer, gwAUD(30)));
1212 uint256 const buyerOfferIndex =
1214 env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1215 token::owner(alice));
1220 env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1226 env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1233 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1239 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1245 uint256 const buyerOfferIndex =
1247 env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1248 token::owner(alice));
1254 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1255 token::brokerFee(XRP(40)),
1261 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1262 token::brokerFee(gwAUD(31)),
1269 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1270 token::brokerFee(gwAUD(1.5)),
1276 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1284 uint256 const buyerOfferIndex =
1286 env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1287 token::owner(alice));
1292 env(token::acceptBuyOffer(buyer, plainOfferIndex),
1298 env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1304 env(pay(buyer, gw, gwAUD(30)));
1306 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1307 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1316 env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1319 env(token::acceptSellOffer(gw, offerIndex));
1323 env(pay(gw, buyer, gwAUD(30)));
1327 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1333 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1341 uint256 const buyerOfferIndex =
1343 env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1344 token::owner(alice));
1349 env(token::acceptSellOffer(alice, buyerOfferIndex),
1355 env(token::acceptSellOffer(alice, plainOfferIndex),
1362 env(token::acceptSellOffer(buyer, plainOfferIndex),
1373 env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1376 env(token::acceptSellOffer(alice, offerIndex));
1380 env(pay(buyer, gw, gwAUD(30)));
1382 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1383 env(token::acceptSellOffer(buyer, audOfferIndex),
1400 testcase(
"Mint flagBurnable");
1402 using namespace test::jtx;
1404 Env env{*
this, features};
1405 Account
const alice{
"alice"};
1406 Account
const buyer{
"buyer"};
1407 Account
const minter1{
"minter1"};
1408 Account
const minter2{
"minter2"};
1410 env.fund(XRP(1000), alice, buyer, minter1, minter2);
1415 env(token::setMinter(alice, minter1));
1422 auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1424 uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1425 env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1430 env(token::createOffer(minter1, nftID, XRP(0)),
1434 env(token::acceptSellOffer(buyer, offerIndex));
1442 uint256 const noBurnID = nftToBuyer(0);
1443 env(token::burn(alice, noBurnID),
1444 token::owner(buyer),
1447 env(token::burn(minter1, noBurnID),
1448 token::owner(buyer),
1451 env(token::burn(minter2, noBurnID),
1452 token::owner(buyer),
1457 env(token::burn(buyer, noBurnID), token::owner(buyer));
1464 env(token::burn(minter2, burnableID),
1465 token::owner(buyer),
1470 env(token::burn(alice, burnableID), token::owner(buyer));
1478 env(token::burn(buyer, burnableID));
1486 env(token::burn(buyer, burnableID), token::owner(buyer));
1496 env(token::setMinter(alice, minter2));
1501 env(token::burn(minter1, burnableID),
1502 token::owner(buyer),
1508 env(token::burn(minter2, burnableID), token::owner(buyer));
1518 testcase(
"Mint flagOnlyXRP");
1520 using namespace test::jtx;
1522 Env env{*
this, features};
1523 Account
const alice{
"alice"};
1524 Account
const buyer{
"buyer"};
1525 Account
const gw(
"gw");
1526 IOU
const gwAUD(gw[
"AUD"]);
1529 env.fund(XRP(1000), alice, buyer, gw);
1531 env(trust(alice, gwAUD(1000)));
1532 env(trust(buyer, gwAUD(1000)));
1534 env(pay(gw, buyer, gwAUD(100)));
1544 uint256 const aliceOfferIndex =
1546 env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1552 uint256 const buyerOfferIndex =
1554 env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1555 token::owner(alice));
1560 env(token::cancelOffer(alice, {aliceOfferIndex}));
1561 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1567 env(token::burn(alice, nftIOUsOkayID));
1580 env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1587 env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1588 token::owner(alice),
1595 env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1601 env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1602 token::owner(alice));
1612 testcase(
"Mint flagCreateTrustLines");
1614 using namespace test::jtx;
1616 Account
const alice{
"alice"};
1617 Account
const becky{
"becky"};
1618 Account
const cheri{
"cheri"};
1619 Account
const gw(
"gw");
1620 IOU
const gwAUD(gw[
"AUD"]);
1621 IOU
const gwCAD(gw[
"CAD"]);
1622 IOU
const gwEUR(gw[
"EUR"]);
1627 for (
auto const& tweakedFeatures :
1631 Env env{*
this, tweakedFeatures};
1632 env.fund(XRP(1000), alice, becky, cheri, gw);
1636 env(trust(becky, gwAUD(1000)));
1637 env(trust(cheri, gwAUD(1000)));
1638 env(trust(becky, gwCAD(1000)));
1639 env(trust(cheri, gwCAD(1000)));
1640 env(trust(becky, gwEUR(1000)));
1641 env(trust(cheri, gwEUR(1000)));
1643 env(pay(gw, becky, gwAUD(500)));
1644 env(pay(gw, becky, gwCAD(500)));
1645 env(pay(gw, becky, gwEUR(500)));
1646 env(pay(gw, cheri, gwAUD(500)));
1647 env(pay(gw, cheri, gwCAD(500)));
1654 uint256 const nftNoAutoTrustID{
1656 env(token::mint(alice, 0u),
1657 token::xferFee(xferFee),
1662 uint256 const beckyBuyOfferIndex =
1664 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1665 token::owner(alice));
1667 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1671 TER const createOfferTER =
1673 uint256 const beckyOfferIndex =
1675 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1677 ter(createOfferTER));
1681 uint256 const cheriOfferIndex =
1683 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1684 token::owner(becky),
1685 ter(createOfferTER));
1689 env(token::cancelOffer(becky, {beckyOfferIndex}));
1690 env(token::cancelOffer(cheri, {cheriOfferIndex}));
1698 uint256 const nftAutoTrustID{token::getNextID(
1709 env(token::mint(alice, 0u),
1710 token::xferFee(transferFee),
1721 uint256 const beckyBuyOfferIndex =
1723 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1724 token::owner(alice));
1726 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1730 uint256 const beckySellOfferIndex =
1732 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1735 env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1739 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1742 uint256 const beckyBuyBackOfferIndex =
1744 env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1745 token::owner(cheri));
1747 env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1751 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1752 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1758 uint256 const nftNoAutoTrustID{token::getNextID(
1760 env(token::mint(alice, 0u),
1761 token::xferFee(transferFee),
1766 uint256 const aliceSellOfferIndex =
1768 env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1771 env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1777 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1780 env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1784 uint256 const cheriSellOfferIndex =
1786 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1789 env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1795 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1804 testcase(
"Mint flagTransferable");
1806 using namespace test::jtx;
1808 Env env{*
this, features};
1810 Account
const alice{
"alice"};
1811 Account
const becky{
"becky"};
1812 Account
const minter{
"minter"};
1814 env.fund(XRP(1000), alice, becky, minter);
1820 uint256 const nftAliceNoTransferID{
1821 token::getNextID(env, alice, 0u)};
1822 env(token::mint(alice, 0u), token::xferFee(0));
1828 env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1829 token::owner(alice),
1833 uint256 const aliceSellOfferIndex =
1835 env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1838 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1844 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1853 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1855 token::destination(alice),
1863 uint256 const aliceBuyOfferIndex =
1865 env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1866 token::owner(becky));
1868 env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1874 env(token::burn(alice, nftAliceNoTransferID));
1881 env(token::setMinter(alice, minter));
1885 uint256 const nftMinterNoTransferID{
1886 token::getNextID(env, alice, 0u)};
1887 env(token::mint(minter), token::issuer(alice));
1893 env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1894 token::owner(minter),
1900 env(token::clearMinter(alice));
1905 env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1913 for (
int i = 0; i < 10; ++i)
1916 env(token::setMinter(alice, minter));
1922 uint256 const minterSellOfferIndex =
1924 env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1931 env(token::clearMinter(alice));
1937 env(token::acceptSellOffer(becky, minterSellOfferIndex));
1943 env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1951 env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1952 token::owner(becky),
1959 uint256 const aliceBuyOfferIndex =
1961 env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1962 token::owner(becky));
1968 for (
int i = 0; i < 10; ++i)
1971 env(token::setMinter(alice, minter));
1976 uint256 const minterBuyOfferIndex =
1978 env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1979 token::owner(becky));
1985 env(token::clearMinter(alice));
1991 env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1999 env(token::burn(minter, nftMinterNoTransferID), ter(
tesSUCCESS));
2001 env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
2018 uint256 const aliceSellOfferIndex =
2020 env(token::createOffer(alice, nftAliceID, XRP(20)),
2025 uint256 const beckyBuyOfferIndex =
2027 env(token::createOffer(becky, nftAliceID, XRP(21)),
2028 token::owner(alice));
2033 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
2039 uint256 const beckySellOfferIndex =
2041 env(token::createOffer(becky, nftAliceID, XRP(22)),
2049 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2056 uint256 const minterSellOfferIndex =
2058 env(token::createOffer(minter, nftAliceID, XRP(23)),
2066 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2074 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2082 env(token::burn(becky, nftAliceID));
2094 testcase(
"Mint transferFee");
2096 using namespace test::jtx;
2098 Env env{*
this, features};
2100 Account
const alice{
"alice"};
2101 Account
const becky{
"becky"};
2102 Account
const carol{
"carol"};
2103 Account
const minter{
"minter"};
2104 Account
const gw{
"gw"};
2105 IOU
const gwXAU(gw[
"XAU"]);
2107 env.fund(XRP(1000), alice, becky, carol, minter, gw);
2110 env(trust(alice, gwXAU(2000)));
2111 env(trust(becky, gwXAU(2000)));
2112 env(trust(carol, gwXAU(2000)));
2113 env(trust(minter, gwXAU(2000)));
2115 env(pay(gw, alice, gwXAU(1000)));
2116 env(pay(gw, becky, gwXAU(1000)));
2117 env(pay(gw, carol, gwXAU(1000)));
2118 env(pay(gw, minter, gwXAU(1000)));
2123 env(token::setMinter(alice, minter));
2140 uint256 const beckyBuyOfferIndex =
2142 env(token::createOffer(becky, nftID, gwXAU(10)),
2143 token::owner(alice));
2145 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2146 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2148 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2150 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2151 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2154 uint256 const beckySellOfferIndex =
2156 env(token::createOffer(becky, nftID, gwXAU(10)),
2159 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2161 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2162 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2163 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2166 uint256 const minterBuyOfferIndex =
2168 env(token::createOffer(minter, nftID, gwXAU(10)),
2169 token::owner(carol));
2171 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2173 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2174 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2175 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2176 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2180 uint256 const minterSellOfferIndex =
2182 env(token::createOffer(minter, nftID, gwXAU(10)),
2185 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2187 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2188 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2189 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2190 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2193 env(token::burn(alice, nftID));
2206 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2210 uint256 const beckyBuyOfferIndex =
2212 env(token::createOffer(becky, nftID, gwXAU(10)),
2213 token::owner(alice));
2215 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2216 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2218 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2220 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2221 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2224 uint256 const beckySellOfferIndex =
2226 env(token::createOffer(becky, nftID, gwXAU(10)),
2229 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2232 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2233 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2234 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2237 uint256 const minterBuyOfferIndex =
2239 env(token::createOffer(minter, nftID, gwXAU(10)),
2240 token::owner(carol));
2242 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2245 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2246 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2247 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2248 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2252 uint256 const minterSellOfferIndex =
2254 env(token::createOffer(minter, nftID, gwXAU(10)),
2257 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2259 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2260 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2261 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2262 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2266 env(pay(alice, becky, gwXAU(0.0001)));
2267 env(pay(alice, carol, gwXAU(0.0001)));
2270 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2271 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2272 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2273 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2276 env(token::burn(alice, nftID));
2287 env(token::mint(alice),
2294 uint256 const nftID = token::getNextID(
2296 env(token::mint(alice),
2302 uint256 const beckyBuyOfferIndex =
2304 env(token::createOffer(becky, nftID, gwXAU(10)),
2305 token::owner(alice));
2307 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2308 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2310 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2312 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2313 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2316 uint256 const beckySellOfferIndex =
2318 env(token::createOffer(becky, nftID, gwXAU(100)),
2321 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2324 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2325 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2326 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2329 uint256 const carolBuyOfferIndex =
2331 env(token::createOffer(carol, nftID, gwXAU(10)),
2332 token::owner(minter));
2334 env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2337 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2338 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2339 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2340 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2344 uint256 const carolSellOfferIndex =
2346 env(token::createOffer(carol, nftID, gwXAU(10)),
2349 env(token::acceptSellOffer(alice, carolSellOfferIndex));
2352 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2353 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2354 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2355 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2358 env(pay(alice, minter, gwXAU(55)));
2359 env(pay(becky, minter, gwXAU(40)));
2361 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2362 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2363 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2364 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2367 env(token::burn(alice, nftID));
2377 for (
auto NumberSwitchOver : {
true})
2379 if (NumberSwitchOver)
2387 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2393 STAmount aliceBalance = env.balance(alice);
2394 STAmount minterBalance = env.balance(minter);
2395 uint256 const minterBuyOfferIndex =
2397 env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2399 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2401 aliceBalance += XRP(1) - fee;
2402 minterBalance -= XRP(1) + fee;
2403 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2404 BEAST_EXPECT(env.balance(minter) == minterBalance);
2408 auto pmt = NumberSwitchOver ? drops(50000) : drops(99999);
2409 STAmount carolBalance = env.balance(carol);
2410 uint256 const minterSellOfferIndex =
2412 env(token::createOffer(minter, nftID, pmt), txflags(
tfSellNFToken));
2414 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2416 minterBalance += pmt - fee;
2417 carolBalance -= pmt + fee;
2418 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2419 BEAST_EXPECT(env.balance(minter) == minterBalance);
2420 BEAST_EXPECT(env.balance(carol) == carolBalance);
2424 STAmount beckyBalance = env.balance(becky);
2425 uint256 const beckyBuyOfferIndex =
2427 pmt = NumberSwitchOver ? drops(50001) : drops(100000);
2428 env(token::createOffer(becky, nftID, pmt), token::owner(carol));
2430 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2432 carolBalance += pmt - drops(1) - fee;
2433 beckyBalance -= pmt + fee;
2434 aliceBalance += drops(1);
2436 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2437 BEAST_EXPECT(env.balance(minter) == minterBalance);
2438 BEAST_EXPECT(env.balance(carol) == carolBalance);
2439 BEAST_EXPECT(env.balance(becky) == beckyBalance);
2448 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2454 env(pay(alice, gw, env.balance(alice, gwXAU)));
2455 env(pay(minter, gw, env.balance(minter, gwXAU)));
2456 env(pay(becky, gw, env.balance(becky, gwXAU)));
2461 env(pay(gw, alice, startXAUBalance));
2462 env(pay(gw, minter, startXAUBalance));
2463 env(pay(gw, becky, startXAUBalance));
2472 STAmount aliceBalance = env.balance(alice, gwXAU);
2473 STAmount minterBalance = env.balance(minter, gwXAU);
2474 uint256 const minterBuyOfferIndex =
2476 env(token::createOffer(minter, nftID, tinyXAU),
2477 token::owner(alice));
2479 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2481 aliceBalance += tinyXAU;
2482 minterBalance -= tinyXAU;
2483 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2484 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2487 STAmount carolBalance = env.balance(carol, gwXAU);
2488 uint256 const minterSellOfferIndex =
2490 env(token::createOffer(minter, nftID, tinyXAU),
2493 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2496 minterBalance += tinyXAU;
2497 carolBalance -= tinyXAU;
2499 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2500 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2501 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2508 STAmount beckyBalance = env.balance(becky, gwXAU);
2509 uint256 const beckyBuyOfferIndex =
2511 env(token::createOffer(becky, nftID, cheapNFT),
2512 token::owner(carol));
2514 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2517 aliceBalance += tinyXAU;
2518 beckyBalance -= cheapNFT;
2519 carolBalance += cheapNFT - tinyXAU;
2520 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2521 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2522 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2523 BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2531 testcase(
"Mint taxon");
2533 using namespace test::jtx;
2535 Env env{*
this, features};
2537 Account
const alice{
"alice"};
2538 Account
const becky{
"becky"};
2540 env.fund(XRP(1000), alice, becky);
2549 uint256 const nftID = token::getNextID(env, alice, 0u);
2556 uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2564 for (
int i = 0; i < 10; ++i)
2574 ss <<
"Taxon recovery failed from nftID "
2575 <<
to_string(nftID) <<
". Expected: " << taxon
2576 <<
"; got: " << gotTaxon;
2581 uint256 const nftAliceID = token::getID(
2585 rand_int<std::uint32_t>(),
2586 rand_int<std::uint16_t>(),
2587 rand_int<std::uint16_t>());
2588 check(taxon, nftAliceID);
2590 uint256 const nftBeckyID = token::getID(
2594 rand_int<std::uint32_t>(),
2595 rand_int<std::uint16_t>(),
2596 rand_int<std::uint16_t>());
2597 check(taxon, nftBeckyID);
2609 testcase(
"Mint URI");
2611 using namespace test::jtx;
2613 Env env{*
this, features};
2615 Account
const alice{
"alice"};
2616 Account
const becky{
"becky"};
2618 env.fund(XRP(10000), alice, becky);
2624 auto randURI = []() {
2646 : uri(std::move(uri_)), taxon(taxon_)
2654 entries.
emplace_back(randURI(), rand_int<std::uint32_t>());
2657 for (Entry
const& entry : entries)
2659 if (entry.uri.empty())
2661 env(token::mint(alice, entry.taxon));
2665 env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2673 params[jss::account] = alice.human();
2674 params[jss::type] =
"state";
2675 return env.rpc(
"json",
"account_nfts",
to_string(params));
2679 Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2680 if (!BEAST_EXPECT(nfts.
size() == entries.size()))
2693 return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2698 Entry
const& entry = entries[i];
2701 if (entry.uri.empty())
2716 testcase(
"Create offer destination");
2718 using namespace test::jtx;
2720 Env env{*
this, features};
2722 Account
const issuer{
"issuer"};
2723 Account
const minter{
"minter"};
2724 Account
const buyer{
"buyer"};
2725 Account
const broker{
"broker"};
2727 env.fund(XRP(1000), issuer, minter, buyer, broker);
2731 env(token::setMinter(issuer, minter));
2736 env(token::mint(minter, 0),
2737 token::issuer(issuer),
2744 uint256 const offerMinterToIssuer =
2746 env(token::createOffer(minter, nftokenID, drops(1)),
2747 token::destination(issuer),
2750 uint256 const offerMinterToBuyer =
2752 env(token::createOffer(minter, nftokenID, drops(1)),
2753 token::destination(buyer),
2756 uint256 const offerIssuerToMinter =
2758 env(token::createOffer(issuer, nftokenID, drops(1)),
2759 token::owner(minter),
2760 token::destination(minter));
2762 uint256 const offerIssuerToBuyer =
2764 env(token::createOffer(issuer, nftokenID, drops(1)),
2765 token::owner(minter),
2766 token::destination(buyer));
2780 env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2782 env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2784 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2786 env(token::cancelOffer(minter, {offerIssuerToBuyer}),
2795 env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2796 env(token::cancelOffer(minter, {offerMinterToIssuer}));
2797 env(token::cancelOffer(buyer, {offerIssuerToBuyer}));
2798 env(token::cancelOffer(issuer, {offerIssuerToMinter}));
2808 uint256 const offerMinterSellsToBuyer =
2810 env(token::createOffer(minter, nftokenID, drops(1)),
2811 token::destination(buyer),
2820 env(token::acceptSellOffer(issuer, offerMinterSellsToBuyer),
2828 env(token::acceptSellOffer(buyer, offerMinterSellsToBuyer));
2838 uint256 const offerMinterBuysFromBuyer =
2840 env(token::createOffer(minter, nftokenID, drops(1)),
2841 token::owner(buyer),
2842 token::destination(buyer));
2850 env(token::acceptBuyOffer(issuer, offerMinterBuysFromBuyer),
2858 env(token::acceptBuyOffer(buyer, offerMinterBuysFromBuyer));
2867 uint256 const offerBuyerBuysFromMinter =
2869 env(token::createOffer(buyer, nftokenID, drops(1)),
2870 token::owner(minter),
2871 token::destination(broker));
2877 env(token::acceptBuyOffer(minter, offerBuyerBuysFromMinter),
2882 env(token::cancelOffer(buyer, {offerBuyerBuysFromMinter}));
2892 uint256 const offerMinterToBroker =
2894 env(token::createOffer(minter, nftokenID, drops(1)),
2895 token::destination(broker),
2898 uint256 const offerBuyerToMinter =
2900 env(token::createOffer(buyer, nftokenID, drops(1)),
2901 token::owner(minter));
2914 env(token::brokerOffers(
2915 issuer, offerBuyerToMinter, offerMinterToBroker),
2925 env(token::brokerOffers(
2926 broker, offerBuyerToMinter, offerMinterToBroker));
2937 uint256 const offerBuyerToMinter =
2939 env(token::createOffer(buyer, nftokenID, drops(1)),
2940 token::destination(minter),
2943 uint256 const offerMinterToBuyer =
2945 env(token::createOffer(minter, nftokenID, drops(1)),
2946 token::owner(buyer));
2948 uint256 const offerIssuerToBuyer =
2950 env(token::createOffer(issuer, nftokenID, drops(1)),
2951 token::owner(buyer));
2964 env(token::brokerOffers(
2965 broker, offerIssuerToBuyer, offerBuyerToMinter),
2978 env(token::brokerOffers(
2979 broker, offerMinterToBuyer, offerBuyerToMinter),
2985 env(token::acceptBuyOffer(buyer, offerMinterToBuyer));
2989 env(token::cancelOffer(buyer, {offerBuyerToMinter}));
2997 env(token::cancelOffer(issuer, {offerIssuerToBuyer}));
3009 uint256 const offerMinterToBroker =
3011 env(token::createOffer(minter, nftokenID, drops(1)),
3012 token::destination(broker),
3015 uint256 const offerBuyerToBroker =
3017 env(token::createOffer(buyer, nftokenID, drops(1)),
3018 token::owner(minter),
3019 token::destination(broker));
3027 env(token::brokerOffers(
3028 issuer, offerBuyerToBroker, offerMinterToBroker),
3037 env(token::brokerOffers(
3038 broker, offerBuyerToBroker, offerMinterToBroker));
3049 testcase(
"Create offer destination disallow incoming");
3051 using namespace test::jtx;
3056 Account
const alice{
"alice"};
3057 env.fund(XRP(10000), alice);
3060 auto const sle = env.le(alice);
3061 uint32_t flags = sle->getFlags();
3067 Account
const issuer{
"issuer"};
3068 Account
const minter{
"minter"};
3069 Account
const buyer{
"buyer"};
3070 Account
const alice{
"alice"};
3072 env.fund(XRP(1000), issuer, minter, buyer, alice);
3074 env(token::setMinter(issuer, minter));
3079 env(token::mint(minter, 0),
3080 token::issuer(issuer),
3090 env(token::createOffer(minter, nftokenID, drops(1)),
3091 token::destination(buyer),
3109 env(token::createOffer(minter, nftokenID, drops(1)),
3110 token::destination(buyer),
3114 env(token::cancelOffer(minter, {offerIndex}));
3123 env(token::createOffer(minter, nftokenID, drops(1)),
3124 token::destination(buyer),
3131 env(token::cancelOffer(minter, {offerIndex}));
3143 env(token::createOffer(minter, nftokenID, drops(1)),
3144 token::destination(buyer),
3148 env(token::acceptSellOffer(buyer, offerIndex));
3160 env(token::createOffer(alice, nftokenID, drops(1)),
3161 token::owner(buyer),
3168 env(token::createOffer(minter, nftokenID, drops(1)),
3169 token::owner(buyer),
3179 testcase(
"Create offer expiration");
3181 using namespace test::jtx;
3183 Env env{*
this, features};
3185 Account
const issuer{
"issuer"};
3186 Account
const minter{
"minter"};
3187 Account
const buyer{
"buyer"};
3189 env.fund(XRP(1000), issuer, minter, buyer);
3193 env(token::setMinter(issuer, minter));
3198 env(token::mint(minter, 0),
3199 token::issuer(issuer),
3205 env(token::mint(minter, 0),
3206 token::issuer(issuer),
3215 uint256 const offerMinterToIssuer =
3217 env(token::createOffer(minter, nftokenID0, drops(1)),
3218 token::destination(issuer),
3219 token::expiration(expiration),
3222 uint256 const offerMinterToAnyone =
3224 env(token::createOffer(minter, nftokenID0, drops(1)),
3225 token::expiration(expiration),
3228 uint256 const offerIssuerToMinter =
3230 env(token::createOffer(issuer, nftokenID0, drops(1)),
3231 token::owner(minter),
3232 token::expiration(expiration));
3234 uint256 const offerBuyerToMinter =
3236 env(token::createOffer(buyer, nftokenID0, drops(1)),
3237 token::owner(minter),
3238 token::expiration(expiration));
3250 env(token::cancelOffer(issuer, {offerMinterToAnyone}),
3252 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
3255 BEAST_EXPECT(
lastClose(env) < expiration);
3261 env(token::cancelOffer(minter, {offerMinterToAnyone}));
3265 env(token::cancelOffer(issuer, {offerMinterToIssuer}));
3276 env(token::cancelOffer(issuer, {offerBuyerToMinter}));
3277 env(token::cancelOffer(buyer, {offerIssuerToMinter}));
3292 env(token::createOffer(minter, nftokenID0, drops(1)),
3293 token::expiration(expiration),
3298 env(token::createOffer(minter, nftokenID1, drops(1)),
3299 token::expiration(expiration),
3302 BEAST_EXPECT(
lastClose(env) < expiration);
3308 env(token::acceptSellOffer(buyer, offer0));
3319 env(token::acceptSellOffer(buyer, offer1), ter(
tecEXPIRED));
3320 env(token::acceptSellOffer(issuer, offer1), ter(
tecEXPIRED));
3329 env(token::cancelOffer(issuer, {offer1}));
3339 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3341 token::destination(minter));
3343 env(token::acceptSellOffer(minter, offerSellBack));
3357 env(token::createOffer(buyer, nftokenID0, drops(1)),
3358 token::owner(minter),
3359 token::expiration(expiration));
3362 env(token::createOffer(buyer, nftokenID1, drops(1)),
3363 token::owner(minter),
3364 token::expiration(expiration));
3366 BEAST_EXPECT(
lastClose(env) < expiration);
3372 env(token::acceptBuyOffer(minter, offer0));
3383 env(token::acceptBuyOffer(minter, offer1), ter(
tecEXPIRED));
3384 env(token::acceptBuyOffer(issuer, offer1), ter(
tecEXPIRED));
3393 env(token::cancelOffer(issuer, {offer1}));
3403 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3405 token::destination(minter));
3407 env(token::acceptSellOffer(minter, offerSellBack));
3422 env(token::createOffer(minter, nftokenID0, drops(1)),
3423 token::expiration(expiration),
3428 env(token::createOffer(minter, nftokenID1, drops(1)),
3429 token::expiration(expiration),
3434 env(token::createOffer(buyer, nftokenID0, drops(1)),
3435 token::owner(minter));
3439 env(token::createOffer(buyer, nftokenID1, drops(1)),
3440 token::owner(minter));
3443 BEAST_EXPECT(
lastClose(env) < expiration);
3449 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3460 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3470 env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3480 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3482 token::destination(minter));
3484 env(token::acceptSellOffer(minter, offerSellBack));
3499 env(token::createOffer(minter, nftokenID0, drops(1)),
3504 env(token::createOffer(minter, nftokenID1, drops(1)),
3509 env(token::createOffer(buyer, nftokenID0, drops(1)),
3510 token::expiration(expiration),
3511 token::owner(minter));
3515 env(token::createOffer(buyer, nftokenID1, drops(1)),
3516 token::expiration(expiration),
3517 token::owner(minter));
3520 BEAST_EXPECT(
lastClose(env) < expiration);
3526 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3537 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3547 env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3557 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3559 token::destination(minter));
3561 env(token::acceptSellOffer(minter, offerSellBack));
3577 env(token::createOffer(minter, nftokenID0, drops(1)),
3578 token::expiration(expiration),
3583 env(token::createOffer(minter, nftokenID1, drops(1)),
3584 token::expiration(expiration),
3589 env(token::createOffer(buyer, nftokenID0, drops(1)),
3590 token::expiration(expiration),
3591 token::owner(minter));
3595 env(token::createOffer(buyer, nftokenID1, drops(1)),
3596 token::expiration(expiration),
3597 token::owner(minter));
3600 BEAST_EXPECT(
lastClose(env) < expiration);
3606 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3617 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3627 env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3637 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3639 token::destination(minter));
3641 env(token::acceptSellOffer(minter, offerSellBack));
3653 testcase(
"Cancel offers");
3655 using namespace test::jtx;
3657 Env env{*
this, features};
3659 Account
const alice(
"alice");
3660 Account
const becky(
"becky");
3661 Account
const minter(
"minter");
3662 env.fund(XRP(50000), alice, becky, minter);
3666 env(token::setMinter(alice, minter));
3675 uint256 const expiredOfferIndex =
3678 env(token::createOffer(alice, nftokenID, XRP(1000)),
3680 token::expiration(
lastClose(env) + 13));
3685 env(token::cancelOffer(becky, {expiredOfferIndex}),
3693 env(token::cancelOffer(becky, {expiredOfferIndex}));
3699 uint256 const dest1OfferIndex =
3702 env(token::createOffer(alice, nftokenID, XRP(1000)),
3703 token::destination(becky),
3709 env(token::cancelOffer(minter, {dest1OfferIndex}),
3714 env(token::cancelOffer(becky, {dest1OfferIndex}));
3719 uint256 const dest2OfferIndex =
3722 env(token::createOffer(alice, nftokenID, XRP(1000)),
3723 token::destination(becky),
3728 env(token::cancelOffer(alice, {dest2OfferIndex}));
3735 uint256 const mintersNFTokenID =
3737 env(token::mint(minter, 0),
3738 token::issuer(alice),
3742 uint256 const minterOfferIndex =
3745 env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3751 env(token::cancelOffer(alice, {minterOfferIndex}),
3753 env(token::cancelOffer(becky, {minterOfferIndex}),
3758 env(token::cancelOffer(minter, {minterOfferIndex}));
3767 testcase(
"Cancel too many offers");
3769 using namespace test::jtx;
3771 Env env{*
this, features};
3786 Account
const alice(
"alice");
3787 env.fund(XRP(1000), alice);
3796 Account
const offerAcct(
3798 env.fund(XRP(1000), nftAcct, offerAcct);
3803 env(token::mint(nftAcct, 0),
3810 env(token::createOffer(offerAcct, nftokenID, drops(1)),
3811 token::owner(nftAcct),
3820 for (
uint256 const& offerIndex : offerIndexes)
3827 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3831 env(token::cancelOffer(alice, {offerIndexes.back()}));
3836 offerIndexes.pop_back();
3842 env(token::mint(alice, 0),
3848 env(token::createOffer(alice, nftokenID, drops(1)),
3857 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3861 env(token::burn(alice, nftokenID));
3867 offerIndexes.pop_back();
3872 env(token::cancelOffer(alice, offerIndexes));
3876 for (
uint256 const& offerIndex : offerIndexes)
3886 testcase(
"Brokered NFT offer accept");
3888 using namespace test::jtx;
3890 for (
auto const& tweakedFeatures :
3894 Env env{*
this, tweakedFeatures};
3902 Account
const issuer{
"issuer"};
3903 Account
const minter{
"minter"};
3904 Account
const buyer{
"buyer"};
3905 Account
const broker{
"broker"};
3906 Account
const gw{
"gw"};
3907 IOU
const gwXAU(gw[
"XAU"]);
3909 env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3912 env(trust(issuer, gwXAU(2000)));
3913 env(trust(minter, gwXAU(2000)));
3914 env(trust(buyer, gwXAU(2000)));
3915 env(trust(broker, gwXAU(2000)));
3918 env(token::setMinter(issuer, minter));
3922 auto checkOwnerCountIsOne =
3927 for (Account
const& acct : accounts)
3934 ss <<
"Account " << acct.human()
3935 <<
" expected ownerCount == 1. Got "
3937 fail(ss.
str(), __FILE__, line);
3943 auto mintNFT = [&env, &issuer, &minter](
std::uint16_t xferFee = 0) {
3946 env(token::mint(minter, 0),
3947 token::issuer(issuer),
3948 token::xferFee(xferFee),
3960 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3962 uint256 const nftID = mintNFT();
3965 uint256 const minterOfferIndex =
3967 env(token::createOffer(minter, nftID, XRP(0)),
3975 env(token::createOffer(buyer, nftID, XRP(1)),
3976 token::owner(minter));
3979 auto const minterBalance = env.balance(minter);
3980 auto const buyerBalance = env.balance(buyer);
3981 auto const brokerBalance = env.balance(broker);
3982 auto const issuerBalance = env.balance(issuer);
3985 env(token::brokerOffers(
3986 broker, buyOfferIndex, minterOfferIndex));
3991 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3992 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3993 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3994 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3997 env(token::burn(buyer, nftID));
4007 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4009 uint256 const nftID = mintNFT();
4012 uint256 const minterOfferIndex =
4014 env(token::createOffer(minter, nftID, XRP(0)),
4022 env(token::createOffer(buyer, nftID, XRP(1)),
4023 token::owner(minter));
4027 env(token::brokerOffers(
4028 broker, buyOfferIndex, minterOfferIndex),
4029 token::brokerFee(XRP(1.1)),
4033 auto const minterBalance = env.balance(minter);
4034 auto const buyerBalance = env.balance(buyer);
4035 auto const brokerBalance = env.balance(broker);
4036 auto const issuerBalance = env.balance(issuer);
4039 env(token::brokerOffers(
4040 broker, buyOfferIndex, minterOfferIndex),
4041 token::brokerFee(XRP(0.5)));
4046 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4047 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4049 env.balance(broker) ==
4050 brokerBalance + XRP(0.5) - drops(10));
4051 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
4054 env(token::burn(buyer, nftID));
4064 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4069 uint256 const minterOfferIndex =
4071 env(token::createOffer(minter, nftID, XRP(0)),
4079 env(token::createOffer(buyer, nftID, XRP(1)),
4080 token::owner(minter));
4083 auto const minterBalance = env.balance(minter);
4084 auto const buyerBalance = env.balance(buyer);
4085 auto const brokerBalance = env.balance(broker);
4086 auto const issuerBalance = env.balance(issuer);
4089 env(token::brokerOffers(
4090 broker, buyOfferIndex, minterOfferIndex));
4095 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4096 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4097 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
4098 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
4101 env(token::burn(buyer, nftID));
4111 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4116 uint256 const minterOfferIndex =
4118 env(token::createOffer(minter, nftID, XRP(0)),
4126 env(token::createOffer(buyer, nftID, XRP(1)),
4127 token::owner(minter));
4130 auto const minterBalance = env.balance(minter);
4131 auto const buyerBalance = env.balance(buyer);
4132 auto const brokerBalance = env.balance(broker);
4133 auto const issuerBalance = env.balance(issuer);
4136 env(token::brokerOffers(
4137 broker, buyOfferIndex, minterOfferIndex),
4138 token::brokerFee(XRP(0.75)));
4144 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
4145 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4147 env.balance(broker) ==
4148 brokerBalance + XRP(0.75) - drops(10));
4149 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
4152 env(token::burn(buyer, nftID));
4158 auto setXAUBalance =
4159 [
this, &gw, &gwXAU, &env](
4164 for (Account
const& acct : accounts)
4166 auto const xauAmt = gwXAU(amount);
4167 auto const balance = env.balance(acct, gwXAU);
4168 if (balance < xauAmt)
4170 env(pay(gw, acct, xauAmt - balance));
4173 else if (balance > xauAmt)
4175 env(pay(acct, gw, balance - xauAmt));
4178 if (env.balance(acct, gwXAU) != xauAmt)
4181 ss <<
"Unable to set " << acct.human()
4182 <<
" account balance to gwXAU(" << amount <<
")";
4183 this->fail(ss.
str(), __FILE__, line);
4191 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4192 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4194 uint256 const nftID = mintNFT();
4197 uint256 const minterOfferIndex =
4199 env(token::createOffer(minter, nftID, gwXAU(1000)),
4208 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4209 token::owner(minter));
4213 env(token::brokerOffers(
4214 broker, buyOfferIndex, minterOfferIndex),
4220 env(token::cancelOffer(buyer, {buyOfferIndex}));
4228 env(token::createOffer(buyer, nftID, gwXAU(999)),
4229 token::owner(minter));
4233 env(token::brokerOffers(
4234 broker, buyOfferIndex, minterOfferIndex),
4240 env(token::cancelOffer(buyer, {buyOfferIndex}));
4247 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4248 token::owner(minter));
4252 env(token::brokerOffers(
4253 broker, buyOfferIndex, minterOfferIndex),
4254 token::brokerFee(gwXAU(0.1)),
4259 env(token::brokerOffers(
4260 broker, buyOfferIndex, minterOfferIndex));
4267 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4268 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
4269 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4270 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
4273 env(token::burn(buyer, nftID));
4280 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4281 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4286 uint256 const minterOfferIndex =
4288 env(token::createOffer(minter, nftID, gwXAU(900)),
4296 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4297 token::owner(minter));
4301 env(token::brokerOffers(
4302 broker, buyOfferIndex, minterOfferIndex),
4308 env(token::cancelOffer(buyer, {buyOfferIndex}));
4316 env(token::createOffer(buyer, nftID, gwXAU(899)),
4317 token::owner(minter));
4321 env(token::brokerOffers(
4322 broker, buyOfferIndex, minterOfferIndex),
4328 env(token::cancelOffer(buyer, {buyOfferIndex}));
4334 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4335 token::owner(minter));
4340 env(token::brokerOffers(
4341 broker, buyOfferIndex, minterOfferIndex),
4342 token::brokerFee(gwXAU(101)),
4348 env(token::brokerOffers(
4349 broker, buyOfferIndex, minterOfferIndex),
4350 token::brokerFee(gwXAU(100)));
4357 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4358 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4359 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4360 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4363 env(token::burn(buyer, nftID));
4370 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4371 setXAUBalance({issuer, minter, buyer, broker}, 1000, __LINE__);
4376 uint256 const minterOfferIndex =
4378 env(token::createOffer(minter, nftID, gwXAU(900)),
4385 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4386 token::owner(minter));
4392 env(token::brokerOffers(
4393 broker, buyOfferIndex, minterOfferIndex),
4394 token::brokerFee(gwXAU(50)));
4401 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4402 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4403 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4404 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4407 env(token::burn(buyer, nftID));
4412 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4413 setXAUBalance({issuer, minter, buyer}, 1000, __LINE__);
4414 setXAUBalance({broker}, 500, __LINE__);
4418 uint256 const minterOfferIndex =
4420 env(token::createOffer(minter, nftID, gwXAU(900)),
4427 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4428 token::owner(minter));
4433 env(token::brokerOffers(
4434 broker, buyOfferIndex, minterOfferIndex),
4435 token::brokerFee(gwXAU(50)));
4441 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4442 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4443 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4444 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(550));
4447 env(token::burn(buyer, nftID));
4452 env(token::brokerOffers(
4453 broker, buyOfferIndex, minterOfferIndex),
4454 token::brokerFee(gwXAU(50)),
4461 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4462 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
4463 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000));
4464 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(500));
4467 env(token::burn(minter, nftID));
4478 testcase(
"NFToken offer owner");
4480 using namespace test::jtx;
4482 Env env{*
this, features};
4484 Account
const issuer{
"issuer"};
4485 Account
const buyer1{
"buyer1"};
4486 Account
const buyer2{
"buyer2"};
4487 env.fund(XRP(10000), issuer, buyer1, buyer2);
4496 BEAST_EXPECT(
nftCount(env, issuer) == 1);
4497 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4498 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4501 uint256 const buyer1OfferIndex =
4503 env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4504 uint256 const buyer2OfferIndex =
4506 env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4516 env.rpc(
"json",
"nft_buy_offers",
to_string(params));
4518 if (buyOffers.
isMember(jss::result) &&
4519 buyOffers[jss::result].
isMember(jss::offers))
4520 return buyOffers[jss::result][jss::offers].
size();
4526 BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4529 env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4533 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4534 BEAST_EXPECT(
nftCount(env, buyer1) == 1);
4535 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4539 BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4543 env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4547 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4548 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4549 BEAST_EXPECT(
nftCount(env, buyer2) == 1);
4552 BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4559 testcase(
"NFToken transactions with tickets");
4561 using namespace test::jtx;
4563 Env env{*
this, features};
4565 Account
const issuer{
"issuer"};
4566 Account
const buyer{
"buyer"};
4567 env.fund(XRP(10000), issuer, buyer);
4574 env(ticket::create(issuer, 10));
4580 env(ticket::create(buyer, 10));
4588 env(token::mint(issuer, 0u),
4590 ticket::use(issuerTicketSeq++));
4598 env(token::createOffer(buyer, nftId, XRP(1)),
4599 token::owner(issuer),
4600 ticket::use(buyerTicketSeq++));
4606 env(token::cancelOffer(buyer, {offerIndex0}),
4607 ticket::use(buyerTicketSeq++));
4614 env(token::createOffer(buyer, nftId, XRP(2)),
4615 token::owner(issuer),
4616 ticket::use(buyerTicketSeq++));
4622 env(token::acceptBuyOffer(issuer, offerIndex1),
4623 ticket::use(issuerTicketSeq++));
4630 env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4637 BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4638 BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4649 testcase(
"NFToken delete account");
4651 using namespace test::jtx;
4653 Env env{*
this, features};
4655 Account
const issuer{
"issuer"};
4656 Account
const minter{
"minter"};
4657 Account
const becky{
"becky"};
4658 Account
const carla{
"carla"};
4659 Account
const daria{
"daria"};
4661 env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4665 for (
int i = 0; i < 300; ++i)
4668 env(token::setMinter(issuer, minter));
4672 env(token::mint(minter, 0u),
4673 token::issuer(issuer),
4686 for (
int i = 0; i < 15; ++i)
4690 env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4693 uint256 const carlaOfferIndex =
4695 env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4700 env(acctdelete(becky, daria), fee(XRP(50)));
4704 env(token::acceptBuyOffer(minter, carlaOfferIndex));
4709 env(acctdelete(minter, daria), fee(XRP(50)));
4721 for (
int i = 0; i < 15; ++i)
4726 env(token::burn(carla, nftId));
4729 env(acctdelete(issuer, daria), fee(XRP(50)));
4730 env(acctdelete(carla, daria), fee(XRP(50)));
4737 testcase(
"nft_buy_offers and nft_sell_offers");
4746 using namespace test::jtx;
4748 Env env{*
this, features};
4750 Account
const issuer{
"issuer"};
4751 Account
const buyer{
"buyer"};
4754 env.fund(XRP(1000000), issuer, buyer);
4763 auto checkOffers = [
this, &env, &nftID](
4764 char const* request,
4766 int expectMarkerCount,
4768 int markerCount = 0;
4775 Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4779 if (!marker.
empty())
4780 params[jss::marker] = marker;
4781 return env.rpc(
"json", request,
to_string(params));
4785 if (expectCount == 0)
4789 "expected \"result\"",
4794 nftOffers[jss::result].isMember(jss::error),
4795 "expected \"error\"",
4800 nftOffers[jss::result][jss::error].asString() ==
4802 "expected \"objectNotFound\"",
4813 "expected \"result\"",
4822 marker = result[jss::marker].
asString();
4827 "expected \"offers\"",
4833 allOffers.
append(someOffers[i]);
4836 }
while (!marker.
empty());
4840 allOffers.
size() == expectCount,
4841 "Unexpected returned offer count",
4845 markerCount == expectMarkerCount,
4846 "Unexpected marker count",
4856 globalFlags = offer[jss::flags].asInt();
4859 *globalFlags == offer[jss::flags].asInt(),
4860 "Inconsistent flags returned",
4866 offerIndexes.
insert(offer[jss::nft_offer_index].asString());
4867 amounts.
insert(offer[jss::amount].asString());
4871 offerIndexes.
size() == expectCount,
4872 "Duplicate indexes returned?",
4876 amounts.
size() == expectCount,
4877 "Duplicate amounts returned?",
4883 checkOffers(
"nft_sell_offers", 0,
false, __LINE__);
4887 auto makeSellOffers =
4888 [&env, &issuer, &nftID, &sellPrice](
STAmount const& limit) {
4891 while (sellPrice < limit)
4893 sellPrice += XRP(1);
4894 env(token::createOffer(issuer, nftID, sellPrice),
4896 if (++offerCount % 10 == 0)
4903 makeSellOffers(XRP(1));
4904 checkOffers(
"nft_sell_offers", 1, 0, __LINE__);
4907 makeSellOffers(XRP(250));
4908 checkOffers(
"nft_sell_offers", 250, 0, __LINE__);
4911 makeSellOffers(XRP(251));
4912 checkOffers(
"nft_sell_offers", 251, 1, __LINE__);
4915 makeSellOffers(XRP(500));
4916 checkOffers(
"nft_sell_offers", 500, 1, __LINE__);
4919 makeSellOffers(XRP(501));
4920 checkOffers(
"nft_sell_offers", 501, 2, __LINE__);
4923 checkOffers(
"nft_buy_offers", 0, 0, __LINE__);
4927 auto makeBuyOffers =
4928 [&env, &buyer, &issuer, &nftID, &buyPrice](
STAmount const& limit) {
4931 while (buyPrice < limit)
4934 env(token::createOffer(buyer, nftID, buyPrice),
4935 token::owner(issuer));
4936 if (++offerCount % 10 == 0)
4943 makeBuyOffers(XRP(1));
4944 checkOffers(
"nft_buy_offers", 1, 0, __LINE__);
4947 makeBuyOffers(XRP(250));
4948 checkOffers(
"nft_buy_offers", 250, 0, __LINE__);
4951 makeBuyOffers(XRP(251));
4952 checkOffers(
"nft_buy_offers", 251, 1, __LINE__);
4955 makeBuyOffers(XRP(500));
4956 checkOffers(
"nft_buy_offers", 500, 1, __LINE__);
4959 makeBuyOffers(XRP(501));
4960 checkOffers(
"nft_buy_offers", 501, 2, __LINE__);
4967 using namespace test::jtx;
4969 testcase(
"fixNFTokenNegOffer");
4971 Account
const issuer{
"issuer"};
4972 Account
const buyer{
"buyer"};
4973 Account
const gw{
"gw"};
4974 IOU
const gwXAU(gw[
"XAU"]);
4980 for (
auto const& tweakedFeatures :
4989 Env env{*
this, tweakedFeatures};
4991 env.fund(XRP(1000000), issuer, buyer, gw);
4994 env(trust(issuer, gwXAU(2000)));
4995 env(trust(buyer, gwXAU(2000)));
4998 env(pay(gw, issuer, gwXAU(1000)));
4999 env(pay(gw, buyer, gwXAU(1000)));
5019 uint256 const sellNegXrpOfferIndex =
5021 env(token::createOffer(issuer, nftID0, XRP(-2)),
5023 ter(offerCreateTER));
5026 uint256 const sellNegIouOfferIndex =
5028 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
5030 ter(offerCreateTER));
5033 uint256 const buyNegXrpOfferIndex =
5035 env(token::createOffer(buyer, nftID0, XRP(-1)),
5036 token::owner(issuer),
5037 ter(offerCreateTER));
5040 uint256 const buyNegIouOfferIndex =
5042 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
5043 token::owner(issuer),
5044 ter(offerCreateTER));
5056 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
5057 ter(offerAcceptTER));
5059 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
5060 ter(offerAcceptTER));
5064 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
5065 ter(offerAcceptTER));
5067 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5068 ter(offerAcceptTER));
5081 env(token::brokerOffers(
5082 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5083 ter(offerAcceptTER));
5085 env(token::brokerOffers(
5086 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5087 ter(offerAcceptTER));
5099 env.fund(XRP(1000000), issuer, buyer, gw);
5102 env(trust(issuer, gwXAU(2000)));
5103 env(trust(buyer, gwXAU(2000)));
5106 env(pay(gw, issuer, gwXAU(1000)));
5107 env(pay(gw, buyer, gwXAU(1000)));
5123 uint256 const sellNegXrpOfferIndex =
5125 env(token::createOffer(issuer, nftID0, XRP(-2)),
5129 uint256 const sellNegIouOfferIndex =
5131 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
5135 uint256 const buyNegXrpOfferIndex =
5137 env(token::createOffer(buyer, nftID0, XRP(-1)),
5138 token::owner(issuer));
5141 uint256 const buyNegIouOfferIndex =
5143 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
5144 token::owner(issuer));
5153 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
5156 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
5161 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
5164 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5169 env(token::brokerOffers(
5170 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5173 env(token::brokerOffers(
5174 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5181 for (
auto const& tweakedFeatures :
5185 Env env{*
this, tweakedFeatures};
5187 env.fund(XRP(1000000), issuer, buyer);
5199 env(token::createOffer(buyer, nftID, drops(1)),
5200 token::owner(issuer),
5201 token::destination(issuer),
5202 ter(offerCreateTER));
5210 using namespace test::jtx;
5212 testcase(
"Payments with IOU transfer fees");
5214 for (
auto const& tweakedFeatures :
5218 Env env{*
this, tweakedFeatures};
5220 Account
const minter{
"minter"};
5221 Account
const secondarySeller{
"seller"};
5222 Account
const buyer{
"buyer"};
5223 Account
const gw{
"gateway"};
5224 Account
const broker{
"broker"};
5225 IOU
const gwXAU(gw[
"XAU"]);
5226 IOU
const gwXPB(gw[
"XPB"]);
5228 env.fund(XRP(1000), gw, minter, secondarySeller, buyer, broker);
5231 env(trust(minter, gwXAU(2000)));
5232 env(trust(secondarySeller, gwXAU(2000)));
5233 env(trust(broker, gwXAU(10000)));
5234 env(trust(buyer, gwXAU(2000)));
5235 env(trust(buyer, gwXPB(2000)));
5239 env(rate(gw, 1.02));
5242 auto expectInitialState = [
this,
5255 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(1000));
5256 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(0));
5257 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(0));
5258 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(0));
5259 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(0));
5260 BEAST_EXPECT(env.balance(secondarySeller, gwXPB) == gwXPB(0));
5261 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5000));
5262 BEAST_EXPECT(env.balance(broker, gwXPB) == gwXPB(0));
5263 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-1000));
5264 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(0));
5265 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(0));
5266 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(0));
5268 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(0));
5270 env.balance(gw, secondarySeller[
"XPB"]) == gwXPB(0));
5271 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5000));
5272 BEAST_EXPECT(env.balance(gw, broker[
"XPB"]) == gwXPB(0));
5275 auto reinitializeTrustLineBalances = [&expectInitialState,
5284 if (
auto const difference =
5285 gwXAU(1000) - env.balance(buyer, gwXAU);
5286 difference > gwXAU(0))
5287 env(pay(gw, buyer, difference));
5288 if (env.balance(buyer, gwXPB) > gwXPB(0))
5289 env(pay(buyer, gw, env.balance(buyer, gwXPB)));
5290 if (env.balance(minter, gwXAU) > gwXAU(0))
5291 env(pay(minter, gw, env.balance(minter, gwXAU)));
5292 if (env.balance(minter, gwXPB) > gwXPB(0))
5293 env(pay(minter, gw, env.balance(minter, gwXPB)));
5294 if (env.balance(secondarySeller, gwXAU) > gwXAU(0))
5296 pay(secondarySeller,
5298 env.balance(secondarySeller, gwXAU)));
5299 if (env.balance(secondarySeller, gwXPB) > gwXPB(0))
5301 pay(secondarySeller,
5303 env.balance(secondarySeller, gwXPB)));
5304 auto brokerDiff = gwXAU(5000) - env.balance(broker, gwXAU);
5305 if (brokerDiff > gwXAU(0))
5306 env(pay(gw, broker, brokerDiff));
5307 else if (brokerDiff < gwXAU(0))
5309 brokerDiff.negate();
5310 env(pay(broker, gw, brokerDiff));
5312 if (env.balance(broker, gwXPB) > gwXPB(0))
5313 env(pay(broker, gw, env.balance(broker, gwXPB)));
5315 expectInitialState();
5318 auto mintNFT = [&env](Account
const& minter,
int transferFee = 0) {
5319 uint256 const nftID = token::getNextID(
5321 env(token::mint(minter),
5322 token::xferFee(transferFee),
5328 auto createBuyOffer =
5330 Account
const& offerer,
5331 Account
const& owner,
5337 env(token::createOffer(offerer, nftID, amount),
5338 token::owner(owner),
5339 terCode ? ter(*terCode)
5345 auto createSellOffer =
5347 Account
const& offerer,
5353 env(token::createOffer(offerer, nftID, amount),
5355 terCode ? ter(*terCode)
5364 reinitializeTrustLineBalances();
5365 auto const nftID = mintNFT(minter);
5366 auto const offerID =
5367 createSellOffer(minter, nftID, gwXAU(1000));
5371 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5375 expectInitialState();
5378 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5379 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5381 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5382 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5388 reinitializeTrustLineBalances();
5389 auto const nftID = mintNFT(minter);
5390 auto const offerID =
5391 createBuyOffer(buyer, minter, nftID, gwXAU(1000));
5395 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5399 expectInitialState();
5402 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5403 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5405 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5406 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5413 reinitializeTrustLineBalances();
5414 auto const nftID = mintNFT(minter);
5415 auto const offerID = createSellOffer(minter, nftID, gwXAU(995));
5419 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5423 expectInitialState();
5426 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(995));
5427 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-14.9));
5428 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-995));
5429 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(14.9));
5436 reinitializeTrustLineBalances();
5437 auto const nftID = mintNFT(minter);
5438 auto const offerID =
5439 createBuyOffer(buyer, minter, nftID, gwXAU(995));
5443 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5447 expectInitialState();
5450 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(995));
5451 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-14.9));
5452 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-995));
5453 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(14.9));
5461 reinitializeTrustLineBalances();
5462 auto const nftID = mintNFT(minter);
5463 auto const offerID = createSellOffer(minter, nftID, gwXAU(900));
5464 env(token::acceptSellOffer(buyer, offerID));
5467 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
5468 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5469 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-900));
5470 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5477 reinitializeTrustLineBalances();
5478 auto const nftID = mintNFT(minter);
5479 auto const offerID =
5480 createBuyOffer(buyer, minter, nftID, gwXAU(900));
5481 env(token::acceptBuyOffer(minter, offerID));
5484 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
5485 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5486 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-900));
5487 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5494 reinitializeTrustLineBalances();
5497 env(pay(gw, buyer, gwXAU(20)));
5500 auto const nftID = mintNFT(minter);
5501 auto const offerID =
5502 createSellOffer(minter, nftID, gwXAU(1000));
5503 env(token::acceptSellOffer(buyer, offerID));
5506 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5507 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5508 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5509 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5516 reinitializeTrustLineBalances();
5519 env(pay(gw, buyer, gwXAU(20)));
5522 auto const nftID = mintNFT(minter);
5523 auto const offerID =
5524 createBuyOffer(buyer, minter, nftID, gwXAU(1000));
5525 env(token::acceptBuyOffer(minter, offerID));
5528 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5529 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5530 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5531 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5536 reinitializeTrustLineBalances();
5538 auto const nftID = mintNFT(minter);
5539 auto const offerID =
5540 createSellOffer(minter, nftID, gwXAU(1000));
5544 env(token::acceptSellOffer(gw, offerID), ter(sellTER));
5549 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5551 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5554 expectInitialState();
5559 reinitializeTrustLineBalances();
5561 auto const nftID = mintNFT(minter);
5565 auto const offerID =
5566 createBuyOffer(gw, minter, nftID, gwXAU(1000), {offerTER});
5570 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5575 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
5577 env.balance(gw, minter[
"XAU"]) == gwXAU(-1000));
5580 expectInitialState();
5585 reinitializeTrustLineBalances();
5586 auto const nftID = mintNFT(minter);
5587 auto const offerID =
5588 createSellOffer(minter, nftID, gwXAU(5000));
5592 env(token::acceptSellOffer(gw, offerID), ter(sellTER));
5597 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(5000));
5599 env.balance(gw, minter[
"XAU"]) == gwXAU(-5000));
5602 expectInitialState();
5607 reinitializeTrustLineBalances();
5609 auto const nftID = mintNFT(minter);
5613 auto const offerID =
5614 createBuyOffer(gw, minter, nftID, gwXAU(5000), {offerTER});
5618 env(token::acceptBuyOffer(minter, offerID), ter(sellTER));
5623 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(5000));
5625 env.balance(gw, minter[
"XAU"]) == gwXAU(-5000));
5628 expectInitialState();
5634 reinitializeTrustLineBalances();
5635 auto const nftID = mintNFT(gw);
5636 auto const offerID = createSellOffer(gw, nftID, gwXAU(1000));
5637 env(token::acceptSellOffer(buyer, offerID));
5640 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5641 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5647 reinitializeTrustLineBalances();
5649 auto const nftID = mintNFT(gw);
5650 auto const offerID =
5651 createBuyOffer(buyer, gw, nftID, gwXAU(1000));
5652 env(token::acceptBuyOffer(gw, offerID));
5655 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
5656 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(0));
5662 reinitializeTrustLineBalances();
5663 auto const nftID = mintNFT(gw);
5664 auto const offerID = createSellOffer(gw, nftID, gwXAU(2000));
5665 env(token::acceptSellOffer(buyer, offerID),
5668 expectInitialState();
5674 reinitializeTrustLineBalances();
5675 auto const nftID = mintNFT(gw);
5676 auto const offerID =
5677 createBuyOffer(buyer, gw, nftID, gwXAU(2000));
5678 env(token::acceptBuyOffer(gw, offerID),
5681 expectInitialState();
5686 reinitializeTrustLineBalances();
5687 auto const nftID = mintNFT(minter);
5688 auto const offerID = createSellOffer(minter, nftID, gwXPB(10));
5689 env(token::acceptSellOffer(buyer, offerID),
5692 expectInitialState();
5697 reinitializeTrustLineBalances();
5698 auto const nftID = mintNFT(minter);
5699 auto const offerID = createBuyOffer(
5705 env(token::acceptBuyOffer(minter, offerID),
5708 expectInitialState();
5714 reinitializeTrustLineBalances();
5715 env(pay(gw, buyer, gwXPB(100)));
5718 auto const nftID = mintNFT(minter);
5719 auto const offerID = createSellOffer(minter, nftID, gwXPB(10));
5720 env(token::acceptSellOffer(buyer, offerID));
5723 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(10));
5724 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(89.8));
5725 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(-10));
5726 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(-89.8));
5732 reinitializeTrustLineBalances();
5733 env(pay(gw, buyer, gwXPB(100)));
5736 auto const nftID = mintNFT(minter);
5737 auto const offerID =
5738 createBuyOffer(buyer, minter, nftID, gwXPB(10));
5739 env(token::acceptBuyOffer(minter, offerID));
5742 BEAST_EXPECT(env.balance(minter, gwXPB) == gwXPB(10));
5743 BEAST_EXPECT(env.balance(buyer, gwXPB) == gwXPB(89.8));
5744 BEAST_EXPECT(env.balance(gw, minter[
"XPB"]) == gwXPB(-10));
5745 BEAST_EXPECT(env.balance(gw, buyer[
"XPB"]) == gwXPB(-89.8));
5750 reinitializeTrustLineBalances();
5754 auto const nftID = mintNFT(minter, 3000);
5755 auto const primaryOfferID =
5756 createSellOffer(minter, nftID, XRP(0));
5757 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5761 auto const offerID =
5762 createSellOffer(secondarySeller, nftID, gwXAU(1000));
5766 env(token::acceptSellOffer(buyer, offerID), ter(sellTER));
5770 expectInitialState();
5773 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(30));
5775 env.balance(secondarySeller, gwXAU) == gwXAU(970));
5776 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5777 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-30));
5779 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-970));
5780 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5786 reinitializeTrustLineBalances();
5790 auto const nftID = mintNFT(minter, 3000);
5791 auto const primaryOfferID =
5792 createSellOffer(minter, nftID, XRP(0));
5793 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5797 auto const offerID =
5798 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(1000));
5802 env(token::acceptBuyOffer(secondarySeller, offerID),
5807 expectInitialState();
5810 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(30));
5812 env.balance(secondarySeller, gwXAU) == gwXAU(970));
5813 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(-20));
5814 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-30));
5816 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-970));
5817 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(20));
5823 reinitializeTrustLineBalances();
5827 auto const nftID = mintNFT(minter, 3000);
5828 auto const primaryOfferID =
5829 createSellOffer(minter, nftID, XRP(0));
5830 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5834 auto const offerID =
5835 createSellOffer(secondarySeller, nftID, gwXAU(900));
5836 env(token::acceptSellOffer(buyer, offerID));
5839 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(27));
5840 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(873));
5841 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5842 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-27));
5844 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-873));
5845 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5850 reinitializeTrustLineBalances();
5854 auto const nftID = mintNFT(minter, 3000);
5855 auto const primaryOfferID =
5856 createSellOffer(minter, nftID, XRP(0));
5857 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5861 auto const offerID =
5862 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(900));
5863 env(token::acceptBuyOffer(secondarySeller, offerID));
5867 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(27));
5869 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(873));
5871 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(82));
5872 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-27));
5874 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-873));
5875 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-82));
5893 reinitializeTrustLineBalances();
5895 auto const nftID = mintNFT(minter);
5896 auto const sellOffer =
5897 createSellOffer(minter, nftID, gwXAU(300));
5898 auto const buyOffer =
5899 createBuyOffer(buyer, minter, nftID, gwXAU(500));
5900 env(token::brokerOffers(broker, buyOffer, sellOffer),
5901 token::brokerFee(gwXAU(100)));
5904 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(400));
5905 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(490));
5906 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5100));
5907 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-400));
5908 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-490));
5909 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5100));
5927 reinitializeTrustLineBalances();
5931 auto const nftID = mintNFT(minter, 3000);
5932 auto const primaryOfferID =
5933 createSellOffer(minter, nftID, XRP(0));
5934 env(token::acceptSellOffer(secondarySeller, primaryOfferID));
5938 auto const sellOffer =
5939 createSellOffer(secondarySeller, nftID, gwXAU(300));
5940 auto const buyOffer =
5941 createBuyOffer(buyer, secondarySeller, nftID, gwXAU(500));
5942 env(token::brokerOffers(broker, buyOffer, sellOffer),
5943 token::brokerFee(gwXAU(100)));
5946 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(12));
5947 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(490));
5948 BEAST_EXPECT(env.balance(secondarySeller, gwXAU) == gwXAU(388));
5949 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(5100));
5950 BEAST_EXPECT(env.balance(gw, minter[
"XAU"]) == gwXAU(-12));
5951 BEAST_EXPECT(env.balance(gw, buyer[
"XAU"]) == gwXAU(-490));
5953 env.balance(gw, secondarySeller[
"XAU"]) == gwXAU(-388));
5954 BEAST_EXPECT(env.balance(gw, broker[
"XAU"]) == gwXAU(-5100));
5974 testcase(
"Brokered sale to self");
5976 using namespace test::jtx;
5978 Account
const alice{
"alice"};
5979 Account
const bob{
"bob"};
5980 Account
const broker{
"broker"};
5982 Env env{*
this, features};
5983 env.fund(XRP(10000), alice, bob, broker);
6009 uint256 const bobBuyOfferIndex =
6011 env(token::createOffer(bob, nftId, XRP(5)), token::owner(alice));
6013 uint256 const aliceSellOfferIndex =
6015 env(token::createOffer(alice, nftId, XRP(0)),
6016 token::destination(bob),
6021 env(token::acceptSellOffer(bob, aliceSellOfferIndex));
6028 uint256 const bobSellOfferIndex =
6030 env(token::createOffer(bob, nftId, XRP(4)), txflags(
tfSellNFToken));
6035 BEAST_EXPECT(
nftCount(env, bob) == 1);
6036 auto const bobsPriorBalance = env.balance(bob);
6037 auto const brokersPriorBalance = env.balance(broker);
6041 env(token::brokerOffers(broker, bobBuyOfferIndex, bobSellOfferIndex),
6042 token::brokerFee(XRP(1)),
6051 BEAST_EXPECT(
nftCount(env, bob) == 1);
6052 BEAST_EXPECT(env.balance(bob) == bobsPriorBalance - XRP(1));
6054 env.balance(broker) ==
6055 brokersPriorBalance + XRP(1) - drops(10));
6061 BEAST_EXPECT(
nftCount(env, bob) == 1);
6062 BEAST_EXPECT(env.balance(bob) == bobsPriorBalance);
6064 env.balance(broker) == brokersPriorBalance - drops(10));
6071 using namespace test::jtx;
6073 testcase(
"fixNFTokenRemint");
6076 auto openLedgerSeq = [](Env& env) {
return env.current()->seq(); };
6081 auto incLgrSeqForAcctDel = [&](Env& env, Account
const& acct) {
6082 int const delta = [&]() ->
int {
6083 if (env.seq(acct) + 255 > openLedgerSeq(env))
6084 return env.seq(acct) - openLedgerSeq(env) + 255;
6087 BEAST_EXPECT(delta >= 0);
6088 for (
int i = 0; i < delta; ++i)
6090 BEAST_EXPECT(openLedgerSeq(env) == env.seq(acct) + 255);
6096 auto incLgrSeqForFixNftRemint = [&](Env& env, Account
const& acct) {
6098 auto const deletableLgrSeq =
6102 if (deletableLgrSeq > openLedgerSeq(env))
6103 delta = deletableLgrSeq - openLedgerSeq(env);
6105 BEAST_EXPECT(delta >= 0);
6106 for (
int i = 0; i < delta; ++i)
6108 BEAST_EXPECT(openLedgerSeq(env) == deletableLgrSeq);
6114 Env env{*
this, features};
6115 Account
const alice(
"alice");
6116 Account
const becky(
"becky");
6118 env.fund(XRP(10000), alice, becky);
6122 uint256 const prevNFTokenID = token::getNextID(env, alice, 0u);
6123 env(token::mint(alice));
6125 env(token::burn(alice, prevNFTokenID));
6132 incLgrSeqForAcctDel(env, alice);
6136 auto const acctDelFee{drops(env.current()->fees().increment)};
6137 env(acctdelete(alice, becky), fee(acctDelFee));
6142 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6143 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6146 env.fund(XRP(10000), alice);
6150 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6151 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6155 uint256 const remintNFTokenID = token::getNextID(env, alice, 0u);
6156 env(token::mint(alice));
6160 env(token::burn(alice, remintNFTokenID));
6165 BEAST_EXPECT(remintNFTokenID != prevNFTokenID);
6168 BEAST_EXPECT(remintNFTokenID == prevNFTokenID);
6174 Env env{*
this, features};
6175 Account
const alice(
"alice");
6176 Account
const becky(
"becky");
6177 Account
const minter{
"minter"};
6179 env.fund(XRP(10000), alice, becky, minter);
6183 env(token::setMinter(alice, minter));
6189 for (
int i = 0; i < 500; i++)
6191 uint256 const nftokenID = token::getNextID(env, alice, 0u);
6193 env(token::mint(minter), token::issuer(alice));
6198 for (
auto const nftokenID : nftIDs)
6200 env(token::burn(minter, nftokenID));
6206 incLgrSeqForAcctDel(env, alice);
6210 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6211 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6213 auto const acctDelFee{drops(env.current()->fees().increment)};
6218 env(acctdelete(alice, becky), fee(acctDelFee));
6220 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6223 env.fund(XRP(10000), alice);
6227 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6228 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6233 uint256 const remintNFTokenID =
6234 token::getNextID(env, alice, 0u);
6235 env(token::mint(alice));
6239 env(token::burn(alice, remintNFTokenID));
6245 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) !=
6257 env(acctdelete(alice, becky),
6263 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6268 incLgrSeqForFixNftRemint(env, alice);
6271 env(acctdelete(alice, becky), fee(acctDelFee));
6276 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6277 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6280 env.fund(XRP(10000), alice);
6284 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6285 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6290 uint256 const remintNFTokenID =
6291 token::getNextID(env, alice, 0u);
6292 env(token::mint(alice));
6296 env(token::burn(alice, remintNFTokenID));
6302 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
6310 Env env{*
this, features};
6312 Account
const alice{
"alice"};
6313 Account
const becky{
"becky"};
6314 env.fund(XRP(10000), alice, becky);
6321 env(ticket::create(alice, 100));
6330 for (
int i = 0; i < 50; i++)
6332 nftIDs.
push_back(token::getNextID(env, alice, 0u));
6333 env(token::mint(alice, 0u), ticket::use(aliceTicketSeq++));
6338 for (
auto const nftokenID : nftIDs)
6340 env(token::burn(alice, nftokenID),
6341 ticket::use(aliceTicketSeq++));
6349 incLgrSeqForAcctDel(env, alice);
6353 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6354 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6356 auto const acctDelFee{drops(env.current()->fees().increment)};
6361 env(acctdelete(alice, becky), fee(acctDelFee));
6366 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6367 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6370 env.fund(XRP(10000), alice);
6374 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6375 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6380 uint256 const remintNFTokenID =
6381 token::getNextID(env, alice, 0u);
6382 env(token::mint(alice));
6386 env(token::burn(alice, remintNFTokenID));
6392 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) !=
6404 env(acctdelete(alice, becky),
6410 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6415 incLgrSeqForFixNftRemint(env, alice);
6418 env(acctdelete(alice, becky), fee(acctDelFee));
6423 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6424 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6427 env.fund(XRP(10000), alice);
6431 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6432 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6437 uint256 const remintNFTokenID =
6438 token::getNextID(env, alice, 0u);
6439 env(token::mint(alice));
6443 env(token::burn(alice, remintNFTokenID));
6449 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
6461 Env env{*
this, features};
6462 Account
const alice(
"alice");
6463 Account
const becky(
"becky");
6464 Account
const minter{
"minter"};
6466 env.fund(XRP(10000), alice, becky, minter);
6470 env(token::setMinter(alice, minter));
6475 env(ticket::create(minter, 100));
6479 BEAST_EXPECT(
ownerCount(env, minter) == 100);
6484 for (
int i = 0; i < 50; i++)
6486 uint256 const nftokenID = token::getNextID(env, alice, 0u);
6488 env(token::mint(minter),
6489 token::issuer(alice),
6490 ticket::use(minterTicketSeq++));
6495 for (
auto const nftokenID : nftIDs)
6497 env(token::burn(minter, nftokenID),
6498 ticket::use(minterTicketSeq++));
6506 incLgrSeqForAcctDel(env, alice);
6510 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6511 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6520 auto const acctDelFee{drops(env.current()->fees().increment)};
6521 env(acctdelete(alice, becky), fee(acctDelFee), ter(
tecTOO_SOON));
6525 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6530 incLgrSeqForFixNftRemint(env, alice);
6533 env(acctdelete(alice, becky), fee(acctDelFee));
6538 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
6539 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
6542 env.fund(XRP(10000), alice);
6546 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
6547 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
6552 uint256 const remintNFTokenID = token::getNextID(env, alice, 0u);
6553 env(token::mint(alice));
6557 env(token::burn(alice, remintNFTokenID));
6563 std::find(nftIDs.begin(), nftIDs.end(), remintNFTokenID) ==
6606 using namespace test::jtx;