20 #include <ripple/app/tx/impl/details/NFTokenUtils.h>
21 #include <ripple/basics/random.h>
22 #include <ripple/protocol/Feature.h>
23 #include <ripple/protocol/jss.h>
39 if (
auto const sleAcct = env.
le(acct))
49 if (
auto const sleIssuer = env.
le(issuer))
59 if (
auto const sleIssuer = env.
le(issuer))
69 params[jss::account] = acct.
human();
70 params[jss::type] =
"state";
72 return nfts[jss::result][jss::account_nfts].
size();
80 if (
auto const sleAcct = env.
le(acct))
89 return env.
current()->info().parentCloseTime.time_since_epoch().count();
97 using namespace test::jtx;
105 Account
const& master = env.master;
111 uint256 const nftId{token::getNextID(env, master, 0u)};
126 env(token::createOffer(master, nftId, XRP(10)), ter(
temDISABLED));
132 env(token::cancelOffer(master, {offerIndex}), ter(
temDISABLED));
138 env(token::acceptBuyOffer(master, offerIndex), ter(
temDISABLED));
147 Env env{*
this, features};
148 Account
const& master = env.master;
154 uint256 const nftId0{token::getNextID(env, env.master, 0u)};
155 env(token::mint(env.master, 0u));
161 env(token::burn(env.master, nftId0));
175 Account
const alice{
"alice"};
176 env.fund(XRP(10000), alice);
178 uint256 const aliceOfferIndex =
180 env(token::createOffer(alice, nftId1, XRP(1000)),
181 token::owner(master));
192 env(token::acceptBuyOffer(master, aliceOfferIndex));
209 testcase(
"Mint reserve");
211 using namespace test::jtx;
213 Env env{*
this, features};
214 Account
const alice{
"alice"};
215 Account
const minter{
"minter"};
220 env.fund(XRP(200), alice, minter);
222 BEAST_EXPECT(env.balance(alice) == XRP(200));
223 BEAST_EXPECT(env.balance(minter) == XRP(200));
235 env(pay(env.master, alice, XRP(50) + drops(9)));
240 auto checkAliceOwnerMintedBurned = [&env,
this, &alice](
253 ss <<
"Wrong " << type <<
" count. Found: " << found
254 <<
"; Expected: " << exp;
255 fail(ss.
str(), __FILE__, line);
258 oneCheck(
"owner",
ownerCount(env, alice), owners);
259 oneCheck(
"minted",
mintedCount(env, alice), minted);
260 oneCheck(
"burned",
burnedCount(env, alice), burned);
266 checkAliceOwnerMintedBurned(0, 0, 0, __LINE__);
269 env(pay(env.master, alice, drops(11)));
273 env(token::mint(alice));
275 checkAliceOwnerMintedBurned(1, 1, 0, __LINE__);
279 for (
int i = 1; i < 32; ++i)
281 env(token::mint(alice));
282 checkAliceOwnerMintedBurned(1, i + 1, 0, __LINE__);
289 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
292 env(pay(env.master, alice, XRP(50) + drops(329)));
298 checkAliceOwnerMintedBurned(1, 32, 0, __LINE__);
301 env(pay(env.master, alice, drops(11)));
305 env(token::mint(alice));
307 checkAliceOwnerMintedBurned(2, 33, 0, __LINE__);
314 env(token::burn(alice, token::getID(alice, 0, seq++)));
316 checkAliceOwnerMintedBurned((33 - seq) ? 1 : 0, 33, seq, __LINE__);
320 env(token::burn(alice, token::getID(alice, 197, 5)), ter(
tecNO_ENTRY));
322 checkAliceOwnerMintedBurned(0, 33, 33, __LINE__);
326 env(token::setMinter(alice, minter));
333 auto checkMintersOwnerMintedBurned = [&env,
this, &alice, &minter](
341 auto oneCheck = [
this](
351 ss <<
"Wrong " << type <<
" count. Found: " << found
352 <<
"; Expected: " << exp;
353 fail(ss.
str(), __FILE__, line);
356 oneCheck(
"alice owner",
ownerCount(env, alice), aliceOwners, line);
358 "alice minted",
mintedCount(env, alice), aliceMinted, line);
360 "alice burned",
burnedCount(env, alice), aliceBurned, line);
362 "minter owner",
ownerCount(env, minter), minterOwners, line);
364 "minter minted",
mintedCount(env, minter), minterMinted, line);
366 "minter burned",
burnedCount(env, minter), minterBurned, line);
372 env(pay(env.master, minter, XRP(50) - drops(1)));
374 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
379 env(token::mint(minter),
380 token::issuer(alice),
384 checkMintersOwnerMintedBurned(0, 33, nftSeq, 0, 0, 0, __LINE__);
387 env(pay(env.master, minter, drops(11)));
391 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
393 checkMintersOwnerMintedBurned(0, 34, nftSeq, 1, 0, 0, __LINE__);
397 for (
int i = 1; i < 32; ++i)
399 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
400 checkMintersOwnerMintedBurned(0, i + 34, nftSeq, 1, 0, 0, __LINE__);
404 env(pay(env.master, minter, XRP(50) + drops(319)));
409 env(token::mint(minter),
410 token::issuer(alice),
414 checkMintersOwnerMintedBurned(0, 65, nftSeq, 1, 0, 0, __LINE__);
417 env(pay(env.master, minter, drops(11)));
421 env(token::mint(minter), token::issuer(alice), token::uri(
"uri"));
423 checkMintersOwnerMintedBurned(0, 66, nftSeq, 2, 0, 0, __LINE__);
428 env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
430 checkMintersOwnerMintedBurned(
431 0, 66, nftSeq, (65 - seq) ? 1 : 0, 0, 0, __LINE__);
435 env(token::burn(minter, token::getID(alice, 0, nftSeq++)));
437 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
440 env(token::burn(minter, token::getID(alice, 2009, 3)),
443 checkMintersOwnerMintedBurned(0, 66, nftSeq, 0, 0, 0, __LINE__);
451 testcase(
"Mint max tokens");
453 using namespace test::jtx;
455 Account
const alice{
"alice"};
456 Env env{*
this, features};
457 env.fund(XRP(1000), alice);
466 uint256 const nftId0{token::getNextID(env, alice, 0u)};
467 env(token::mint(alice, 0u));
470 env(token::burn(alice, nftId0));
477 env.app().openLedger().modify(
486 auto replacement = std::make_shared<SLE>(*sle, sle->key());
492 view.rawReplace(replacement);
496 // See whether alice is at the boundary that causes an error.
497 env(token::mint(alice, 0u), ter(tesSUCCESS));
498 env(token::mint(alice, 0u), ter(tecMAX_SEQUENCE_REACHED));
502 testMintInvalid(FeatureBitset features)
504 // Explore many of the invalid ways to mint an NFT.
505 testcase("Mint invalid");
507 using namespace test::jtx;
509 Env env{*this, features};
510 Account const alice{"alice"};
511 Account const minter{"minter"};
513 // Fund alice and minter enough to exist, but not enough to meet
514 // the reserve for creating their first NFT. Account reserve for unit
515 // tests is 200 XRP, not 20.
516 env.fund(XRP(200), alice, minter);
519 env(token::mint(alice, 0u), ter(tecINSUFFICIENT_RESERVE));
522 // Fund alice enough to start minting NFTs.
523 env(pay(env.master, alice, XRP(1000)));
526 //----------------------------------------------------------------------
529 // Set a negative fee.
530 env(token::mint(alice, 0u),
531 fee(STAmount(10ull, true)),
534 // Set an invalid flag.
535 env(token::mint(alice, 0u), txflags(0x00008000), ter(temINVALID_FLAG));
537 // Can't
set a transfer fee
if the NFT does not have the tfTRANSFERABLE
539 env(token::mint(alice, 0u),
544 env(token::mint(alice, 0u),
550 env(token::mint(alice, 0u), token::issuer(alice), ter(
temMALFORMED));
553 env(token::mint(alice, 0u), token::uri(
""), ter(
temMALFORMED));
556 env(token::mint(alice, 0u),
564 env(token::mint(alice, 0u),
565 token::issuer(Account(
"demon")),
572 env(token::mint(minter, 0u),
573 token::issuer(alice),
581 testcase(
"Burn invalid");
583 using namespace test::jtx;
585 Env env{*
this, features};
586 Account
const alice{
"alice"};
587 Account
const buyer{
"buyer"};
588 Account
const minter{
"minter"};
589 Account
const gw(
"gw");
590 IOU
const gwAUD(gw[
"AUD"]);
595 env.fund(XRP(250), alice, buyer, minter, gw);
609 env(token::burn(alice, nftAlice0ID),
616 env(token::burn(alice, nftAlice0ID),
626 env(token::burn(alice, token::getID(alice, 0, 1)), ter(
tecNO_ENTRY));
640 testcase(
"Invalid NFT offer create");
642 using namespace test::jtx;
644 Env env{*
this, features};
645 Account
const alice{
"alice"};
646 Account
const buyer{
"buyer"};
647 Account
const gw(
"gw");
648 IOU
const gwAUD(gw[
"AUD"]);
653 env.fund(XRP(250), alice, buyer, gw);
659 env(token::mint(alice, 0u),
671 uint256 nftNoXferID = token::getNextID(env, alice, 0);
672 env(token::mint(alice, 0));
685 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
692 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
699 env(token::createOffer(buyer, nftAlice0ID, XRP(1000)),
706 env(token::createOffer(buyer, nftXrpOnlyID, buyer[
"USD"](1)),
708 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](0)),
710 env(token::createOffer(buyer, nftXrpOnlyID, drops(0)),
716 env(token::createOffer(buyer, nftAlice0ID, buyer[
"USD"](1)),
717 token::expiration(0),
724 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
730 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
738 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
745 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
746 token::destination(alice),
753 env(token::createOffer(alice, nftXrpOnlyID, XRP(1000)),
754 token::destination(Account(
"demon")),
764 env(token::createOffer(buyer, nftXrpOnlyID, XRP(1000)),
772 env(token::createOffer(buyer, token::getID(alice, 0, 1), XRP(1000)),
779 env(token::createOffer(alice, token::getID(alice, 0, 1), XRP(1000)),
786 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
792 env(trust(buyer, gwAUD(1000)));
798 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
810 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
821 env(token::createOffer(buyer, nftNoXferID, gwAUD(1000)),
831 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
840 env(trust(buyer, gwAUD(1000)));
843 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
853 env(pay(gw, buyer, gwAUD(999)));
858 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
865 env(pay(env.master, buyer, XRP(50) + drops(119)));
868 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
875 env(pay(env.master, buyer, drops(11)));
880 env(token::createOffer(buyer, nftAlice0ID, gwAUD(1000)),
890 testcase(
"Invalid NFT offer cancel");
892 using namespace test::jtx;
894 Env env{*
this, features};
895 Account
const alice{
"alice"};
896 Account
const buyer{
"buyer"};
897 Account
const gw(
"gw");
898 IOU
const gwAUD(gw[
"AUD"]);
900 env.fund(XRP(1000), alice, buyer, gw);
911 uint256 const buyerOfferIndex =
913 env(token::createOffer(buyer, nftAlice0ID, XRP(1)),
923 env(token::cancelOffer(buyer, {buyerOfferIndex}),
930 env(token::cancelOffer(buyer, {buyerOfferIndex}),
950 env(token::cancelOffer(buyer, offers), ter(
temMALFORMED));
956 env(token::cancelOffer(buyer, {buyerOfferIndex, buyerOfferIndex}),
970 env(pay(env.master, gw, XRP(5000)));
974 env(offer(gw, XRP(i), gwAUD(1)));
981 env(check::create(gw, env.master, XRP(300)));
988 env(check::cancel(gw, gwCheckId));
1002 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1010 testcase(
"Invalid NFT offer accept");
1012 using namespace test::jtx;
1014 Env env{*
this, features};
1015 Account
const alice{
"alice"};
1016 Account
const buyer{
"buyer"};
1017 Account
const gw(
"gw");
1018 IOU
const gwAUD(gw[
"AUD"]);
1020 env.fund(XRP(1000), alice, buyer, gw);
1036 uint256 nftNoXferID = token::getNextID(env, alice, 0);
1037 env(token::mint(alice, 0));
1042 uint256 const plainOfferIndex =
1044 env(token::createOffer(alice, nftAlice0ID, XRP(10)),
1051 env(token::createOffer(alice, nftAlice0ID, gwAUD(30)),
1056 uint256 const xrpOnlyOfferIndex =
1058 env(token::createOffer(alice, nftXrpOnlyID, XRP(20)),
1063 uint256 const noXferOfferIndex =
1065 env(token::createOffer(alice, nftNoXferID, XRP(30)),
1071 uint256 const aliceExpOfferIndex =
1073 env(token::createOffer(alice, nftNoXferID, XRP(40)),
1083 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1090 env(token::acceptSellOffer(buyer, noXferOfferIndex),
1091 txflags(0x00008000),
1098 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1107 Json::Value jv = token::acceptBuyOffer(buyer, noXferOfferIndex);
1117 Json::Value jv = token::acceptSellOffer(buyer, noXferOfferIndex);
1126 env(token::brokerOffers(buyer, noXferOfferIndex, xrpOnlyOfferIndex),
1127 token::brokerFee(gwAUD(0)),
1136 env(token::acceptBuyOffer(buyer, beast::zero),
1143 env(token::acceptBuyOffer(buyer, missingOfferIndex),
1149 env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1154 env(token::acceptSellOffer(buyer, beast::zero),
1160 env(token::acceptSellOffer(buyer, missingOfferIndex),
1166 env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(
tecEXPIRED));
1175 env(trust(alice, gwAUD(1000)));
1176 env(trust(buyer, gwAUD(1000)));
1178 env(pay(gw, buyer, gwAUD(30)));
1187 uint256 const buyerOfferIndex =
1189 env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)),
1190 token::owner(alice));
1195 env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex),
1201 env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex),
1208 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1214 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1220 uint256 const buyerOfferIndex =
1222 env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)),
1223 token::owner(alice));
1229 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1230 token::brokerFee(XRP(40)),
1236 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1237 token::brokerFee(gwAUD(31)),
1244 env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
1245 token::brokerFee(gwAUD(1.5)),
1251 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1259 uint256 const buyerOfferIndex =
1261 env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)),
1262 token::owner(alice));
1267 env(token::acceptBuyOffer(buyer, plainOfferIndex),
1273 env(token::acceptBuyOffer(buyer, buyerOfferIndex),
1279 env(pay(buyer, gw, gwAUD(30)));
1281 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1282 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1291 env(token::createOffer(alice, nftAlice0ID, XRP(0)),
1294 env(token::acceptSellOffer(gw, offerIndex));
1298 env(pay(gw, buyer, gwAUD(30)));
1302 env(token::acceptBuyOffer(alice, buyerOfferIndex),
1308 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1316 uint256 const buyerOfferIndex =
1318 env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)),
1319 token::owner(alice));
1324 env(token::acceptSellOffer(alice, buyerOfferIndex),
1330 env(token::acceptSellOffer(alice, plainOfferIndex),
1337 env(token::acceptSellOffer(buyer, plainOfferIndex),
1348 env(token::createOffer(gw, nftAlice0ID, XRP(0)),
1351 env(token::acceptSellOffer(alice, offerIndex));
1355 env(pay(buyer, gw, gwAUD(30)));
1357 BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
1358 env(token::acceptSellOffer(buyer, audOfferIndex),
1375 testcase(
"Mint flagBurnable");
1377 using namespace test::jtx;
1379 Env env{*
this, features};
1380 Account
const alice{
"alice"};
1381 Account
const buyer{
"buyer"};
1382 Account
const minter1{
"minter1"};
1383 Account
const minter2{
"minter2"};
1385 env.fund(XRP(1000), alice, buyer, minter1, minter2);
1390 env(token::setMinter(alice, minter1));
1397 auto nftToBuyer = [&env, &alice, &minter1, &buyer](
1399 uint256 const nftID{token::getNextID(env, alice, 0u, flags)};
1400 env(token::mint(minter1, 0u), token::issuer(alice), txflags(flags));
1405 env(token::createOffer(minter1, nftID, XRP(0)),
1409 env(token::acceptSellOffer(buyer, offerIndex));
1417 uint256 const noBurnID = nftToBuyer(0);
1418 env(token::burn(alice, noBurnID),
1419 token::owner(buyer),
1422 env(token::burn(minter1, noBurnID),
1423 token::owner(buyer),
1426 env(token::burn(minter2, noBurnID),
1427 token::owner(buyer),
1432 env(token::burn(buyer, noBurnID), token::owner(buyer));
1439 env(token::burn(minter2, burnableID),
1440 token::owner(buyer),
1445 env(token::burn(alice, burnableID), token::owner(buyer));
1453 env(token::burn(buyer, burnableID));
1461 env(token::burn(buyer, burnableID), token::owner(buyer));
1471 env(token::setMinter(alice, minter2));
1476 env(token::burn(minter1, burnableID),
1477 token::owner(buyer),
1483 env(token::burn(minter2, burnableID), token::owner(buyer));
1493 testcase(
"Mint flagOnlyXRP");
1495 using namespace test::jtx;
1497 Env env{*
this, features};
1498 Account
const alice{
"alice"};
1499 Account
const buyer{
"buyer"};
1500 Account
const gw(
"gw");
1501 IOU
const gwAUD(gw[
"AUD"]);
1504 env.fund(XRP(1000), alice, buyer, gw);
1506 env(trust(alice, gwAUD(1000)));
1507 env(trust(buyer, gwAUD(1000)));
1509 env(pay(gw, buyer, gwAUD(100)));
1519 uint256 const aliceOfferIndex =
1521 env(token::createOffer(alice, nftIOUsOkayID, gwAUD(50)),
1527 uint256 const buyerOfferIndex =
1529 env(token::createOffer(buyer, nftIOUsOkayID, gwAUD(50)),
1530 token::owner(alice));
1535 env(token::cancelOffer(alice, {aliceOfferIndex}));
1536 env(token::cancelOffer(buyer, {buyerOfferIndex}));
1542 env(token::burn(alice, nftIOUsOkayID));
1555 env(token::createOffer(alice, nftOnlyXRPID, gwAUD(50)),
1562 env(token::createOffer(buyer, nftOnlyXRPID, gwAUD(50)),
1563 token::owner(alice),
1570 env(token::createOffer(alice, nftOnlyXRPID, XRP(60)),
1576 env(token::createOffer(buyer, nftOnlyXRPID, XRP(60)),
1577 token::owner(alice));
1587 testcase(
"Mint flagCreateTrustLines");
1589 using namespace test::jtx;
1591 Account
const alice{
"alice"};
1592 Account
const becky{
"becky"};
1593 Account
const cheri{
"cheri"};
1594 Account
const gw(
"gw");
1595 IOU
const gwAUD(gw[
"AUD"]);
1596 IOU
const gwCAD(gw[
"CAD"]);
1597 IOU
const gwEUR(gw[
"EUR"]);
1602 for (
auto const& tweakedFeatures :
1606 Env env{*
this, tweakedFeatures};
1607 env.fund(XRP(1000), alice, becky, cheri, gw);
1611 env(trust(becky, gwAUD(1000)));
1612 env(trust(cheri, gwAUD(1000)));
1613 env(trust(becky, gwCAD(1000)));
1614 env(trust(cheri, gwCAD(1000)));
1615 env(trust(becky, gwEUR(1000)));
1616 env(trust(cheri, gwEUR(1000)));
1618 env(pay(gw, becky, gwAUD(500)));
1619 env(pay(gw, becky, gwCAD(500)));
1620 env(pay(gw, becky, gwEUR(500)));
1621 env(pay(gw, cheri, gwAUD(500)));
1622 env(pay(gw, cheri, gwCAD(500)));
1629 uint256 const nftNoAutoTrustID{
1631 env(token::mint(alice, 0u),
1632 token::xferFee(xferFee),
1637 uint256 const beckyBuyOfferIndex =
1639 env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
1640 token::owner(alice));
1642 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1646 TER const createOfferTER =
1648 uint256 const beckyOfferIndex =
1650 env(token::createOffer(becky, nftNoAutoTrustID, gwAUD(100)),
1652 ter(createOfferTER));
1656 uint256 const cheriOfferIndex =
1658 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1659 token::owner(becky),
1660 ter(createOfferTER));
1664 env(token::cancelOffer(becky, {beckyOfferIndex}));
1665 env(token::cancelOffer(cheri, {cheriOfferIndex}));
1673 uint256 const nftAutoTrustID{token::getNextID(
1684 env(token::mint(alice, 0u),
1685 token::xferFee(transferFee),
1696 uint256 const beckyBuyOfferIndex =
1698 env(token::createOffer(becky, nftAutoTrustID, drops(1)),
1699 token::owner(alice));
1701 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
1705 uint256 const beckySellOfferIndex =
1707 env(token::createOffer(becky, nftAutoTrustID, gwAUD(100)),
1710 env(token::acceptSellOffer(cheri, beckySellOfferIndex));
1714 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1717 uint256 const beckyBuyBackOfferIndex =
1719 env(token::createOffer(becky, nftAutoTrustID, gwCAD(50)),
1720 token::owner(cheri));
1722 env(token::acceptBuyOffer(cheri, beckyBuyBackOfferIndex));
1726 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(10));
1727 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(5));
1733 uint256 const nftNoAutoTrustID{token::getNextID(
1735 env(token::mint(alice, 0u),
1736 token::xferFee(transferFee),
1741 uint256 const aliceSellOfferIndex =
1743 env(token::createOffer(alice, nftNoAutoTrustID, gwAUD(200)),
1746 env(token::acceptSellOffer(cheri, aliceSellOfferIndex));
1752 BEAST_EXPECT(env.balance(alice, gwAUD) == gwAUD(210));
1755 env(token::createOffer(cheri, nftNoAutoTrustID, gwEUR(50)),
1759 uint256 const cheriSellOfferIndex =
1761 env(token::createOffer(cheri, nftNoAutoTrustID, gwCAD(100)),
1764 env(token::acceptSellOffer(becky, cheriSellOfferIndex));
1770 BEAST_EXPECT(env.balance(alice, gwCAD) == gwCAD(10));
1779 testcase(
"Mint flagTransferable");
1781 using namespace test::jtx;
1783 Env env{*
this, features};
1785 Account
const alice{
"alice"};
1786 Account
const becky{
"becky"};
1787 Account
const minter{
"minter"};
1789 env.fund(XRP(1000), alice, becky, minter);
1795 uint256 const nftAliceNoTransferID{
1796 token::getNextID(env, alice, 0u)};
1797 env(token::mint(alice, 0u), token::xferFee(0));
1803 env(token::createOffer(becky, nftAliceNoTransferID, XRP(20)),
1804 token::owner(alice),
1808 uint256 const aliceSellOfferIndex =
1810 env(token::createOffer(alice, nftAliceNoTransferID, XRP(20)),
1813 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
1819 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1828 env(token::createOffer(becky, nftAliceNoTransferID, XRP(21)),
1830 token::destination(alice),
1838 uint256 const aliceBuyOfferIndex =
1840 env(token::createOffer(alice, nftAliceNoTransferID, XRP(22)),
1841 token::owner(becky));
1843 env(token::acceptBuyOffer(becky, aliceBuyOfferIndex));
1849 env(token::burn(alice, nftAliceNoTransferID));
1856 env(token::setMinter(alice, minter));
1860 uint256 const nftMinterNoTransferID{
1861 token::getNextID(env, alice, 0u)};
1862 env(token::mint(minter), token::issuer(alice));
1868 env(token::createOffer(becky, nftMinterNoTransferID, XRP(20)),
1869 token::owner(minter),
1875 env(token::clearMinter(alice));
1880 env(token::createOffer(minter, nftMinterNoTransferID, XRP(21)),
1888 for (
int i = 0; i < 10; ++i)
1891 env(token::setMinter(alice, minter));
1897 uint256 const minterSellOfferIndex =
1899 env(token::createOffer(minter, nftMinterNoTransferID, XRP(22)),
1906 env(token::clearMinter(alice));
1912 env(token::acceptSellOffer(becky, minterSellOfferIndex));
1918 env(token::createOffer(becky, nftMinterNoTransferID, XRP(23)),
1926 env(token::createOffer(minter, nftMinterNoTransferID, XRP(24)),
1927 token::owner(becky),
1934 uint256 const aliceBuyOfferIndex =
1936 env(token::createOffer(alice, nftMinterNoTransferID, XRP(25)),
1937 token::owner(becky));
1943 for (
int i = 0; i < 10; ++i)
1946 env(token::setMinter(alice, minter));
1951 uint256 const minterBuyOfferIndex =
1953 env(token::createOffer(minter, nftMinterNoTransferID, XRP(26)),
1954 token::owner(becky));
1960 env(token::clearMinter(alice));
1966 env(token::acceptBuyOffer(becky, minterBuyOfferIndex));
1974 env(token::burn(minter, nftMinterNoTransferID), ter(
tesSUCCESS));
1976 env(token::cancelOffer(alice, {aliceBuyOfferIndex}));
1993 uint256 const aliceSellOfferIndex =
1995 env(token::createOffer(alice, nftAliceID, XRP(20)),
2000 uint256 const beckyBuyOfferIndex =
2002 env(token::createOffer(becky, nftAliceID, XRP(21)),
2003 token::owner(alice));
2008 env(token::acceptSellOffer(becky, aliceSellOfferIndex));
2014 uint256 const beckySellOfferIndex =
2016 env(token::createOffer(becky, nftAliceID, XRP(22)),
2024 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2031 uint256 const minterSellOfferIndex =
2033 env(token::createOffer(minter, nftAliceID, XRP(23)),
2041 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2049 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2057 env(token::burn(becky, nftAliceID));
2069 testcase(
"Mint transferFee");
2071 using namespace test::jtx;
2073 Env env{*
this, features};
2075 Account
const alice{
"alice"};
2076 Account
const becky{
"becky"};
2077 Account
const carol{
"carol"};
2078 Account
const minter{
"minter"};
2079 Account
const gw{
"gw"};
2080 IOU
const gwXAU(gw[
"XAU"]);
2082 env.fund(XRP(1000), alice, becky, carol, minter, gw);
2085 env(trust(alice, gwXAU(2000)));
2086 env(trust(becky, gwXAU(2000)));
2087 env(trust(carol, gwXAU(2000)));
2088 env(trust(minter, gwXAU(2000)));
2090 env(pay(gw, alice, gwXAU(1000)));
2091 env(pay(gw, becky, gwXAU(1000)));
2092 env(pay(gw, carol, gwXAU(1000)));
2093 env(pay(gw, minter, gwXAU(1000)));
2098 env(token::setMinter(alice, minter));
2115 uint256 const beckyBuyOfferIndex =
2117 env(token::createOffer(becky, nftID, gwXAU(10)),
2118 token::owner(alice));
2120 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2121 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2123 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2125 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2126 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2129 uint256 const beckySellOfferIndex =
2131 env(token::createOffer(becky, nftID, gwXAU(10)),
2134 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2136 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2137 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2138 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2141 uint256 const minterBuyOfferIndex =
2143 env(token::createOffer(minter, nftID, gwXAU(10)),
2144 token::owner(carol));
2146 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2148 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2149 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2150 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2151 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2155 uint256 const minterSellOfferIndex =
2157 env(token::createOffer(minter, nftID, gwXAU(10)),
2160 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2162 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2163 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2164 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2165 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2168 env(token::burn(alice, nftID));
2181 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2185 uint256 const beckyBuyOfferIndex =
2187 env(token::createOffer(becky, nftID, gwXAU(10)),
2188 token::owner(alice));
2190 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2191 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2193 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2195 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2196 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2199 uint256 const beckySellOfferIndex =
2201 env(token::createOffer(becky, nftID, gwXAU(10)),
2204 env(token::acceptSellOffer(carol, beckySellOfferIndex));
2207 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0001));
2208 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2209 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2212 uint256 const minterBuyOfferIndex =
2214 env(token::createOffer(minter, nftID, gwXAU(10)),
2215 token::owner(carol));
2217 env(token::acceptBuyOffer(carol, minterBuyOfferIndex));
2220 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010.0002));
2221 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2222 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2223 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(990));
2227 uint256 const minterSellOfferIndex =
2229 env(token::createOffer(minter, nftID, gwXAU(10)),
2232 env(token::acceptSellOffer(alice, minterSellOfferIndex));
2234 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000.0002));
2235 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(999.9999));
2236 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(999.9999));
2237 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2241 env(pay(alice, becky, gwXAU(0.0001)));
2242 env(pay(alice, carol, gwXAU(0.0001)));
2245 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2246 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2247 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2248 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2251 env(token::burn(alice, nftID));
2262 env(token::mint(alice),
2269 uint256 const nftID = token::getNextID(
2271 env(token::mint(alice),
2277 uint256 const beckyBuyOfferIndex =
2279 env(token::createOffer(becky, nftID, gwXAU(10)),
2280 token::owner(alice));
2282 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2283 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2285 env(token::acceptBuyOffer(alice, beckyBuyOfferIndex));
2287 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1010));
2288 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(990));
2291 uint256 const beckySellOfferIndex =
2293 env(token::createOffer(becky, nftID, gwXAU(100)),
2296 env(token::acceptSellOffer(minter, beckySellOfferIndex));
2299 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1060));
2300 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2301 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(900));
2304 uint256 const carolBuyOfferIndex =
2306 env(token::createOffer(carol, nftID, gwXAU(10)),
2307 token::owner(minter));
2309 env(token::acceptBuyOffer(minter, carolBuyOfferIndex));
2312 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1065));
2313 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2314 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2315 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(990));
2319 uint256 const carolSellOfferIndex =
2321 env(token::createOffer(carol, nftID, gwXAU(10)),
2324 env(token::acceptSellOffer(alice, carolSellOfferIndex));
2327 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1055));
2328 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1040));
2329 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(905));
2330 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2333 env(pay(alice, minter, gwXAU(55)));
2334 env(pay(becky, minter, gwXAU(40)));
2336 BEAST_EXPECT(env.balance(alice, gwXAU) == gwXAU(1000));
2337 BEAST_EXPECT(env.balance(becky, gwXAU) == gwXAU(1000));
2338 BEAST_EXPECT(env.balance(carol, gwXAU) == gwXAU(1000));
2339 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1000));
2342 env(token::burn(alice, nftID));
2356 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2362 STAmount aliceBalance = env.balance(alice);
2363 STAmount minterBalance = env.balance(minter);
2364 uint256 const minterBuyOfferIndex =
2366 env(token::createOffer(minter, nftID, XRP(1)), token::owner(alice));
2368 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2370 aliceBalance += XRP(1) - fee;
2371 minterBalance -= XRP(1) + fee;
2372 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2373 BEAST_EXPECT(env.balance(minter) == minterBalance);
2377 STAmount carolBalance = env.balance(carol);
2378 uint256 const minterSellOfferIndex =
2380 env(token::createOffer(minter, nftID, drops(99999)),
2383 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2385 minterBalance += drops(99999) - fee;
2386 carolBalance -= drops(99999) + fee;
2387 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2388 BEAST_EXPECT(env.balance(minter) == minterBalance);
2389 BEAST_EXPECT(env.balance(carol) == carolBalance);
2393 STAmount beckyBalance = env.balance(becky);
2394 uint256 const beckyBuyOfferIndex =
2396 env(token::createOffer(becky, nftID, drops(100000)),
2397 token::owner(carol));
2399 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2401 carolBalance += drops(99999) - fee;
2402 beckyBalance -= drops(100000) + fee;
2403 aliceBalance += drops(1);
2405 BEAST_EXPECT(env.balance(alice) == aliceBalance);
2406 BEAST_EXPECT(env.balance(minter) == minterBalance);
2407 BEAST_EXPECT(env.balance(carol) == carolBalance);
2408 BEAST_EXPECT(env.balance(becky) == beckyBalance);
2417 env(token::mint(alice), txflags(
tfTransferable), token::xferFee(1));
2423 env(pay(alice, gw, env.balance(alice, gwXAU)));
2424 env(pay(minter, gw, env.balance(minter, gwXAU)));
2425 env(pay(becky, gw, env.balance(becky, gwXAU)));
2430 env(pay(gw, alice, startXAUBalance));
2431 env(pay(gw, minter, startXAUBalance));
2432 env(pay(gw, becky, startXAUBalance));
2441 STAmount aliceBalance = env.balance(alice, gwXAU);
2442 STAmount minterBalance = env.balance(minter, gwXAU);
2443 uint256 const minterBuyOfferIndex =
2445 env(token::createOffer(minter, nftID, tinyXAU),
2446 token::owner(alice));
2448 env(token::acceptBuyOffer(alice, minterBuyOfferIndex));
2450 aliceBalance += tinyXAU;
2451 minterBalance -= tinyXAU;
2452 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2453 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2456 STAmount carolBalance = env.balance(carol, gwXAU);
2457 uint256 const minterSellOfferIndex =
2459 env(token::createOffer(minter, nftID, tinyXAU),
2462 env(token::acceptSellOffer(carol, minterSellOfferIndex));
2465 minterBalance += tinyXAU;
2466 carolBalance -= tinyXAU;
2468 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2469 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2470 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2477 STAmount beckyBalance = env.balance(becky, gwXAU);
2478 uint256 const beckyBuyOfferIndex =
2480 env(token::createOffer(becky, nftID, cheapNFT),
2481 token::owner(carol));
2483 env(token::acceptBuyOffer(carol, beckyBuyOfferIndex));
2486 aliceBalance += tinyXAU;
2487 beckyBalance -= cheapNFT;
2488 carolBalance += cheapNFT - tinyXAU;
2489 BEAST_EXPECT(env.balance(alice, gwXAU) == aliceBalance);
2490 BEAST_EXPECT(env.balance(minter, gwXAU) == minterBalance);
2491 BEAST_EXPECT(env.balance(carol, gwXAU) == carolBalance);
2492 BEAST_EXPECT(env.balance(becky, gwXAU) == beckyBalance);
2500 testcase(
"Mint taxon");
2502 using namespace test::jtx;
2504 Env env{*
this, features};
2506 Account
const alice{
"alice"};
2507 Account
const becky{
"becky"};
2509 env.fund(XRP(1000), alice, becky);
2518 uint256 const nftID = token::getNextID(env, alice, 0u);
2525 uint256 const nftID = token::getNextID(env, alice, 0xFFFFFFFFu);
2533 for (
int i = 0; i < 10; ++i)
2543 ss <<
"Taxon recovery failed from nftID "
2544 <<
to_string(nftID) <<
". Expected: " << taxon
2545 <<
"; got: " << gotTaxon;
2550 uint256 const nftAliceID = token::getID(
2553 rand_int<std::uint32_t>(),
2554 rand_int<std::uint16_t>(),
2555 rand_int<std::uint16_t>());
2556 check(taxon, nftAliceID);
2558 uint256 const nftBeckyID = token::getID(
2561 rand_int<std::uint32_t>(),
2562 rand_int<std::uint16_t>(),
2563 rand_int<std::uint16_t>());
2564 check(taxon, nftBeckyID);
2576 testcase(
"Mint URI");
2578 using namespace test::jtx;
2580 Env env{*
this, features};
2582 Account
const alice{
"alice"};
2583 Account
const becky{
"becky"};
2585 env.fund(XRP(10000), alice, becky);
2591 auto randURI = []() {
2613 : uri(std::move(uri_)), taxon(taxon_)
2621 entries.
emplace_back(randURI(), rand_int<std::uint32_t>());
2624 for (Entry
const& entry : entries)
2626 if (entry.uri.empty())
2628 env(token::mint(alice, entry.taxon));
2632 env(token::mint(alice, entry.taxon), token::uri(entry.uri));
2640 params[jss::account] = alice.human();
2641 params[jss::type] =
"state";
2642 return env.rpc(
"json",
"account_nfts",
to_string(params));
2646 Json::Value& nfts = aliceNFTs[jss::result][jss::account_nfts];
2647 if (!BEAST_EXPECT(nfts.
size() == entries.size()))
2660 return lhs[jss::nft_serial] < rhs[jss::nft_serial];
2665 Entry
const& entry = entries[i];
2668 if (entry.uri.empty())
2683 testcase(
"Create offer destination");
2685 using namespace test::jtx;
2687 Env env{*
this, features};
2689 Account
const issuer{
"issuer"};
2690 Account
const minter{
"minter"};
2691 Account
const buyer{
"buyer"};
2692 Account
const broker{
"broker"};
2694 env.fund(XRP(1000), issuer, minter, buyer, broker);
2698 env(token::setMinter(issuer, minter));
2703 env(token::mint(minter, 0),
2704 token::issuer(issuer),
2711 uint256 const offerMinterToIssuer =
2713 env(token::createOffer(minter, nftokenID, drops(1)),
2714 token::destination(issuer),
2717 uint256 const offerMinterToBuyer =
2719 env(token::createOffer(minter, nftokenID, drops(1)),
2720 token::destination(buyer),
2723 uint256 const offerIssuerToMinter =
2725 env(token::createOffer(issuer, nftokenID, drops(1)),
2726 token::owner(minter),
2727 token::destination(minter));
2729 uint256 const offerIssuerToBuyer =
2731 env(token::createOffer(issuer, nftokenID, drops(1)),
2732 token::owner(minter),
2733 token::destination(buyer));
2747 env(token::cancelOffer(issuer, {offerMinterToBuyer}),
2749 env(token::cancelOffer(buyer, {offerMinterToIssuer}),
2751 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
2753 env(token::cancelOffer(minter, {offerIssuerToBuyer}),
2762 env(token::cancelOffer(buyer, {offerMinterToBuyer}));
2763 env(token::cancelOffer(minter, {offerMinterToIssuer}));
2764 env(token::cancelOffer(buyer, {offerIssuerToBuyer}));
2765 env(token::cancelOffer(issuer, {offerIssuerToMinter}));
2775 uint256 const offerMinterSellsToBuyer =
2777 env(token::createOffer(minter, nftokenID, drops(1)),
2778 token::destination(buyer),
2787 env(token::acceptSellOffer(issuer, offerMinterSellsToBuyer),
2795 env(token::acceptSellOffer(buyer, offerMinterSellsToBuyer));
2805 uint256 const offerMinterBuysFromBuyer =
2807 env(token::createOffer(minter, nftokenID, drops(1)),
2808 token::owner(buyer),
2809 token::destination(buyer));
2817 env(token::acceptBuyOffer(issuer, offerMinterBuysFromBuyer),
2825 env(token::acceptBuyOffer(buyer, offerMinterBuysFromBuyer));
2834 uint256 const offerBuyerBuysFromMinter =
2836 env(token::createOffer(buyer, nftokenID, drops(1)),
2837 token::owner(minter),
2838 token::destination(broker));
2844 env(token::acceptBuyOffer(minter, offerBuyerBuysFromMinter),
2849 env(token::cancelOffer(buyer, {offerBuyerBuysFromMinter}));
2859 uint256 const offerMinterToBroker =
2861 env(token::createOffer(minter, nftokenID, drops(1)),
2862 token::destination(broker),
2865 uint256 const offerBuyerToMinter =
2867 env(token::createOffer(buyer, nftokenID, drops(1)),
2868 token::owner(minter));
2877 env(token::brokerOffers(
2878 issuer, offerBuyerToMinter, offerMinterToBroker),
2887 env(token::brokerOffers(
2888 broker, offerBuyerToMinter, offerMinterToBroker));
2899 uint256 const offerBuyerToMinter =
2901 env(token::createOffer(buyer, nftokenID, drops(1)),
2902 token::destination(minter),
2905 uint256 const offerMinterToBuyer =
2907 env(token::createOffer(minter, nftokenID, drops(1)),
2908 token::owner(buyer));
2910 uint256 const offerIssuerToBuyer =
2912 env(token::createOffer(issuer, nftokenID, drops(1)),
2913 token::owner(buyer));
2921 env(token::brokerOffers(
2922 broker, offerIssuerToBuyer, offerBuyerToMinter),
2930 env(token::brokerOffers(
2931 broker, offerMinterToBuyer, offerBuyerToMinter));
2938 env(token::cancelOffer(issuer, {offerIssuerToBuyer}));
2948 uint256 const offerMinterToBroker =
2950 env(token::createOffer(minter, nftokenID, drops(1)),
2951 token::destination(broker),
2954 uint256 const offerBuyerToBroker =
2956 env(token::createOffer(buyer, nftokenID, drops(1)),
2957 token::owner(minter),
2958 token::destination(broker));
2962 env(token::brokerOffers(
2963 issuer, offerBuyerToBroker, offerMinterToBroker),
2971 env(token::brokerOffers(
2972 broker, offerBuyerToBroker, offerMinterToBroker));
2983 testcase(
"Create offer destination disallow incoming");
2985 using namespace test::jtx;
2990 Account
const alice{
"alice"};
2991 env.fund(XRP(10000), alice);
2994 auto const sle = env.le(alice);
2995 uint32_t flags = sle->getFlags();
3001 Account
const issuer{
"issuer"};
3002 Account
const minter{
"minter"};
3003 Account
const buyer{
"buyer"};
3004 Account
const alice{
"alice"};
3006 env.fund(XRP(1000), issuer, minter, buyer, alice);
3008 env(token::setMinter(issuer, minter));
3013 env(token::mint(minter, 0),
3014 token::issuer(issuer),
3024 env(token::createOffer(minter, nftokenID, drops(1)),
3025 token::destination(buyer),
3043 env(token::createOffer(minter, nftokenID, drops(1)),
3044 token::destination(buyer),
3048 env(token::cancelOffer(minter, {offerIndex}));
3057 env(token::createOffer(minter, nftokenID, drops(1)),
3058 token::destination(buyer),
3065 env(token::cancelOffer(minter, {offerIndex}));
3077 env(token::createOffer(minter, nftokenID, drops(1)),
3078 token::destination(buyer),
3082 env(token::acceptSellOffer(buyer, offerIndex));
3094 env(token::createOffer(alice, nftokenID, drops(1)),
3095 token::owner(buyer),
3102 env(token::createOffer(minter, nftokenID, drops(1)),
3103 token::owner(buyer),
3113 testcase(
"Create offer expiration");
3115 using namespace test::jtx;
3117 Env env{*
this, features};
3119 Account
const issuer{
"issuer"};
3120 Account
const minter{
"minter"};
3121 Account
const buyer{
"buyer"};
3123 env.fund(XRP(1000), issuer, minter, buyer);
3127 env(token::setMinter(issuer, minter));
3132 env(token::mint(minter, 0),
3133 token::issuer(issuer),
3139 env(token::mint(minter, 0),
3140 token::issuer(issuer),
3149 uint256 const offerMinterToIssuer =
3151 env(token::createOffer(minter, nftokenID0, drops(1)),
3152 token::destination(issuer),
3153 token::expiration(expiration),
3156 uint256 const offerMinterToAnyone =
3158 env(token::createOffer(minter, nftokenID0, drops(1)),
3159 token::expiration(expiration),
3162 uint256 const offerIssuerToMinter =
3164 env(token::createOffer(issuer, nftokenID0, drops(1)),
3165 token::owner(minter),
3166 token::expiration(expiration));
3168 uint256 const offerBuyerToMinter =
3170 env(token::createOffer(buyer, nftokenID0, drops(1)),
3171 token::owner(minter),
3172 token::expiration(expiration));
3184 env(token::cancelOffer(issuer, {offerMinterToAnyone}),
3186 env(token::cancelOffer(buyer, {offerIssuerToMinter}),
3189 BEAST_EXPECT(
lastClose(env) < expiration);
3195 env(token::cancelOffer(minter, {offerMinterToAnyone}));
3199 env(token::cancelOffer(issuer, {offerMinterToIssuer}));
3210 env(token::cancelOffer(issuer, {offerBuyerToMinter}));
3211 env(token::cancelOffer(buyer, {offerIssuerToMinter}));
3226 env(token::createOffer(minter, nftokenID0, drops(1)),
3227 token::expiration(expiration),
3232 env(token::createOffer(minter, nftokenID1, drops(1)),
3233 token::expiration(expiration),
3236 BEAST_EXPECT(
lastClose(env) < expiration);
3242 env(token::acceptSellOffer(buyer, offer0));
3253 env(token::acceptSellOffer(buyer, offer1), ter(
tecEXPIRED));
3254 env(token::acceptSellOffer(issuer, offer1), ter(
tecEXPIRED));
3263 env(token::cancelOffer(issuer, {offer1}));
3273 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3275 token::destination(minter));
3277 env(token::acceptSellOffer(minter, offerSellBack));
3291 env(token::createOffer(buyer, nftokenID0, drops(1)),
3292 token::owner(minter),
3293 token::expiration(expiration));
3296 env(token::createOffer(buyer, nftokenID1, drops(1)),
3297 token::owner(minter),
3298 token::expiration(expiration));
3300 BEAST_EXPECT(
lastClose(env) < expiration);
3306 env(token::acceptBuyOffer(minter, offer0));
3317 env(token::acceptBuyOffer(minter, offer1), ter(
tecEXPIRED));
3318 env(token::acceptBuyOffer(issuer, offer1), ter(
tecEXPIRED));
3327 env(token::cancelOffer(issuer, {offer1}));
3337 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3339 token::destination(minter));
3341 env(token::acceptSellOffer(minter, offerSellBack));
3356 env(token::createOffer(minter, nftokenID0, drops(1)),
3357 token::expiration(expiration),
3362 env(token::createOffer(minter, nftokenID1, drops(1)),
3363 token::expiration(expiration),
3368 env(token::createOffer(buyer, nftokenID0, drops(1)),
3369 token::owner(minter));
3373 env(token::createOffer(buyer, nftokenID1, drops(1)),
3374 token::owner(minter));
3377 BEAST_EXPECT(
lastClose(env) < expiration);
3383 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3394 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3404 env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
3414 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3416 token::destination(minter));
3418 env(token::acceptSellOffer(minter, offerSellBack));
3433 env(token::createOffer(minter, nftokenID0, drops(1)),
3438 env(token::createOffer(minter, nftokenID1, drops(1)),
3443 env(token::createOffer(buyer, nftokenID0, drops(1)),
3444 token::expiration(expiration),
3445 token::owner(minter));
3449 env(token::createOffer(buyer, nftokenID1, drops(1)),
3450 token::expiration(expiration),
3451 token::owner(minter));
3454 BEAST_EXPECT(
lastClose(env) < expiration);
3460 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3471 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3481 env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
3491 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3493 token::destination(minter));
3495 env(token::acceptSellOffer(minter, offerSellBack));
3511 env(token::createOffer(minter, nftokenID0, drops(1)),
3512 token::expiration(expiration),
3517 env(token::createOffer(minter, nftokenID1, drops(1)),
3518 token::expiration(expiration),
3523 env(token::createOffer(buyer, nftokenID0, drops(1)),
3524 token::expiration(expiration),
3525 token::owner(minter));
3529 env(token::createOffer(buyer, nftokenID1, drops(1)),
3530 token::expiration(expiration),
3531 token::owner(minter));
3534 BEAST_EXPECT(
lastClose(env) < expiration);
3540 env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
3551 env(token::brokerOffers(issuer, buyOffer1, sellOffer1),
3561 env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
3571 env(token::createOffer(buyer, nftokenID0, XRP(0)),
3573 token::destination(minter));
3575 env(token::acceptSellOffer(minter, offerSellBack));
3587 testcase(
"Cancel offers");
3589 using namespace test::jtx;
3591 Env env{*
this, features};
3593 Account
const alice(
"alice");
3594 Account
const becky(
"becky");
3595 Account
const minter(
"minter");
3596 env.fund(XRP(50000), alice, becky, minter);
3600 env(token::setMinter(alice, minter));
3609 uint256 const expiredOfferIndex =
3612 env(token::createOffer(alice, nftokenID, XRP(1000)),
3614 token::expiration(
lastClose(env) + 13));
3619 env(token::cancelOffer(becky, {expiredOfferIndex}),
3627 env(token::cancelOffer(becky, {expiredOfferIndex}));
3633 uint256 const dest1OfferIndex =
3636 env(token::createOffer(alice, nftokenID, XRP(1000)),
3637 token::destination(becky),
3643 env(token::cancelOffer(minter, {dest1OfferIndex}),
3648 env(token::cancelOffer(becky, {dest1OfferIndex}));
3653 uint256 const dest2OfferIndex =
3656 env(token::createOffer(alice, nftokenID, XRP(1000)),
3657 token::destination(becky),
3662 env(token::cancelOffer(alice, {dest2OfferIndex}));
3669 uint256 const mintersNFTokenID =
3671 env(token::mint(minter, 0),
3672 token::issuer(alice),
3676 uint256 const minterOfferIndex =
3679 env(token::createOffer(minter, mintersNFTokenID, XRP(1000)),
3685 env(token::cancelOffer(alice, {minterOfferIndex}),
3687 env(token::cancelOffer(becky, {minterOfferIndex}),
3692 env(token::cancelOffer(minter, {minterOfferIndex}));
3701 testcase(
"Cancel too many offers");
3703 using namespace test::jtx;
3705 Env env{*
this, features};
3720 Account
const alice(
"alice");
3721 env.fund(XRP(1000), alice);
3730 Account
const offerAcct(
3732 env.fund(XRP(1000), nftAcct, offerAcct);
3737 env(token::mint(nftAcct, 0),
3744 env(token::createOffer(offerAcct, nftokenID, drops(1)),
3745 token::owner(nftAcct),
3754 for (
uint256 const& offerIndex : offerIndexes)
3761 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3765 env(token::cancelOffer(alice, {offerIndexes.back()}));
3770 offerIndexes.pop_back();
3776 env(token::mint(alice, 0),
3782 env(token::createOffer(alice, nftokenID, drops(1)),
3791 env(token::cancelOffer(alice, offerIndexes), ter(
temMALFORMED));
3795 env(token::burn(alice, nftokenID));
3801 offerIndexes.pop_back();
3806 env(token::cancelOffer(alice, offerIndexes));
3810 for (
uint256 const& offerIndex : offerIndexes)
3820 testcase(
"Brokered NFT offer accept");
3822 using namespace test::jtx;
3824 Env env{*
this, features};
3832 Account
const issuer{
"issuer"};
3833 Account
const minter{
"minter"};
3834 Account
const buyer{
"buyer"};
3835 Account
const broker{
"broker"};
3836 Account
const gw{
"gw"};
3837 IOU
const gwXAU(gw[
"XAU"]);
3839 env.fund(XRP(1000), issuer, minter, buyer, broker, gw);
3842 env(trust(issuer, gwXAU(2000)));
3843 env(trust(minter, gwXAU(2000)));
3844 env(trust(buyer, gwXAU(2000)));
3845 env(trust(broker, gwXAU(2000)));
3848 env(token::setMinter(issuer, minter));
3852 auto checkOwnerCountIsOne =
3857 for (Account
const& acct : accounts)
3863 ss <<
"Account " << acct.human()
3864 <<
" expected ownerCount == 1. Got " <<
ownerCount;
3865 fail(ss.
str(), __FILE__, line);
3871 auto mintNFT = [&env, &issuer, &minter](
std::uint16_t xferFee = 0) {
3874 env(token::mint(minter, 0),
3875 token::issuer(issuer),
3876 token::xferFee(xferFee),
3888 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3890 uint256 const nftID = mintNFT();
3893 uint256 const minterOfferIndex =
3895 env(token::createOffer(minter, nftID, XRP(0)),
3903 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3906 auto const minterBalance = env.balance(minter);
3907 auto const buyerBalance = env.balance(buyer);
3908 auto const brokerBalance = env.balance(broker);
3909 auto const issuerBalance = env.balance(issuer);
3912 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
3917 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(1));
3918 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3919 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
3920 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3923 env(token::burn(buyer, nftID));
3933 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3935 uint256 const nftID = mintNFT();
3938 uint256 const minterOfferIndex =
3940 env(token::createOffer(minter, nftID, XRP(0)),
3948 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
3952 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3953 token::brokerFee(XRP(1.1)),
3957 auto const minterBalance = env.balance(minter);
3958 auto const buyerBalance = env.balance(buyer);
3959 auto const brokerBalance = env.balance(broker);
3960 auto const issuerBalance = env.balance(issuer);
3963 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
3964 token::brokerFee(XRP(0.5)));
3969 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
3970 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
3972 env.balance(broker) == brokerBalance + XRP(0.5) - drops(10));
3973 BEAST_EXPECT(env.balance(issuer) == issuerBalance);
3976 env(token::burn(buyer, nftID));
3986 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
3991 uint256 const minterOfferIndex =
3993 env(token::createOffer(minter, nftID, XRP(0)),
4001 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
4004 auto const minterBalance = env.balance(minter);
4005 auto const buyerBalance = env.balance(buyer);
4006 auto const brokerBalance = env.balance(broker);
4007 auto const issuerBalance = env.balance(issuer);
4010 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
4015 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.5));
4016 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4017 BEAST_EXPECT(env.balance(broker) == brokerBalance - drops(10));
4018 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.5));
4021 env(token::burn(buyer, nftID));
4031 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4036 uint256 const minterOfferIndex =
4038 env(token::createOffer(minter, nftID, XRP(0)),
4046 env(token::createOffer(buyer, nftID, XRP(1)), token::owner(minter));
4049 auto const minterBalance = env.balance(minter);
4050 auto const buyerBalance = env.balance(buyer);
4051 auto const brokerBalance = env.balance(broker);
4052 auto const issuerBalance = env.balance(issuer);
4055 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4056 token::brokerFee(XRP(0.75)));
4062 BEAST_EXPECT(env.balance(minter) == minterBalance + XRP(0.125));
4063 BEAST_EXPECT(env.balance(buyer) == buyerBalance - XRP(1));
4065 env.balance(broker) == brokerBalance + XRP(0.75) - drops(10));
4066 BEAST_EXPECT(env.balance(issuer) == issuerBalance + XRP(0.125));
4069 env(token::burn(buyer, nftID));
4074 auto setXAUBalance_1000 =
4075 [
this, &gw, &gwXAU, &env](
4079 for (Account
const& acct : accounts)
4081 static const auto xau1000 = gwXAU(1000);
4082 auto const balance = env.balance(acct, gwXAU);
4083 if (balance < xau1000)
4085 env(pay(gw, acct, xau1000 - balance));
4088 else if (balance > xau1000)
4090 env(pay(acct, gw, balance - xau1000));
4093 if (env.balance(acct, gwXAU) != xau1000)
4096 ss <<
"Unable to set " << acct.human()
4097 <<
" account balance to gwXAU(1000)";
4098 this->fail(ss.
str(), __FILE__, line);
4106 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4107 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4109 uint256 const nftID = mintNFT();
4112 uint256 const minterOfferIndex =
4114 env(token::createOffer(minter, nftID, gwXAU(1000)),
4122 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4123 token::owner(minter));
4127 env(token::brokerOffers(
4128 broker, buyOfferIndex, minterOfferIndex),
4134 env(token::cancelOffer(buyer, {buyOfferIndex}));
4141 env(token::createOffer(buyer, nftID, gwXAU(999)),
4142 token::owner(minter));
4146 env(token::brokerOffers(
4147 broker, buyOfferIndex, minterOfferIndex),
4153 env(token::cancelOffer(buyer, {buyOfferIndex}));
4160 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4161 token::owner(minter));
4165 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4166 token::brokerFee(gwXAU(0.1)),
4171 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex));
4178 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1000));
4179 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(2000));
4180 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4181 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1000));
4184 env(token::burn(buyer, nftID));
4191 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4192 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4197 uint256 const minterOfferIndex =
4199 env(token::createOffer(minter, nftID, gwXAU(900)),
4206 env(token::createOffer(buyer, nftID, gwXAU(1001)),
4207 token::owner(minter));
4211 env(token::brokerOffers(
4212 broker, buyOfferIndex, minterOfferIndex),
4218 env(token::cancelOffer(buyer, {buyOfferIndex}));
4225 env(token::createOffer(buyer, nftID, gwXAU(899)),
4226 token::owner(minter));
4230 env(token::brokerOffers(
4231 broker, buyOfferIndex, minterOfferIndex),
4237 env(token::cancelOffer(buyer, {buyOfferIndex}));
4243 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4244 token::owner(minter));
4249 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4250 token::brokerFee(gwXAU(101)),
4256 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4257 token::brokerFee(gwXAU(100)));
4264 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1450));
4265 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1450));
4266 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4267 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1100));
4270 env(token::burn(buyer, nftID));
4277 checkOwnerCountIsOne({issuer, minter, buyer, broker}, __LINE__);
4278 setXAUBalance_1000({issuer, minter, buyer, broker}, __LINE__);
4283 uint256 const minterOfferIndex =
4285 env(token::createOffer(minter, nftID, gwXAU(900)),
4292 env(token::createOffer(buyer, nftID, gwXAU(1000)),
4293 token::owner(minter));
4299 env(token::brokerOffers(broker, buyOfferIndex, minterOfferIndex),
4300 token::brokerFee(gwXAU(50)));
4307 BEAST_EXPECT(env.balance(issuer, gwXAU) == gwXAU(1237.5));
4308 BEAST_EXPECT(env.balance(minter, gwXAU) == gwXAU(1712.5));
4309 BEAST_EXPECT(env.balance(buyer, gwXAU) == gwXAU(0));
4310 BEAST_EXPECT(env.balance(broker, gwXAU) == gwXAU(1050));
4313 env(token::burn(buyer, nftID));
4322 testcase(
"NFToken offer owner");
4324 using namespace test::jtx;
4326 Env env{*
this, features};
4328 Account
const issuer{
"issuer"};
4329 Account
const buyer1{
"buyer1"};
4330 Account
const buyer2{
"buyer2"};
4331 env.fund(XRP(10000), issuer, buyer1, buyer2);
4340 BEAST_EXPECT(
nftCount(env, issuer) == 1);
4341 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4342 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4345 uint256 const buyer1OfferIndex =
4347 env(token::createOffer(buyer1, nftId, XRP(100)), token::owner(issuer));
4348 uint256 const buyer2OfferIndex =
4350 env(token::createOffer(buyer2, nftId, XRP(100)), token::owner(issuer));
4360 env.rpc(
"json",
"nft_buy_offers",
to_string(params));
4362 if (buyOffers.
isMember(jss::result) &&
4363 buyOffers[jss::result].
isMember(jss::offers))
4364 return buyOffers[jss::result][jss::offers].
size();
4370 BEAST_EXPECT(nftBuyOfferCount(nftId) == 2);
4373 env(token::acceptBuyOffer(issuer, buyer1OfferIndex));
4377 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4378 BEAST_EXPECT(
nftCount(env, buyer1) == 1);
4379 BEAST_EXPECT(
nftCount(env, buyer2) == 0);
4383 BEAST_EXPECT(nftBuyOfferCount(nftId) == 1);
4387 env(token::acceptBuyOffer(buyer1, buyer2OfferIndex));
4391 BEAST_EXPECT(
nftCount(env, issuer) == 0);
4392 BEAST_EXPECT(
nftCount(env, buyer1) == 0);
4393 BEAST_EXPECT(
nftCount(env, buyer2) == 1);
4396 BEAST_EXPECT(nftBuyOfferCount(nftId) == 0);
4403 testcase(
"NFToken transactions with tickets");
4405 using namespace test::jtx;
4407 Env env{*
this, features};
4409 Account
const issuer{
"issuer"};
4410 Account
const buyer{
"buyer"};
4411 env.fund(XRP(10000), issuer, buyer);
4418 env(ticket::create(issuer, 10));
4424 env(ticket::create(buyer, 10));
4432 env(token::mint(issuer, 0u),
4434 ticket::use(issuerTicketSeq++));
4442 env(token::createOffer(buyer, nftId, XRP(1)),
4443 token::owner(issuer),
4444 ticket::use(buyerTicketSeq++));
4450 env(token::cancelOffer(buyer, {offerIndex0}),
4451 ticket::use(buyerTicketSeq++));
4458 env(token::createOffer(buyer, nftId, XRP(2)),
4459 token::owner(issuer),
4460 ticket::use(buyerTicketSeq++));
4466 env(token::acceptBuyOffer(issuer, offerIndex1),
4467 ticket::use(issuerTicketSeq++));
4474 env(token::burn(buyer, nftId), ticket::use(buyerTicketSeq++));
4481 BEAST_EXPECT(env.seq(issuer) == issuerSeq);
4482 BEAST_EXPECT(env.seq(buyer) == buyerSeq);
4493 testcase(
"NFToken delete account");
4495 using namespace test::jtx;
4497 Env env{*
this, features};
4499 Account
const issuer{
"issuer"};
4500 Account
const minter{
"minter"};
4501 Account
const becky{
"becky"};
4502 Account
const carla{
"carla"};
4503 Account
const daria{
"daria"};
4505 env.fund(XRP(10000), issuer, minter, becky, carla, daria);
4509 for (
int i = 0; i < 300; ++i)
4512 env(token::setMinter(issuer, minter));
4516 env(token::mint(minter, 0u),
4517 token::issuer(issuer),
4530 for (
int i = 0; i < 15; ++i)
4534 env(token::createOffer(becky, nftId, XRP(2)), token::owner(minter));
4537 uint256 const carlaOfferIndex =
4539 env(token::createOffer(carla, nftId, XRP(3)), token::owner(minter));
4544 env(acctdelete(becky, daria), fee(XRP(50)));
4548 env(token::acceptBuyOffer(minter, carlaOfferIndex));
4553 env(acctdelete(minter, daria), fee(XRP(50)));
4565 for (
int i = 0; i < 15; ++i)
4570 env(token::burn(carla, nftId));
4573 env(acctdelete(issuer, daria), fee(XRP(50)));
4574 env(acctdelete(carla, daria), fee(XRP(50)));
4581 testcase(
"nft_buy_offers and nft_sell_offers");
4590 using namespace test::jtx;
4592 Env env{*
this, features};
4594 Account
const issuer{
"issuer"};
4595 Account
const buyer{
"buyer"};
4598 env.fund(XRP(1000000), issuer, buyer);
4607 auto checkOffers = [
this, &env, &nftID](
4608 char const* request,
4610 int expectMarkerCount,
4612 int markerCount = 0;
4619 Json::Value nftOffers = [&env, &nftID, &request, &marker]() {
4623 if (!marker.
empty())
4624 params[jss::marker] = marker;
4625 return env.rpc(
"json", request,
to_string(params));
4629 if (expectCount == 0)
4633 "expected \"result\"",
4638 nftOffers[jss::result].isMember(jss::error),
4639 "expected \"error\"",
4644 nftOffers[jss::result][jss::error].asString() ==
4646 "expected \"objectNotFound\"",
4657 "expected \"result\"",
4666 marker = result[jss::marker].
asString();
4671 "expected \"offers\"",
4677 allOffers.
append(someOffers[i]);
4680 }
while (!marker.
empty());
4684 allOffers.
size() == expectCount,
4685 "Unexpected returned offer count",
4689 markerCount == expectMarkerCount,
4690 "Unexpected marker count",
4700 globalFlags = offer[jss::flags].asInt();
4703 *globalFlags == offer[jss::flags].asInt(),
4704 "Inconsistent flags returned",
4710 offerIndexes.
insert(offer[jss::nft_offer_index].asString());
4711 amounts.
insert(offer[jss::amount].asString());
4715 offerIndexes.
size() == expectCount,
4716 "Duplicate indexes returned?",
4720 amounts.
size() == expectCount,
4721 "Duplicate amounts returned?",
4727 checkOffers(
"nft_sell_offers", 0,
false, __LINE__);
4731 auto makeSellOffers =
4732 [&env, &issuer, &nftID, &sellPrice](
STAmount const& limit) {
4735 while (sellPrice < limit)
4737 sellPrice += XRP(1);
4738 env(token::createOffer(issuer, nftID, sellPrice),
4740 if (++offerCount % 10 == 0)
4747 makeSellOffers(XRP(1));
4748 checkOffers(
"nft_sell_offers", 1, 0, __LINE__);
4751 makeSellOffers(XRP(250));
4752 checkOffers(
"nft_sell_offers", 250, 0, __LINE__);
4755 makeSellOffers(XRP(251));
4756 checkOffers(
"nft_sell_offers", 251, 1, __LINE__);
4759 makeSellOffers(XRP(500));
4760 checkOffers(
"nft_sell_offers", 500, 1, __LINE__);
4763 makeSellOffers(XRP(501));
4764 checkOffers(
"nft_sell_offers", 501, 2, __LINE__);
4767 checkOffers(
"nft_buy_offers", 0, 0, __LINE__);
4771 auto makeBuyOffers =
4772 [&env, &buyer, &issuer, &nftID, &buyPrice](
STAmount const& limit) {
4775 while (buyPrice < limit)
4778 env(token::createOffer(buyer, nftID, buyPrice),
4779 token::owner(issuer));
4780 if (++offerCount % 10 == 0)
4787 makeBuyOffers(XRP(1));
4788 checkOffers(
"nft_buy_offers", 1, 0, __LINE__);
4791 makeBuyOffers(XRP(250));
4792 checkOffers(
"nft_buy_offers", 250, 0, __LINE__);
4795 makeBuyOffers(XRP(251));
4796 checkOffers(
"nft_buy_offers", 251, 1, __LINE__);
4799 makeBuyOffers(XRP(500));
4800 checkOffers(
"nft_buy_offers", 500, 1, __LINE__);
4803 makeBuyOffers(XRP(501));
4804 checkOffers(
"nft_buy_offers", 501, 2, __LINE__);
4811 using namespace test::jtx;
4813 testcase(
"fixNFTokenNegOffer");
4815 Account
const issuer{
"issuer"};
4816 Account
const buyer{
"buyer"};
4817 Account
const gw{
"gw"};
4818 IOU
const gwXAU(gw[
"XAU"]);
4821 for (
auto const& tweakedFeatures :
4828 Env env{*
this, tweakedFeatures};
4830 env.fund(XRP(1000000), issuer, buyer, gw);
4833 env(trust(issuer, gwXAU(2000)));
4834 env(trust(buyer, gwXAU(2000)));
4837 env(pay(gw, issuer, gwXAU(1000)));
4838 env(pay(gw, buyer, gwXAU(1000)));
4858 uint256 const sellNegXrpOfferIndex =
4860 env(token::createOffer(issuer, nftID0, XRP(-2)),
4862 ter(offerCreateTER));
4865 uint256 const sellNegIouOfferIndex =
4867 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4869 ter(offerCreateTER));
4872 uint256 const buyNegXrpOfferIndex =
4874 env(token::createOffer(buyer, nftID0, XRP(-1)),
4875 token::owner(issuer),
4876 ter(offerCreateTER));
4879 uint256 const buyNegIouOfferIndex =
4881 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4882 token::owner(issuer),
4883 ter(offerCreateTER));
4895 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4896 ter(offerAcceptTER));
4898 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
4899 ter(offerAcceptTER));
4903 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
4904 ter(offerAcceptTER));
4906 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
4907 ter(offerAcceptTER));
4918 env(token::brokerOffers(
4919 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
4920 ter(offerAcceptTER));
4922 env(token::brokerOffers(
4923 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
4924 ter(offerAcceptTER));
4936 env.fund(XRP(1000000), issuer, buyer, gw);
4939 env(trust(issuer, gwXAU(2000)));
4940 env(trust(buyer, gwXAU(2000)));
4943 env(pay(gw, issuer, gwXAU(1000)));
4944 env(pay(gw, buyer, gwXAU(1000)));
4960 uint256 const sellNegXrpOfferIndex =
4962 env(token::createOffer(issuer, nftID0, XRP(-2)),
4966 uint256 const sellNegIouOfferIndex =
4968 env(token::createOffer(issuer, nftID1, gwXAU(-2)),
4972 uint256 const buyNegXrpOfferIndex =
4974 env(token::createOffer(buyer, nftID0, XRP(-1)),
4975 token::owner(issuer));
4978 uint256 const buyNegIouOfferIndex =
4980 env(token::createOffer(buyer, nftID1, gwXAU(-1)),
4981 token::owner(issuer));
4990 env(token::acceptSellOffer(buyer, sellNegXrpOfferIndex),
4993 env(token::acceptSellOffer(buyer, sellNegIouOfferIndex),
4998 env(token::acceptBuyOffer(issuer, buyNegXrpOfferIndex),
5001 env(token::acceptBuyOffer(issuer, buyNegIouOfferIndex),
5006 env(token::brokerOffers(
5007 gw, buyNegXrpOfferIndex, sellNegXrpOfferIndex),
5010 env(token::brokerOffers(
5011 gw, buyNegIouOfferIndex, sellNegIouOfferIndex),
5018 for (
auto const& tweakedFeatures :
5022 Env env{*
this, tweakedFeatures};
5024 env.fund(XRP(1000000), issuer, buyer);
5036 env(token::createOffer(buyer, nftID, drops(1)),
5037 token::owner(issuer),
5038 token::destination(issuer),
5039 ter(offerCreateTER));
5079 using namespace test::jtx;